OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h" | 5 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h" |
6 | 6 |
7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/base64url.h" |
8 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
9 #include "base/strings/string_util.h" | 10 #include "base/strings/string_util.h" |
| 11 #include "components/gcm_driver/crypto/p256_key_util.h" |
10 #include "crypto/random.h" | 12 #include "crypto/random.h" |
11 #include "crypto/symmetric_key.h" | 13 #include "crypto/symmetric_key.h" |
12 #include "testing/gtest/include/gtest/gtest.h" | 14 #include "testing/gtest/include/gtest/gtest.h" |
13 | 15 |
| 16 #include "crypto/ec_private_key.h" |
| 17 #include "crypto/scoped_openssl_types.h" |
| 18 #include <openssl/ec.h> |
| 19 #include <openssl/pkcs12.h> |
| 20 #include <openssl/x509.h> |
| 21 |
14 namespace gcm { | 22 namespace gcm { |
15 | 23 |
16 namespace { | 24 namespace { |
17 | 25 |
18 // The number of bits of the key in AEAD_AES_128_GCM. | 26 // The number of bits of the key in AEAD_AES_128_GCM. |
19 const size_t kKeySizeBits = 128; | 27 const size_t kKeySizeBits = 128; |
20 | 28 |
21 // Example plaintext data to use in the tests. | 29 // Example plaintext data to use in the tests. |
22 const char kExamplePlaintext[] = "Example plaintext"; | 30 const char kExamplePlaintext[] = "Example plaintext"; |
23 | 31 |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
102 // Message with multiple (2) records. | 110 // Message with multiple (2) records. |
103 { "H2ujfPbpRbVSy+adIG2NRe4VxkX4V0r/zl6e9xnMSF6LSutblGdWLrwQc82Xh7DXAQlihW0q3" | 111 { "H2ujfPbpRbVSy+adIG2NRe4VxkX4V0r/zl6e9xnMSF6LSutblGdWLrwQc82Xh7DXAQlihW0q3" |
104 "IQzHP+LIxuAiA==", | 112 "IQzHP+LIxuAiA==", |
105 "W3W4gx7sqcfmBnvNNdO9d4MBCC1bvJkvsNjZOGD+CCg=", | 113 "W3W4gx7sqcfmBnvNNdO9d4MBCC1bvJkvsNjZOGD+CCg=", |
106 "xG0TPGi9aIcxjpXKmaYBBQ==", | 114 "xG0TPGi9aIcxjpXKmaYBBQ==", |
107 7, | 115 7, |
108 nullptr // expected to fail | 116 nullptr // expected to fail |
109 } | 117 } |
110 }; | 118 }; |
111 | 119 |
| 120 // ----------------------------------------------------------------------------- |
| 121 |
| 122 using ScopedPKCS8_PRIV_KEY_INFO = |
| 123 crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>; |
| 124 using ScopedX509_SIG = crypto::ScopedOpenSSL<X509_SIG, X509_SIG_free>; |
| 125 |
| 126 // Takes a private key in X.509 SubjectPublicKeyInfo block format and converts |
| 127 // it to an ASN.1-encoded PKCS #8 EncryptedPrivateKeyInfo, as is accepted by |
| 128 // the ECPrivateKey infrastructure. |
| 129 std::string ConvertRawPrivateKeyToPKCS8EncryptedPrivateKeyInfo( |
| 130 const base::StringPiece& raw_private_key, |
| 131 const base::StringPiece& public_key) { |
| 132 const uint8_t* raw_private_key_ptr = |
| 133 reinterpret_cast<const uint8_t*>(raw_private_key.data()); |
| 134 const uint8_t* public_key_ptr = |
| 135 reinterpret_cast<const uint8_t*>(public_key.data()); |
| 136 |
| 137 crypto::ScopedEC_GROUP p256(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); |
| 138 crypto::ScopedEC_KEY key(EC_KEY_new()); |
| 139 DCHECK(p256 && key); |
| 140 |
| 141 // Import the |raw_private_key| to the |key| through a BIGNUM. |
| 142 crypto::ScopedBIGNUM bignum( |
| 143 BN_bin2bn(raw_private_key_ptr, raw_private_key.size(), nullptr)); |
| 144 if (!bignum) { |
| 145 LOG(ERROR) << "Unable to initialize the ScopedBIGNUM"; |
| 146 return std::string(); |
| 147 } |
| 148 |
| 149 if (!EC_KEY_set_private_key(key.get(), bignum.get())) { |
| 150 LOG(ERROR) << "Unable to set the private key"; |
| 151 return std::string(); |
| 152 } |
| 153 |
| 154 // Import the |public_key| to the |key| by assembling an EC point. Because the |
| 155 // |public_key| is an uncompressed EC point, we can access the data directly. |
| 156 crypto::ScopedEC_POINT point(EC_POINT_new(p256.get())); |
| 157 crypto::ScopedBIGNUM x(BN_new()), y(BN_new()); |
| 158 DCHECK(point && x && y); |
| 159 |
| 160 if (BN_bin2bn(public_key_ptr + 1 + 0, 32, x.get()) == nullptr || |
| 161 BN_bin2bn(public_key_ptr + 1 + 32, 32, y.get()) == nullptr) { |
| 162 LOG(ERROR) << "Unable to create BIGNUMs for the public point's x/y"; |
| 163 return std::string(); |
| 164 } |
| 165 |
| 166 if (!EC_POINT_set_affine_coordinates_GFp(p256.get(), point.get(), x.get(), |
| 167 y.get(), nullptr)) { |
| 168 LOG(ERROR) << "Unable to set the coordinates of the public point"; |
| 169 return std::string(); |
| 170 } |
| 171 |
| 172 if (!EC_KEY_set_group(key.get(), p256.get()) || |
| 173 !EC_KEY_set_public_key(key.get(), point.get())) { |
| 174 LOG(ERROR) << "Unable to set the public key"; |
| 175 return std::string(); |
| 176 } |
| 177 |
| 178 // Verify that the created EC_KEY is valid. Crashes might occur if it's not. |
| 179 if (!EC_KEY_check_key(key.get())) { |
| 180 LOG(ERROR) << "Unable to verify validity of the key"; |
| 181 return std::string(); |
| 182 } |
| 183 |
| 184 // Create a EVP_PKEY from the EC_KEY pair that was created. |
| 185 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new()); |
| 186 if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey.get(), key.get())) { |
| 187 LOG(ERROR) << "Unable to create the private key"; |
| 188 return std::string(); |
| 189 } |
| 190 |
| 191 std::vector<uint8_t> encrypted_private_key_buf; |
| 192 scoped_ptr<crypto::ECPrivateKey> ec_private_key( |
| 193 crypto::ECPrivateKey::Create()); |
| 194 ec_private_key->key_ = pkey.release(); |
| 195 |
| 196 // Export the EVP_PKEY as an ASN.1-encoded PKCS #8 EncryptedPrivateKeyInfo |
| 197 // block, as expected by the ECPrivateKey import routines. |
| 198 if (!ec_private_key->ExportEncryptedPrivateKey("" /* password */, |
| 199 1 /* iterations */, |
| 200 &encrypted_private_key_buf)) { |
| 201 LOG(ERROR) << "Unable to export the encrypted private key"; |
| 202 return std::string(); |
| 203 } |
| 204 |
| 205 return std::string(reinterpret_cast<char*>(encrypted_private_key_buf.data()), |
| 206 encrypted_private_key_buf.size()); |
| 207 |
| 208 /** |
| 209 std::string raw_public_key, raw_public_key_rep; |
| 210 DCHECK(ec_private_key->ExportRawPublicKey(&raw_public_key)); |
| 211 |
| 212 raw_public_key_rep = "\04" + raw_public_key; |
| 213 |
| 214 LOG(INFO) << "r_p_k: (" << raw_public_key_rep.size() << ")"; |
| 215 LOG(INFO) << "p_k: (" << raw_public_key_rep.size() << ")"; |
| 216 |
| 217 LOG(INFO) << (raw_public_key_rep == public_key); |
| 218 |
| 219 return std::string(); |
| 220 |
| 221 |
| 222 int public_key_len = i2d_PublicKey(pkey.get(), nullptr); |
| 223 if (public_key_len != 65) |
| 224 return std::string(); |
| 225 |
| 226 uint8_t public_key_buffer[public_key_len]; |
| 227 |
| 228 uint8_t* public_key_buffer_ptr = public_key_buffer; |
| 229 if (i2d_PublicKey(pkey.get(), &public_key_buffer_ptr) != public_key_len) |
| 230 return std::string(); |
| 231 |
| 232 std::string regenerated_public_key( |
| 233 reinterpret_cast<char*>(public_key_buffer), public_key_len); |
| 234 |
| 235 LOG(INFO) << (regenerated_public_key == public_key); |
| 236 |
| 237 ScopedPKCS8_PRIV_KEY_INFO pkcs8(EVP_PKEY2PKCS8(pkey.get())); |
| 238 if (!pkcs8) { |
| 239 LOG(ERROR) << "Unable to initialize the PKCS8_PRIV_KEY_INFO"; |
| 240 return std::string(); |
| 241 } |
| 242 |
| 243 ScopedX509_SIG encrypted(PKCS8_encrypt_pbe( |
| 244 NID_pbe_WithSHA1And3_Key_TripleDES_CBC, |
| 245 nullptr, |
| 246 nullptr, |
| 247 0, |
| 248 nullptr, |
| 249 0, |
| 250 1, |
| 251 pkcs8.get())); |
| 252 if (!encrypted) { |
| 253 LOG(ERROR) << "Unable to encrypt the PKCS8_PRIV_KEY_INFO"; |
| 254 return std::string(); |
| 255 } |
| 256 |
| 257 crypto::ScopedBIO bio(BIO_new(BIO_s_mem())); |
| 258 if (!i2d_PKCS8_bio(bio.get(), encrypted.get())) { |
| 259 LOG(ERROR) << "Unable to convert PKCS8 to a PKI ScopedBIO (#1)"; |
| 260 return std::string(); |
| 261 } |
| 262 |
| 263 char* bio_data = nullptr; |
| 264 long bio_length = BIO_get_mem_data(bio.get(), &bio_data); |
| 265 if (!bio_data || bio_length < 0) { |
| 266 LOG(ERROR) << "Unable to convert PKCS8 to a PKI ScopedBIO (#2)"; |
| 267 return std::string(); |
| 268 } |
| 269 |
| 270 std::string key_string = std::string(bio_data, bio_data + bio_length); |
| 271 |
| 272 std::string encoded_string; |
| 273 base::Base64UrlEncode(key_string, base::Base64UrlEncodePolicy::OMIT_PADDING, |
| 274 &encoded_string); |
| 275 LOG(INFO) << "Encrypted key: [" << encoded_string << "]"; |
| 276 |
| 277 // TODO: Get a X.509 SubjectPublicKeyInfo |
| 278 |
| 279 return key_string; |
| 280 **/ |
| 281 } |
| 282 |
112 } // namespace | 283 } // namespace |
113 | 284 |
114 class GCMMessageCryptographerTest : public ::testing::Test { | 285 class GCMMessageCryptographerTest : public ::testing::Test { |
115 public: | 286 public: |
116 void SetUp() override { | 287 void SetUp() override { |
117 scoped_ptr<crypto::SymmetricKey> random_key( | 288 scoped_ptr<crypto::SymmetricKey> random_key( |
118 crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, | 289 crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, |
119 kKeySizeBits)); | 290 kKeySizeBits)); |
120 | 291 |
121 ASSERT_TRUE(random_key->GetRawKey(&key_)); | 292 ASSERT_TRUE(random_key->GetRawKey(&key_)); |
| 293 |
| 294 std::string local_public_key, local_public_key_x509, local_private_key; |
| 295 std::string peer_public_key, peer_public_key_x509, peer_private_key; |
| 296 |
| 297 ASSERT_TRUE(CreateP256KeyPair(&local_private_key, &local_public_key_x509, |
| 298 &local_public_key)); |
| 299 ASSERT_TRUE(CreateP256KeyPair(&peer_private_key, &peer_public_key_x509, |
| 300 &peer_public_key)); |
| 301 |
| 302 cryptographer_.reset( |
| 303 new GCMMessageCryptographer(local_public_key, peer_public_key)); |
122 } | 304 } |
123 | 305 |
124 protected: | 306 protected: |
125 // Generates a cryptographically secure random salt of 16-octets in size, the | 307 // Generates a cryptographically secure random salt of 16-octets in size, the |
126 // required length as expected by the HKDF. | 308 // required length as expected by the HKDF. |
127 std::string GenerateRandomSalt() { | 309 std::string GenerateRandomSalt() { |
128 const size_t kSaltSize = 16; | 310 const size_t kSaltSize = 16; |
129 | 311 |
130 std::string salt; | 312 std::string salt; |
131 | 313 |
132 crypto::RandBytes(base::WriteInto(&salt, kSaltSize + 1), kSaltSize); | 314 crypto::RandBytes(base::WriteInto(&salt, kSaltSize + 1), kSaltSize); |
133 return salt; | 315 return salt; |
134 } | 316 } |
135 | 317 |
136 GCMMessageCryptographer* cryptographer() { return &cryptographer_; } | 318 GCMMessageCryptographer* cryptographer() { return cryptographer_.get(); } |
137 | 319 |
138 base::StringPiece key() const { return key_; } | 320 base::StringPiece key() const { return key_; } |
139 | 321 |
140 private: | 322 private: |
141 GCMMessageCryptographer cryptographer_; | 323 scoped_ptr<GCMMessageCryptographer> cryptographer_; |
142 | 324 |
143 std::string key_; | 325 std::string key_; |
144 }; | 326 }; |
145 | 327 |
146 TEST_F(GCMMessageCryptographerTest, RoundTrip) { | 328 TEST_F(GCMMessageCryptographerTest, RoundTrip) { |
147 const std::string salt = GenerateRandomSalt(); | 329 const std::string salt = GenerateRandomSalt(); |
148 | 330 |
149 size_t record_size = 0; | 331 size_t record_size = 0; |
150 | 332 |
151 std::string ciphertext, plaintext; | 333 std::string ciphertext, plaintext; |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
291 if (!has_output) { | 473 if (!has_output) { |
292 EXPECT_FALSE(result); | 474 EXPECT_FALSE(result); |
293 continue; | 475 continue; |
294 } | 476 } |
295 | 477 |
296 EXPECT_TRUE(result); | 478 EXPECT_TRUE(result); |
297 EXPECT_EQ(kDecryptionTestVectors[i].output, plaintext); | 479 EXPECT_EQ(kDecryptionTestVectors[i].output, plaintext); |
298 } | 480 } |
299 } | 481 } |
300 | 482 |
| 483 template <typename I> std::string n2hexstr(I w, size_t hex_len = sizeof(I)<<1) { |
| 484 static const char* digits = "0123456789ABCDEF"; |
| 485 std::string rc(hex_len,'0'); |
| 486 for (size_t i=0, j=(hex_len-1)*4 ; i<hex_len; ++i,j-=4) |
| 487 rc[i] = digits[(w>>j) & 0x0f]; |
| 488 return rc; |
| 489 } |
| 490 |
| 491 TEST_F(GCMMessageCryptographerTest, ReferenceTest) { |
| 492 // This test verifies Chrome's implementation against the reference vector |
| 493 // given in the draft-thomson-http-encryption examples. |
| 494 const char kSalt[] = "Qg61ZJRva_XBE9IEUelU3A"; |
| 495 const char kPayload[] = "G6j_sfKg0qebO62yXpTCayN2KV24QitNiTvLgcFiEj0"; |
| 496 |
| 497 const char kLocalPrivateKey[] = "9FWl15_QUQAWDaD3k3l50ZBZQJ4au27F1V4F0uLSD_M"; |
| 498 const char kLocalPublicKey[] = |
| 499 "BCEkBjzL8Z3C-oi2Q7oE5t2Np-p7osjGLg93qUP0wvqRT21EEWyf0cQDQcakQMqz4hQKYOQ3" |
| 500 "il2nNZct4HgAUQU"; |
| 501 |
| 502 // Note: X.509 SubjectPublicKeyInfo representation of |kLocalPublicKey|. |
| 503 const char kLocalPublicKeyX509[] = ""; // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXX |
| 504 |
| 505 const char kPeerPrivateKey[] = "vG7TmzUX9NfVR4XUGBkLAFu8iDyQe-q_165JkkN0Vlw"; |
| 506 const char kPeerPublicKey[] = |
| 507 "BDgpRKok2GZZDmS4r63vbJSUtcQx4Fq1V58-6-3NbZzSTlZsQiCEDTQy3CZ0ZMsqeqsEb7qW" |
| 508 "2blQHA4S48fynTk"; |
| 509 |
| 510 const char kExpectedOutput[] = "I am the walrus"; |
| 511 |
| 512 std::string salt; |
| 513 ASSERT_TRUE(base::Base64UrlDecode( |
| 514 kSalt, base::Base64UrlDecodePolicy::IGNORE_PADDING, &salt)); |
| 515 |
| 516 std::string payload; |
| 517 ASSERT_TRUE(base::Base64UrlDecode( |
| 518 kPayload, base::Base64UrlDecodePolicy::IGNORE_PADDING, &payload)); |
| 519 |
| 520 std::string local_private_key, local_public_key, local_public_key_x509; |
| 521 ASSERT_TRUE(base::Base64UrlDecode(kLocalPrivateKey, |
| 522 base::Base64UrlDecodePolicy::IGNORE_PADDING, |
| 523 &local_private_key)); |
| 524 ASSERT_TRUE(base::Base64UrlDecode(kLocalPublicKey, |
| 525 base::Base64UrlDecodePolicy::IGNORE_PADDING, |
| 526 &local_public_key)); |
| 527 ASSERT_TRUE(base::Base64UrlDecode(kLocalPublicKeyX509, |
| 528 base::Base64UrlDecodePolicy::IGNORE_PADDING, |
| 529 &local_public_key_x509)); |
| 530 |
| 531 std::string peer_private_key, peer_public_key; |
| 532 ASSERT_TRUE(base::Base64UrlDecode(kPeerPrivateKey, |
| 533 base::Base64UrlDecodePolicy::IGNORE_PADDING, |
| 534 &peer_private_key)); |
| 535 ASSERT_TRUE(base::Base64UrlDecode(kPeerPublicKey, |
| 536 base::Base64UrlDecodePolicy::IGNORE_PADDING, |
| 537 &peer_public_key)); |
| 538 |
| 539 std::string local_output, peer_output; |
| 540 for (size_t i = 0; i < local_public_key.size(); ++i) |
| 541 local_output += "0x" + n2hexstr(local_public_key[i]) + " "; |
| 542 |
| 543 for (size_t i = 0; i < peer_public_key.size(); ++i) |
| 544 peer_output += " " + std::to_string(static_cast<int>(peer_public_key[i])); |
| 545 |
| 546 LOG(INFO) << "Local private: (" << local_public_key.size() << ") [" << local_o
utput << "]"; |
| 547 //LOG(INFO) << "Peer public: (" << peer_public_key.size() << ") [" << peer_out
put << "]"; |
| 548 |
| 549 std::string encrypted_private_key = |
| 550 ConvertRawPrivateKeyToPKCS8EncryptedPrivateKeyInfo(local_private_key, |
| 551 local_public_key); |
| 552 |
| 553 std::string shared_secret; |
| 554 ASSERT_TRUE(ComputeSharedP256Secret(encrypted_private_key, local_public_key_x5
09, |
| 555 peer_public_key, &shared_secret)); |
| 556 |
| 557 std::string plaintext; |
| 558 |
| 559 GCMMessageCryptographer cryptographer(local_public_key, peer_public_key); |
| 560 ASSERT_TRUE(cryptographer.Decrypt(payload, shared_secret, salt, 4096, |
| 561 &plaintext)); |
| 562 |
| 563 EXPECT_EQ(kExpectedOutput, plaintext); |
| 564 } |
| 565 |
301 } // namespace gcm | 566 } // namespace gcm |
OLD | NEW |