Support signing payload with 4096 bits RSA keys am: cda3c034bc
am: 5bb6cf142b
Change-Id: Icbef166e2fa4d96784f605e803cf53d52ee18a26
diff --git a/Android.bp b/Android.bp
index dac1acd..8b1730b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -582,14 +582,17 @@
genrule {
name: "ue_unittest_keys",
cmd: "openssl rsa -in $(location unittest_key.pem) -pubout -out $(location unittest_key.pub.pem) &&" +
- "openssl rsa -in $(location unittest_key2.pem) -pubout -out $(location unittest_key2.pub.pem)",
+ "openssl rsa -in $(location unittest_key2.pem) -pubout -out $(location unittest_key2.pub.pem) &&" +
+ "openssl rsa -in $(location unittest_key_RSA4096.pem) -pubout -out $(location unittest_key_RSA4096.pub.pem)",
srcs: [
"unittest_key.pem",
"unittest_key2.pem",
+ "unittest_key_RSA4096.pem",
],
out: [
"unittest_key.pub.pem",
"unittest_key2.pub.pem",
+ "unittest_key_RSA4096.pub.pem",
],
}
@@ -641,6 +644,7 @@
":ue_unittest_keys",
"unittest_key.pem",
"unittest_key2.pem",
+ "unittest_key_RSA4096.pem",
"update_engine.conf",
],
diff --git a/common/constants.h b/common/constants.h
index d5a8ae3..af1c0ab 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -197,6 +197,9 @@
const int kDownloadConnectTimeoutSeconds = 30;
const int kDownloadP2PConnectTimeoutSeconds = 5;
+// Size in bytes of SHA256 hash.
+const int kSHA256Size = 32;
+
} // namespace chromeos_update_engine
#endif // UPDATE_ENGINE_COMMON_CONSTANTS_H_
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index ae73d03..d76a959 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -1816,9 +1816,7 @@
!signatures_message_data_.empty());
brillo::Blob hash_data = signed_hash_calculator_.raw_hash();
TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError,
- PayloadVerifier::PadRSA2048SHA256Hash(&hash_data));
- TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError,
- !hash_data.empty());
+ hash_data.size() == kSHA256Size);
if (!PayloadVerifier::VerifySignature(
signatures_message_data_, public_key, hash_data)) {
diff --git a/payload_consumer/payload_metadata.cc b/payload_consumer/payload_metadata.cc
index 8b3eb4e..3739767 100644
--- a/payload_consumer/payload_metadata.cc
+++ b/payload_consumer/payload_metadata.cc
@@ -20,6 +20,7 @@
#include <brillo/data_encoding.h>
+#include "update_engine/common/constants.h"
#include "update_engine/common/hash_calculator.h"
#include "update_engine/common/utils.h"
#include "update_engine/payload_consumer/payload_constants.h"
@@ -187,16 +188,16 @@
return ErrorCode::kDownloadMetadataSignatureMissingError;
}
- brillo::Blob calculated_metadata_hash;
+ brillo::Blob metadata_hash;
if (!HashCalculator::RawHashOfBytes(
- payload.data(), metadata_size_, &calculated_metadata_hash)) {
+ payload.data(), metadata_size_, &metadata_hash)) {
LOG(ERROR) << "Unable to compute actual hash of manifest";
return ErrorCode::kDownloadMetadataSignatureVerificationError;
}
- PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash);
- if (calculated_metadata_hash.empty()) {
- LOG(ERROR) << "Computed actual hash of metadata is empty.";
+ if (metadata_hash.size() != kSHA256Size) {
+ LOG(ERROR) << "Computed actual hash of metadata has incorrect size: "
+ << metadata_hash.size();
return ErrorCode::kDownloadMetadataSignatureVerificationError;
}
@@ -207,17 +208,25 @@
LOG(ERROR) << "Unable to compute expected hash from metadata signature";
return ErrorCode::kDownloadMetadataSignatureError;
}
- if (calculated_metadata_hash != expected_metadata_hash) {
+
+ brillo::Blob padded_metadata_hash = metadata_hash;
+ if (!PayloadVerifier::PadRSASHA256Hash(&padded_metadata_hash,
+ expected_metadata_hash.size())) {
+ LOG(ERROR) << "Failed to pad the SHA256 hash to "
+ << expected_metadata_hash.size() << " bytes.";
+ return ErrorCode::kDownloadMetadataSignatureVerificationError;
+ }
+
+ if (padded_metadata_hash != expected_metadata_hash) {
LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
utils::HexDumpVector(expected_metadata_hash);
LOG(ERROR) << "Calculated hash = ";
- utils::HexDumpVector(calculated_metadata_hash);
+ utils::HexDumpVector(padded_metadata_hash);
return ErrorCode::kDownloadMetadataSignatureMismatch;
}
} else {
- if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf,
- pem_public_key,
- calculated_metadata_hash)) {
+ if (!PayloadVerifier::VerifySignature(
+ metadata_signature_protobuf, pem_public_key, metadata_hash)) {
LOG(ERROR) << "Manifest hash verification failed.";
return ErrorCode::kDownloadMetadataSignatureMismatch;
}
diff --git a/payload_consumer/payload_verifier.cc b/payload_consumer/payload_verifier.cc
index 3eb1da8..3a3ccbf 100644
--- a/payload_consumer/payload_verifier.cc
+++ b/payload_consumer/payload_verifier.cc
@@ -16,11 +16,13 @@
#include "update_engine/payload_consumer/payload_verifier.h"
+#include <utility>
#include <vector>
#include <base/logging.h>
#include <openssl/pem.h>
+#include "update_engine/common/constants.h"
#include "update_engine/common/hash_calculator.h"
#include "update_engine/common/utils.h"
#include "update_engine/update_metadata.pb.h"
@@ -31,57 +33,27 @@
namespace {
-// The following is a standard PKCS1-v1_5 padding for SHA256 signatures, as
-// defined in RFC3447. It is prepended to the actual signature (32 bytes) to
-// form a sequence of 256 bytes (2048 bits) that is amenable to RSA signing. The
-// padded hash will look as follows:
+// The ASN.1 DigestInfo prefix for encoding SHA256 digest. The complete 51-byte
+// DigestInfo consists of 19-byte SHA256_DIGEST_INFO_PREFIX and 32-byte SHA256
+// digest.
//
-// 0x00 0x01 0xff ... 0xff 0x00 ASN1HEADER SHA256HASH
-// |--------------205-----------||----19----||----32----|
-//
-// where ASN1HEADER is the ASN.1 description of the signed data. The complete 51
-// bytes of actual data (i.e. the ASN.1 header complete with the hash) are
-// packed as follows:
-//
-// SEQUENCE(2+49) {
+// SEQUENCE(2+49) {
// SEQUENCE(2+13) {
-// OBJECT(2+9) id-sha256
-// NULL(2+0)
+// OBJECT(2+9) id-sha256
+// NULL(2+0)
// }
// OCTET STRING(2+32) <actual signature bytes...>
-// }
-// clang-format off
-const uint8_t kRSA2048SHA256Padding[] = {
- // PKCS1-v1_5 padding
- 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0x00,
- // ASN.1 header
- 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
- 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20,
+// }
+const uint8_t kSHA256DigestInfoPrefix[] = {
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+ 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20,
};
-// clang-format on
} // namespace
bool PayloadVerifier::VerifySignature(const string& signature_proto,
const string& pem_public_key,
- const brillo::Blob& hash_data) {
+ const brillo::Blob& sha256_hash_data) {
Signatures signatures;
LOG(INFO) << "signature blob size = " << signature_proto.size();
TEST_AND_RETURN_FALSE(signatures.ParseFromString(signature_proto));
@@ -100,7 +72,9 @@
if (!GetRawHashFromSignature(sig_data, pem_public_key, &sig_hash_data))
continue;
- if (hash_data == sig_hash_data) {
+ brillo::Blob padded_hash_data = sha256_hash_data;
+ if (PadRSASHA256Hash(&padded_hash_data, sig_hash_data.size()) &&
+ padded_hash_data == sig_hash_data) {
LOG(INFO) << "Verified correct signature " << i + 1 << " out of "
<< signatures.signatures_size() << " signatures.";
return true;
@@ -108,8 +82,8 @@
tested_hashes.push_back(sig_hash_data);
}
LOG(ERROR) << "None of the " << signatures.signatures_size()
- << " signatures is correct. Expected:";
- utils::HexDumpVector(hash_data);
+ << " signatures is correct. Expected hash before padding:";
+ utils::HexDumpVector(sha256_hash_data);
LOG(ERROR) << "But found decrypted hashes:";
for (const auto& sig_hash_data : tested_hashes) {
utils::HexDumpVector(sig_hash_data);
@@ -150,13 +124,30 @@
return true;
}
-bool PayloadVerifier::PadRSA2048SHA256Hash(brillo::Blob* hash) {
- TEST_AND_RETURN_FALSE(hash->size() == 32);
- hash->insert(hash->begin(),
- reinterpret_cast<const char*>(kRSA2048SHA256Padding),
- reinterpret_cast<const char*>(kRSA2048SHA256Padding +
- sizeof(kRSA2048SHA256Padding)));
- TEST_AND_RETURN_FALSE(hash->size() == 256);
+bool PayloadVerifier::PadRSASHA256Hash(brillo::Blob* hash, size_t rsa_size) {
+ TEST_AND_RETURN_FALSE(hash->size() == kSHA256Size);
+ TEST_AND_RETURN_FALSE(rsa_size == 256 || rsa_size == 512);
+
+ // The following is a standard PKCS1-v1_5 padding for SHA256 signatures, as
+ // defined in RFC3447 section 9.2. It is prepended to the actual signature
+ // (32 bytes) to form a sequence of 256|512 bytes (2048|4096 bits) that is
+ // amenable to RSA signing. The padded hash will look as follows:
+ //
+ // 0x00 0x01 0xff ... 0xff 0x00 ASN1HEADER SHA256HASH
+ // |-----------205|461----------||----19----||----32----|
+ size_t padding_string_size =
+ rsa_size - hash->size() - sizeof(kSHA256DigestInfoPrefix) - 3;
+ brillo::Blob padded_result = brillo::CombineBlobs({
+ {0x00, 0x01},
+ brillo::Blob(padding_string_size, 0xff),
+ {0x00},
+ brillo::Blob(kSHA256DigestInfoPrefix,
+ kSHA256DigestInfoPrefix + sizeof(kSHA256DigestInfoPrefix)),
+ *hash,
+ });
+
+ *hash = std::move(padded_result);
+ TEST_AND_RETURN_FALSE(hash->size() == rsa_size);
return true;
}
diff --git a/payload_consumer/payload_verifier.h b/payload_consumer/payload_verifier.h
index 09bdbf9..af8e05f 100644
--- a/payload_consumer/payload_verifier.h
+++ b/payload_consumer/payload_verifier.h
@@ -34,12 +34,13 @@
// Interprets |signature_proto| as a protocol buffer containing the Signatures
// message and decrypts each signature data using the |pem_public_key|.
// |pem_public_key| should be a PEM format RSA public key data.
- // Returns whether *any* of the decrypted hashes matches the |hash_data|.
- // In case of any error parsing the signatures or the public key, returns
- // false.
+ // Pads the 32 bytes |sha256_hash_data| to 256 or 512 bytes according to the
+ // PKCS#1 v1.5 standard; and returns whether *any* of the decrypted hashes
+ // matches the padded hash data. In case of any error parsing the signatures
+ // or the public key, returns false.
static bool VerifySignature(const std::string& signature_proto,
const std::string& pem_public_key,
- const brillo::Blob& hash_data);
+ const brillo::Blob& sha256_hash_data);
// Decrypts |sig_data| with the given |pem_public_key| and populates
// |out_hash_data| with the decoded raw hash. |pem_public_key| should be a PEM
@@ -48,12 +49,13 @@
const std::string& pem_public_key,
brillo::Blob* out_hash_data);
- // Pads a SHA256 hash so that it may be encrypted/signed with RSA2048
- // using the PKCS#1 v1.5 scheme.
- // hash should be a pointer to vector of exactly 256 bits. The vector
- // will be modified in place and will result in having a length of
- // 2048 bits. Returns true on success, false otherwise.
- static bool PadRSA2048SHA256Hash(brillo::Blob* hash);
+ // Pads a SHA256 hash so that it may be encrypted/signed with RSA2048 or
+ // RSA4096 using the PKCS#1 v1.5 scheme.
+ // hash should be a pointer to vector of exactly 256 bits. |rsa_size| must be
+ // one of 256 or 512 bytes. The vector will be modified in place and will
+ // result in having a length of 2048 or 4096 bits, depending on the rsa size.
+ // Returns true on success, false otherwise.
+ static bool PadRSASHA256Hash(brillo::Blob* hash, size_t rsa_size);
private:
// This should never be constructed
diff --git a/payload_generator/payload_signer.cc b/payload_generator/payload_signer.cc
index cbca7fe..92313dc 100644
--- a/payload_generator/payload_signer.cc
+++ b/payload_generator/payload_signer.cc
@@ -28,6 +28,7 @@
#include <openssl/err.h>
#include <openssl/pem.h>
+#include "update_engine/common/constants.h"
#include "update_engine/common/hash_calculator.h"
#include "update_engine/common/subprocess.h"
#include "update_engine/common/utils.h"
@@ -253,14 +254,13 @@
string signature(payload.begin() + signatures_offset, payload.end());
string public_key;
TEST_AND_RETURN_FALSE(utils::ReadFile(public_key_path, &public_key));
- TEST_AND_RETURN_FALSE(PayloadVerifier::PadRSA2048SHA256Hash(&payload_hash));
+ TEST_AND_RETURN_FALSE(payload_hash.size() == kSHA256Size);
TEST_AND_RETURN_FALSE(
PayloadVerifier::VerifySignature(signature, public_key, payload_hash));
if (metadata_signature_size) {
signature.assign(payload.begin() + metadata_size,
payload.begin() + metadata_size + metadata_signature_size);
- TEST_AND_RETURN_FALSE(
- PayloadVerifier::PadRSA2048SHA256Hash(&metadata_hash));
+ TEST_AND_RETURN_FALSE(metadata_hash.size() == kSHA256Size);
TEST_AND_RETURN_FALSE(
PayloadVerifier::VerifySignature(signature, public_key, metadata_hash));
}
@@ -272,10 +272,7 @@
brillo::Blob* out_signature) {
LOG(INFO) << "Signing hash with private key: " << private_key_path;
// We expect unpadded SHA256 hash coming in
- TEST_AND_RETURN_FALSE(hash.size() == 32);
- brillo::Blob padded_hash(hash);
- PayloadVerifier::PadRSA2048SHA256Hash(&padded_hash);
-
+ TEST_AND_RETURN_FALSE(hash.size() == kSHA256Size);
// The code below executes the equivalent of:
//
// openssl rsautl -raw -sign -inkey |private_key_path|
@@ -286,6 +283,10 @@
RSA* rsa = PEM_read_RSAPrivateKey(fprikey, nullptr, nullptr, nullptr);
fclose(fprikey);
TEST_AND_RETURN_FALSE(rsa != nullptr);
+
+ brillo::Blob padded_hash = hash;
+ PayloadVerifier::PadRSASHA256Hash(&padded_hash, RSA_size(rsa));
+
brillo::Blob signature(RSA_size(rsa));
ssize_t signature_size = RSA_private_encrypt(padded_hash.size(),
padded_hash.data(),
diff --git a/payload_generator/payload_signer_unittest.cc b/payload_generator/payload_signer_unittest.cc
index 0b863b1..75fc694 100644
--- a/payload_generator/payload_signer_unittest.cc
+++ b/payload_generator/payload_signer_unittest.cc
@@ -44,6 +44,8 @@
const char* kUnittestPublicKeyPath = "unittest_key.pub.pem";
const char* kUnittestPrivateKey2Path = "unittest_key2.pem";
const char* kUnittestPublicKey2Path = "unittest_key2.pub.pem";
+const char* kUnittestPrivateKeyRSA4096Path = "unittest_key_RSA4096.pem";
+const char* kUnittestPublicKeyRSA4096Path = "unittest_key_RSA4096.pub.pem";
// Some data and its corresponding hash and signature:
const char kDataToSign[] = "This is some data to sign.";
@@ -101,11 +103,7 @@
class PayloadSignerTest : public ::testing::Test {
protected:
- void SetUp() override {
- PayloadVerifier::PadRSA2048SHA256Hash(&padded_hash_data_);
- }
-
- brillo::Blob padded_hash_data_{std::begin(kDataHash), std::end(kDataHash)};
+ brillo::Blob hash_data_{std::begin(kDataHash), std::end(kDataHash)};
};
TEST_F(PayloadSignerTest, SignSimpleTextTest) {
@@ -129,18 +127,23 @@
string signature;
SignSampleData(&signature,
{GetBuildArtifactsPath(kUnittestPrivateKeyPath),
- GetBuildArtifactsPath(kUnittestPrivateKey2Path)});
+ GetBuildArtifactsPath(kUnittestPrivateKey2Path),
+ GetBuildArtifactsPath(kUnittestPrivateKeyRSA4096Path)});
// Either public key should pass the verification.
string public_key;
EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(kUnittestPublicKeyPath),
&public_key));
- EXPECT_TRUE(PayloadVerifier::VerifySignature(
- signature, public_key, padded_hash_data_));
+ EXPECT_TRUE(
+ PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(kUnittestPublicKey2Path),
&public_key));
- EXPECT_TRUE(PayloadVerifier::VerifySignature(
- signature, public_key, padded_hash_data_));
+ EXPECT_TRUE(
+ PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
+ EXPECT_TRUE(utils::ReadFile(
+ GetBuildArtifactsPath(kUnittestPublicKeyRSA4096Path), &public_key));
+ EXPECT_TRUE(
+ PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
}
TEST_F(PayloadSignerTest, VerifySignatureTest) {
@@ -150,13 +153,13 @@
string public_key;
EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(kUnittestPublicKeyPath),
&public_key));
- EXPECT_TRUE(PayloadVerifier::VerifySignature(
- signature, public_key, padded_hash_data_));
+ EXPECT_TRUE(
+ PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
// Passing the invalid key should fail the verification.
EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(kUnittestPublicKey2Path),
&public_key));
- EXPECT_TRUE(PayloadVerifier::VerifySignature(
- signature, public_key, padded_hash_data_));
+ EXPECT_TRUE(
+ PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
}
TEST_F(PayloadSignerTest, SkipMetadataSignatureTest) {
diff --git a/unittest_key_RSA4096.pem b/unittest_key_RSA4096.pem
new file mode 100644
index 0000000..5613910
--- /dev/null
+++ b/unittest_key_RSA4096.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKgIBAAKCAgEAu8vBB+DvDPWgO1IWli5VuXHkMWYtD+wzToKIP/NkiGYdf1b9
+EvCgrvS3J4CutKCHoz1N5Pc29DKZduDhLbqWzqvUldJ8kSKd967PRCdM5vJHmdox
+H8E7m3YROBrppcJG32B5TD5qbn6jqrsSnwjlGZN2RAuURQwalSxq3/ZttiEzEMDR
+o/7v5rQINlF0Rsud+HlNXrxzhR/LQyMV3d+/JvoEWPmz4unzpKyYAMOjdNTcMLB0
+ccULFYKfJtRCqTyfgEUbT+mGLTbDQSkzl6mcNfDg+3hu8lTjQH+6EjjDUSu6oOmV
+OmDf8tZPmGRdw/9R1PUx6A1yKQR6FkdkqRDOt1GvIpUpsfT36Gg59u1a6YyHKudB
+6igFzr3JcTgslQ9KUZK68j6Xr2AlbsffyJ5ltyuHT4gMtCppkuGUi1CZ6+i55LfA
+t1cVx6meE+zJYK46zu9GKgsXg72BNCi/3v1HievZwDcm5A04YQZMZDgtb74pN1Vz
+uCEzxCgpyx3b3Y/K8JI6n9xMeQNIPF2h9JeiO9qasV4vJ51PbnS61BHR1E9Sh845
+2QVT+mqJnA8uRp22+sF0wU5Ae33UAZxnKk9Ax+uz6iRP6wlILlUpl+Chsjla6+Ut
+RkE4NRJh2xdin+j9GCaXFVOzx0RPLAYrD9/cM6BOV7CKZ/2iqVnaXH6sY00CAwEA
+AQKCAgEAgf77ci68i6YD8sxSIkeUw7gZFDD8hIcmxPQKyOn874ZwaNOYf+Hd+P4h
+QtELHrH48oDfSN3wRn44SVhFx9ffyNSdZdC8teLWort5cl4aF8wi0Fd/pdGnJ2hF
+ZycKEdo4ISyxCpwyJKa5ONgifcA0hs3TEitJybolNJn4qWv2ahr1NGWlOPairnp1
+LNSZvt/4TCX77tZYyRBHLgQ9gMb/lUWAeO7xHOSB6b4nwm+q2Jb4jSO4l4CkuZEg
+BkrskiYK60nrLBgk72t3IcYZlqSsI5LIyoqFkm48mUtRTIfKfIfeusC2siCZJYpA
+subXGXPF+7p3f4C/Q7F7qaxl+7pMvN2UtnCY4lYppMKETquVT2vtEgZjSVkQTb0X
+wEYmipMsonVcSLL4xTQxT0KdY2JczH7xicIJwolED+eob64t4q64ljfC3tlPJakE
+O2GgyynLH+oojBsH8XCvuhODFL+/e1eQmV0gu0az3fcHY+lCKrYEhlVB9wVe5Afn
+2GH71deZKY+iF2E8RJXwGdmpN4PbLyWepqCm0TdMUrn5A37TBr++3B9d7/eyMt1o
+afMxDzAZ27HaT6eg6SB/LJdkezvs67jLcur18NFMevSxKc+G+B8g+7euc00sKaLu
+WIX2bf8kc6L2sLECERpXOBNRSQxY33vS72szF6P7rN0+Szb+aQECggEBAOSqtUSY
+DJbyjnxmhGodcbuubNyNZRrs/vXNcYHmBZbWvR9rxtWgA1LYJiAY4wvtc+Ciw/Y6
+468MG6ip4rLu/6lDkekH/ARxW6zErCfCDDg91cP1FXP/Cwo6COJ9V2imCbTsiMSi
+bVigG4Lb4FTgBkreOyxDjnIVzZwCrwtxZWpNwA2GlEBS6IsJqelnUi/roi4rLpCj
+Y5mLvL8YYPduead9TwesYsXdK2qBf6A034GNXxvzhV70HfhnI60ydi6pNRrWamru
+TBJEuY7CipzyevqM3drfkFZDKyEBEVnk7We4IpiaOkBfLsshvFqk9asWzts1eDa8
+GpOqM0RYRCXZya0CggEBANI+YWOrK2qSLxFBbCupNaxG++/tafOXsgjkmGaRxCHt
+IcvPTIzFBQOSCobF51KoZIriVEVKw1bXHFlibeQ1IEuh8yVBo3l6NO8JKlc/tdJf
+pfkUh5XjQWJW8UqWULb5CkJCEheenG0oy8zhjOERPDcQXRYOhIo8JSpWfJFtWSWk
+L/X7kfkEvQxV0omFCUg4sCxdBeqIEItYd0Td0SCmHPZIs2KgSmpLIPBH0BMibNkY
+ZeSaz5nWbw06Unhkas+ulm3S+IEjb7neuAWGPlIXnPch9hw2pdZf49XRW4fjc7Nr
++G+U2Jgjv81+Rn7nFK2Whh22XKL5aP2myoVESlvzdCECggEBAIc9DwgKhSehPPQG
+DbpUv7coaennFizejcwCPWd+C0AysJesvmQJxu1wONwy29VqEmaA3TT7jz0wBAu0
+rgb1ou8Qr3MK7doS0Q1VJBw/f8qjh5HlmVKJPJZHzIlnaBLUYFlIq3rgNZt81ciH
+Eh4ggJg25vg+3DhM/NWQIMa7wick5LkbJwMEBdR1WrBYExuUWM7FazzP5VAifPbo
+DDFKfVi5m8wGAETVkZ/kBv9RRf7xBZcaZ37JEhCfr1H3zj26hVXiCf5EAWmsi7IL
+DL/WCTW1qmCQaGUcRJ24a/KmmmIFXTCzxk/b+2jYAvX5KfKOArlS3k5A4dcDil6Z
+dXSNYeECggEBAIHzRMcSOde5W5ZS1cV25VIC3h5CpMaH8OdGRFzBpHVD2SvcifhI
+nvzB+/epw3130A14L5ZUy8CVXVRyXnI71CZrh5pzo9OmEaneBGnBW2UY8cGvSs7+
+lJ9wFdyAZIt0Cz9BD2XCB/YAzVdp4mYK/Skb2C2V855t5prwsjZBXGTDw1FLmcJN
+h3xkX6nYrRAS2fHR3aJFT9SRbccHRAfmJOilrxs68EQbA9UAzj/Fe3oEdpaCiecQ
+f7uxXOBFUS/lPd3MFQXdHWXJn/zqKQMczUyDlVeC/6YtxumWafjoQc+Y4Qo2+lmv
+XxJpBrHRqxpQe71JxqCFgLunqG4O89c594ECggEAcMlYhrO2+R7mEPFDZLwSvTMV
+BOof6hxLIW8PkLQ/8HHTTacC02sKFiR921sw9NadQ7G0cf23Fecmolg4brJUh9sQ
+evjdYMdqIYPJT5hYSkIkdTk0Ny+tN2Pt4fBTTv3N2D3Da/5ODfrVSj0ib89DXG5D
+bPahlFLIhKaVbXNe1RQL/8j4nFf8D9LwuEMOMYrUpSMw9ULT5dB34QN2TOnwW9JW
+Md7aSY5pK1j1Y8FoWCAFSw+o+yWq5DbTFvcEhttWrUoFl9YxTolbLt6sw6TLy12x
+9haQDvbfvRkg3Es31DEC8plsltfg5S9KwRqCchKKUm7cnAJFhB2/2C6JX2k0XQ==
+-----END RSA PRIVATE KEY-----
diff --git a/update_engine.gyp b/update_engine.gyp
index 754b314..c2c0c62 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -491,6 +491,7 @@
'sources': [
'unittest_key.pem',
'unittest_key2.pem',
+ 'unittest_key_RSA4096.pem',
],
'includes': ['../../../platform2/common-mk/openssl_pem.gypi'],
},