chromium/chromeos/ash/components/multidevice/fake_secure_message_delegate_unittest.cc

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

#include "chromeos/ash/components/multidevice/fake_secure_message_delegate.h"

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash::multidevice {

namespace {

const char kTestPublicKey[] = "the private key is in another castle";
const char kPayload[] = "500 tons of uranium";
const char kSymmetricKey[] = "hunter2";
const char kPublicMetadata[] = "brought to you by our sponsors";
const char kAssociatedData[] = "save 20% bytes on your nonce insurance";
const char kVerificationKeyId[] = "the one with the red stripes";
const char kDecryptionKeyId[] = "it's in your pocket somewhere";

// Callback for saving the result of GenerateKeys().
void SaveKeyPair(std::string* private_key_out,
                 std::string* public_key_out,
                 const std::string& private_key,
                 const std::string& public_key) {
  *private_key_out = private_key;
  *public_key_out = public_key;
}

// Callback for saving the result of DeriveKey() and CreateSecureMessage().
void SaveString(std::string* out, const std::string& value) {
  *out = value;
}

// Callback for saving the result of UnwrapSecureMessage().
void SaveUnwrapResults(std::string* payload_out,
                       securemessage::Header* header_out,
                       bool verified,
                       const std::string& payload,
                       const securemessage::Header& header) {
  ASSERT_TRUE(verified);
  *payload_out = payload;
  *header_out = header;
}

// Returns the CreateOptions struct to create the test message.
SecureMessageDelegate::CreateOptions GetCreateOptions(
    securemessage::EncScheme encryption_scheme,
    securemessage::SigScheme signature_scheme) {
  SecureMessageDelegate::CreateOptions create_options;
  create_options.encryption_scheme = encryption_scheme;
  create_options.signature_scheme = signature_scheme;
  create_options.public_metadata = kPublicMetadata;
  create_options.associated_data = kAssociatedData;
  create_options.verification_key_id = kVerificationKeyId;
  create_options.decryption_key_id = kDecryptionKeyId;
  return create_options;
}

// Returns the UnwrapOptions struct to unwrap the test message.
SecureMessageDelegate::UnwrapOptions GetUnwrapOptions(
    securemessage::EncScheme encryption_scheme,
    securemessage::SigScheme signature_scheme) {
  SecureMessageDelegate::UnwrapOptions unwrap_options;
  unwrap_options.encryption_scheme = encryption_scheme;
  unwrap_options.signature_scheme = signature_scheme;
  unwrap_options.associated_data = kAssociatedData;
  return unwrap_options;
}

void CheckSerializedSecureMessage(
    const std::string& serialized_message,
    const SecureMessageDelegate::CreateOptions& create_options) {
  securemessage::SecureMessage secure_message;
  ASSERT_TRUE(secure_message.ParseFromString(serialized_message));
  securemessage::HeaderAndBody header_and_body;
  ASSERT_TRUE(
      header_and_body.ParseFromString(secure_message.header_and_body()));

  const securemessage::Header& header = header_and_body.header();
  EXPECT_EQ(create_options.signature_scheme, header.signature_scheme());
  EXPECT_EQ(create_options.encryption_scheme, header.encryption_scheme());
  EXPECT_EQ(create_options.verification_key_id, header.verification_key_id());
  EXPECT_EQ(create_options.decryption_key_id, header.decryption_key_id());
  EXPECT_EQ(create_options.public_metadata, header.public_metadata());
}

}  // namespace

class CryptAuthFakeSecureMessageDelegateTest : public testing::Test {
 public:
  CryptAuthFakeSecureMessageDelegateTest(
      const CryptAuthFakeSecureMessageDelegateTest&) = delete;
  CryptAuthFakeSecureMessageDelegateTest& operator=(
      const CryptAuthFakeSecureMessageDelegateTest&) = delete;

 protected:
  CryptAuthFakeSecureMessageDelegateTest() {}

  FakeSecureMessageDelegate delegate_;
};

TEST_F(CryptAuthFakeSecureMessageDelegateTest, GenerateKeyPair) {
  std::string public_key1, private_key1;
  delegate_.GenerateKeyPair(
      base::BindOnce(&SaveKeyPair, &public_key1, &private_key1));
  EXPECT_NE(private_key1, public_key1);

  std::string public_key2, private_key2;
  delegate_.GenerateKeyPair(
      base::BindOnce(&SaveKeyPair, &public_key2, &private_key2));
  EXPECT_NE(private_key2, public_key2);

  EXPECT_NE(public_key1, public_key2);
  EXPECT_NE(private_key1, private_key2);

  delegate_.set_next_public_key(kTestPublicKey);
  std::string public_key3, private_key3;
  delegate_.GenerateKeyPair(
      base::BindOnce(&SaveKeyPair, &public_key3, &private_key3));
  EXPECT_EQ(kTestPublicKey, public_key3);
  EXPECT_NE(private_key3, public_key3);

  EXPECT_NE(public_key1, public_key3);
  EXPECT_NE(private_key1, private_key3);
}

TEST_F(CryptAuthFakeSecureMessageDelegateTest, DeriveKey) {
  delegate_.set_next_public_key("key_pair_1");
  std::string public_key1, private_key1;
  delegate_.GenerateKeyPair(
      base::BindOnce(&SaveKeyPair, &public_key1, &private_key1));

  delegate_.set_next_public_key("key_pair_2");
  std::string public_key2, private_key2;
  delegate_.GenerateKeyPair(
      base::BindOnce(&SaveKeyPair, &public_key2, &private_key2));

  std::string symmetric_key1, symmetric_key2;
  delegate_.DeriveKey(private_key1, public_key2,
                      base::BindOnce(&SaveString, &symmetric_key1));
  delegate_.DeriveKey(private_key2, public_key1,
                      base::BindOnce(&SaveString, &symmetric_key2));

  EXPECT_EQ(symmetric_key1, symmetric_key2);
}

TEST_F(CryptAuthFakeSecureMessageDelegateTest,
       CreateAndUnwrapWithSymmetricKey) {
  // Create SecureMessage using symmetric key.
  SecureMessageDelegate::CreateOptions create_options =
      GetCreateOptions(securemessage::AES_256_CBC, securemessage::HMAC_SHA256);
  std::string serialized_message;
  delegate_.CreateSecureMessage(
      kPayload, kSymmetricKey, create_options,
      base::BindOnce(&SaveString, &serialized_message));

  CheckSerializedSecureMessage(serialized_message, create_options);

  // Unwrap SecureMessage using symmetric key.
  SecureMessageDelegate::UnwrapOptions unwrap_options =
      GetUnwrapOptions(securemessage::AES_256_CBC, securemessage::HMAC_SHA256);
  std::string payload;
  securemessage::Header header;
  delegate_.UnwrapSecureMessage(
      serialized_message, kSymmetricKey, unwrap_options,
      base::BindOnce(&SaveUnwrapResults, &payload, &header));

  EXPECT_EQ(kPayload, payload);
}

TEST_F(CryptAuthFakeSecureMessageDelegateTest,
       CreateAndUnwrapWithAsymmetricKey) {
  delegate_.set_next_public_key(kTestPublicKey);
  std::string public_key, private_key;
  delegate_.GenerateKeyPair(
      base::BindOnce(&SaveKeyPair, &public_key, &private_key));

  // Create SecureMessage using asymmetric key.
  SecureMessageDelegate::CreateOptions create_options =
      GetCreateOptions(securemessage::NONE, securemessage::ECDSA_P256_SHA256);
  std::string serialized_message;
  delegate_.CreateSecureMessage(
      kPayload, private_key, create_options,
      base::BindOnce(&SaveString, &serialized_message));

  CheckSerializedSecureMessage(serialized_message, create_options);

  // Unwrap SecureMessage using symmetric key.
  SecureMessageDelegate::UnwrapOptions unwrap_options =
      GetUnwrapOptions(securemessage::NONE, securemessage::ECDSA_P256_SHA256);
  std::string payload;
  securemessage::Header header;
  delegate_.UnwrapSecureMessage(
      serialized_message, public_key, unwrap_options,
      base::BindOnce(&SaveUnwrapResults, &payload, &header));

  EXPECT_EQ(kPayload, payload);
}

TEST_F(CryptAuthFakeSecureMessageDelegateTest, GetPrivateKeyForPublicKey) {
  delegate_.set_next_public_key(kTestPublicKey);
  std::string public_key, private_key;
  delegate_.GenerateKeyPair(
      base::BindOnce(&SaveKeyPair, &public_key, &private_key));
  EXPECT_EQ(kTestPublicKey, public_key);
  EXPECT_EQ(private_key, delegate_.GetPrivateKeyForPublicKey(kTestPublicKey));
}

}  // namespace ash::multidevice