// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/gcm_driver/crypto/gcm_message_cryptographer.h"

#include <stddef.h>

#include <memory>

#include "base/base64url.h"
#include "base/macros.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"

namespace gcm {

namespace {

// The number of bits of the key in AEAD_AES_128_GCM.
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";

const char kAuthSecretCommon[] = "MyAuthenticationSecret";

// 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.
struct TestVector {
  const char* const input;
  const char* const key;
  const char* const salt;
  size_t record_size;
  const char* const output;
};

const TestVector kEncryptionTestVectors[] = {
  // Simple message.
  { "Hello, world!",
    "AhA6n2oFYPWIh-cXwyv1m2C0JvmjHB4ZkXj8QylESXU",
    "tsJYqAGvFDk6lDEv7daecw",
    4096,
    "FgWrrnZq79oI_N4ORkVLHx1jfVmjeiIk-xFX8PzVuA"
   },
   // Empty message.
   { "",
    "lMyvTong4VR053jfCpWmMDGW5dEDAqiTZUIU-inhTjU",
    "wH3uvZqcN6oey9whiGpn1A",
    4096,
    "MTI9zZ8CJTUzbZ4qNDoQZs0k"
   },
   // Message with an invalid salt size.
   { "Hello, world!",
    "CcdxzkR6z1EY9vSrM7_IxYVxDxu46hV638EZQTPd7XI",
    "aRr1fI1YSGVi5XU",
    4096,
    nullptr  // expected to fail
   }
};

const TestVector kDecryptionTestVectors[] = {
  // Simple message.
  { "ceiEu_YpmqLoakD4smdzvy2XKRQrJ9vBzB2aqYEfzw",
    "47ZytAw9qHlm-Q8g-7rH81rUPzaCgGcoFvlS1qxQtQk",
    "EuR7EVetcaWpndXd_dKeyA",
    4096,
    "Hello, world!"
   },
   // Simple message with 16 bytes of padding.
   { "WSf6fz1O0aIJyaPTCnvk83OqIQxsRKeFOvblPLsPpFB_1AV9ROk09TE1cGrB6zQ",
     "MYSsNybwrTzRIzQYUq_yFPc6ugcTrJdEZJDM4NswvUg",
     "8sEAMQYnufo2UkKl80cUGQ",
     4096,
     "Hello, world!"
   },
   // Empty message.
   { "Ur3vHedGDO5IPYDvbhHYjbjG",
     "S3-Ki_-XtzR66gUp_zR75CC5JXO62pyr5fWfneTYwFE",
     "4RM6s19jJHdmqiVEJDp9jg",
     4096,
     ""
   },
   // Message with an invalid salt size.
   { "iGrOpmJC5XTTf7wtgdhZ_qT",
     "wW3Iy5ma803lLd-ysPdHUe2NB3HqXbY0XhCCdG5Y1Gw",
     "N7oMH_xohAhMhOY",
     4096,
     nullptr  // expected to fail
   },
   // Message with an invalid record size.
   { "iGrOpmJC5XTTf7wtgdhZ_qT",
     "kR5BMfqMKOD1yrLKE2giObXHI7merrMtnoO2oqneqXA",
     "SQeJSPrqHvTdSfAMF8bBzQ",
     8,
     nullptr  // expected to fail
   },
   // Message with multiple (2) records.
   { "RqQVHRXlfYjzW9xhzh3V_KijLKjZiKzGXosqN_IaMzi0zI0tXXhC1urtrk3iWRoqttNXpkD2r"
         "UCgLy8A1FnTjw",
     "W3W4gx7sqcfmBnvNNdO9d4MBCC1bvJkvsNjZOGD-CCg",
     "xG0TPGi9aIcxjpXKmaYBBQ",
     7,
     nullptr  // expected to fail
   }
};

}  // namespace

class GCMMessageCryptographerTest : public ::testing::Test {
 public:
  void SetUp() override {
    std::unique_ptr<crypto::SymmetricKey> random_key(
        crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES,
                                                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,
                                    kAuthSecretCommon));
  }

 protected:
  // Generates a cryptographically secure random salt of 16-octets in size, the
  // required length as expected by the HKDF.
  std::string GenerateRandomSalt() {
    const size_t kSaltSize = 16;

    std::string salt;

    crypto::RandBytes(base::WriteInto(&salt, kSaltSize + 1), kSaltSize);
    return salt;
  }

