Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6793f617 authored by Tianjie Xu's avatar Tianjie Xu Committed by Gerrit Code Review
Browse files

Merge "Add sanity check when loading public keys for OTA package"

parents c17c8196 b5110de1
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@
#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/nid.h>
#include <ziparchive/zip_writer.h>

#include "common/test_constants.h"
@@ -148,6 +151,35 @@ TEST(VerifierTest, LoadCertificateFromBuffer_sha256_ec256bits) {
  VerifyPackageWithSingleCertificate("otasigned_v5.zip", std::move(cert));
}

TEST(VerifierTest, LoadCertificateFromBuffer_check_rsa_keys) {
  std::unique_ptr<RSA, RSADeleter> rsa(RSA_new());
  std::unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), BN_free);
  BN_set_word(exponent.get(), 3);
  RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr);
  ASSERT_TRUE(CheckRSAKey(rsa));

  // Exponent is expected to be 3 or 65537
  BN_set_word(exponent.get(), 17);
  RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr);
  ASSERT_FALSE(CheckRSAKey(rsa));

  // Modulus is expected to be 2048.
  BN_set_word(exponent.get(), 3);
  RSA_generate_key_ex(rsa.get(), 1024, exponent.get(), nullptr);
  ASSERT_FALSE(CheckRSAKey(rsa));
}

TEST(VerifierTest, LoadCertificateFromBuffer_check_ec_keys) {
  std::unique_ptr<EC_KEY, ECKEYDeleter> ec(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
  ASSERT_EQ(1, EC_KEY_generate_key(ec.get()));
  ASSERT_TRUE(CheckECKey(ec));

  // Expects 256-bit EC key with curve NIST P-256
  ec.reset(EC_KEY_new_by_curve_name(NID_secp224r1));
  ASSERT_EQ(1, EC_KEY_generate_key(ec.get()));
  ASSERT_FALSE(CheckECKey(ec));
}

TEST(VerifierTest, LoadKeysFromZipfile_empty_archive) {
  TemporaryFile otacerts;
  BuildCertificateArchive({}, otacerts.release());
+46 −6
Original line number Diff line number Diff line
@@ -500,6 +500,48 @@ std::vector<Certificate> LoadKeysFromZipfile(const std::string& zip_name) {
  return result;
}

bool CheckRSAKey(const std::unique_ptr<RSA, RSADeleter>& rsa) {
  if (!rsa) {
    return false;
  }

  const BIGNUM* out_n;
  const BIGNUM* out_e;
  RSA_get0_key(rsa.get(), &out_n, &out_e, nullptr /* private exponent */);
  auto modulus_bits = BN_num_bits(out_n);
  if (modulus_bits != 2048) {
    LOG(ERROR) << "Modulus should be 2048 bits long, actual: " << modulus_bits;
    return false;
  }

  BN_ULONG exponent = BN_get_word(out_e);
  if (exponent != 3 && exponent != 65537) {
    LOG(ERROR) << "Public exponent should be 3 or 65537, actual: " << exponent;
    return false;
  }

  return true;
}

bool CheckECKey(const std::unique_ptr<EC_KEY, ECKEYDeleter>& ec_key) {
  if (!ec_key) {
    return false;
  }

  const EC_GROUP* ec_group = EC_KEY_get0_group(ec_key.get());
  if (!ec_group) {
    LOG(ERROR) << "Failed to get the ec_group from the ec_key";
    return false;
  }
  auto degree = EC_GROUP_get_degree(ec_group);
  if (degree != 256) {
    LOG(ERROR) << "Field size of the ec key should be 256 bits long, actual: " << degree;
    return false;
  }

  return true;
}

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);
@@ -538,22 +580,20 @@ bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certific
  }

  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";
    if (!cert->rsa || !CheckRSAKey(cert->rsa)) {
      LOG(ERROR) << "Failed to validate 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";
    if (!cert->ec || !CheckECKey(cert->ec)) {
      LOG(ERROR) << "Failed to validate the ec key info from the public key";
      return false;
    }
  } else {
+6 −0
Original line number Diff line number Diff line
@@ -72,6 +72,12 @@ int verify_file(const unsigned char* addr, size_t length, const std::vector<Cert

bool load_keys(const char* filename, std::vector<Certificate>& certs);

// Checks that the RSA key has a modulus of 2048 bits long, and public exponent is 3 or 65537.
bool CheckRSAKey(const std::unique_ptr<RSA, RSADeleter>& rsa);

// Checks that the field size of the curve for the EC key is 256 bits.
bool CheckECKey(const std::unique_ptr<EC_KEY, ECKEYDeleter>& ec_key);

// 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);