Loading tests/component/verifier_test.cpp +84 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <android-base/file.h> #include <android-base/stringprintf.h> #include <android-base/test_utils.h> #include <android-base/unique_fd.h> #include <gtest/gtest.h> #include "common/test_constants.h" Loading @@ -35,6 +36,89 @@ using namespace std::string_literals; static void LoadKeyFromFile(const std::string& file_name, Certificate* cert) { std::string testkey_string; ASSERT_TRUE(android::base::ReadFileToString(file_name, &testkey_string)); ASSERT_TRUE(LoadCertificateFromBuffer( std::vector<uint8_t>(testkey_string.begin(), testkey_string.end()), cert)); } static void VerifyPackageWithCertificate(const std::string& name, Certificate&& cert) { std::string package = from_testdata_base(name); MemMapping memmap; if (!memmap.MapFile(package)) { FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n"; } std::vector<Certificate> certs; certs.emplace_back(std::move(cert)); ASSERT_EQ(VERIFY_SUCCESS, verify_file(memmap.addr, memmap.length, certs)); } TEST(VerifierTest, LoadCertificateFromBuffer_failure) { Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); std::string testkey_string; ASSERT_TRUE( android::base::ReadFileToString(from_testdata_base("testkey_v1.txt"), &testkey_string)); ASSERT_FALSE(LoadCertificateFromBuffer( std::vector<uint8_t>(testkey_string.begin(), testkey_string.end()), &cert)); } TEST(VerifierTest, LoadCertificateFromBuffer_sha1_exponent3) { Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); LoadKeyFromFile(from_testdata_base("testkey_v1.x509.pem"), &cert); ASSERT_EQ(SHA_DIGEST_LENGTH, cert.hash_len); ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); ASSERT_EQ(nullptr, cert.ec); VerifyPackageWithCertificate("otasigned_v1.zip", std::move(cert)); } TEST(VerifierTest, LoadCertificateFromBuffer_sha1_exponent65537) { Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); LoadKeyFromFile(from_testdata_base("testkey_v2.x509.pem"), &cert); ASSERT_EQ(SHA_DIGEST_LENGTH, cert.hash_len); ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); ASSERT_EQ(nullptr, cert.ec); VerifyPackageWithCertificate("otasigned_v2.zip", std::move(cert)); } TEST(VerifierTest, LoadCertificateFromBuffer_sha256_exponent3) { Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &cert); ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); ASSERT_EQ(nullptr, cert.ec); VerifyPackageWithCertificate("otasigned_v3.zip", std::move(cert)); } TEST(VerifierTest, LoadCertificateFromBuffer_sha256_exponent65537) { Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); LoadKeyFromFile(from_testdata_base("testkey_v4.x509.pem"), &cert); ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); ASSERT_EQ(nullptr, cert.ec); VerifyPackageWithCertificate("otasigned_v4.zip", std::move(cert)); } TEST(VerifierTest, LoadCertificateFromBuffer_sha256_ec256bits) { Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); LoadKeyFromFile(from_testdata_base("testkey_v5.x509.pem"), &cert); ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); ASSERT_EQ(Certificate::KEY_TYPE_EC, cert.key_type); ASSERT_EQ(nullptr, cert.rsa); VerifyPackageWithCertificate("otasigned_v5.zip", std::move(cert)); } class VerifierTest : public testing::TestWithParam<std::vector<std::string>> { protected: void SetUp() override { Loading verifier.cpp +68 −0 Original line number Diff line number Diff line Loading @@ -27,9 +27,13 @@ #include <vector> #include <android-base/logging.h> #include <openssl/bio.h> #include <openssl/bn.h> #include <openssl/ecdsa.h> #include <openssl/evp.h> #include <openssl/obj_mac.h> #include <openssl/pem.h> #include <openssl/rsa.h> #include "asn1_decoder.h" #include "otautil/print_sha1.h" Loading Loading @@ -441,6 +445,70 @@ std::unique_ptr<EC_KEY, ECKEYDeleter> parse_ec_key(FILE* file) { return key; } bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certificate* cert) { std::unique_ptr<BIO, decltype(&BIO_free)> content( BIO_new_mem_buf(pem_content.data(), pem_content.size()), BIO_free); std::unique_ptr<X509, decltype(&X509_free)> x509( PEM_read_bio_X509(content.get(), nullptr, nullptr, nullptr), X509_free); if (!x509) { LOG(ERROR) << "Failed to read x509 certificate"; return false; } int nid = X509_get_signature_nid(x509.get()); switch (nid) { // SignApk has historically accepted md5WithRSA certificates, but treated them as // sha1WithRSA anyway. Continue to do so for backwards compatibility. case NID_md5WithRSA: case NID_md5WithRSAEncryption: case NID_sha1WithRSA: case NID_sha1WithRSAEncryption: cert->hash_len = SHA_DIGEST_LENGTH; break; case NID_sha256WithRSAEncryption: case NID_ecdsa_with_SHA256: cert->hash_len = SHA256_DIGEST_LENGTH; break; default: LOG(ERROR) << "Unrecognized signature nid " << OBJ_nid2ln(nid); return false; } std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> public_key(X509_get_pubkey(x509.get()), EVP_PKEY_free); if (!public_key) { LOG(ERROR) << "Failed to extract the public key from x509 certificate"; return false; } int key_type = EVP_PKEY_id(public_key.get()); // TODO(xunchang) check the rsa key has exponent 3 or 65537 with RSA_get0_key; and ec key is // 256 bits. if (key_type == EVP_PKEY_RSA) { cert->key_type = Certificate::KEY_TYPE_RSA; cert->ec.reset(); cert->rsa.reset(EVP_PKEY_get1_RSA(public_key.get())); if (!cert->rsa) { LOG(ERROR) << "Failed to get the rsa key info from public key"; return false; } } else if (key_type == EVP_PKEY_EC) { cert->key_type = Certificate::KEY_TYPE_EC; cert->rsa.reset(); cert->ec.reset(EVP_PKEY_get1_EC_KEY(public_key.get())); if (!cert->ec) { LOG(ERROR) << "Failed to get the ec key info from the public key"; return false; } } else { LOG(ERROR) << "Unrecognized public key type " << OBJ_nid2ln(key_type); return false; } return true; } // Reads a file containing one or more public keys as produced by // DumpPublicKey: this is an RSAPublicKey struct as it would appear // as a C source literal, eg: Loading verifier.h +6 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #ifndef _RECOVERY_VERIFIER_H #define _RECOVERY_VERIFIER_H #include <stdint.h> #include <functional> #include <memory> #include <vector> Loading Loading @@ -70,6 +72,10 @@ int verify_file(const unsigned char* addr, size_t length, const std::vector<Cert bool load_keys(const char* filename, std::vector<Certificate>& certs); // Parses a PEM-encoded x509 certificate from the given buffer and saves it into |cert|. Returns // false if there is a parsing failure or the signature's encryption algorithm is not supported. bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certificate* cert); #define VERIFY_SUCCESS 0 #define VERIFY_FAILURE 1 Loading Loading
tests/component/verifier_test.cpp +84 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <android-base/file.h> #include <android-base/stringprintf.h> #include <android-base/test_utils.h> #include <android-base/unique_fd.h> #include <gtest/gtest.h> #include "common/test_constants.h" Loading @@ -35,6 +36,89 @@ using namespace std::string_literals; static void LoadKeyFromFile(const std::string& file_name, Certificate* cert) { std::string testkey_string; ASSERT_TRUE(android::base::ReadFileToString(file_name, &testkey_string)); ASSERT_TRUE(LoadCertificateFromBuffer( std::vector<uint8_t>(testkey_string.begin(), testkey_string.end()), cert)); } static void VerifyPackageWithCertificate(const std::string& name, Certificate&& cert) { std::string package = from_testdata_base(name); MemMapping memmap; if (!memmap.MapFile(package)) { FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n"; } std::vector<Certificate> certs; certs.emplace_back(std::move(cert)); ASSERT_EQ(VERIFY_SUCCESS, verify_file(memmap.addr, memmap.length, certs)); } TEST(VerifierTest, LoadCertificateFromBuffer_failure) { Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); std::string testkey_string; ASSERT_TRUE( android::base::ReadFileToString(from_testdata_base("testkey_v1.txt"), &testkey_string)); ASSERT_FALSE(LoadCertificateFromBuffer( std::vector<uint8_t>(testkey_string.begin(), testkey_string.end()), &cert)); } TEST(VerifierTest, LoadCertificateFromBuffer_sha1_exponent3) { Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); LoadKeyFromFile(from_testdata_base("testkey_v1.x509.pem"), &cert); ASSERT_EQ(SHA_DIGEST_LENGTH, cert.hash_len); ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); ASSERT_EQ(nullptr, cert.ec); VerifyPackageWithCertificate("otasigned_v1.zip", std::move(cert)); } TEST(VerifierTest, LoadCertificateFromBuffer_sha1_exponent65537) { Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); LoadKeyFromFile(from_testdata_base("testkey_v2.x509.pem"), &cert); ASSERT_EQ(SHA_DIGEST_LENGTH, cert.hash_len); ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); ASSERT_EQ(nullptr, cert.ec); VerifyPackageWithCertificate("otasigned_v2.zip", std::move(cert)); } TEST(VerifierTest, LoadCertificateFromBuffer_sha256_exponent3) { Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &cert); ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); ASSERT_EQ(nullptr, cert.ec); VerifyPackageWithCertificate("otasigned_v3.zip", std::move(cert)); } TEST(VerifierTest, LoadCertificateFromBuffer_sha256_exponent65537) { Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); LoadKeyFromFile(from_testdata_base("testkey_v4.x509.pem"), &cert); ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); ASSERT_EQ(nullptr, cert.ec); VerifyPackageWithCertificate("otasigned_v4.zip", std::move(cert)); } TEST(VerifierTest, LoadCertificateFromBuffer_sha256_ec256bits) { Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); LoadKeyFromFile(from_testdata_base("testkey_v5.x509.pem"), &cert); ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); ASSERT_EQ(Certificate::KEY_TYPE_EC, cert.key_type); ASSERT_EQ(nullptr, cert.rsa); VerifyPackageWithCertificate("otasigned_v5.zip", std::move(cert)); } class VerifierTest : public testing::TestWithParam<std::vector<std::string>> { protected: void SetUp() override { Loading
verifier.cpp +68 −0 Original line number Diff line number Diff line Loading @@ -27,9 +27,13 @@ #include <vector> #include <android-base/logging.h> #include <openssl/bio.h> #include <openssl/bn.h> #include <openssl/ecdsa.h> #include <openssl/evp.h> #include <openssl/obj_mac.h> #include <openssl/pem.h> #include <openssl/rsa.h> #include "asn1_decoder.h" #include "otautil/print_sha1.h" Loading Loading @@ -441,6 +445,70 @@ std::unique_ptr<EC_KEY, ECKEYDeleter> parse_ec_key(FILE* file) { return key; } bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certificate* cert) { std::unique_ptr<BIO, decltype(&BIO_free)> content( BIO_new_mem_buf(pem_content.data(), pem_content.size()), BIO_free); std::unique_ptr<X509, decltype(&X509_free)> x509( PEM_read_bio_X509(content.get(), nullptr, nullptr, nullptr), X509_free); if (!x509) { LOG(ERROR) << "Failed to read x509 certificate"; return false; } int nid = X509_get_signature_nid(x509.get()); switch (nid) { // SignApk has historically accepted md5WithRSA certificates, but treated them as // sha1WithRSA anyway. Continue to do so for backwards compatibility. case NID_md5WithRSA: case NID_md5WithRSAEncryption: case NID_sha1WithRSA: case NID_sha1WithRSAEncryption: cert->hash_len = SHA_DIGEST_LENGTH; break; case NID_sha256WithRSAEncryption: case NID_ecdsa_with_SHA256: cert->hash_len = SHA256_DIGEST_LENGTH; break; default: LOG(ERROR) << "Unrecognized signature nid " << OBJ_nid2ln(nid); return false; } std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> public_key(X509_get_pubkey(x509.get()), EVP_PKEY_free); if (!public_key) { LOG(ERROR) << "Failed to extract the public key from x509 certificate"; return false; } int key_type = EVP_PKEY_id(public_key.get()); // TODO(xunchang) check the rsa key has exponent 3 or 65537 with RSA_get0_key; and ec key is // 256 bits. if (key_type == EVP_PKEY_RSA) { cert->key_type = Certificate::KEY_TYPE_RSA; cert->ec.reset(); cert->rsa.reset(EVP_PKEY_get1_RSA(public_key.get())); if (!cert->rsa) { LOG(ERROR) << "Failed to get the rsa key info from public key"; return false; } } else if (key_type == EVP_PKEY_EC) { cert->key_type = Certificate::KEY_TYPE_EC; cert->rsa.reset(); cert->ec.reset(EVP_PKEY_get1_EC_KEY(public_key.get())); if (!cert->ec) { LOG(ERROR) << "Failed to get the ec key info from the public key"; return false; } } else { LOG(ERROR) << "Unrecognized public key type " << OBJ_nid2ln(key_type); return false; } return true; } // Reads a file containing one or more public keys as produced by // DumpPublicKey: this is an RSAPublicKey struct as it would appear // as a C source literal, eg: Loading
verifier.h +6 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #ifndef _RECOVERY_VERIFIER_H #define _RECOVERY_VERIFIER_H #include <stdint.h> #include <functional> #include <memory> #include <vector> Loading Loading @@ -70,6 +72,10 @@ int verify_file(const unsigned char* addr, size_t length, const std::vector<Cert bool load_keys(const char* filename, std::vector<Certificate>& certs); // Parses a PEM-encoded x509 certificate from the given buffer and saves it into |cert|. Returns // false if there is a parsing failure or the signature's encryption algorithm is not supported. bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certificate* cert); #define VERIFY_SUCCESS 0 #define VERIFY_FAILURE 1 Loading