Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(533)

Unified Diff: components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc

Issue 1453503002: [NOT FOR REVIEW] Ensure contributory behaviour for Web Push encryption. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@push-renames
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698