Merge "Add function to load the key from x509.pem file"
This commit is contained in:
commit
ff89e0004f
3 changed files with 158 additions and 0 deletions
|
@ -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"
|
||||
|
@ -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 {
|
||||
|
|
68
verifier.cpp
68
verifier.cpp
|
@ -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"
|
||||
|
@ -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:
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#ifndef _RECOVERY_VERIFIER_H
|
||||
#define _RECOVERY_VERIFIER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
@ -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…
Reference in a new issue