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 diff --git a/adb/proto/Android.bp b/adb/proto/Android.bp new file mode 100644 index 000000000..a7e5d9ca1 --- /dev/null +++ b/adb/proto/Android.bp @@ -0,0 +1,70 @@ +// Copyright (C) 2020 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_protos_defaults", + cflags: [ + "-Wall", + "-Wextra", + "-Wthread-safety", + "-Werror", + ], + + compile_multilib: "both", + + proto: { + export_proto_headers: true, + type: "lite", + }, + srcs: [ + "adb_known_hosts.proto", + "key_type.proto", + "pairing.proto", + ], + target: { + windows: { + compile_multilib: "first", + enabled: true, + }, + }, + + visibility: [ + "//system/core/adb:__subpackages__", + ], + + stl: "libc++_static", + + host_supported: true, + recovery_available: true, +} + +cc_library { + name: "libadb_protos", + defaults: ["libadb_protos_defaults"], + + apex_available: [ + "com.android.adbd", + "test_com.android.adbd", + ], +} + +// For running atest (b/147158681) +cc_library_static { + name: "libadb_protos_static", + defaults: ["libadb_protos_defaults"], + + apex_available: [ + "//apex_available:platform", + ], +} diff --git a/adb/proto/adb_known_hosts.proto b/adb/proto/adb_known_hosts.proto new file mode 100644 index 000000000..85d1489ff --- /dev/null +++ b/adb/proto/adb_known_hosts.proto @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 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. + */ + +syntax = "proto3"; + +option java_package = "com.android.server.adb.protos"; +option java_outer_classname = "AdbKnownHostsProto"; + +package adb.proto; + +// Each known host +message HostInfo { + string guid = 1; +} + +// Protobuf definition for the adb_known_hosts. +message AdbKnownHosts { + repeated HostInfo host_infos = 1; +} diff --git a/adb/proto/jarjar-rules.txt b/adb/proto/jarjar-rules.txt new file mode 100644 index 000000000..4e4063706 --- /dev/null +++ b/adb/proto/jarjar-rules.txt @@ -0,0 +1 @@ +rule com.google.protobuf.** com.android.framework.protobuf.@1 diff --git a/adb/proto/key_type.proto b/adb/proto/key_type.proto new file mode 100644 index 000000000..ed451c511 --- /dev/null +++ b/adb/proto/key_type.proto @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2020 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. + */ + +syntax = "proto3"; + +option java_package = "com.android.server.adb.protos"; +option java_outer_classname = "KeyTypeProto"; + +package adb.proto; + +enum KeyType { + RSA_2048 = 0; +} diff --git a/adb/proto/pairing.proto b/adb/proto/pairing.proto new file mode 100644 index 000000000..b0be20e53 --- /dev/null +++ b/adb/proto/pairing.proto @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 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. + */ + +syntax = "proto3"; + +option java_package = "com.android.server.adb.protos"; +option java_outer_classname = "PairingProto"; + +package adb.proto; + +// The type of packets used in the pairing protocol +message PairingPacket { + enum Type { + SPAKE2_MSG = 0; + PEER_INFO = 1; + } +} diff --git a/adb/tls/Android.bp b/adb/tls/Android.bp new file mode 100644 index 000000000..49833ff4a --- /dev/null +++ b/adb/tls/Android.bp @@ -0,0 +1,75 @@ +// Copyright (C) 2020 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_tls_connection_defaults", + cflags: [ + "-Wall", + "-Wextra", + "-Wthread-safety", + "-Werror", + ], + + compile_multilib: "both", + + srcs: [ + "adb_ca_list.cpp", + "tls_connection.cpp", + ], + target: { + windows: { + compile_multilib: "first", + enabled: true, + }, + }, + export_include_dirs: ["include"], + + host_supported: true, + recovery_available: true, + + visibility: [ + "//system/core/adb:__subpackages__", + ], + + stl: "libc++_static", + + static_libs: [ + "libbase", + ], + shared_libs: [ + "libcrypto", + "liblog", + "libssl", + ], +} + +cc_library { + name: "libadb_tls_connection", + defaults: ["libadb_tls_connection_defaults"], + + apex_available: [ + "com.android.adbd", + "test_com.android.adbd", + ], +} + +// For running atest (b/147158681) +cc_library_static { + name: "libadb_tls_connection_static", + defaults: ["libadb_tls_connection_defaults"], + + apex_available: [ + "//apex_available:platform", + ], +} diff --git a/adb/tls/adb_ca_list.cpp b/adb/tls/adb_ca_list.cpp new file mode 100644 index 000000000..8d37bbe80 --- /dev/null +++ b/adb/tls/adb_ca_list.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2020 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/tls/adb_ca_list.h" + +#include +#include +#include + +#include +#include +#include + +namespace adb { +namespace tls { + +namespace { + +// CA issuer identifier to distinguished embedded keys. Also has version +// information appended to the end of the string (e.g. "AdbKey-0"). +static constexpr int kAdbKeyIdentifierNid = NID_organizationName; +static constexpr char kAdbKeyIdentifierPrefix[] = "AdbKey-"; +static constexpr int kAdbKeyVersion = 0; + +// Where we store the actual data +static constexpr int kAdbKeyValueNid = NID_commonName; + +// TODO: Remove this once X509_NAME_add_entry_by_NID is fixed to use const unsigned char* +int X509_NAME_add_entry_by_NID_const(X509_NAME* name, int nid, int type, const unsigned char* bytes, + int len, int loc, int set) { + return X509_NAME_add_entry_by_NID(name, nid, type, const_cast(bytes), len, loc, + set); +} + +bool IsHexDigit(char c) { + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); +} + +// Wrapper around X509_NAME_get_text_by_NID that first calculates the size +// of the string. Returns empty string on failure. +std::optional GetX509NameTextByNid(X509_NAME* name, int nid) { + // |len| is the len of the text excluding the final null + int len = X509_NAME_get_text_by_NID(name, nid, nullptr, -1); + if (len <= 0) { + return {}; + } + + // Include the space for the final null byte + std::vector buf(len + 1, '\0'); + CHECK(X509_NAME_get_text_by_NID(name, nid, buf.data(), buf.size())); + return buf.data(); +} + +} // namespace + +// Takes an encoded public key and generates a X509_NAME that can be used in +// TlsConnection::SetClientCAList(), to allow the client to figure out which of +// its keys it should try to use in the TLS handshake. +bssl::UniquePtr CreateCAIssuerFromEncodedKey(std::string_view key) { + // "O=AdbKey-0;CN=;" + CHECK(!key.empty()); + + std::string identifier = kAdbKeyIdentifierPrefix; + identifier += std::to_string(kAdbKeyVersion); + bssl::UniquePtr name(X509_NAME_new()); + CHECK(X509_NAME_add_entry_by_NID_const(name.get(), kAdbKeyIdentifierNid, MBSTRING_ASC, + reinterpret_cast(identifier.data()), + identifier.size(), -1, 0)); + + CHECK(X509_NAME_add_entry_by_NID_const(name.get(), kAdbKeyValueNid, MBSTRING_ASC, + reinterpret_cast(key.data()), key.size(), + -1, 0)); + return name; +} + +// Parses a CA issuer and returns the encoded key, if any. +std::optional ParseEncodedKeyFromCAIssuer(X509_NAME* issuer) { + CHECK(issuer); + + auto buf = GetX509NameTextByNid(issuer, kAdbKeyIdentifierNid); + if (!buf || !android::base::StartsWith(*buf, kAdbKeyIdentifierPrefix)) { + return {}; + } + + return GetX509NameTextByNid(issuer, kAdbKeyValueNid); +} + +std::string SHA256BitsToHexString(std::string_view sha256) { + CHECK_EQ(sha256.size(), static_cast(SHA256_DIGEST_LENGTH)); + std::stringstream ss; + ss << std::uppercase << std::setfill('0') << std::hex; + // Convert to hex-string representation + for (size_t i = 0; i < SHA256_DIGEST_LENGTH; ++i) { + ss << std::setw(2) << (0x00FF & sha256[i]); + } + return ss.str(); +} + +std::optional SHA256HexStringToBits(std::string_view sha256_str) { + if (sha256_str.size() != SHA256_DIGEST_LENGTH * 2) { + return {}; + } + + std::string result; + for (size_t i = 0; i < SHA256_DIGEST_LENGTH; ++i) { + auto bytestr = std::string(sha256_str.substr(i * 2, 2)); + if (!IsHexDigit(bytestr[0]) || !IsHexDigit(bytestr[1])) { + LOG(ERROR) << "SHA256 string has invalid non-hex chars"; + return {}; + } + result += static_cast(std::stol(bytestr, nullptr, 16)); + } + return result; +} + +} // namespace tls +} // namespace adb diff --git a/adb/tls/include/adb/tls/adb_ca_list.h b/adb/tls/include/adb/tls/adb_ca_list.h new file mode 100644 index 000000000..a1ab9a779 --- /dev/null +++ b/adb/tls/include/adb/tls/adb_ca_list.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 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 + +// These APIs is used to embed adbd's known public keys into client-allowed CA +// issuer list that can indicate to the client which key to use. +namespace adb { +namespace tls { + +// Takes an encoded public key and generates a X509_NAME that can be used in +// TlsConnection::SetClientCAList(), to allow the client to figure out which of +// its keys it should try to use in the TLS handshake. This is guaranteed to +// return a valid X509_NAME, given a non-empty key. +bssl::UniquePtr CreateCAIssuerFromEncodedKey(std::string_view key); + +// Parses a CA issuer and returns the encoded key, if any. On failure, returns +// nullopt. +std::optional ParseEncodedKeyFromCAIssuer(X509_NAME* issuer); + +// Converts SHA256 bits to a hex string representation. |sha256| must be exactly +// |SHA256_DIGEST_LENGTH| in size. +std::string SHA256BitsToHexString(std::string_view sha256); + +// Converts a valid SHA256 hex string to the actual bits. Returns nullopt on +// failure. +std::optional SHA256HexStringToBits(std::string_view sha256_str); + +} // namespace tls +} // namespace adb diff --git a/adb/tls/include/adb/tls/tls_connection.h b/adb/tls/include/adb/tls/tls_connection.h new file mode 100644 index 000000000..ae7085733 --- /dev/null +++ b/adb/tls/include/adb/tls/tls_connection.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2020 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 +#include + +#include +#include +#include + +namespace adb { +namespace tls { + +class TlsConnection { + public: + // This class will require both client and server to exchange valid + // certificates. + enum class Role { + Server, + Client, + }; + + enum class TlsError : uint8_t { + Success = 0, + // An error indicating that we rejected the peer's certificate. + CertificateRejected, + // An error indicating that the peer rejected our certificate. + PeerRejectedCertificate, + // Add more if needed + UnknownFailure, + }; + + using CertVerifyCb = std::function; + using SetCertCb = std::function; + + virtual ~TlsConnection() = default; + + // Adds a trusted certificate to the list for the SSL connection. + // During the handshake phase, it will check the list of trusted certificates. + // The connection will fail if the peer's certificate is not in the list. Use + // |EnableCertificateVerification(false)| to disable certificate + // verification. + // + // Returns true if |cert| was successfully added, false otherwise. + virtual bool AddTrustedCertificate(std::string_view cert) = 0; + + // Sets a custom certificate verify callback. |cb| must return 1 if the + // certificate is trusted. Otherwise, return 0 if not. Note that |cb| is + // only used if EnableCertificateVerification(false). + virtual void SetCertVerifyCallback(CertVerifyCb cb) = 0; + + // Configures a client |ca_list| that the server sends to the client in the + // CertificateRequest message. + virtual void SetClientCAList(STACK_OF(X509_NAME) * ca_list) = 0; + + // Sets a callback that will be called to select a certificate. See + // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_cert_cb + // for more details. + virtual void SetCertificateCallback(SetCertCb cb) = 0; + + // Exports a value derived from the master secret used in the TLS + // connection. This value should be used alongside any PAKE to ensure the + // peer is the intended peer. |length| is the requested length for the + // keying material. This is only valid after |DoHandshake| succeeds. + virtual std::vector ExportKeyingMaterial(size_t length) = 0; + + // Enable client-side check on whether server accepted the handshake. In TLS + // 1.3, client will not know the server rejected the handshake until after + // performing a read operation. Basically, this will perform an + // SSL_peek right after the handshake and see whether that succeeds. + // + // IMPORTANT: this will only work if the protocol is a server-speaks-first + // type. Enabling this for the server is a no-op. This is disabled by + // default. + virtual void EnableClientPostHandshakeCheck(bool enable) = 0; + + // Starts the handshake process. Returns TlsError::Success if handshake + // succeeded. + virtual TlsError DoHandshake() = 0; + + // Reads |size| bytes and returns the data. The returned data has either + // size |size| or zero, in which case the read failed. + virtual std::vector ReadFully(size_t size) = 0; + + // Overloaded ReadFully method, which accepts a buffer for writing in. + // Returns true iff exactly |size| amount of data was written into |buf|, + // false otherwise. + virtual bool ReadFully(void* buf, size_t size) = 0; + + // Writes |size| bytes. Returns true if all |size| bytes were read. + // Returns false otherwise. + virtual bool WriteFully(std::string_view data) = 0; + + // Create a new TlsConnection instance. |cert| and |priv_key| cannot be + // empty. + static std::unique_ptr Create(Role role, std::string_view cert, + std::string_view priv_key, + android::base::borrowed_fd fd); + + // Helper to set the certificate and key strings to a SSL client/server. + // Useful when in the set-certificate callback. + static bool SetCertAndKey(SSL* ssl, std::string_view cert_chain, std::string_view priv_key); + + protected: + TlsConnection() = default; +}; // TlsConnection + +} // namespace tls +} // namespace adb diff --git a/adb/tls/tests/Android.bp b/adb/tls/tests/Android.bp new file mode 100644 index 000000000..198de58da --- /dev/null +++ b/adb/tls/tests/Android.bp @@ -0,0 +1,42 @@ +// +// 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_tls_connection_test", + srcs: [ + "adb_ca_list_test.cpp", + "tls_connection_test.cpp", + ], + + compile_multilib: "first", + + shared_libs: [ + "libbase", + "libcrypto", + "libcrypto_utils", + "libssl", + ], + + // 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", + "libadb_tls_connection_static", + ], + + test_suites: ["device-tests"], +} diff --git a/adb/tls/tests/adb_ca_list_test.cpp b/adb/tls/tests/adb_ca_list_test.cpp new file mode 100644 index 000000000..c727e5f1a --- /dev/null +++ b/adb/tls/tests/adb_ca_list_test.cpp @@ -0,0 +1,163 @@ +/* + * Copyright 2020 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. + */ + +#define LOG_TAG "AdbCAListTest" + +#include + +#include +#include +#include +#include + +namespace adb { +namespace tls { + +class AdbCAListTest : public testing::Test { + protected: + virtual void SetUp() override {} + + virtual void TearDown() override {} +}; + +TEST_F(AdbCAListTest, SHA256BitsToHexString_BadParam) { + // Should crash if not exactly SHA256_DIGEST_LENGTH size + ASSERT_DEATH( + { + // empty + std::string sha; + SHA256BitsToHexString(sha); + }, + ""); + ASSERT_DEATH( + { + std::string sha(1, 0x80); + SHA256BitsToHexString(sha); + }, + ""); + ASSERT_DEATH( + { + std::string sha(SHA256_DIGEST_LENGTH - 1, 0x80); + SHA256BitsToHexString(sha); + }, + ""); + ASSERT_DEATH( + { + std::string sha(SHA256_DIGEST_LENGTH + 1, 0x80); + SHA256BitsToHexString(sha); + }, + ""); +} + +TEST_F(AdbCAListTest, SHA256HexStringToBits_BadParam) { + { + // empty + std::string sha_str; + auto res = SHA256HexStringToBits(sha_str); + EXPECT_FALSE(res.has_value()); + } + { + std::string sha_str(1, 'a'); + auto res = SHA256HexStringToBits(sha_str); + EXPECT_FALSE(res.has_value()); + } + { + std::string sha_str(SHA256_DIGEST_LENGTH * 2 - 1, 'a'); + auto res = SHA256HexStringToBits(sha_str); + EXPECT_FALSE(res.has_value()); + } + { + std::string sha_str(SHA256_DIGEST_LENGTH * 2 + 1, 'a'); + auto res = SHA256HexStringToBits(sha_str); + EXPECT_FALSE(res.has_value()); + } + { + // Non-hex chars + std::string sha_str(SHA256_DIGEST_LENGTH * 2, 'a'); + sha_str[32] = 'x'; + auto res = SHA256HexStringToBits(sha_str); + EXPECT_FALSE(res.has_value()); + } +} + +TEST_F(AdbCAListTest, SHA256BitsToHexString_ValidParam) { + uint8_t ct = 0; + // Test every possible byte + std::vector expectedStr = { + "000102030405060708090A0B0C0D0E0F" + "101112131415161718191A1B1C1D1E1F", + + "202122232425262728292A2B2C2D2E2F" + "303132333435363738393A3B3C3D3E3F", + + "404142434445464748494A4B4C4D4E4F" + "505152535455565758595A5B5C5D5E5F", + + "606162636465666768696A6B6C6D6E6F" + "707172737475767778797A7B7C7D7E7F", + + "808182838485868788898A8B8C8D8E8F" + "909192939495969798999A9B9C9D9E9F", + + "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF" + "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF", + + "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF" + "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF", + + "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF" + "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF", + }; + + for (auto& expected : expectedStr) { + std::string sha; + while (sha.size() < SHA256_DIGEST_LENGTH) { + sha += ct++; + } + + auto sha_str = SHA256BitsToHexString(sha); + EXPECT_EQ(expected, sha_str); + + // try to convert back to bits + auto out_sha = SHA256HexStringToBits(sha_str); + ASSERT_TRUE(out_sha.has_value()); + EXPECT_EQ(*out_sha, sha); + } +} + +TEST_F(AdbCAListTest, CreateCAIssuerFromEncodedKey_EmptyKey) { + ASSERT_DEATH({ auto issuer = CreateCAIssuerFromEncodedKey(""); }, ""); +} + +TEST_F(AdbCAListTest, Smoke) { + { + std::string key = + "A45BC1FF6C89BF0E" + "65F9BA153FBC9876" + "4969B4113F1CF878" + "EEF9BF1C3F9C9227"; + auto issuer = CreateCAIssuerFromEncodedKey(key); + ASSERT_NE(issuer, nullptr); + + // Try to parse the encoded key out of the X509_NAME + auto out_key = ParseEncodedKeyFromCAIssuer(issuer.get()); + ASSERT_TRUE(out_key.has_value()); + EXPECT_EQ(key, *out_key); + } +} + +} // namespace tls +} // namespace adb diff --git a/adb/tls/tests/tls_connection_test.cpp b/adb/tls/tests/tls_connection_test.cpp new file mode 100644 index 000000000..880904bb1 --- /dev/null +++ b/adb/tls/tests/tls_connection_test.cpp @@ -0,0 +1,616 @@ +/* + * Copyright 2020 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. + */ + +#define LOG_TAG "AdbWifiTlsConnectionTest" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace adb::crypto; + +namespace adb { +namespace tls { + +using android::base::unique_fd; +using TlsError = TlsConnection::TlsError; + +// Test X.509 certificates (RSA 2048) +static const std::string kTestRsa2048ServerCert = + "-----BEGIN CERTIFICATE-----\n" + "MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n" + "MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyMTIyMjU1NVoX\n" + "DTMwMDExODIyMjU1NVowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n" + "DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8E\n" + "2Ck9TfuKlz7wqWdMfknjZ1luFDp2IHxAUZzh/F6jeI2dOFGAjpeloSnGOE86FIaT\n" + "d1EvpyTh7nBwbrLZAA6XFZTo7Bl6BdNOQdqb2d2+cLEN0inFxqUIycevRtohUE1Y\n" + "FHM9fg442X1jOTWXjDZWeiqFWo95paAPhzm6pWqfJK1+YKfT1LsWZpYqJGGQE5pi\n" + "C3qOBYYgFpoXMxTYJNoZo3uOYEdM6upc8/vh15nMgIxX/ymJxEY5BHPpZPPWjXLg\n" + "BfzVaV9fUfv0JT4HQ4t2WvxC3cD/UsjWp2a6p454uUp2ENrANa+jRdRJepepg9D2\n" + "DKsx9L8zjc5Obqexrt0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n" + "Af8EBAMCAYYwHQYDVR0OBBYEFDFW+8GTErwoZN5Uu9KyY4QdGYKpMA0GCSqGSIb3\n" + "DQEBCwUAA4IBAQBCDEn6SHXGlq5TU7J8cg1kRPd9bsJW+0hDuKSq0REXDkl0PcBf\n" + "fy282Agg9enKPPKmnpeQjM1dmnxdM8tT8LIUbMl779i3fn6v9HJVB+yG4gmRFThW\n" + "c+AGlBnrIT820cX/gU3h3R3FTahfsq+1rrSJkEgHyuC0HYeRyveSckBdaEOLvx0S\n" + "toun+32JJl5hWydpUUZhE9Mbb3KHBRM2YYZZU9JeJ08Apjl+3lRUeMAUwI5fkAAu\n" + "z/1SqnuGL96bd8P5ixdkA1+rF8FPhodGcq9mQOuUGP9g5HOXjaNoJYvwVRUdLeGh\n" + "cP/ReOTwQIzM1K5a83p8cX8AGGYmM7dQp7ec\n" + "-----END CERTIFICATE-----\n"; + +static const std::string kTestRsa2048ServerPrivKey = + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCvBNgpPU37ipc+\n" + "8KlnTH5J42dZbhQ6diB8QFGc4fxeo3iNnThRgI6XpaEpxjhPOhSGk3dRL6ck4e5w\n" + "cG6y2QAOlxWU6OwZegXTTkHam9ndvnCxDdIpxcalCMnHr0baIVBNWBRzPX4OONl9\n" + "Yzk1l4w2VnoqhVqPeaWgD4c5uqVqnyStfmCn09S7FmaWKiRhkBOaYgt6jgWGIBaa\n" + "FzMU2CTaGaN7jmBHTOrqXPP74deZzICMV/8picRGOQRz6WTz1o1y4AX81WlfX1H7\n" + "9CU+B0OLdlr8Qt3A/1LI1qdmuqeOeLlKdhDawDWvo0XUSXqXqYPQ9gyrMfS/M43O\n" + "Tm6nsa7dAgMBAAECggEAFCS2bPdUKIgjbzLgtHW+hT+J2hD20rcHdyAp+dNH/2vI\n" + "yLfDJHJA4chGMRondKA704oDw2bSJxxlG9t83326lB35yxPhye7cM8fqgWrK8PVl\n" + "tU22FhO1ZgeJvb9OeXWNxKZyDW9oOOJ8eazNXVMuEo+dFj7B6l3MXQyHJPL2mJDm\n" + "u9ofFLdypX+gJncVO0oW0FNJnEUn2MMwHDNlo7gc4WdQuidPkuZItKRGcB8TTGF3\n" + "Ka1/2taYdTQ4Aq//Z84LlFvE0zD3T4c8LwYYzOzD4gGGTXvft7vSHzIun1S8YLRS\n" + "dEKXdVjtaFhgH3uUe4j+1b/vMvSHeoGBNX/G88GD+wKBgQDWUYVlMVqc9HD2IeYi\n" + "EfBcNwAJFJkh51yAl5QbUBgFYgFJVkkS/EDxEGFPvEmI3/pAeQFHFY13BI466EPs\n" + "o8Z8UUwWDp+Z1MFHHKQKnFakbsZbZlbqjJ9VJsqpezbpWhMHTOmcG0dmE7rf0lyM\n" + "eQv9slBB8qp2NEUs5Of7f2C2bwKBgQDRDq4nUuMQF1hbjM05tGKSIwkobmGsLspv\n" + "TMhkM7fq4RpbFHmbNgsFqMhcqYZ8gY6/scv5KCuAZ4yHUkbqwf5h+QCwrJ4uJeUJ\n" + "ZgJfHus2mmcNSo8FwSkNoojIQtzcbJav7bs2K9VTuertk/i7IJLApU4FOZZ5pghN\n" + "EXu0CZF1cwKBgDWFGhjRIF29tU/h20R60llU6s9Zs3wB+NmsALJpZ/ZAKS4VPB5f\n" + "nCAXBRYSYRKrTCU5kpYbzb4BBzuysPOxWmnFK4j+keCqfrGxd02nCQP7HdHJVr8v\n" + "6sIq88UrHeVcNxBFprjzHvtgxfQK5k22FMZ/9wbhAKyQFQ5HA5+MiaxFAoGAIcZZ\n" + "ZIkDninnYIMS9OursShv5lRO+15j3i9tgKLKZ+wOMgDQ1L6acUOfezj4PU1BHr8+\n" + "0PYocQpJreMhCfRlgLaV4fVBaPs+UZJld7CrF5tCYudUy/01ALrtlk0XGZWBktK5\n" + "mDrksC4tQkzRtonAq9cJD9cJ9IVaefkFH0UcdvkCgYBpZj50VLeGhnHHBnkJRlV1\n" + "fV+/P6PAq6RtqjA6O9Qdaoj5V3w2d63aQcQXQLJjH2BBmtCIy47r04rFvZpbCxP7\n" + "NH/OnK9NHpk2ucRTe8TAnVbvF/TZzPJoIxAO/D3OWaW6df4R8en8u6GYzWFglAyT\n" + "sydGT8yfWD1FYUWgfrVRbg==\n" + "-----END PRIVATE KEY-----\n"; + +static const std::string kTestRsa2048ClientCert = + "-----BEGIN CERTIFICATE-----\n" + "MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n" + "MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyMTIyMjU1NloX\n" + "DTMwMDExODIyMjU1NlowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n" + "DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI3a\n" + "EXh1S5FTbet7JVONswffRPaekdIK53cb8SnAbSO9X5OLA4zGwdkrBvDTsd96SKrp\n" + "JxmoNOE1DhbZh05KPlWAPkGKacjGWaz+S7biDOL0I6aaLbTlU/il1Ub9olPSBVUx\n" + "0nhdtEFgIOzddnP6/1KmyIIeRxS5lTKeg4avqUkZNXkz/wL1dHBFL7FNFf0SCcbo\n" + "tsub/deFbjZ27LTDN+SIBgFttTNqC5NTvoBAoMdyCOAgNYwaHO+fKiK3edfJieaw\n" + "7HD8qqmQxcpCtRlA8CUPj7GfR+WHiCJmlevhnkFXCo56R1BS0F4wuD4KPdSWt8gc\n" + "27ejH/9/z2cKo/6SLJMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n" + "Af8EBAMCAYYwHQYDVR0OBBYEFO/Mr5ygqqpyU/EHM9v7RDvcqaOkMA0GCSqGSIb3\n" + "DQEBCwUAA4IBAQAH33KMouzF2DYbjg90KDrDQr4rq3WfNb6P743knxdUFuvb+40U\n" + "QjC2OJZHkSexH7wfG/y6ic7vfCfF4clNs3QvU1lEjOZC57St8Fk7mdNdsWLwxEMD\n" + "uePFz0dvclSxNUHyCVMqNxddzQYzxiDWQRmXWrUBliMduQqEQelcxW2yDtg8bj+s\n" + "aMpR1ra9scaD4jzIZIIxLoOS9zBMuNRbgP217sZrniyGMhzoI1pZ/izN4oXpyH7O\n" + "THuaCzzRT3ph2f8EgmHSodz3ttgSf2DHzi/Ez1xUkk7NOlgNtmsxEdrM47+cC5ae\n" + "fIf2V+1o1JW8J7D11RmRbNPh3vfisueB4f88\n" + "-----END CERTIFICATE-----\n"; + +static const std::string kTestRsa2048ClientPrivKey = + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCN2hF4dUuRU23r\n" + "eyVTjbMH30T2npHSCud3G/EpwG0jvV+TiwOMxsHZKwbw07Hfekiq6ScZqDThNQ4W\n" + "2YdOSj5VgD5BimnIxlms/ku24gzi9COmmi205VP4pdVG/aJT0gVVMdJ4XbRBYCDs\n" + "3XZz+v9SpsiCHkcUuZUynoOGr6lJGTV5M/8C9XRwRS+xTRX9EgnG6LbLm/3XhW42\n" + "duy0wzfkiAYBbbUzaguTU76AQKDHcgjgIDWMGhzvnyoit3nXyYnmsOxw/KqpkMXK\n" + "QrUZQPAlD4+xn0flh4giZpXr4Z5BVwqOekdQUtBeMLg+Cj3UlrfIHNu3ox//f89n\n" + "CqP+kiyTAgMBAAECggEAAa64eP6ggCob1P3c73oayYPIbvRqiQdAFOrr7Vwu7zbr\n" + "z0rde+n6RU0mrpc+4NuzyPMtrOGQiatLbidJB5Cx3z8U00ovqbCl7PtcgorOhFKe\n" + "VEzihebCcYyQqbWQcKtpDMhOgBxRwFoXieJb6VGXfa96FAZalCWvXgOrTl7/BF2X\n" + "qMqIm9nJi+yS5tIO8VdOsOmrMWRH/b/ENUcef4WpLoxTXr0EEgyKWraeZ/hhXo1e\n" + "z29dZKqdr9wMsq11NPsRddwS94jnDkXTo+EQyWVTfB7gb6yyp07s8jysaDb21tVv\n" + "UXB9MRhDV1mOv0ncXfXZ4/+4A2UahmZaLDAVLaat4QKBgQDAVRredhGRGl2Nkic3\n" + "KvZCAfyxug788CgasBdEiouz19iCCwcgMIDwnq0s3/WM7h/laCamT2x38riYDnpq\n" + "rkYMfuVtU9CjEL9pTrdfwbIRhTwYNqADaPz2mXwQUhRXutE5TIdgxxC/a+ZTh0qN\n" + "S+vhTj/4hf0IZhMh5Nqj7IPExQKBgQC8zxEzhmSGjys0GuE6Wl6Doo2TpiR6vwvi\n" + "xPLU9lmIz5eca/Rd/eERioFQqeoIWDLzx52DXuz6rUoQhbJWz9hP3yqCwXD+pbNP\n" + "oDJqDDbCC4IMYEb0IK/PEPH+gIpnTjoFcW+ecKDFG7W5Lt05J8WsJsfOaJvMrOU+\n" + "dLXq3IgxdwKBgQC5RAFq0v6e8G+3hFaEHL0z3igkpt3zJf7rnj37hx2FMmDa+3Z0\n" + "umQp5B9af61PgL12xLmeMBmC/Wp1BlVDV/Yf6Uhk5Hyv5t0KuomHEtTNbbLyfAPs\n" + "5P/vJu/L5NS1oT4S3LX3MineyjgGs+bLbpub3z1dzutrYLADUSiPCK/xJQKBgBQt\n" + "nQ0Ao+Wtj1R2OvPdjJRM3wyUiPmFSWPm4HzaBx+T8AQLlYYmB9O0FbXlMtnJc0iS\n" + "YMcVcgYoVu4FG9YjSF7g3s4yljzgwJUV7c1fmMqMKE3iTDLy+1cJ3JLycdgwiArk\n" + "4KTyLHxkRbuQwpvFIF8RlfD9RQlOwQE3v+llwDhpAoGBAL6XG6Rp6mBoD2Ds5c9R\n" + "943yYgSUes3ji1SI9zFqeJtj8Ml/enuK1xu+8E/BxB0//+vgZsH6i3i8GFwygKey\n" + "CGJF8CbiHc3EJc3NQIIRXcni/CGacf0HwC6m+PGFDBIpA4H2iDpVvCSofxttQiq0\n" + "/Z7HXmXUvZHVyYi/QzX2Gahj\n" + "-----END PRIVATE KEY-----\n"; + +static const std::string kTestRsa2048UnknownPrivKey = + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCrIhr+CS+6UI0w\n" + "CTaVzQAicKBe6X531LeQAGYx7j5RLHR1QIoJ0WCc5msmXKe2VzcWuLbVdTGAIP1H\n" + "mwbPqlbO4ioxeJhiDv+WPuLG8+j4Iw1Yqxt8cfohxjfvNmIQM8aF5hGyyaaTetDF\n" + "EYWONoYCBC4WnFWgYCPb8mzWXlhHE3F66GnHpc32zydPTg3ZurGvSsFf7fNY9yRw\n" + "8WtwPiI6mpRxt+n2bQUp+LZ+g/3rXLFPg8uWDGYG7IvLluWc9gR9lxjL64t6ryLU\n" + "2cm7eTfDgLw/B1F/wEgCJDnby1JgQ4rq6klJO3BR2ooUr/7T343y5njG5hQJreV7\n" + "5ZnSmRLZAgMBAAECggEABPrfeHZFuWkj7KqN+DbAmt/2aMCodZ3+7/20+528WkIe\n" + "CvXzdmTth+9UHagLWNzpnVuHdYd9JuZ+3F00aelh8JAIDIu++naHhUSj9ohtRoBF\n" + "oIeNK5ZJAj/Zi5hkauaIz8dxyyc/VdIYfm2bundXd7pNqYqH2tyFWp6PwH67GKlZ\n" + "1lC7o8gKAK8sz9g0Ctdoe+hDqAsvYFCW4EWDM2qboucSgn8g3E/Gux/KrpXVv7d0\n" + "PMQ60m+dyTOCMGqXIoDR3TAvQR7ex5sQ/QZSREdxKy878s/2FY4ktxtCUWlhrmcI\n" + "VKtrDOGEKwNoiMluf2635rsVq2e01XhQlmdxbRFU0QKBgQDjOhhD1m9duFTQ2b+J\n" + "Xfn6m8Rs7sZqO4Az7gLOWmD/vYWlK4n2nZsh6u5/cB1N+PA+ncvvV4yKJAlLHxbT\n" + "pVvfzJ/jbUsj/NJg/w7+KYC9gXgRmBonuG2gRZF/5Otdlza4vMcoSkqGjlGxJyzL\n" + "+9umEziN3tEYMRwipYvt7BgbUQKBgQDAzaXryJ3YD3jpecy/+fSnQvFjpyeDRqU1\n" + "KDA9nxN5tJN6bnKhUlMhy64SsgvVX9jUuN7cK+qYV0uzdBn6kIAJNLWTdbtH93+e\n" + "vNVgluR3jmixW4QfY9vfZKdXZbVGNc0DFMi1vJqgxTgQ5Mq5PxxxRL4FsAF840V1\n" + "Wu9uhU0NCQKBgBfjga2QG8E0oeYbHmHouWE5gxsYt09v1fifqzfalJwOZsCIpUaC\n" + "J08Xjd9kABC0fT14BXqyL5pOU5PMPvAdUF1k++JDGUU9TTjZV9AsuNYziFYBMa6/\n" + "WvcgmT1i6cO7JAuj/SQlO1SOHdSME8+WOO9q0eVIaZ8repPB58YprhchAoGBAJyR\n" + "Y8AJdkTSq7nNszvi245IioYGY8vzPo3gSOyBlesrfOfbcTMYC3JSWNXNyFZKM2br\n" + "ie75qtRzb4IXMlGLrq3LI/jPjnpuvjBF4HFDl9yOxO3iB3UGPrM2pb4PVhnh7s4l\n" + "vqf2tQsBnPn7EbVFTu+ch0NPHqYwWWNnqS/zCBMhAoGBAIkYjOE0iD9W2FXee6VL\n" + "iN8wDqlqsGEEtLvykIDmTmM+ZX5ftQuPo18khpE9wQKmJ5OpoVTYIP1UsJFBakgo\n" + "+dGaf6xVuPvmydNFqixlW3z227n4Px6GX7CXlCaAleTeItezli+dWf/9astwTA3x\n" + "IazYzsxUUpZFC4dJ1GhBn3y1\n" + "-----END PRIVATE KEY-----\n"; + +static const std::string kTestRsa2048UnknownCert = + "-----BEGIN CERTIFICATE-----\n" + "MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n" + "MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyNDE4MzMwNVoX\n" + "DTMwMDEyMTE4MzMwNVowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n" + "DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKsi\n" + "Gv4JL7pQjTAJNpXNACJwoF7pfnfUt5AAZjHuPlEsdHVAignRYJzmayZcp7ZXNxa4\n" + "ttV1MYAg/UebBs+qVs7iKjF4mGIO/5Y+4sbz6PgjDVirG3xx+iHGN+82YhAzxoXm\n" + "EbLJppN60MURhY42hgIELhacVaBgI9vybNZeWEcTcXroacelzfbPJ09ODdm6sa9K\n" + "wV/t81j3JHDxa3A+IjqalHG36fZtBSn4tn6D/etcsU+Dy5YMZgbsi8uW5Zz2BH2X\n" + "GMvri3qvItTZybt5N8OAvD8HUX/ASAIkOdvLUmBDiurqSUk7cFHaihSv/tPfjfLm\n" + "eMbmFAmt5XvlmdKZEtkCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n" + "Af8EBAMCAYYwHQYDVR0OBBYEFDtRSOm1ilhnq6bKN4qJ1ekK/PAkMA0GCSqGSIb3\n" + "DQEBCwUAA4IBAQAP6Q8/OxnBA3BO8oxKer0tjI4rZMefUhbAKUWXYjTTNEBm5//b\n" + "lVGP2RptO7bxj8w1L3rxsjmVcv2TqBOhrbJqvGVPE2ntoYlFhBBkRvmxuu1y5W9V\n" + "uJU7SF9lNmDXShTURULu3P8GdeT1HGeXzWQ4x7VhY9a3VIbmN5VxjB+3C6hYZxSs\n" + "DCpmidu/sR+n5Azlh6oqrhOxmv17PuF/ioTUsHd4y2Z41IvvO47oghxNDtboUUsg\n" + "LfsM1MOxVC9PqOfQphFU4i8owNIYzBMadDLw+1TSQj0ALqZVyc9Dq+WDFdz+JAE+\n" + "k7TkVU06UPGVSnLVzJeYwGCXQp3apBszY9vO\n" + "-----END CERTIFICATE-----\n"; + +struct CAIssuerField { + int nid; + std::vector val; +}; +using CAIssuer = std::vector; +static std::vector kCAIssuers = { + { + {NID_commonName, {'a', 'b', 'c', 'd', 'e'}}, + {NID_organizationName, + { + 'd', + 'e', + 'f', + 'g', + }}, + }, + { + {NID_commonName, + { + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + }}, + {NID_countryName, {'n', 'o'}}, + }, +}; + +class AdbWifiTlsConnectionTest : public testing::Test { + protected: + virtual void SetUp() override { + // TODO: move client code in each test into its own thread, as the + // socket pair buffer is limited. + android::base::Socketpair(SOCK_STREAM, &server_fd_, &client_fd_); + server_ = TlsConnection::Create(TlsConnection::Role::Server, kTestRsa2048ServerCert, + kTestRsa2048ServerPrivKey, server_fd_); + client_ = TlsConnection::Create(TlsConnection::Role::Client, kTestRsa2048ClientCert, + kTestRsa2048ClientPrivKey, client_fd_); + ASSERT_NE(nullptr, server_); + ASSERT_NE(nullptr, client_); + } + + virtual void TearDown() override { + WaitForClientConnection(); + // Shutdown the SSL connection first. + server_.reset(); + client_.reset(); + } + + bssl::UniquePtr GetCAIssuerList() { + bssl::UniquePtr ret(sk_X509_NAME_new_null()); + for (auto& issuer : kCAIssuers) { + bssl::UniquePtr name(X509_NAME_new()); + for (auto& attr : issuer) { + CHECK(X509_NAME_add_entry_by_NID(name.get(), attr.nid, MBSTRING_ASC, + attr.val.data(), attr.val.size(), -1, 0)); + } + + CHECK(bssl::PushToStack(ret.get(), std::move(name))); + } + + return ret; + } + + void StartClientHandshakeAsync(bool expect_success) { + client_thread_ = std::thread([=]() { + if (expect_success) { + EXPECT_EQ(client_->DoHandshake(), TlsError::Success); + } else { + EXPECT_NE(client_->DoHandshake(), TlsError::Success); + } + }); + } + + void WaitForClientConnection() { + if (client_thread_.joinable()) { + client_thread_.join(); + } + } + + unique_fd server_fd_; + unique_fd client_fd_; + const std::vector msg_{0xff, 0xab, 0x32, 0xf6, 0x12, 0x56}; + std::unique_ptr server_; + std::unique_ptr client_; + std::thread client_thread_; +}; + +TEST_F(AdbWifiTlsConnectionTest, InvalidCreationParams) { + // Verify that passing empty certificate/private key results in a crash. + ASSERT_DEATH( + { + server_ = TlsConnection::Create(TlsConnection::Role::Server, "", + kTestRsa2048ServerPrivKey, server_fd_); + }, + ""); + ASSERT_DEATH( + { + server_ = TlsConnection::Create(TlsConnection::Role::Server, kTestRsa2048ServerCert, + "", server_fd_); + }, + ""); + ASSERT_DEATH( + { + client_ = TlsConnection::Create(TlsConnection::Role::Client, "", + kTestRsa2048ClientPrivKey, client_fd_); + }, + ""); + ASSERT_DEATH( + { + client_ = TlsConnection::Create(TlsConnection::Role::Client, kTestRsa2048ClientCert, + "", client_fd_); + }, + ""); +} + +TEST_F(AdbWifiTlsConnectionTest, NoCertificateVerification) { + // Allow any certificate + server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; }); + client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; }); + StartClientHandshakeAsync(true); + + // Handshake should succeed + EXPECT_EQ(server_->DoHandshake(), TlsError::Success); + WaitForClientConnection(); + + // Client write, server read + EXPECT_TRUE(client_->WriteFully( + std::string_view(reinterpret_cast(msg_.data()), msg_.size()))); + auto data = server_->ReadFully(msg_.size()); + EXPECT_EQ(data, msg_); + + // Client read, server write + EXPECT_TRUE(server_->WriteFully( + std::string_view(reinterpret_cast(msg_.data()), msg_.size()))); + // Try with overloaded ReadFully + std::vector buf(msg_.size()); + ASSERT_TRUE(client_->ReadFully(buf.data(), msg_.size())); + EXPECT_EQ(buf, msg_); +} + +TEST_F(AdbWifiTlsConnectionTest, NoTrustedCertificates) { + StartClientHandshakeAsync(false); + + // Handshake should not succeed + EXPECT_NE(server_->DoHandshake(), TlsError::Success); + WaitForClientConnection(); + + // Client write, server read should fail + EXPECT_FALSE(client_->WriteFully( + std::string_view(reinterpret_cast(msg_.data()), msg_.size()))); + auto data = server_->ReadFully(msg_.size()); + EXPECT_EQ(data.size(), 0); + + // Client read, server write should fail + EXPECT_FALSE(server_->WriteFully( + std::string_view(reinterpret_cast(msg_.data()), msg_.size()))); + data = client_->ReadFully(msg_.size()); + EXPECT_EQ(data.size(), 0); +} + +TEST_F(AdbWifiTlsConnectionTest, AddTrustedCertificates) { + // Add peer certificates + EXPECT_TRUE(client_->AddTrustedCertificate(kTestRsa2048ServerCert)); + EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048ClientCert)); + + StartClientHandshakeAsync(true); + + // Handshake should succeed + EXPECT_EQ(server_->DoHandshake(), TlsError::Success); + WaitForClientConnection(); + + // Client write, server read + EXPECT_TRUE(client_->WriteFully( + std::string_view(reinterpret_cast(msg_.data()), msg_.size()))); + auto data = server_->ReadFully(msg_.size()); + EXPECT_EQ(data, msg_); + + // Client read, server write + EXPECT_TRUE(server_->WriteFully( + std::string_view(reinterpret_cast(msg_.data()), msg_.size()))); + data = client_->ReadFully(msg_.size()); + EXPECT_EQ(data, msg_); +} + +TEST_F(AdbWifiTlsConnectionTest, AddTrustedCertificates_ClientWrongCert) { + // Server trusts a certificate, client has the wrong certificate + EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert)); + // Client accepts any certificate + client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; }); + + // Without enabling EnableClientPostHandshakeCheck(), DoHandshake() will + // succeed, because in TLS 1.3, the client doesn't get notified if the + // server rejected the certificate until a read operation is called. + StartClientHandshakeAsync(true); + + // Handshake should fail for server, succeed for client + EXPECT_NE(server_->DoHandshake(), TlsError::Success); + WaitForClientConnection(); + + // Client write succeeds, server read should fail + EXPECT_TRUE(client_->WriteFully( + std::string_view(reinterpret_cast(msg_.data()), msg_.size()))); + auto data = server_->ReadFully(msg_.size()); + EXPECT_EQ(data.size(), 0); + + // Client read, server write should fail + EXPECT_FALSE(server_->WriteFully( + std::string_view(reinterpret_cast(msg_.data()), msg_.size()))); + data = client_->ReadFully(msg_.size()); + EXPECT_EQ(data.size(), 0); +} + +TEST_F(AdbWifiTlsConnectionTest, ExportKeyingMaterial) { + // Allow any certificate + server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; }); + client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; }); + + // Add peer certificates + EXPECT_TRUE(client_->AddTrustedCertificate(kTestRsa2048ServerCert)); + EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048ClientCert)); + + StartClientHandshakeAsync(true); + + // Handshake should succeed + EXPECT_EQ(server_->DoHandshake(), TlsError::Success); + WaitForClientConnection(); + + // Verify the client and server's exported key material match. + const size_t key_size = 64; + auto client_key_material = client_->ExportKeyingMaterial(key_size); + ASSERT_FALSE(client_key_material.empty()); + auto server_key_material = server_->ExportKeyingMaterial(key_size); + ASSERT_TRUE(!server_key_material.empty()); + ASSERT_EQ(client_key_material.size(), key_size); + ASSERT_EQ(client_key_material, server_key_material); +} + +TEST_F(AdbWifiTlsConnectionTest, SetCertVerifyCallback_ClientAcceptsServerRejects) { + // Client accepts all + client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; }); + // Server rejects all + server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; }); + // Client handshake should succeed, because in TLS 1.3, client does not + // realize that the peer rejected the certificate until after a read + // operation. + client_thread_ = std::thread([&]() { EXPECT_EQ(client_->DoHandshake(), TlsError::Success); }); + + // Server handshake should fail + EXPECT_EQ(server_->DoHandshake(), TlsError::CertificateRejected); + WaitForClientConnection(); +} + +TEST_F(AdbWifiTlsConnectionTest, SetCertVerifyCallback_ClientAcceptsServerRejects_PostHSCheck) { + // Client accepts all + client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; }); + // Client should now get a failure in the handshake + client_->EnableClientPostHandshakeCheck(true); + // Server rejects all + server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; }); + + // Client handshake should fail because server rejects everything + client_thread_ = std::thread( + [&]() { EXPECT_EQ(client_->DoHandshake(), TlsError::PeerRejectedCertificate); }); + + // Server handshake should fail + EXPECT_EQ(server_->DoHandshake(), TlsError::CertificateRejected); + WaitForClientConnection(); +} + +TEST_F(AdbWifiTlsConnectionTest, SetCertVerifyCallback_ClientRejectsServerAccepts) { + // Client rejects all + client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; }); + // Server accepts all + server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; }); + // Client handshake should fail + client_thread_ = std::thread( + [&]() { EXPECT_EQ(client_->DoHandshake(), TlsError::CertificateRejected); }); + + // Server handshake should fail + EXPECT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate); + WaitForClientConnection(); +} + +TEST_F(AdbWifiTlsConnectionTest, SetCertVerifyCallback_ClientRejectsServerAccepts_PostHSCheck) { + // Client rejects all + client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; }); + // This shouldn't affect the error types returned in the + // #SetCertVerifyCallback_ClientRejectsServerAccepts test, since + // the failure is still within the TLS 1.3 handshake. + client_->EnableClientPostHandshakeCheck(true); + // Server accepts all + server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; }); + + // Client handshake should fail + client_thread_ = std::thread( + [&]() { EXPECT_EQ(client_->DoHandshake(), TlsError::CertificateRejected); }); + + // Server handshake should fail + EXPECT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate); + WaitForClientConnection(); +} + +TEST_F(AdbWifiTlsConnectionTest, EnableClientPostHandshakeCheck_ClientWrongCert) { + // client's DoHandshake() will fail if the server rejected the certificate + client_->EnableClientPostHandshakeCheck(true); + + // Add peer certificates + EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert)); + + // Handshake should fail for client + StartClientHandshakeAsync(false); + + // Handshake should fail for server + EXPECT_NE(server_->DoHandshake(), TlsError::Success); + WaitForClientConnection(); + + // Client write fails, server read should fail + EXPECT_FALSE(client_->WriteFully( + std::string_view(reinterpret_cast(msg_.data()), msg_.size()))); + auto data = server_->ReadFully(msg_.size()); + EXPECT_EQ(data.size(), 0); + + // Client read, server write should fail + EXPECT_FALSE(server_->WriteFully( + std::string_view(reinterpret_cast(msg_.data()), msg_.size()))); + data = client_->ReadFully(msg_.size()); + EXPECT_EQ(data.size(), 0); +} + +TEST_F(AdbWifiTlsConnectionTest, SetClientCAList_Empty) { + // Setting an empty CA list should not crash + server_->SetClientCAList(nullptr); + ASSERT_DEATH( + { + // Client cannot use this API + client_->SetClientCAList(nullptr); + }, + ""); +} + +TEST_F(AdbWifiTlsConnectionTest, SetClientCAList_Smoke) { + auto bsslIssuerList = GetCAIssuerList(); + server_->SetClientCAList(bsslIssuerList.get()); + client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; }); + + client_thread_ = std::thread([&]() { + client_->SetCertificateCallback([&](SSL* ssl) -> int { + const STACK_OF(X509_NAME)* received = SSL_get_client_CA_list(ssl); + EXPECT_NE(received, nullptr); + const size_t num_names = sk_X509_NAME_num(received); + EXPECT_EQ(kCAIssuers.size(), num_names); + + // Client initially registered with the wrong key. Let's change it + // here to verify this callback actually changes the client + // certificate to the right one. + EXPECT_TRUE(TlsConnection::SetCertAndKey(ssl, kTestRsa2048UnknownCert, + kTestRsa2048UnknownPrivKey)); + + const size_t buf_size = 256; + uint8_t buf[buf_size]; + size_t idx = 0; + for (auto& issuer : kCAIssuers) { + auto* name = sk_X509_NAME_value(received, idx++); + for (auto& attr : issuer) { + EXPECT_EQ(X509_NAME_get_text_by_NID(name, attr.nid, + reinterpret_cast(buf), buf_size), + attr.val.size()); + std::vector out(buf, buf + attr.val.size()); + EXPECT_EQ(out, attr.val); + } + } + + return 1; + }); + // Client handshake should succeed + EXPECT_EQ(client_->DoHandshake(), TlsError::Success); + }); + + EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert)); + // Server handshake should succeed + EXPECT_EQ(server_->DoHandshake(), TlsError::Success); + client_thread_.join(); +} + +TEST_F(AdbWifiTlsConnectionTest, SetClientCAList_AdbCAList) { + bssl::UniquePtr ca_list(sk_X509_NAME_new_null()); + std::string keyhash = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + auto issuer = CreateCAIssuerFromEncodedKey(keyhash); + ASSERT_TRUE(bssl::PushToStack(ca_list.get(), std::move(issuer))); + server_->SetClientCAList(ca_list.get()); + client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; }); + + client_thread_ = std::thread([&]() { + client_->SetCertificateCallback([&](SSL* ssl) -> int { + // Client initially registered with a certificate that is not trusted by + // the server. Let's test that we can change the certificate to the + // trusted one here. + const STACK_OF(X509_NAME)* received = SSL_get_client_CA_list(ssl); + EXPECT_NE(received, nullptr); + const size_t num_names = sk_X509_NAME_num(received); + EXPECT_EQ(1, num_names); + + auto* name = sk_X509_NAME_value(received, 0); + EXPECT_NE(name, nullptr); + auto enc_key = ParseEncodedKeyFromCAIssuer(name); + EXPECT_EQ(keyhash, enc_key); + + return 1; + }); + // Client handshake should succeed + EXPECT_EQ(client_->DoHandshake(), TlsError::Success); + }); + + server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; }); + // Server handshake should succeed + EXPECT_EQ(server_->DoHandshake(), TlsError::Success); + client_thread_.join(); +} +} // namespace tls +} // namespace adb diff --git a/adb/tls/tls_connection.cpp b/adb/tls/tls_connection.cpp new file mode 100644 index 000000000..7df6ef410 --- /dev/null +++ b/adb/tls/tls_connection.cpp @@ -0,0 +1,387 @@ +/* + * 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/tls/tls_connection.h" + +#include +#include + +#include +#include +#include +#include + +using android::base::borrowed_fd; + +namespace adb { +namespace tls { + +namespace { + +static constexpr char kExportedKeyLabel[] = "adb-label"; + +class TlsConnectionImpl : public TlsConnection { + public: + explicit TlsConnectionImpl(Role role, std::string_view cert, std::string_view priv_key, + borrowed_fd fd); + ~TlsConnectionImpl() override; + + bool AddTrustedCertificate(std::string_view cert) override; + void SetCertVerifyCallback(CertVerifyCb cb) override; + void SetCertificateCallback(SetCertCb cb) override; + void SetClientCAList(STACK_OF(X509_NAME) * ca_list) override; + std::vector ExportKeyingMaterial(size_t length) override; + void EnableClientPostHandshakeCheck(bool enable) override; + TlsError DoHandshake() override; + std::vector ReadFully(size_t size) override; + bool ReadFully(void* buf, size_t size) override; + bool WriteFully(std::string_view data) override; + + static bssl::UniquePtr EvpPkeyFromPEM(std::string_view pem); + static bssl::UniquePtr BufferFromPEM(std::string_view pem); + + private: + static int SSLSetCertVerifyCb(X509_STORE_CTX* ctx, void* opaque); + static int SSLSetCertCb(SSL* ssl, void* opaque); + + static bssl::UniquePtr X509FromBuffer(bssl::UniquePtr buffer); + static const char* SSLErrorString(); + void Invalidate(); + TlsError GetFailureReason(int err); + + Role role_; + bssl::UniquePtr priv_key_; + bssl::UniquePtr cert_; + + bssl::UniquePtr ca_list_; + bssl::UniquePtr ssl_ctx_; + bssl::UniquePtr ssl_; + std::vector> known_certificates_; + bool client_verify_post_handshake_ = false; + + CertVerifyCb cert_verify_cb_; + SetCertCb set_cert_cb_; + borrowed_fd fd_; +}; // TlsConnectionImpl + +TlsConnectionImpl::TlsConnectionImpl(Role role, std::string_view cert, std::string_view priv_key, + borrowed_fd fd) + : role_(role), fd_(fd) { + CHECK(!cert.empty() && !priv_key.empty()); + LOG(INFO) << "Initializing adbwifi TlsConnection"; + cert_ = BufferFromPEM(cert); + priv_key_ = EvpPkeyFromPEM(priv_key); +} + +TlsConnectionImpl::~TlsConnectionImpl() { + // shutdown the SSL connection + if (ssl_ != nullptr) { + SSL_shutdown(ssl_.get()); + } +} + +// static +const char* TlsConnectionImpl::SSLErrorString() { + auto sslerr = ERR_peek_last_error(); + return ERR_reason_error_string(sslerr); +} + +// static +bssl::UniquePtr TlsConnectionImpl::EvpPkeyFromPEM(std::string_view pem) { + bssl::UniquePtr bio(BIO_new_mem_buf(pem.data(), pem.size())); + return bssl::UniquePtr(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); +} + +// static +bssl::UniquePtr TlsConnectionImpl::BufferFromPEM(std::string_view pem) { + bssl::UniquePtr bio(BIO_new_mem_buf(pem.data(), pem.size())); + char* name = nullptr; + char* header = nullptr; + uint8_t* data = nullptr; + long data_len = 0; + + if (!PEM_read_bio(bio.get(), &name, &header, &data, &data_len)) { + LOG(ERROR) << "Failed to read certificate"; + return nullptr; + } + OPENSSL_free(name); + OPENSSL_free(header); + + auto ret = bssl::UniquePtr(CRYPTO_BUFFER_new(data, data_len, nullptr)); + OPENSSL_free(data); + return ret; +} + +// static +bssl::UniquePtr TlsConnectionImpl::X509FromBuffer(bssl::UniquePtr buffer) { + if (!buffer) { + return nullptr; + } + return bssl::UniquePtr(X509_parse_from_buffer(buffer.get())); +} + +// static +int TlsConnectionImpl::SSLSetCertVerifyCb(X509_STORE_CTX* ctx, void* opaque) { + auto* p = reinterpret_cast(opaque); + return p->cert_verify_cb_(ctx); +} + +// static +int TlsConnectionImpl::SSLSetCertCb(SSL* ssl, void* opaque) { + auto* p = reinterpret_cast(opaque); + return p->set_cert_cb_(ssl); +} + +bool TlsConnectionImpl::AddTrustedCertificate(std::string_view cert) { + // Create X509 buffer from the certificate string + auto buf = X509FromBuffer(BufferFromPEM(cert)); + if (buf == nullptr) { + LOG(ERROR) << "Failed to create a X509 buffer for the certificate."; + return false; + } + known_certificates_.push_back(std::move(buf)); + return true; +} + +void TlsConnectionImpl::SetCertVerifyCallback(CertVerifyCb cb) { + cert_verify_cb_ = cb; +} + +void TlsConnectionImpl::SetCertificateCallback(SetCertCb cb) { + set_cert_cb_ = cb; +} + +void TlsConnectionImpl::SetClientCAList(STACK_OF(X509_NAME) * ca_list) { + CHECK(role_ == Role::Server); + ca_list_.reset(ca_list != nullptr ? SSL_dup_CA_list(ca_list) : nullptr); +} + +std::vector TlsConnectionImpl::ExportKeyingMaterial(size_t length) { + if (ssl_.get() == nullptr) { + return {}; + } + + std::vector out(length); + if (SSL_export_keying_material(ssl_.get(), out.data(), out.size(), kExportedKeyLabel, + sizeof(kExportedKeyLabel), nullptr, 0, false) == 0) { + return {}; + } + return out; +} + +void TlsConnectionImpl::EnableClientPostHandshakeCheck(bool enable) { + client_verify_post_handshake_ = enable; +} + +TlsConnection::TlsError TlsConnectionImpl::GetFailureReason(int err) { + switch (ERR_GET_REASON(err)) { + case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE: + case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE: + case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED: + case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED: + case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN: + case SSL_R_TLSV1_ALERT_ACCESS_DENIED: + case SSL_R_TLSV1_ALERT_UNKNOWN_CA: + case SSL_R_TLSV1_CERTIFICATE_REQUIRED: + return TlsError::PeerRejectedCertificate; + case SSL_R_CERTIFICATE_VERIFY_FAILED: + return TlsError::CertificateRejected; + default: + return TlsError::UnknownFailure; + } +} + +TlsConnection::TlsError TlsConnectionImpl::DoHandshake() { + int err = -1; + LOG(INFO) << "Starting adbwifi tls handshake"; + ssl_ctx_.reset(SSL_CTX_new(TLS_method())); + // TODO: Remove set_max_proto_version() once external/boringssl is updated + // past + // https://boringssl.googlesource.com/boringssl/+/58d56f4c59969a23e5f52014e2651c76fea2f877 + if (ssl_ctx_.get() == nullptr || + !SSL_CTX_set_min_proto_version(ssl_ctx_.get(), TLS1_3_VERSION) || + !SSL_CTX_set_max_proto_version(ssl_ctx_.get(), TLS1_3_VERSION)) { + LOG(ERROR) << "Failed to create SSL context"; + return TlsError::UnknownFailure; + } + + // Register user-supplied known certificates + for (auto const& cert : known_certificates_) { + if (X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx_.get()), cert.get()) == 0) { + LOG(ERROR) << "Unable to add certificates into the X509_STORE"; + return TlsError::UnknownFailure; + } + } + + // Custom certificate verification + if (cert_verify_cb_) { + SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), SSLSetCertVerifyCb, this); + } + + // set select certificate callback, if any. + if (set_cert_cb_) { + SSL_CTX_set_cert_cb(ssl_ctx_.get(), SSLSetCertCb, this); + } + + // Server-allowed client CA list + if (ca_list_ != nullptr) { + bssl::UniquePtr names(SSL_dup_CA_list(ca_list_.get())); + SSL_CTX_set_client_CA_list(ssl_ctx_.get(), names.release()); + } + + // Register our certificate and private key. + std::vector cert_chain = { + cert_.get(), + }; + if (!SSL_CTX_set_chain_and_key(ssl_ctx_.get(), cert_chain.data(), cert_chain.size(), + priv_key_.get(), nullptr)) { + LOG(ERROR) << "Unable to register the certificate chain file and private key [" + << SSLErrorString() << "]"; + Invalidate(); + return TlsError::UnknownFailure; + } + + SSL_CTX_set_verify(ssl_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); + + // Okay! Let's try to do the handshake! + ssl_.reset(SSL_new(ssl_ctx_.get())); + if (!SSL_set_fd(ssl_.get(), fd_.get())) { + LOG(ERROR) << "SSL_set_fd failed. [" << SSLErrorString() << "]"; + return TlsError::UnknownFailure; + } + switch (role_) { + case Role::Server: + err = SSL_accept(ssl_.get()); + break; + case Role::Client: + err = SSL_connect(ssl_.get()); + break; + } + if (err != 1) { + LOG(ERROR) << "Handshake failed in SSL_accept/SSL_connect [" << SSLErrorString() << "]"; + auto sslerr = ERR_get_error(); + Invalidate(); + return GetFailureReason(sslerr); + } + + if (client_verify_post_handshake_ && role_ == Role::Client) { + uint8_t check; + // Try to peek one byte for any failures. This assumes on success that + // the server actually sends something. + err = SSL_peek(ssl_.get(), &check, 1); + if (err <= 0) { + LOG(ERROR) << "Post-handshake SSL_peek failed [" << SSLErrorString() << "]"; + auto sslerr = ERR_get_error(); + Invalidate(); + return GetFailureReason(sslerr); + } + } + + LOG(INFO) << "Handshake succeeded."; + return TlsError::Success; +} + +void TlsConnectionImpl::Invalidate() { + ssl_.reset(); + ssl_ctx_.reset(); +} + +std::vector TlsConnectionImpl::ReadFully(size_t size) { + std::vector buf(size); + if (!ReadFully(buf.data(), buf.size())) { + return {}; + } + + return buf; +} + +bool TlsConnectionImpl::ReadFully(void* buf, size_t size) { + CHECK_GT(size, 0U); + if (!ssl_) { + LOG(ERROR) << "Tried to read on a null SSL connection"; + return false; + } + + size_t offset = 0; + uint8_t* p8 = reinterpret_cast(buf); + while (size > 0) { + int bytes_read = + SSL_read(ssl_.get(), p8 + offset, std::min(static_cast(INT_MAX), size)); + if (bytes_read <= 0) { + LOG(WARNING) << "SSL_read failed [" << SSLErrorString() << "]"; + return false; + } + size -= bytes_read; + offset += bytes_read; + } + return true; +} + +bool TlsConnectionImpl::WriteFully(std::string_view data) { + CHECK(!data.empty()); + if (!ssl_) { + LOG(ERROR) << "Tried to read on a null SSL connection"; + return false; + } + + while (!data.empty()) { + int bytes_out = SSL_write(ssl_.get(), data.data(), + std::min(static_cast(INT_MAX), data.size())); + if (bytes_out <= 0) { + LOG(WARNING) << "SSL_write failed [" << SSLErrorString() << "]"; + return false; + } + data = data.substr(bytes_out); + } + return true; +} +} // namespace + +// static +std::unique_ptr TlsConnection::Create(TlsConnection::Role role, + std::string_view cert, + std::string_view priv_key, borrowed_fd fd) { + CHECK(!cert.empty()); + CHECK(!priv_key.empty()); + + return std::make_unique(role, cert, priv_key, fd); +} + +// static +bool TlsConnection::SetCertAndKey(SSL* ssl, std::string_view cert, std::string_view priv_key) { + CHECK(ssl); + // Note: declaring these in local scope is okay because + // SSL_set_chain_and_key will increase the refcount (bssl::UpRef). + auto x509_cert = TlsConnectionImpl::BufferFromPEM(cert); + auto evp_pkey = TlsConnectionImpl::EvpPkeyFromPEM(priv_key); + if (x509_cert == nullptr || evp_pkey == nullptr) { + return false; + } + + std::vector cert_chain = { + x509_cert.get(), + }; + if (!SSL_set_chain_and_key(ssl, cert_chain.data(), cert_chain.size(), evp_pkey.get(), + nullptr)) { + LOG(ERROR) << "SSL_set_chain_and_key failed"; + return false; + } + + return true; +} + +} // namespace tls +} // namespace adb