  GCMMessageCryptographer* cryptographer() { return cryptographer_.get(); }

  base::StringPiece key() const { return key_; }

 private:
  std::unique_ptr<GCMMessageCryptographer> cryptographer_;

  std::string key_;
};

TEST_F(GCMMessageCryptographerTest, RoundTrip) {
  const std::string salt = GenerateRandomSalt();

  size_t record_size = 0;

  std::string ciphertext, plaintext;
  ASSERT_TRUE(cryptographer()->Encrypt(kExamplePlaintext, key(), salt,
                                       &record_size, &ciphertext));

  EXPECT_GT(record_size, ciphertext.size() - 16);
  EXPECT_GT(ciphertext.size(), 0u);

  ASSERT_TRUE(cryptographer()->Decrypt(ciphertext, key(), salt, record_size,
                                       &plaintext));

  EXPECT_EQ(kExamplePlaintext, plaintext);
}

TEST_F(GCMMessageCryptographerTest, RoundTripEmptyMessage) {
  const std::string salt = GenerateRandomSalt();
  const std::string message = "";

  size_t record_size = 0;

  std::string ciphertext, plaintext;
  ASSERT_TRUE(cryptographer()->Encrypt(message, key(), salt, &record_size,
                                       &ciphertext));

  EXPECT_GT(record_size, ciphertext.size() - 16);
  EXPECT_GT(ciphertext.size(), 0u);

  ASSERT_TRUE(cryptographer()->Decrypt(ciphertext, key(), salt, record_size,
                                       &plaintext));

  EXPECT_EQ(message, plaintext);
}

TEST_F(GCMMessageCryptographerTest, InvalidRecordSize) {
  const std::string salt = GenerateRandomSalt();

  size_t record_size = 0;

  std::string ciphertext, plaintext;
  ASSERT_TRUE(cryptographer()->Encrypt(kExamplePlaintext, key(), salt,
                                       &record_size, &ciphertext));

  EXPECT_GT(record_size, ciphertext.size() - 16);
  EXPECT_FALSE(cryptographer()->Decrypt(ciphertext, key(), salt,
                                        0 /* record_size */, &plaintext));

  EXPECT_FALSE(cryptographer()->Decrypt(ciphertext, key(), salt,
                                        ciphertext.size() - 17, &plaintext));

  EXPECT_TRUE(cryptographer()->Decrypt(ciphertext, key(), salt,
                                       ciphertext.size() - 16, &plaintext));
}

TEST_F(GCMMessageCryptographerTest, InvalidRecordPadding) {
  std::string message = std::string(sizeof(uint16_t), '\0') + kExamplePlaintext;

  const std::string salt = GenerateRandomSalt();

  const std::string prk = cryptographer()->DerivePseudoRandomKey(key());
  const std::string nonce = cryptographer()->DeriveNonce(prk, salt);
  const std::string content_encryption_key =
      cryptographer()->DeriveContentEncryptionKey(prk, salt);

  ASSERT_GT(message.size(), 1u);
  const size_t record_size = message.size() + 1;

  std::string ciphertext, plaintext;
  ASSERT_TRUE(cryptographer()->EncryptDecryptRecordInternal(
      GCMMessageCryptographer::ENCRYPT, message, content_encryption_key, nonce,
      &ciphertext));

  ASSERT_TRUE(cryptographer()->Decrypt(ciphertext, key(), salt, record_size,
                                       &plaintext));

  // Note that GCMMessageCryptographer::Decrypt removes the padding.
  EXPECT_EQ(kExamplePlaintext, plaintext);

  // Now run the same steps again, but say that there are four padding octets.
  // This should be rejected because the padding will not be all zeros.
  message[0] = 4;

  ASSERT_TRUE(cryptographer()->EncryptDecryptRecordInternal(
      GCMMessageCryptographer::ENCRYPT, message, content_encryption_key, nonce,
      &ciphertext));

  ASSERT_FALSE(cryptographer()->Decrypt(ciphertext, key(), salt, record_size,
                                        &plaintext));

  // Do the same but changing the second octet indicating padding size, leaving
  // the first octet at zero.
  message[0] = 0;
  message[1] = 4;

  ASSERT_TRUE(cryptographer()->EncryptDecryptRecordInternal(
      GCMMessageCryptographer::ENCRYPT, message, content_encryption_key, nonce,
      &ciphertext));

  ASSERT_FALSE(cryptographer()->Decrypt(ciphertext, key(), salt, record_size,
                                        &plaintext));

  // Run the same steps again, but say that there are more padding octets than
  // the length of the message.
  message[0] = 64;

  EXPECT_GT(static_cast<size_t>(message[0]), message.size());
  ASSERT_TRUE(cryptographer()->EncryptDecryptRecordInternal(
      GCMMessageCryptographer::ENCRYPT, message, content_encryption_key, nonce,
      &ciphertext));

  ASSERT_FALSE(cryptographer()->Decrypt(ciphertext, key(), salt, record_size,
                                        &plaintext));
}

