diff --git a/TEST_MAPPING b/TEST_MAPPING index d6945e380..0ec505da8 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -3,6 +3,12 @@ { "name": "adbd_test" }, + { + "name": "adb_crypto_test" + }, + { + "name": "adb_tls_connection_test" + }, { "name": "CtsInitTestCases" }, diff --git a/adb/Android.bp b/adb/Android.bp index 675525cc1..c71138af1 100644 --- a/adb/Android.bp +++ b/adb/Android.bp @@ -255,6 +255,8 @@ cc_library_host_static { }, static_libs: [ + "libadb_crypto", + "libadb_protos", "libbase", "libcrypto_utils", "libcrypto", @@ -272,6 +274,7 @@ cc_test_host { defaults: ["adb_defaults"], srcs: libadb_test_srcs, static_libs: [ + "libadb_crypto", "libadb_host", "libbase", "libcutils", @@ -347,6 +350,7 @@ cc_binary_host { ], static_libs: [ + "libadb_crypto", "libadb_host", "libandroidfw", "libbase", @@ -422,6 +426,7 @@ cc_library_static { ], shared_libs: [ + "libadb_crypto", "libadbd_auth", "libasyncio", "libbase", @@ -765,6 +770,7 @@ cc_test_host { "fastdeploy/deploypatchgenerator/patch_utils_test.cpp", ], static_libs: [ + "libadb_crypto", "libadb_host", "libandroidfw", "libbase", diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp index e8be784cd..dcf4bc0ad 100644 --- a/adb/client/auth.cpp +++ b/adb/client/auth.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -53,100 +54,50 @@ static std::map>& g_keys = *new std::map>; static std::map& g_monitored_paths = *new std::map; -static std::string get_user_info() { - std::string hostname; - if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME"); -#if !defined(_WIN32) - char buf[64]; - if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf; -#endif - if (hostname.empty()) hostname = "unknown"; +using namespace adb::crypto; - std::string username; - if (getenv("LOGNAME")) username = getenv("LOGNAME"); -#if !defined(_WIN32) - if (username.empty() && getlogin()) username = getlogin(); -#endif - if (username.empty()) hostname = "unknown"; - - return " " + username + "@" + hostname; -} - -static bool calculate_public_key(std::string* out, RSA* private_key) { - uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE]; - if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) { - LOG(ERROR) << "Failed to convert to public key"; - return false; - } - - size_t expected_length; - if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) { - LOG(ERROR) << "Public key too large to base64 encode"; - return false; - } - - out->resize(expected_length); - size_t actual_length = EVP_EncodeBlock(reinterpret_cast(out->data()), binary_key_data, - sizeof(binary_key_data)); - out->resize(actual_length); - out->append(get_user_info()); - return true; -} - -static int generate_key(const std::string& file) { +static bool generate_key(const std::string& file) { LOG(INFO) << "generate_key(" << file << ")..."; - mode_t old_mask; - FILE *f = nullptr; - int ret = 0; + auto rsa_2048 = CreateRSA2048Key(); + if (!rsa_2048) { + LOG(ERROR) << "Unable to create key"; + return false; + } std::string pubkey; - EVP_PKEY* pkey = EVP_PKEY_new(); - BIGNUM* exponent = BN_new(); - RSA* rsa = RSA_new(); - if (!pkey || !exponent || !rsa) { - LOG(ERROR) << "Failed to allocate key"; - goto out; - } + RSA* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey()); + CHECK(rsa); - BN_set_word(exponent, RSA_F4); - RSA_generate_key_ex(rsa, 2048, exponent, nullptr); - EVP_PKEY_set1_RSA(pkey, rsa); - - if (!calculate_public_key(&pubkey, rsa)) { + if (!CalculatePublicKey(&pubkey, rsa)) { LOG(ERROR) << "failed to calculate public key"; - goto out; + return false; } - old_mask = umask(077); + mode_t old_mask = umask(077); - f = fopen(file.c_str(), "w"); + std::unique_ptr f(nullptr, &fclose); + f.reset(fopen(file.c_str(), "w")); if (!f) { PLOG(ERROR) << "Failed to open " << file; umask(old_mask); - goto out; + return false; } umask(old_mask); - if (!PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr)) { + if (!PEM_write_PrivateKey(f.get(), rsa_2048->GetEvpPkey(), nullptr, nullptr, 0, nullptr, + nullptr)) { LOG(ERROR) << "Failed to write key"; - goto out; + return false; } if (!android::base::WriteStringToFile(pubkey, file + ".pub")) { PLOG(ERROR) << "failed to write public key"; - goto out; + return false; } - ret = 1; - -out: - if (f) fclose(f); - EVP_PKEY_free(pkey); - RSA_free(rsa); - BN_free(exponent); - return ret; + return true; } static std::string hash_key(RSA* key) { @@ -325,7 +276,7 @@ static bool pubkey_from_privkey(std::string* out, const std::string& path) { if (!privkey) { return false; } - return calculate_public_key(out, privkey.get()); + return CalculatePublicKey(out, privkey.get()); } std::string adb_auth_get_userkey() { @@ -343,7 +294,7 @@ std::string adb_auth_get_userkey() { } int adb_auth_keygen(const char* filename) { - return (generate_key(filename) == 0); + return !generate_key(filename); } int adb_auth_pubkey(const char* filename) { diff --git a/adb/crypto/Android.bp b/adb/crypto/Android.bp new file mode 100644 index 000000000..da4869a9a --- /dev/null +++ b/adb/crypto/Android.bp @@ -0,0 +1,85 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_defaults { + name: "libadb_crypto_defaults", + cflags: [ + "-Wall", + "-Wextra", + "-Wthread-safety", + "-Werror", + ], + + compile_multilib: "both", + + srcs: [ + "key.cpp", + "rsa_2048_key.cpp", + "x509_generator.cpp", + ], + + target: { + windows: { + compile_multilib: "first", + enabled: true, + }, + }, + + export_include_dirs: ["include"], + + visibility: [ + "//system/core/adb:__subpackages__", + ], + + host_supported: true, + recovery_available: true, + + stl: "libc++_static", + + shared_libs: [ + "libadb_protos", + "libbase", + "liblog", + "libcrypto", + "libcrypto_utils", + ], +} + +cc_library { + name: "libadb_crypto", + defaults: ["libadb_crypto_defaults"], + + apex_available: [ + "com.android.adbd", + "test_com.android.adbd", + ], + + static_libs: [ + "libadb_protos", + ], +} + +// For running atest (b/147158681) +cc_library_static { + name: "libadb_crypto_static", + defaults: ["libadb_crypto_defaults"], + + apex_available: [ + "//apex_available:platform", + ], + + static_libs: [ + "libadb_protos_static", + ], +} diff --git a/adb/crypto/include/adb/crypto/key.h b/adb/crypto/include/adb/crypto/key.h new file mode 100644 index 000000000..d9ce69e03 --- /dev/null +++ b/adb/crypto/include/adb/crypto/key.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +#include "key_type.pb.h" + +namespace adb { +namespace crypto { + +// Class that represents a public/private key pair. +class Key { + public: + explicit Key(bssl::UniquePtr&& pkey, adb::proto::KeyType type) + : pkey_(std::move(pkey)), key_type_(type) {} + Key(Key&&) = default; + Key& operator=(Key&&) = default; + + EVP_PKEY* GetEvpPkey() const { return pkey_.get(); } + adb::proto::KeyType GetKeyType() const { return key_type_; } + static std::string ToPEMString(EVP_PKEY* pkey); + + private: + bssl::UniquePtr pkey_; + adb::proto::KeyType key_type_; +}; // Key + +} // namespace crypto +} // namespace adb diff --git a/adb/crypto/include/adb/crypto/rsa_2048_key.h b/adb/crypto/include/adb/crypto/rsa_2048_key.h new file mode 100644 index 000000000..2983a84c4 --- /dev/null +++ b/adb/crypto/include/adb/crypto/rsa_2048_key.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "adb/crypto/key.h" + +namespace adb { +namespace crypto { + +// Create a new RSA2048 key pair. +std::optional CreateRSA2048Key(); + +// Generates the public key from the RSA private key. +bool CalculatePublicKey(std::string* out, RSA* private_key); + +} // namespace crypto +} // namespace adb diff --git a/adb/crypto/include/adb/crypto/x509_generator.h b/adb/crypto/include/adb/crypto/x509_generator.h new file mode 100644 index 000000000..a26924312 --- /dev/null +++ b/adb/crypto/include/adb/crypto/x509_generator.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace adb { +namespace crypto { + +// Generate a X.509 certificate based on the key |pkey|. +bssl::UniquePtr GenerateX509Certificate(EVP_PKEY* pkey); + +// Convert X509* to PEM string format +std::string X509ToPEMString(X509* x509); + +} // namespace crypto +} // namespace adb diff --git a/adb/crypto/key.cpp b/adb/crypto/key.cpp new file mode 100644 index 000000000..4d870069c --- /dev/null +++ b/adb/crypto/key.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "adb/crypto/key.h" + +#include +#include +#include +#include + +namespace adb { +namespace crypto { + +// static +std::string Key::ToPEMString(EVP_PKEY* pkey) { + bssl::UniquePtr bio(BIO_new(BIO_s_mem())); + int rc = PEM_write_bio_PKCS8PrivateKey(bio.get(), pkey, nullptr, nullptr, 0, nullptr, nullptr); + if (rc != 1) { + LOG(ERROR) << "PEM_write_bio_PKCS8PrivateKey failed"; + return ""; + } + + BUF_MEM* mem = nullptr; + BIO_get_mem_ptr(bio.get(), &mem); + if (!mem || !mem->data || !mem->length) { + LOG(ERROR) << "BIO_get_mem_ptr failed"; + return ""; + } + + return std::string(mem->data, mem->length); +} + +} // namespace crypto +} // namespace adb diff --git a/adb/crypto/rsa_2048_key.cpp b/adb/crypto/rsa_2048_key.cpp new file mode 100644 index 000000000..7911af95f --- /dev/null +++ b/adb/crypto/rsa_2048_key.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "adb/crypto/rsa_2048_key.h" + +#include +#include +#include +#include + +namespace adb { +namespace crypto { + +namespace { +std::string get_user_info() { + std::string hostname; + if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME"); +#if !defined(_WIN32) + char buf[64]; + if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf; +#endif + if (hostname.empty()) hostname = "unknown"; + + std::string username; + if (getenv("LOGNAME")) username = getenv("LOGNAME"); +#if !defined(_WIN32) + if (username.empty() && getlogin()) username = getlogin(); +#endif + if (username.empty()) hostname = "unknown"; + + return " " + username + "@" + hostname; +} + +} // namespace + +bool CalculatePublicKey(std::string* out, RSA* private_key) { + uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE]; + if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) { + LOG(ERROR) << "Failed to convert to public key"; + return false; + } + + size_t expected_length; + if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) { + LOG(ERROR) << "Public key too large to base64 encode"; + return false; + } + + out->resize(expected_length); + size_t actual_length = EVP_EncodeBlock(reinterpret_cast(out->data()), binary_key_data, + sizeof(binary_key_data)); + out->resize(actual_length); + out->append(get_user_info()); + return true; +} + +std::optional CreateRSA2048Key() { + bssl::UniquePtr pkey(EVP_PKEY_new()); + bssl::UniquePtr exponent(BN_new()); + bssl::UniquePtr rsa(RSA_new()); + if (!pkey || !exponent || !rsa) { + LOG(ERROR) << "Failed to allocate key"; + return std::nullopt; + } + + BN_set_word(exponent.get(), RSA_F4); + RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr); + EVP_PKEY_set1_RSA(pkey.get(), rsa.get()); + + return std::optional{Key(std::move(pkey), adb::proto::KeyType::RSA_2048)}; +} + +} // namespace crypto +} // namespace adb diff --git a/adb/crypto/tests/Android.bp b/adb/crypto/tests/Android.bp new file mode 100644 index 000000000..b32dcf731 --- /dev/null +++ b/adb/crypto/tests/Android.bp @@ -0,0 +1,41 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_test { + name: "adb_crypto_test", + srcs: [ + "rsa_2048_key_test.cpp", + "x509_generator_test.cpp", + ], + + compile_multilib: "first", + + shared_libs: [ + "libbase", + "libcrypto", + "libcrypto_utils", + "libprotobuf-cpp-lite", + ], + + // Let's statically link them so we don't have to install it onto the + // system image for testing. + static_libs: [ + "libadb_crypto_static", + "libadb_protos_static", + ], + + test_suites: ["device-tests"], +} diff --git a/adb/crypto/tests/key_test.cpp b/adb/crypto/tests/key_test.cpp new file mode 100644 index 000000000..1feb6e8fb --- /dev/null +++ b/adb/crypto/tests/key_test.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace adb { +namespace crypto { + +TEST(RSA2048Key, Smoke) { + auto rsa_2048 = CreateRSA2048Key(); + EXPECT_NE(rsa_2048, std::nullopt); + EXPECT_EQ(rsa_2048->GetKeyType(), adb::proto::KeyType::RSA_2048); + ASSERT_NE(rsa_2048->GetEvpPkey(), nullptr); + + // The public key string format is expected to be: " " + std::string pub_key_plus_name; + auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey()); + ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa)); + std::vector split = android::base::Split(std::string(pub_key_plus_name), " \t"); + EXPECT_EQ(split.size(), 2); + + LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]"; + + // Try to sign something and decode it. + const char token[SHA_DIGEST_LENGTH] = "abcdefghij123456789"; + std::vector sig(RSA_size(rsa)); + unsigned sig_len; + EXPECT_EQ(RSA_sign(NID_sha1, reinterpret_cast(token), sizeof(token), sig.data(), + &sig_len, rsa), + 1); + sig.resize(sig_len); + + { + uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1]; + const std::string& pubkey = split[0]; + ASSERT_EQ(b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)), ANDROID_PUBKEY_ENCODED_SIZE); + RSA* key = nullptr; + ASSERT_TRUE(android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)); + EXPECT_EQ(RSA_verify(NID_sha1, reinterpret_cast(token), sizeof(token), + sig.data(), sig.size(), key), + 1); + RSA_free(key); + } +} + +} // namespace crypto +} // namespace adb diff --git a/adb/crypto/tests/rsa_2048_key_test.cpp b/adb/crypto/tests/rsa_2048_key_test.cpp new file mode 100644 index 000000000..1d8880e15 --- /dev/null +++ b/adb/crypto/tests/rsa_2048_key_test.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace adb { +namespace crypto { + +TEST(RSA2048Key, Smoke) { + auto rsa_2048 = CreateRSA2048Key(); + EXPECT_NE(rsa_2048, std::nullopt); + EXPECT_EQ(rsa_2048->GetKeyType(), adb::proto::KeyType::RSA_2048); + ASSERT_NE(rsa_2048->GetEvpPkey(), nullptr); + + // The public key string format is expected to be: " " + std::string pub_key_plus_name; + auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey()); + ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa)); + std::vector split = android::base::Split(std::string(pub_key_plus_name), " \t"); + EXPECT_EQ(split.size(), 2); + + LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]"; + + std::string pemString = Key::ToPEMString(rsa_2048->GetEvpPkey()); + ASSERT_FALSE(pemString.empty()); + + // Try to sign something and decode it. + const char token[SHA_DIGEST_LENGTH] = "abcdefghij123456789"; + std::vector sig(RSA_size(rsa)); + unsigned sig_len; + EXPECT_EQ(RSA_sign(NID_sha1, reinterpret_cast(token), sizeof(token), sig.data(), + &sig_len, rsa), + 1); + sig.resize(sig_len); + + { + uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1]; + const std::string& pubkey = split[0]; + ASSERT_EQ(b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)), ANDROID_PUBKEY_ENCODED_SIZE); + RSA* key = nullptr; + ASSERT_TRUE(android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)); + EXPECT_EQ(RSA_verify(NID_sha1, reinterpret_cast(token), sizeof(token), + sig.data(), sig.size(), key), + 1); + RSA_free(key); + } +} + +} // namespace crypto +} // namespace adb diff --git a/adb/crypto/tests/x509_generator_test.cpp b/adb/crypto/tests/x509_generator_test.cpp new file mode 100644 index 000000000..281776bd4 --- /dev/null +++ b/adb/crypto/tests/x509_generator_test.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +namespace adb { +namespace crypto { + +TEST(X509Generator, Smoke) { + auto rsa_2048 = CreateRSA2048Key(); + + std::string pub_key_plus_name; + auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey()); + ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa)); + std::vector split = android::base::Split(std::string(pub_key_plus_name), " \t"); + EXPECT_EQ(split.size(), 2); + + LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]"; + auto x509_cert = GenerateX509Certificate(rsa_2048->GetEvpPkey()); + ASSERT_NE(x509_cert.get(), nullptr); + + std::string x509_str = X509ToPEMString(x509_cert.get()); + ASSERT_FALSE(x509_str.empty()); +} + +} // namespace crypto +} // namespace adb diff --git a/adb/crypto/x509_generator.cpp b/adb/crypto/x509_generator.cpp new file mode 100644 index 000000000..43b815304 --- /dev/null +++ b/adb/crypto/x509_generator.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "adb/crypto/x509_generator.h" + +#include + +#include +#include +#include +#include +#include + +namespace adb { +namespace crypto { + +namespace { + +const char kBasicConstraints[] = "critical,CA:TRUE"; +const char kKeyUsage[] = "critical,keyCertSign,cRLSign,digitalSignature"; +const char kSubjectKeyIdentifier[] = "hash"; +constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60; + +bool add_ext(X509* cert, int nid, const char* value) { + size_t len = strlen(value) + 1; + std::vector mutableValue(value, value + len); + X509V3_CTX context; + + X509V3_set_ctx_nodb(&context); + + X509V3_set_ctx(&context, cert, cert, nullptr, nullptr, 0); + X509_EXTENSION* ex = X509V3_EXT_nconf_nid(nullptr, &context, nid, mutableValue.data()); + if (!ex) { + return false; + } + + X509_add_ext(cert, ex, -1); + X509_EXTENSION_free(ex); + return true; +} + +} // namespace + +bssl::UniquePtr GenerateX509Certificate(EVP_PKEY* pkey) { + CHECK(pkey); + bssl::UniquePtr x509(X509_new()); + if (!x509) { + LOG(ERROR) << "Unable to allocate x509 container"; + return nullptr; + } + X509_set_version(x509.get(), 2); + + ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 1); + X509_gmtime_adj(X509_get_notBefore(x509.get()), 0); + X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds); + + if (!X509_set_pubkey(x509.get(), pkey)) { + LOG(ERROR) << "Unable to set x509 public key"; + return nullptr; + } + + X509_NAME* name = X509_get_subject_name(x509.get()); + if (!name) { + LOG(ERROR) << "Unable to get x509 subject name"; + return nullptr; + } + X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, + reinterpret_cast("US"), -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, + reinterpret_cast("Android"), -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + reinterpret_cast("Adb"), -1, -1, 0); + if (!X509_set_issuer_name(x509.get(), name)) { + LOG(ERROR) << "Unable to set x509 issuer name"; + return nullptr; + } + + add_ext(x509.get(), NID_basic_constraints, kBasicConstraints); + add_ext(x509.get(), NID_key_usage, kKeyUsage); + add_ext(x509.get(), NID_subject_key_identifier, kSubjectKeyIdentifier); + + int bytes = X509_sign(x509.get(), pkey, EVP_sha256()); + if (bytes <= 0) { + LOG(ERROR) << "Unable to sign x509 certificate"; + return nullptr; + } + + return x509; +} + +std::string X509ToPEMString(X509* x509) { + bssl::UniquePtr bio(BIO_new(BIO_s_mem())); + int rc = PEM_write_bio_X509(bio.get(), x509); + if (rc != 1) { + LOG(ERROR) << "PEM_write_bio_X509 failed"; + return ""; + } + + BUF_MEM* mem = nullptr; + BIO_get_mem_ptr(bio.get(), &mem); + if (!mem || !mem->data || !mem->length) { + LOG(ERROR) << "BIO_get_mem_ptr failed"; + return ""; + } + + return std::string(mem->data, mem->length); +} + +} // namespace crypto +} // namespace adb