| Index: components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc
|
| diff --git a/components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc b/components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc
|
| index a1a0280e013012f42ced3b5b2bc2df00e1d6d366..aadd2b3b185d812a102275a72c68c0b95d3ed546 100644
|
| --- a/components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc
|
| +++ b/components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc
|
| @@ -5,12 +5,20 @@
|
| #include "components/gcm_driver/crypto/gcm_message_cryptographer.h"
|
|
|
| #include "base/base64.h"
|
| +#include "base/base64url.h"
|
| #include "base/memory/scoped_ptr.h"
|
| #include "base/strings/string_util.h"
|
| +#include "components/gcm_driver/crypto/p256_key_util.h"
|
| #include "crypto/random.h"
|
| #include "crypto/symmetric_key.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| +#include "crypto/ec_private_key.h"
|
| +#include "crypto/scoped_openssl_types.h"
|
| +#include <openssl/ec.h>
|
| +#include <openssl/pkcs12.h>
|
| +#include <openssl/x509.h>
|
| +
|
| namespace gcm {
|
|
|
| namespace {
|
| @@ -109,6 +117,169 @@ const TestVector kDecryptionTestVectors[] = {
|
| }
|
| };
|
|
|
| +// -----------------------------------------------------------------------------
|
| +
|
| +using ScopedPKCS8_PRIV_KEY_INFO =
|
| + crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
|
| +using ScopedX509_SIG = crypto::ScopedOpenSSL<X509_SIG, X509_SIG_free>;
|
| +
|
| +// Takes a private key in X.509 SubjectPublicKeyInfo block format and converts
|
| +// it to an ASN.1-encoded PKCS #8 EncryptedPrivateKeyInfo, as is accepted by
|
| +// the ECPrivateKey infrastructure.
|
| +std::string ConvertRawPrivateKeyToPKCS8EncryptedPrivateKeyInfo(
|
| + const base::StringPiece& raw_private_key,
|
| + const base::StringPiece& public_key) {
|
| + const uint8_t* raw_private_key_ptr =
|
| + reinterpret_cast<const uint8_t*>(raw_private_key.data());
|
| + const uint8_t* public_key_ptr =
|
| + reinterpret_cast<const uint8_t*>(public_key.data());
|
| +
|
| + crypto::ScopedEC_GROUP p256(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
|
| + crypto::ScopedEC_KEY key(EC_KEY_new());
|
| + DCHECK(p256 && key);
|
| +
|
| + // Import the |raw_private_key| to the |key| through a BIGNUM.
|
| + crypto::ScopedBIGNUM bignum(
|
| + BN_bin2bn(raw_private_key_ptr, raw_private_key.size(), nullptr));
|
| + if (!bignum) {
|
| + LOG(ERROR) << "Unable to initialize the ScopedBIGNUM";
|
| + return std::string();
|
| + }
|
| +
|
| + if (!EC_KEY_set_private_key(key.get(), bignum.get())) {
|
| + LOG(ERROR) << "Unable to set the private key";
|
| + return std::string();
|
| + }
|
| +
|
| + // Import the |public_key| to the |key| by assembling an EC point. Because the
|
| + // |public_key| is an uncompressed EC point, we can access the data directly.
|
| + crypto::ScopedEC_POINT point(EC_POINT_new(p256.get()));
|
| + crypto::ScopedBIGNUM x(BN_new()), y(BN_new());
|
| + DCHECK(point && x && y);
|
| +
|
| + if (BN_bin2bn(public_key_ptr + 1 + 0, 32, x.get()) == nullptr ||
|
| + BN_bin2bn(public_key_ptr + 1 + 32, 32, y.get()) == nullptr) {
|
| + LOG(ERROR) << "Unable to create BIGNUMs for the public point's x/y";
|
| + return std::string();
|
| + }
|
| +
|
| + if (!EC_POINT_set_affine_coordinates_GFp(p256.get(), point.get(), x.get(),
|
| + y.get(), nullptr)) {
|
| + LOG(ERROR) << "Unable to set the coordinates of the public point";
|
| + return std::string();
|
| + }
|
| +
|
| + if (!EC_KEY_set_group(key.get(), p256.get()) ||
|
| + !EC_KEY_set_public_key(key.get(), point.get())) {
|
| + LOG(ERROR) << "Unable to set the public key";
|
| + return std::string();
|
| + }
|
| +
|
| + // Verify that the created EC_KEY is valid. Crashes might occur if it's not.
|
| + if (!EC_KEY_check_key(key.get())) {
|
| + LOG(ERROR) << "Unable to verify validity of the key";
|
| + return std::string();
|
| + }
|
| +
|
| + // Create a EVP_PKEY from the EC_KEY pair that was created.
|
| + crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
|
| + if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey.get(), key.get())) {
|
| + LOG(ERROR) << "Unable to create the private key";
|
| + return std::string();
|
| + }
|
| +
|
| + std::vector<uint8_t> encrypted_private_key_buf;
|
| + scoped_ptr<crypto::ECPrivateKey> ec_private_key(
|
| + crypto::ECPrivateKey::Create());
|
| + ec_private_key->key_ = pkey.release();
|
| +
|
| + // Export the EVP_PKEY as an ASN.1-encoded PKCS #8 EncryptedPrivateKeyInfo
|
| + // block, as expected by the ECPrivateKey import routines.
|
| + if (!ec_private_key->ExportEncryptedPrivateKey("" /* password */,
|
| + 1 /* iterations */,
|
| + &encrypted_private_key_buf)) {
|
| + LOG(ERROR) << "Unable to export the encrypted private key";
|
| + return std::string();
|
| + }
|
| +
|
| + return std::string(reinterpret_cast<char*>(encrypted_private_key_buf.data()),
|
| + encrypted_private_key_buf.size());
|
| +
|
| +/**
|
| + std::string raw_public_key, raw_public_key_rep;
|
| + DCHECK(ec_private_key->ExportRawPublicKey(&raw_public_key));
|
| +
|
| + raw_public_key_rep = "\04" + raw_public_key;
|
| +
|
| + LOG(INFO) << "r_p_k: (" << raw_public_key_rep.size() << ")";
|
| + LOG(INFO) << "p_k: (" << raw_public_key_rep.size() << ")";
|
| +
|
| + LOG(INFO) << (raw_public_key_rep == public_key);
|
| +
|
| + return std::string();
|
| +
|
| +
|
| + int public_key_len = i2d_PublicKey(pkey.get(), nullptr);
|
| + if (public_key_len != 65)
|
| + return std::string();
|
| +
|
| + uint8_t public_key_buffer[public_key_len];
|
| +
|
| + uint8_t* public_key_buffer_ptr = public_key_buffer;
|
| + if (i2d_PublicKey(pkey.get(), &public_key_buffer_ptr) != public_key_len)
|
| + return std::string();
|
| +
|
| + std::string regenerated_public_key(
|
| + reinterpret_cast<char*>(public_key_buffer), public_key_len);
|
| +
|
| + LOG(INFO) << (regenerated_public_key == public_key);
|
| +
|
| + ScopedPKCS8_PRIV_KEY_INFO pkcs8(EVP_PKEY2PKCS8(pkey.get()));
|
| + if (!pkcs8) {
|
| + LOG(ERROR) << "Unable to initialize the PKCS8_PRIV_KEY_INFO";
|
| + return std::string();
|
| + }
|
| +
|
| + ScopedX509_SIG encrypted(PKCS8_encrypt_pbe(
|
| + NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
|
| + nullptr,
|
| + nullptr,
|
| + 0,
|
| + nullptr,
|
| + 0,
|
| + 1,
|
| + pkcs8.get()));
|
| + if (!encrypted) {
|
| + LOG(ERROR) << "Unable to encrypt the PKCS8_PRIV_KEY_INFO";
|
| + return std::string();
|
| + }
|
| +
|
| + crypto::ScopedBIO bio(BIO_new(BIO_s_mem()));
|
| + if (!i2d_PKCS8_bio(bio.get(), encrypted.get())) {
|
| + LOG(ERROR) << "Unable to convert PKCS8 to a PKI ScopedBIO (#1)";
|
| + return std::string();
|
| + }
|
| +
|
| + char* bio_data = nullptr;
|
| + long bio_length = BIO_get_mem_data(bio.get(), &bio_data);
|
| + if (!bio_data || bio_length < 0) {
|
| + LOG(ERROR) << "Unable to convert PKCS8 to a PKI ScopedBIO (#2)";
|
| + return std::string();
|
| + }
|
| +
|
| + std::string key_string = std::string(bio_data, bio_data + bio_length);
|
| +
|
| + std::string encoded_string;
|
| + base::Base64UrlEncode(key_string, base::Base64UrlEncodePolicy::OMIT_PADDING,
|
| + &encoded_string);
|
| + LOG(INFO) << "Encrypted key: [" << encoded_string << "]";
|
| +
|
| + // TODO: Get a X.509 SubjectPublicKeyInfo
|
| +
|
| + return key_string;
|
| +**/
|
| +}
|
| +
|
| } // namespace
|
|
|
| class GCMMessageCryptographerTest : public ::testing::Test {
|
| @@ -119,6 +290,17 @@ class GCMMessageCryptographerTest : public ::testing::Test {
|
| kKeySizeBits));
|
|
|
| ASSERT_TRUE(random_key->GetRawKey(&key_));
|
| +
|
| + std::string local_public_key, local_public_key_x509, local_private_key;
|
| + std::string peer_public_key, peer_public_key_x509, peer_private_key;
|
| +
|
| + ASSERT_TRUE(CreateP256KeyPair(&local_private_key, &local_public_key_x509,
|
| + &local_public_key));
|
| + ASSERT_TRUE(CreateP256KeyPair(&peer_private_key, &peer_public_key_x509,
|
| + &peer_public_key));
|
| +
|
| + cryptographer_.reset(
|
| + new GCMMessageCryptographer(local_public_key, peer_public_key));
|
| }
|
|
|
| protected:
|
| @@ -133,12 +315,12 @@ class GCMMessageCryptographerTest : public ::testing::Test {
|
| return salt;
|
| }
|
|
|
| - GCMMessageCryptographer* cryptographer() { return &cryptographer_; }
|
| + GCMMessageCryptographer* cryptographer() { return cryptographer_.get(); }
|
|
|
| base::StringPiece key() const { return key_; }
|
|
|
| private:
|
| - GCMMessageCryptographer cryptographer_;
|
| + scoped_ptr<GCMMessageCryptographer> cryptographer_;
|
|
|
| std::string key_;
|
| };
|
| @@ -298,4 +480,87 @@ TEST_F(GCMMessageCryptographerTest, DecryptionTestVectors) {
|
| }
|
| }
|
|
|
| +template <typename I> std::string n2hexstr(I w, size_t hex_len = sizeof(I)<<1) {
|
| + static const char* digits = "0123456789ABCDEF";
|
| + std::string rc(hex_len,'0');
|
| + for (size_t i=0, j=(hex_len-1)*4 ; i<hex_len; ++i,j-=4)
|
| + rc[i] = digits[(w>>j) & 0x0f];
|
| + return rc;
|
| +}
|
| +
|
| +TEST_F(GCMMessageCryptographerTest, ReferenceTest) {
|
| + // This test verifies Chrome's implementation against the reference vector
|
| + // given in the draft-thomson-http-encryption examples.
|
| + const char kSalt[] = "Qg61ZJRva_XBE9IEUelU3A";
|
| + const char kPayload[] = "G6j_sfKg0qebO62yXpTCayN2KV24QitNiTvLgcFiEj0";
|
| +
|
| + const char kLocalPrivateKey[] = "9FWl15_QUQAWDaD3k3l50ZBZQJ4au27F1V4F0uLSD_M";
|
| + const char kLocalPublicKey[] =
|
| + "BCEkBjzL8Z3C-oi2Q7oE5t2Np-p7osjGLg93qUP0wvqRT21EEWyf0cQDQcakQMqz4hQKYOQ3"
|
| + "il2nNZct4HgAUQU";
|
| +
|
| + // Note: X.509 SubjectPublicKeyInfo representation of |kLocalPublicKey|.
|
| + const char kLocalPublicKeyX509[] = ""; // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
| +
|
| + const char kPeerPrivateKey[] = "vG7TmzUX9NfVR4XUGBkLAFu8iDyQe-q_165JkkN0Vlw";
|
| + const char kPeerPublicKey[] =
|
| + "BDgpRKok2GZZDmS4r63vbJSUtcQx4Fq1V58-6-3NbZzSTlZsQiCEDTQy3CZ0ZMsqeqsEb7qW"
|
| + "2blQHA4S48fynTk";
|
| +
|
| + const char kExpectedOutput[] = "I am the walrus";
|
| +
|
| + std::string salt;
|
| + ASSERT_TRUE(base::Base64UrlDecode(
|
| + kSalt, base::Base64UrlDecodePolicy::IGNORE_PADDING, &salt));
|
| +
|
| + std::string payload;
|
| + ASSERT_TRUE(base::Base64UrlDecode(
|
| + kPayload, base::Base64UrlDecodePolicy::IGNORE_PADDING, &payload));
|
| +
|
| + std::string local_private_key, local_public_key, local_public_key_x509;
|
| + ASSERT_TRUE(base::Base64UrlDecode(kLocalPrivateKey,
|
| + base::Base64UrlDecodePolicy::IGNORE_PADDING,
|
| + &local_private_key));
|
| + ASSERT_TRUE(base::Base64UrlDecode(kLocalPublicKey,
|
| + base::Base64UrlDecodePolicy::IGNORE_PADDING,
|
| + &local_public_key));
|
| + ASSERT_TRUE(base::Base64UrlDecode(kLocalPublicKeyX509,
|
| + base::Base64UrlDecodePolicy::IGNORE_PADDING,
|
| + &local_public_key_x509));
|
| +
|
| + std::string peer_private_key, peer_public_key;
|
| + ASSERT_TRUE(base::Base64UrlDecode(kPeerPrivateKey,
|
| + base::Base64UrlDecodePolicy::IGNORE_PADDING,
|
| + &peer_private_key));
|
| + ASSERT_TRUE(base::Base64UrlDecode(kPeerPublicKey,
|
| + base::Base64UrlDecodePolicy::IGNORE_PADDING,
|
| + &peer_public_key));
|
| +
|
| + std::string local_output, peer_output;
|
| + for (size_t i = 0; i < local_public_key.size(); ++i)
|
| + local_output += "0x" + n2hexstr(local_public_key[i]) + " ";
|
| +
|
| + for (size_t i = 0; i < peer_public_key.size(); ++i)
|
| + peer_output += " " + std::to_string(static_cast<int>(peer_public_key[i]));
|
| +
|
| + LOG(INFO) << "Local private: (" << local_public_key.size() << ") [" << local_output << "]";
|
| + //LOG(INFO) << "Peer public: (" << peer_public_key.size() << ") [" << peer_output << "]";
|
| +
|
| + std::string encrypted_private_key =
|
| + ConvertRawPrivateKeyToPKCS8EncryptedPrivateKeyInfo(local_private_key,
|
| + local_public_key);
|
| +
|
| + std::string shared_secret;
|
| + ASSERT_TRUE(ComputeSharedP256Secret(encrypted_private_key, local_public_key_x509,
|
| + peer_public_key, &shared_secret));
|
| +
|
| + std::string plaintext;
|
| +
|
| + GCMMessageCryptographer cryptographer(local_public_key, peer_public_key);
|
| + ASSERT_TRUE(cryptographer.Decrypt(payload, shared_secret, salt, 4096,
|
| + &plaintext));
|
| +
|
| + EXPECT_EQ(kExpectedOutput, plaintext);
|
| +}
|
| +
|
| } // namespace gcm
|
|
|