TEST_F(GCMMessageCryptographerTest, EncryptionTestVectors) {
  std::string key, salt, output, ciphertext;
  size_t record_size = 0;

  for (size_t i = 0; i < arraysize(kEncryptionTestVectors); ++i) {
    SCOPED_TRACE(i);

    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(
        kEncryptionTestVectors[i].input, key, salt, &record_size, &ciphertext);

    if (!has_output) {
      EXPECT_FALSE(result);
      continue;
    }

    EXPECT_TRUE(result);
    ASSERT_TRUE(base::Base64UrlDecode(
        kEncryptionTestVectors[i].output,
        base::Base64UrlDecodePolicy::IGNORE_PADDING, &output));

    EXPECT_EQ(kEncryptionTestVectors[i].record_size, record_size);
    EXPECT_EQ(output, ciphertext);
  }
}

TEST_F(GCMMessageCryptographerTest, DecryptionTestVectors) {
  std::string input, key, salt, plaintext;
  for (size_t i = 0; i < arraysize(kDecryptionTestVectors); ++i) {
    SCOPED_TRACE(i);

    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(
        input, key, salt, kDecryptionTestVectors[i].record_size, &plaintext);

    if (!has_output) {
      EXPECT_FALSE(result);
      continue;
    }

    EXPECT_TRUE(result);
    EXPECT_EQ(kDecryptionTestVectors[i].output, plaintext);
  }
}

TEST_F(GCMMessageCryptographerTest, AuthSecretAffectsIKM) {
  std::string public_key;
  ASSERT_TRUE(base::Base64UrlDecode(
      kLocalPublicKeyCommon, base::Base64UrlDecodePolicy::IGNORE_PADDING,
      &public_key));

  // Fake IKM to use in the DerivePseudoRandomKey calls.
  const char kFakeIKM[] = "HelloWorld";

  GCMMessageCryptographer hello_cryptographer(
      GCMMessageCryptographer::Label::P256, public_key, public_key, "Hello");

  GCMMessageCryptographer world_cryptographer(
      GCMMessageCryptographer::Label::P256, public_key, public_key, "World");

  ASSERT_NE(hello_cryptographer.DerivePseudoRandomKey(kFakeIKM), kFakeIKM);
  ASSERT_NE(world_cryptographer.DerivePseudoRandomKey(kFakeIKM), kFakeIKM);

  ASSERT_NE(hello_cryptographer.DerivePseudoRandomKey(kFakeIKM),
            world_cryptographer.DerivePseudoRandomKey(kFakeIKM));

  std::string salt = GenerateRandomSalt();

  // Verify that the IKM actually gets used by the transformations.
  size_t hello_record_size, world_record_size;
  std::string hello_ciphertext, world_ciphertext;

  ASSERT_TRUE(hello_cryptographer.Encrypt(kExamplePlaintext, key(), salt,
                                          &hello_record_size,
                                          &hello_ciphertext));
  ASSERT_TRUE(world_cryptographer.Encrypt(kExamplePlaintext, key(), salt,
                                          &world_record_size,
                                          &world_ciphertext));

  // If the ciphertexts differ despite the same key and salt, it got used.
  ASSERT_NE(hello_ciphertext, world_ciphertext);

  // Verify that the different ciphertexts can also be translated back to the
  // plaintext content. This will fail if the auth secret isn't considered.
  std::string hello_plaintext, world_plaintext;

  ASSERT_TRUE(hello_cryptographer.Decrypt(hello_ciphertext, key(), salt,
                                          hello_record_size, &hello_plaintext));
  ASSERT_TRUE(world_cryptographer.Decrypt(world_ciphertext, key(), salt,
                                          world_record_size, &world_plaintext));

  EXPECT_EQ(kExamplePlaintext, hello_plaintext);
  EXPECT_EQ(kExamplePlaintext, world_plaintext);
}

