summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorpeter <peter@chromium.org>2015-11-30 14:43:46 -0800
committerCommit bot <commit-bot@chromium.org>2015-11-30 22:44:19 +0000
commitfb05ff09fc86a64b31761b4eddd587816c2a206d (patch)
treeb86816a44bab5b42e76e82c52e952d664745d4b2 /components
parentf8d112407eed9fad36473b7e13f990b51e9897e4 (diff)
downloadchromium_src-fb05ff09fc86a64b31761b4eddd587816c2a206d.zip
chromium_src-fb05ff09fc86a64b31761b4eddd587816c2a206d.tar.gz
chromium_src-fb05ff09fc86a64b31761b4eddd587816c2a206d.tar.bz2
Include the public keys in the Web Push CEK/nonce info
This CL implements the changes introduced in the following pull request to the IETF HTTP Encrypted Encoding draft: https://goo.gl/f9ktM0 A reference test is also introduced to verify our implementation against an example given in the specification. BUG=486040 Review URL: https://codereview.chromium.org/1457463002 Cr-Commit-Position: refs/heads/master@{#362250}
Diffstat (limited to 'components')
-rw-r--r--components/gcm_driver/crypto/gcm_encryption_provider.cc3
-rw-r--r--components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc5
-rw-r--r--components/gcm_driver/crypto/gcm_message_cryptographer.cc79
-rw-r--r--components/gcm_driver/crypto/gcm_message_cryptographer.h26
-rw-r--r--components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc231
5 files changed, 278 insertions, 66 deletions
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider.cc b/components/gcm_driver/crypto/gcm_encryption_provider.cc
index 5ed56e4..3378c61 100644
--- a/components/gcm_driver/crypto/gcm_encryption_provider.cc
+++ b/components/gcm_driver/crypto/gcm_encryption_provider.cc
@@ -177,7 +177,8 @@ void GCMEncryptionProvider::DecryptMessageWithKey(
std::string plaintext;
- GCMMessageCryptographer cryptographer;
+ GCMMessageCryptographer cryptographer(
+ GCMMessageCryptographer::Label::P256, pair.public_key(), dh);
if (!cryptographer.Decrypt(message.raw_data, shared_secret, salt, rs,
&plaintext)) {
DLOG(ERROR) << "Unable to decrypt the incoming data.";
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
index c8ad5dd..edb0bae 100644
--- a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
+++ b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
@@ -276,7 +276,10 @@ TEST_F(GCMEncryptionProviderTest, EncryptionRoundTrip) {
// Encrypts the |kExampleMessage| using the generated shared key and the
// random |salt|, storing the result in |record_size| and the message.
- GCMMessageCryptographer cryptographer;
+ GCMMessageCryptographer cryptographer(
+ GCMMessageCryptographer::Label::P256, pair.public_key(),
+ server_pair.public_key());
+
ASSERT_TRUE(cryptographer.Encrypt(kExampleMessage, shared_secret, salt,
&record_size, &message.raw_data));
diff --git a/components/gcm_driver/crypto/gcm_message_cryptographer.cc b/components/gcm_driver/crypto/gcm_message_cryptographer.cc
index a3bcfad..1daf579 100644
--- a/components/gcm_driver/crypto/gcm_message_cryptographer.cc
+++ b/components/gcm_driver/crypto/gcm_message_cryptographer.cc
@@ -5,9 +5,10 @@
#include "components/gcm_driver/crypto/gcm_message_cryptographer.h"
#include <algorithm>
+#include <sstream>
-#include "base/big_endian.h"
#include "base/logging.h"
+#include "base/sys_byteorder.h"
#include "crypto/hkdf.h"
namespace gcm {
@@ -17,18 +18,68 @@ namespace {
// of a uint64_t, which is used to indicate the record sequence number.
const uint64_t kNonceSize = 12;
-// The default record size as defined by draft-thomson-http-encryption-01.
+// The default record size as defined by draft-thomson-http-encryption.
const size_t kDefaultRecordSize = 4096;
// Key size, in bytes, of a valid AEAD_AES_128_GCM key.
const size_t kContentEncryptionKeySize = 16;
+// Creates the info parameter for an HKDF value for the given |content_encoding|
+// in accordance with draft-thomson-http-encryption.
+//
+// cek_info = "Content-Encoding: aesgcm128" || 0x00 || context
+// nonce_info = "Content-Encoding: nonce" || 0x00 || context
+//
+// context = label || 0x00 ||
+// length(recipient_public) || recipient_public ||
+// length(sender_public) || sender_public
+//
+// The length of the public keys must be written as a two octet unsigned integer
+// in network byte order (big endian).
+std::string InfoForContentEncoding(
+ const char* content_encoding,
+ GCMMessageCryptographer::Label label,
+ const base::StringPiece& recipient_public_key,
+ const base::StringPiece& sender_public_key) {
+ DCHECK(GCMMessageCryptographer::Label::P256 == label);
+ DCHECK_EQ(recipient_public_key.size(), 65u);
+ DCHECK_EQ(sender_public_key.size(), 65u);
+
+ std::stringstream info_stream;
+ info_stream << "Content-Encoding: " << content_encoding << '\x00';
+
+ switch (label) {
+ case GCMMessageCryptographer::Label::P256:
+ info_stream << "P-256" << '\x00';
+ break;
+ }
+
+ uint16_t local_len = base::HostToNet16(recipient_public_key.size());
+ info_stream.write(reinterpret_cast<char*>(&local_len), sizeof(local_len));
+ info_stream << recipient_public_key;
+
+ uint16_t peer_len = base::HostToNet16(sender_public_key.size());
+ info_stream.write(reinterpret_cast<char*>(&peer_len), sizeof(peer_len));
+ info_stream << sender_public_key;
+
+ return info_stream.str();
+}
+
} // namespace
const size_t GCMMessageCryptographer::kAuthenticationTagBytes = 16;
const size_t GCMMessageCryptographer::kSaltSize = 16;
-GCMMessageCryptographer::GCMMessageCryptographer() {}
+GCMMessageCryptographer::GCMMessageCryptographer(
+ Label label,
+ const base::StringPiece& recipient_public_key,
+ const base::StringPiece& sender_public_key)
+ : content_encryption_key_info_(
+ InfoForContentEncoding("aesgcm128", label, recipient_public_key,
+ sender_public_key)),
+ nonce_info_(
+ InfoForContentEncoding("nonce", label, recipient_public_key,
+ sender_public_key)) {}
GCMMessageCryptographer::~GCMMessageCryptographer() {}
@@ -46,10 +97,10 @@ bool GCMMessageCryptographer::Encrypt(const base::StringPiece& plaintext,
std::string content_encryption_key = DeriveContentEncryptionKey(key, salt);
std::string nonce = DeriveNonce(key, salt);
- // draft-nottingham-http-encryption-encoding-00 allows between 0 and 255
- // octets of padding to be inserted before the enciphered content, with the
- // length of the padding stored in the first octet of the payload. Since
- // there is no necessity for payloads to contain padding, don't add any.
+ // draft-thomson-http-encryption allows between 0 and 255 octets of padding to
+ // be inserted before the enciphered content, with the length of the padding
+ // stored in the first octet of the payload. Since there is no necessity for
+ // payloads to contain padding, don't add any.
std::string record;
record.reserve(plaintext.size() + 1);
record.append(1, '\0');
@@ -82,8 +133,8 @@ bool GCMMessageCryptographer::Decrypt(
// The |ciphertext| must be at least kAuthenticationTagBytes + 1 bytes, which
// would be used for an empty message. Per
- // https://tools.ietf.org/html/draft-thomson-webpush-encryption-01#section-3,
- // the |record_size| parameter must be large enough to use only one record.
+ // https://tools.ietf.org/html/draft-thomson-http-encryption-02#section-3, the
+ // |record_size| parameter must be large enough to use only one record.
if (ciphertext.size() < kAuthenticationTagBytes + 1 ||
ciphertext.size() >= record_size + kAuthenticationTagBytes + 1) {
return false;
@@ -125,7 +176,7 @@ std::string GCMMessageCryptographer::DeriveContentEncryptionKey(
const base::StringPiece& key,
const base::StringPiece& salt) const {
crypto::HKDF hkdf(key, salt,
- "Content-Encoding: aesgcm128",
+ content_encryption_key_info_,
kContentEncryptionKeySize,
0, /* iv_bytes_to_generate */
0 /* subkey_secret_bytes_to_generate */);
@@ -137,14 +188,14 @@ std::string GCMMessageCryptographer::DeriveNonce(
const base::StringPiece& key,
const base::StringPiece& salt) const {
crypto::HKDF hkdf(key, salt,
- "Content-Encoding: nonce",
+ nonce_info_,
kNonceSize,
0, /* iv_bytes_to_generate */
0 /* subkey_secret_bytes_to_generate */);
- // draft-thomson-http-encryption-01 defines that the result should be XOR'ed
- // with the record's sequence number, but because Web Push encryption is
- // limited to a single record we do not have to do that.
+ // draft-thomson-http-encryption defines that the result should be XOR'ed with
+ // the record's sequence number, however, Web Push encryption is limited to a
+ // single record per draft-ietf-webpush-encryption.
return hkdf.client_write_key().as_string();
}
diff --git a/components/gcm_driver/crypto/gcm_message_cryptographer.h b/components/gcm_driver/crypto/gcm_message_cryptographer.h
index e2b299e..0ee275d 100644
--- a/components/gcm_driver/crypto/gcm_message_cryptographer.h
+++ b/components/gcm_driver/crypto/gcm_message_cryptographer.h
@@ -15,16 +15,16 @@
namespace gcm {
// Messages delivered through GCM may be encrypted according to the IETF Web
-// Push protocol, as described in draft-thomson-webpush-encryption-01:
+// Push protocol, as described in draft-ietf-webpush-encryption:
//
-// https://tools.ietf.org/html/draft-thomson-webpush-encryption-01
+// https://tools.ietf.org/html/draft-ietf-webpush-encryption
//
// This class implements the ability to encrypt or decrypt such messages using
// AEAD_AES_128_GCM with a 16-octet authentication tag. The encrypted payload
// will be stored in a single record as described in
-// draft-thomson-http-encryption-01:
+// draft-thomson-http-encryption:
//
-// https://tools.ietf.org/html/draft-thomson-http-encryption-01
+// https://tools.ietf.org/html/draft-thomson-http-encryption
//
// Note that while this class is not responsible for creating or storing the
// actual keys, it uses a key derivation function for the actual message
@@ -36,7 +36,17 @@ class GCMMessageCryptographer {
// unique content encryption key for a given message.
static const size_t kSaltSize;
- GCMMessageCryptographer();
+ // Label of the encryption group used to calculate the shared secret.
+ enum class Label {
+ P256
+ };
+
+ // Creates a new cryptographer with |label|, identifying the group used for
+ // the key agreement, and the public keys of both the recipient and sender.
+ GCMMessageCryptographer(Label label,
+ const base::StringPiece& recipient_public_key,
+ const base::StringPiece& sender_public_key);
+
~GCMMessageCryptographer();
// Encrypts |plaintext| using the |key| and the |salt|, both of which must be
@@ -83,6 +93,12 @@ class GCMMessageCryptographer {
// Derives the nonce from |key| and |salt|.
std::string DeriveNonce(const base::StringPiece& key,
const base::StringPiece& salt) const;
+
+ // The info parameters to the HKDFs used for deriving the content encryption
+ // key and the nonce. These contain the label of the used curve, as well as
+ // the sender and recipient's public keys.
+ std::string content_encryption_key_info_;
+ std::string nonce_info_;
};
} // namespace gcm
diff --git a/components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc b/components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc
index a1a0280..b54e5fc 100644
--- a/components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc
+++ b/components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc
@@ -4,9 +4,10 @@
#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"
@@ -21,6 +22,15 @@ const size_t kKeySizeBits = 128;
// Example plaintext data to use in the tests.
const char kExamplePlaintext[] = "Example plaintext";
+// Fixed local and peer public keys must be used to get consistent results.
+const char kLocalPublicKeyCommon[] =
+ "BIXzEKOFquzVlr_1tS1bhmobZU3IJq2bswDflMJsizixqd_HFSvCJaCAotNjBw6A-iKQk7FshA"
+ "jdAA-T9Rh1a7U";
+
+const char kPeerPublicKeyCommon[] =
+ "BAuzSrdIyKZsHnuOhqklkIKi6fl65V9OdPy6nFwI2SywL5-6I5SkkDtfIL9y7NkoEE345jv2Eo"
+ "5n4NIbLJIBjTM";
+
// A test vector contains the information necessary to either encrypt or decrypt
// a message. These vectors were created using a JavaScript implementation of
// the same RFCs that the GCMMessageCryptographer implements.
@@ -35,22 +45,22 @@ struct TestVector {
const TestVector kEncryptionTestVectors[] = {
// Simple message.
{ "Hello, world!",
- "AhA6n2oFYPWIh+cXwyv1m2C0JvmjHB4ZkXj8QylESXU=",
- "tsJYqAGvFDk6lDEv7daecw==",
+ "AhA6n2oFYPWIh-cXwyv1m2C0JvmjHB4ZkXj8QylESXU",
+ "tsJYqAGvFDk6lDEv7daecw",
4096,
- "KNXRqBR9VKhtajeMaeKR/rHYIiORcyeFpUwWFGyS"
+ "Vhe0Wn01NnZ_AFDgdvD4rthl2Pz9u7-WX-BAHsGX"
},
// Empty message.
{ "",
- "lMyvTong4VR053jfCpWmMDGW5dEDAqiTZUIU+inhTjU=",
- "wH3uvZqcN6oey9whiGpn1A==",
+ "lMyvTong4VR053jfCpWmMDGW5dEDAqiTZUIU-inhTjU",
+ "wH3uvZqcN6oey9whiGpn1A",
4096,
- "Mnfr+AU5o7D30gjFdVOTFtw="
+ "l81ck9Au-SbeTqQdPx-ky7Y"
},
// Message with an invalid salt size.
{ "Hello, world!",
- "CcdxzkR6z1EY9vSrM7/IxYVxDxu46hV638EZQTPd7XI=",
- "aRr1fI1YSGVi5XU=",
+ "CcdxzkR6z1EY9vSrM7_IxYVxDxu46hV638EZQTPd7XI",
+ "aRr1fI1YSGVi5XU",
4096,
nullptr // expected to fail
}
@@ -58,52 +68,45 @@ const TestVector kEncryptionTestVectors[] = {
const TestVector kDecryptionTestVectors[] = {
// Simple message.
- { "avAFNhdbQohzizu+ORbU4XHhHSaXzw9lTN7UzB/j",
- "47ZytAw9qHlm+Q8g+7rH81rUPzaCgGcoFvlS1qxQtQk=",
- "EuR7EVetcaWpndXd/dKeyA==",
+ { "bkXjWrS4fIVFJOnBgUG6ZY9Kgip6bDX7QYHueYVc",
+ "47ZytAw9qHlm-Q8g-7rH81rUPzaCgGcoFvlS1qxQtQk",
+ "EuR7EVetcaWpndXd_dKeyA",
4096,
"Hello, world!"
},
// Simple message with 16 bytes of padding.
- { "0198n7ZJ/ZPMnl4ZU2l9Lma5ktKbuzXCiJEXyYtROmWTP8RSiZd8sUd48xpk6Q==",
- "MYSsNybwrTzRIzQYUq/yFPc6ugcTrJdEZJDM4NswvUg=",
- "8sEAMQYnufo2UkKl80cUGQ==",
+ { "IkCA6-h14nYSJHsCy0iC7BlkXuO4bc_dyNo_D_d5R1fVaG7jggdhdO29H-1d5w",
+ "MYSsNybwrTzRIzQYUq_yFPc6ugcTrJdEZJDM4NswvUg",
+ "8sEAMQYnufo2UkKl80cUGQ",
4096,
"Hello, world!"
},
// Empty message.
- { "g+ACk32a4gK2dS2xllKXn4c=",
- "S3+Ki/+XtzR66gUp/zR75CC5JXO62pyr5fWfneTYwFE=",
- "4RM6s19jJHdmqiVEJDp9jg==",
+ { "ebRdG2oMvjURqU4NXeY_XvM",
+ "S3-Ki_-XtzR66gUp_zR75CC5JXO62pyr5fWfneTYwFE",
+ "4RM6s19jJHdmqiVEJDp9jg",
4096,
""
},
// Message with an invalid salt size.
- { "rt4OiodS087DAQo6e24wA55k0hRPAHgz7OX7m+nj",
- "wW3Iy5ma803lLd+ysPdHUe2NB3HqXbY0XhCCdG5Y1Gw=",
- "N7oMH/xohAhMhOY=",
+ { "rt4OiodS087DAQo6e24wA55k0hRPAHgz7OX7m-nj",
+ "wW3Iy5ma803lLd-ysPdHUe2NB3HqXbY0XhCCdG5Y1Gw",
+ "N7oMH_xohAhMhOY",
4096,
nullptr // expected to fail
},
// Message with an invalid record size.
- { "AsuoRkFtqLE1c0mGCae4OvgZSCSHWCoeRL9mXKjY",
- "omxWz7tse3lgDpxUP+e7u14Dp1irvV3BdzXTcZOtsHs=",
- "vKJD3bexto1hY64KVzS7ug==",
- 3,
+ { "HNT3GJAt4XHmIagUhh-osH1",
+ "kR5BMfqMKOD1yrLKE2giObXHI7merrMtnoO2oqneqXA",
+ "SQeJSPrqHvTdSfAMF8bBzQ",
+ 8,
nullptr // expected to fail
},
- // Truncated message.
- { "AGr4BfZSXW9txWkAG8pjg7IuRWWm1Mo8bDli/PSv",
- "kR5BMfqMKOD1yrLKE2giObXHI7merrMtnoO2oqneqXA=",
- "SQeJSPrqHvTdSfAMF8bBzQ==",
- 13,
- nullptr // expected to fail
- },
// Message with multiple (2) records.
- { "H2ujfPbpRbVSy+adIG2NRe4VxkX4V0r/zl6e9xnMSF6LSutblGdWLrwQc82Xh7DXAQlihW0q3"
- "IQzHP+LIxuAiA==",
- "W3W4gx7sqcfmBnvNNdO9d4MBCC1bvJkvsNjZOGD+CCg=",
- "xG0TPGi9aIcxjpXKmaYBBQ==",
+ { "RqQVHRXlfYjzW9xhzh3V_KijLKjZiKzGXosqN_IaMzi0zI0tXXhC1urtrk3iWRoqttNXpkD2r"
+ "UCgLy8A1FnTjw",
+ "W3W4gx7sqcfmBnvNNdO9d4MBCC1bvJkvsNjZOGD-CCg",
+ "xG0TPGi9aIcxjpXKmaYBBQ",
7,
nullptr // expected to fail
}
@@ -119,6 +122,18 @@ class GCMMessageCryptographerTest : public ::testing::Test {
kKeySizeBits));
ASSERT_TRUE(random_key->GetRawKey(&key_));
+
+ std::string local_public_key, peer_public_key;
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kLocalPublicKeyCommon, base::Base64UrlDecodePolicy::IGNORE_PADDING,
+ &local_public_key));
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kPeerPublicKeyCommon, base::Base64UrlDecodePolicy::IGNORE_PADDING,
+ &peer_public_key));
+
+ cryptographer_.reset(
+ new GCMMessageCryptographer(GCMMessageCryptographer::Label::P256,
+ local_public_key, peer_public_key));
}
protected:
@@ -133,12 +148,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_;
};
@@ -254,8 +269,12 @@ TEST_F(GCMMessageCryptographerTest, EncryptionTestVectors) {
for (size_t i = 0; i < arraysize(kEncryptionTestVectors); ++i) {
SCOPED_TRACE(i);
- ASSERT_TRUE(base::Base64Decode(kEncryptionTestVectors[i].key, &key));
- ASSERT_TRUE(base::Base64Decode(kEncryptionTestVectors[i].salt, &salt));
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kEncryptionTestVectors[i].key,
+ base::Base64UrlDecodePolicy::IGNORE_PADDING, &key));
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kEncryptionTestVectors[i].salt,
+ base::Base64UrlDecodePolicy::IGNORE_PADDING, &salt));
const bool has_output = kEncryptionTestVectors[i].output;
const bool result = cryptographer()->Encrypt(
@@ -267,8 +286,9 @@ TEST_F(GCMMessageCryptographerTest, EncryptionTestVectors) {
}
EXPECT_TRUE(result);
- ASSERT_TRUE(base::Base64Decode(kEncryptionTestVectors[i].output,
- &output));
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kEncryptionTestVectors[i].output,
+ base::Base64UrlDecodePolicy::IGNORE_PADDING, &output));
EXPECT_EQ(kEncryptionTestVectors[i].record_size, record_size);
EXPECT_EQ(output, ciphertext);
@@ -280,9 +300,15 @@ TEST_F(GCMMessageCryptographerTest, DecryptionTestVectors) {
for (size_t i = 0; i < arraysize(kDecryptionTestVectors); ++i) {
SCOPED_TRACE(i);
- ASSERT_TRUE(base::Base64Decode(kDecryptionTestVectors[i].input, &input));
- ASSERT_TRUE(base::Base64Decode(kDecryptionTestVectors[i].key, &key));
- ASSERT_TRUE(base::Base64Decode(kDecryptionTestVectors[i].salt, &salt));
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kDecryptionTestVectors[i].input,
+ base::Base64UrlDecodePolicy::IGNORE_PADDING, &input));
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kDecryptionTestVectors[i].key,
+ base::Base64UrlDecodePolicy::IGNORE_PADDING, &key));
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kDecryptionTestVectors[i].salt,
+ base::Base64UrlDecodePolicy::IGNORE_PADDING, &salt));
const bool has_output = kDecryptionTestVectors[i].output;
const bool result = cryptographer()->Decrypt(
@@ -298,4 +324,119 @@ TEST_F(GCMMessageCryptographerTest, DecryptionTestVectors) {
}
}
+// Reference test against the HTTP encryption coding IETF draft. Both the
+// encrypting and decrypting routines of the GCMMessageCryptographer are
+// covered by this test.
+//
+// https://tools.ietf.org/html/draft-thomson-http-encryption#section-5.5
+TEST_F(GCMMessageCryptographerTest, ReferenceTest) {
+ // base64url-encoded representation of the 16 octet salt.
+ const char kSalt[] = "Qg61ZJRva_XBE9IEUelU3A";
+
+ // base64url-encoded representation of the ciphertext, and the plaintext as
+ // a normal character array.
+ const char kCiphertext[] = "G6j_sfKg0qebO62yXpTCayN2KV24QitNiTvLgcFiEj0";
+ const char kPlaintext[] = "I am the walrus";
+
+ // Private keys of the sender and receiver represented as ASN.1-encoded PKCS
+ // #8 EncryptedPrivateKeyInfo blocks, as required by the ECPrivateKey.
+ const char kReceiverPrivate[] =
+ "MIGxMBwGCiqGSIb3DQEMAQMwDgQIqMt4d7uJdt4CAggABIGQeikRHE3CqUeF-uUtJno9BL0g"
+ "mNRyDihZe8P3nF_g-NYVzvdQowsXfYeza6OQOdDuMXxnGgNToVy2jsiWVN6rxCaSMTY622y8"
+ "ajW5voSdqC2PakQ8ZNTPNHarLDMC9NpgGKrUh8hfRLhvb7vtbKIWmx-22rQB5yTYdqzN2m7A"
+ "GHMWRnVk0mMzMsMjZqYFaa2D";
+
+ const char kSenderPrivate[] =
+ "MIGxMBwGCiqGSIb3DQEMAQMwDgQIFfJ62c9VwXgCAggABIGQkRxDRPQjwuWp1C3-z1pYTDqF"
+ "_NZ1kbPsjmkC3JSv02oAYHtBAtKa2e3oAPqsPfCvoCJBJs6G4WY4EuEO1YFL6RKpNl3DpIUc"
+ "v9ShR27p_je_nyLpNBAxn2drnjlF_K6s4gcJmcvCxuNjAwOlLMPvQqGjOR2K_oMs1Hdq0EKJ"
+ "NwWt3WUVEpuQF_WhYjCVIeGO";
+
+ // Public keys of the sender and receiver represented as uncompressed points,
+ // and X.509 SubjectPublicKeyInfo blocks as required by NSS.
+ const char kReceiverPublicUncompressed[] =
+ "BCEkBjzL8Z3C-oi2Q7oE5t2Np-p7osjGLg93qUP0wvqRT21EEWyf0cQDQcakQMqz4hQKYOQ3"
+ "il2nNZct4HgAUQU";
+ const char kReceiverPublicX509[] =
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEISQGPMvxncL6iLZDugTm3Y2n6nuiyMYuD3ep"
+ "Q_TC-pFPbUQRbJ_RxANBxqRAyrPiFApg5DeKXac1ly3geABRBQ";
+
+ const char kSenderPublicUncompressed[] =
+ "BDgpRKok2GZZDmS4r63vbJSUtcQx4Fq1V58-6-3NbZzSTlZsQiCEDTQy3CZ0ZMsqeqsEb7qW"
+ "2blQHA4S48fynTk";
+ const char kSenderPublicX509[] =
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOClEqiTYZlkOZLivre9slJS1xDHgWrVXnz7r"
+ "7c1tnNJOVmxCIIQNNDLcJnRkyyp6qwRvupbZuVAcDhLjx_KdOQ";
+
+ // Convert the salt and the ciphertext to binary representations.
+ std::string salt, reference_ciphertext;
+
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kSalt, base::Base64UrlDecodePolicy::IGNORE_PADDING, &salt));
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kCiphertext, base::Base64UrlDecodePolicy::IGNORE_PADDING,
+ &reference_ciphertext));
+
+ // Convert the public and private keys to binary representations.
+ std::string receiver_private, receiver_public, receiver_public_x509;
+ std::string sender_private, sender_public, sender_public_x509;
+
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kReceiverPrivate, base::Base64UrlDecodePolicy::IGNORE_PADDING,
+ &receiver_private));
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kReceiverPublicUncompressed, base::Base64UrlDecodePolicy::IGNORE_PADDING,
+ &receiver_public));
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kReceiverPublicX509, base::Base64UrlDecodePolicy::IGNORE_PADDING,
+ &receiver_public_x509));
+
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kSenderPrivate, base::Base64UrlDecodePolicy::IGNORE_PADDING,
+ &sender_private));
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kSenderPublicUncompressed, base::Base64UrlDecodePolicy::IGNORE_PADDING,
+ &sender_public));
+ ASSERT_TRUE(base::Base64UrlDecode(
+ kSenderPublicX509, base::Base64UrlDecodePolicy::IGNORE_PADDING,
+ &sender_public_x509));
+
+ // Compute the shared secret between the sender and the receiver's keys.
+ std::string sender_shared_secret, receiver_shared_secret;
+
+ ASSERT_TRUE(ComputeSharedP256Secret(sender_private, sender_public_x509,
+ receiver_public, &sender_shared_secret));
+ ASSERT_TRUE(ComputeSharedP256Secret(receiver_private, receiver_public_x509,
+ sender_public, &receiver_shared_secret));
+
+ ASSERT_GT(sender_shared_secret.size(), 0u);
+ ASSERT_EQ(sender_shared_secret, receiver_shared_secret);
+
+ GCMMessageCryptographer cryptographer(
+ GCMMessageCryptographer::Label::P256, receiver_public, sender_public);
+
+ size_t record_size = 0;
+ std::string ciphertext;
+
+ ASSERT_TRUE(cryptographer.Encrypt(kPlaintext, sender_shared_secret, salt,
+ &record_size, &ciphertext));
+
+ EXPECT_GT(record_size, 1u);
+ EXPECT_EQ(16u + 1u + strlen(kPlaintext), ciphertext.size());
+
+ // Verify that the created ciphertext matches the reference ciphertext.
+ EXPECT_EQ(reference_ciphertext, ciphertext);
+
+ // Decrypt the ciphertext with the default record size to verify that the
+ // resulting plaintext matches the input text.
+ std::string plaintext;
+
+ ASSERT_TRUE(cryptographer.Decrypt(
+ reference_ciphertext, receiver_shared_secret, salt,
+ 4096 /* record size */, &plaintext));
+
+ // Verify that the decrypted plaintext matches the reference plaintext.
+ EXPECT_EQ(kPlaintext, plaintext);
+}
+
} // namespace gcm