// Common infrastructure for reference tests against the examples in the draft:
// https://tools.ietf.org/html/draft-thomson-http-encryption
class GCMMessageCryptographerReferenceTest
    : public GCMMessageCryptographerTest {
 protected:
  // Computes the shared secret between the sender and the receiver. The sender
  // must have a key-pair containing a X.509 SubjectPublicKeyInfo block and a
  // ASN.1-encoded PKCS #8 EncryptedPrivateKeyInfo block, whereas the receiver
  // must have a public key in uncompressed EC point format.
  void ComputeSharedSecret(const char* encoded_sender_private_key,
                           const char* encoded_sender_public_key_x509,
                           const char* encoded_receiver_public_key,
                           std::string* shared_secret) const {
    std::string sender_private_key, sender_public_key_x509, receiver_public_key;
    ASSERT_TRUE(base::Base64UrlDecode(
        encoded_sender_private_key,
        base::Base64UrlDecodePolicy::IGNORE_PADDING, &sender_private_key));
    ASSERT_TRUE(base::Base64UrlDecode(
        encoded_sender_public_key_x509,
        base::Base64UrlDecodePolicy::IGNORE_PADDING, &sender_public_key_x509));
    ASSERT_TRUE(base::Base64UrlDecode(
        encoded_receiver_public_key,
        base::Base64UrlDecodePolicy::IGNORE_PADDING, &receiver_public_key));

    ASSERT_TRUE(ComputeSharedP256Secret(
        sender_private_key, sender_public_key_x509, receiver_public_key,
        shared_secret));
  }

  // Creates a new cryptographer based on the P-256 curve with the given public
  // keys of the sender and receiver, and optionally, the authentication secret.
  // The public keys must be given as uncompressed P-256 EC points.
  void CreateCryptographer(
      const char* encoded_receiver_public_key,
      const char* encoded_sender_public_key,
      const char* encoded_auth_secret,
      std::unique_ptr<GCMMessageCryptographer>* cryptographer) const {
    std::string receiver_public_key, sender_public_key, auth_secret;
    ASSERT_TRUE(base::Base64UrlDecode(
        encoded_receiver_public_key,
        base::Base64UrlDecodePolicy::IGNORE_PADDING, &receiver_public_key));
    ASSERT_TRUE(base::Base64UrlDecode(
        encoded_sender_public_key,
        base::Base64UrlDecodePolicy::IGNORE_PADDING, &sender_public_key));

    if (encoded_auth_secret) {
      ASSERT_TRUE(base::Base64UrlDecode(
          encoded_auth_secret,
          base::Base64UrlDecodePolicy::IGNORE_PADDING, &auth_secret));
    }

    std::unique_ptr<GCMMessageCryptographer> instance(
        new GCMMessageCryptographer(GCMMessageCryptographer::Label::P256,
                                    receiver_public_key, sender_public_key,
                                    auth_secret));

    if (auth_secret.empty())
      instance->set_allow_empty_auth_secret_for_tests(true);

    cryptographer->swap(instance);
  }
};

TEST_F(GCMMessageCryptographerReferenceTest, WithAuthSecret) {
  // The 16-byte salt unique to the message.
  const char kSalt[] = "lngarbyKfMoi9Z75xYXmkg";

  // The 16-byte prearranged secret between the sender and receiver.
  const char kAuthSecret[] = "R29vIGdvbyBnJyBqb29iIQ";

  // The keying material used by the sender to encrypt the |kCiphertext|.
  const char kSenderPrivate[] =
      "MIGxMBwGCiqGSIb3DQEMAQMwDgQIh9aZ3UvuDloCAggABIGQZ-T8CJZe-no4mOTDgX1Gm986"
      "Gsbe3mjJeABhA4KOmut_qJh5kt_DLqdNShiQr-afk3AdkX-fxLZdrcHiW9aWvBjnMAY65zg5"
      "oHsuUaoEuG88Ksbku2u193OENWTQTsYaYE2O44qmRfsX773UNVcWXg_omwIbhbgf6tLZUZH_"
      "dTC3YjzuxjbSP89HPEJ-eBXA";
  const char kSenderPublicUncompressed[] =
      "BNoRDbb84JGm8g5Z5CFxurSqsXWJ11ItfXEWYVLE85Y7CYkDjXsIEc4aqxYaQ1G8BqkXCJ6D"
      "PpDrWtdWj_mugHU";
  const char kSenderPublicX509[] =
      "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2hENtvzgkabyDlnkIXG6tKqxdYnXUi19cRZh"
      "UsTzljsJiQONewgRzhqrFhpDUbwGqRcInoM-kOta11aP-a6AdQ";

  // The keying material used by the client to decrypt the |kCiphertext|.
  const char kReceiverPrivate[] =
      "MIGxMBwGCiqGSIb3DQEMAQMwDgQIqMt4d7uJdt4CAggABIGQeikRHE3CqUeF-uUtJno9BL0g"
      "mNRyDihZe8P3nF_g-NYVzvdQowsXfYeza6OQOdDuMXxnGgNToVy2jsiWVN6rxCaSMTY622y8"
      "ajW5voSdqC2PakQ8ZNTPNHarLDMC9NpgGKrUh8hfRLhvb7vtbKIWmx-22rQB5yTYdqzN2m7A"
      "GHMWRnVk0mMzMsMjZqYFaa2D";
  const char kReceiverPublicUncompressed[] =
      "BCEkBjzL8Z3C-oi2Q7oE5t2Np-p7osjGLg93qUP0wvqRT21EEWyf0cQDQcakQMqz4hQKYOQ3"
      "il2nNZct4HgAUQU";
  const char kReceiverPublicX509[] =
      "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEISQGPMvxncL6iLZDugTm3Y2n6nuiyMYuD3ep"
      "Q_TC-pFPbUQRbJ_RxANBxqRAyrPiFApg5DeKXac1ly3geABRBQ";

  // The ciphertext and associated plaintext of the message.
  const char kCiphertext[] = "6nqAQUME8hNqw5J3kl8cpVVJylXKYqZOeseZG8UueKpA";
  const char kPlaintext[] = "I am the walrus";

  std::string sender_shared_secret, receiver_shared_secret;

  // Compute the shared secrets between the sender and receiver's keys.
  ASSERT_NO_FATAL_FAILURE(ComputeSharedSecret(
      kSenderPrivate, kSenderPublicX509, kReceiverPublicUncompressed,
      &sender_shared_secret));
  ASSERT_NO_FATAL_FAILURE(ComputeSharedSecret(
      kReceiverPrivate, kReceiverPublicX509, kSenderPublicUncompressed,
      &receiver_shared_secret));

  ASSERT_GT(sender_shared_secret.size(), 0u);
  ASSERT_EQ(sender_shared_secret, receiver_shared_secret);

  std::unique_ptr<GCMMessageCryptographer> cryptographer;
  ASSERT_NO_FATAL_FAILURE(CreateCryptographer(
      kReceiverPublicUncompressed, kSenderPublicUncompressed, kAuthSecret,
      &cryptographer));

  std::string salt;
  ASSERT_TRUE(base::Base64UrlDecode(
        kSalt, base::Base64UrlDecodePolicy::IGNORE_PADDING, &salt));

  std::string encoded_ciphertext, ciphertext, plaintext;
  size_t record_size = 0;

  // Verify that encrypting |kPlaintext| yields the expected |kCiphertext|.
  ASSERT_TRUE(cryptographer->Encrypt(kPlaintext, sender_shared_secret, salt,
                                     &record_size, &ciphertext));

  base::Base64UrlEncode(ciphertext, base::Base64UrlEncodePolicy::OMIT_PADDING,
                        &encoded_ciphertext);
  ASSERT_EQ(kCiphertext, encoded_ciphertext);

  // Verify that decrypting |kCiphertext| yields the expected |kPlaintext|.
  ASSERT_TRUE(cryptographer->Decrypt(ciphertext, sender_shared_secret, salt,
                                     record_size, &plaintext));
  ASSERT_EQ(kPlaintext, plaintext);
}

TEST_F(GCMMessageCryptographerReferenceTest, WithoutAuthSecret) {
  // The 16-byte salt unique to the message.
  const char kSalt[] = "Qg61ZJRva_XBE9IEUelU3A";

  // The keying material used by the sender to encrypt the |kCiphertext|.
  const char kSenderPrivate[] =
      "MIGxMBwGCiqGSIb3DQEMAQMwDgQIFfJ62c9VwXgCAggABIGQkRxDRPQjwuWp1C3-z1pYTDqF"
      "_NZ1kbPsjmkC3JSv02oAYHtBAtKa2e3oAPqsPfCvoCJBJs6G4WY4EuEO1YFL6RKpNl3DpIUc"
      "v9ShR27p_je_nyLpNBAxn2drnjlF_K6s4gcJmcvCxuNjAwOlLMPvQqGjOR2K_oMs1Hdq0EKJ"
      "NwWt3WUVEpuQF_WhYjCVIeGO";
  const char kSenderPublicUncompressed[] =
      "BDgpRKok2GZZDmS4r63vbJSUtcQx4Fq1V58-6-3NbZzSTlZsQiCEDTQy3CZ0ZMsqeqsEb7qW"
      "2blQHA4S48fynTk";
  const char kSenderPublicX509[] =
      "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOClEqiTYZlkOZLivre9slJS1xDHgWrVXnz7r"
      "7c1tnNJOVmxCIIQNNDLcJnRkyyp6qwRvupbZuVAcDhLjx_KdOQ";

  // The keying material used by the client to decrypt the |kCiphertext|.
  const char kReceiverPrivate[] =
      "MIGxMBwGCiqGSIb3DQEMAQMwDgQIqMt4d7uJdt4CAggABIGQeikRHE3CqUeF-uUtJno9BL0g"
      "mNRyDihZe8P3nF_g-NYVzvdQowsXfYeza6OQOdDuMXxnGgNToVy2jsiWVN6rxCaSMTY622y8"
      "ajW5voSdqC2PakQ8ZNTPNHarLDMC9NpgGKrUh8hfRLhvb7vtbKIWmx-22rQB5yTYdqzN2m7A"
      "GHMWRnVk0mMzMsMjZqYFaa2D";
  const char kReceiverPublicUncompressed[] =
      "BCEkBjzL8Z3C-oi2Q7oE5t2Np-p7osjGLg93qUP0wvqRT21EEWyf0cQDQcakQMqz4hQKYOQ3"
      "il2nNZct4HgAUQU";
  const char kReceiverPublicX509[] =
      "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEISQGPMvxncL6iLZDugTm3Y2n6nuiyMYuD3ep"
      "Q_TC-pFPbUQRbJ_RxANBxqRAyrPiFApg5DeKXac1ly3geABRBQ";

  // The ciphertext and associated plaintext of the message.
  const char kCiphertext[] = "yqD2bapcx14XxUbtwjiGx69eHE3Yd6AqXcwBpT2Kd1uy";
  const char kPlaintext[] = "I am the walrus";

  std::string sender_shared_secret, receiver_shared_secret;

  // Compute the shared secrets between the sender and receiver's keys.
  ASSERT_NO_FATAL_FAILURE(ComputeSharedSecret(
      kSenderPrivate, kSenderPublicX509, kReceiverPublicUncompressed,
      &sender_shared_secret));
  ASSERT_NO_FATAL_FAILURE(ComputeSharedSecret(
      kReceiverPrivate, kReceiverPublicX509, kSenderPublicUncompressed,
      &receiver_shared_secret));

  ASSERT_GT(sender_shared_secret.size(), 0u);
  ASSERT_EQ(sender_shared_secret, receiver_shared_secret);

  std::unique_ptr<GCMMessageCryptographer> cryptographer;
  ASSERT_NO_FATAL_FAILURE(CreateCryptographer(
      kReceiverPublicUncompressed, kSenderPublicUncompressed,
      nullptr /* auth_secret */, &cryptographer));

  std::string salt;
  ASSERT_TRUE(base::Base64UrlDecode(
        kSalt, base::Base64UrlDecodePolicy::IGNORE_PADDING, &salt));

  std::string encoded_ciphertext, ciphertext, plaintext;
  size_t record_size = 0;

  // Verify that encrypting |kPlaintext| yields the expected |kCiphertext|.
  ASSERT_TRUE(cryptographer->Encrypt(kPlaintext, sender_shared_secret, salt,
                                     &record_size, &ciphertext));

  base::Base64UrlEncode(ciphertext, base::Base64UrlEncodePolicy::OMIT_PADDING,
                        &encoded_ciphertext);
  ASSERT_EQ(kCiphertext, encoded_ciphertext);

  // Verify that decrypting |kCiphertext| yields the expected |kPlaintext|.
  ASSERT_TRUE(cryptographer->Decrypt(ciphertext, sender_shared_secret, salt,
                                     record_size, &plaintext));
  ASSERT_EQ(kPlaintext, plaintext);
}

}  // namespace gcm
