chromium/chromeos/ash/components/dbus/easy_unlock/fake_easy_unlock_client_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/dbus/easy_unlock/fake_easy_unlock_client.h"

#include <string>

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

namespace {

// Callback for |GenerateEcP256KeyPair| method. Saves keys returned by the
// method in |private_key_target| and |public_key_target|.
void RecordKeyPair(std::string* private_key_target,
                   std::string* public_key_target,
                   const std::string& private_key_source,
                   const std::string& public_key_source) {
  *private_key_target = private_key_source;
  *public_key_target = public_key_source;
}

// Callback for |EasyUnlockClient| methods that return a single piece of data.
// It saves the returned data in |data_target|.
void RecordData(std::string* data_target, const std::string& data_source) {
  *data_target = data_source;
}

TEST(FakeEasyUnlockClientTest, GenerateEcP256KeyPair) {
  ash::FakeEasyUnlockClient client;

  std::string private_key_1;
  std::string public_key_1;
  client.GenerateEcP256KeyPair(
      base::BindOnce(&RecordKeyPair, &private_key_1, &public_key_1));
  ASSERT_EQ("{\"ec_p256_private_key\": 1}", private_key_1);
  ASSERT_EQ("{\"ec_p256_public_key\": 1}", public_key_1);

  std::string private_key_2;
  std::string public_key_2;
  client.GenerateEcP256KeyPair(
      base::BindOnce(&RecordKeyPair, &private_key_2, &public_key_2));
  ASSERT_EQ("{\"ec_p256_private_key\": 2}", private_key_2);
  ASSERT_EQ("{\"ec_p256_public_key\": 2}", public_key_2);

  EXPECT_NE(private_key_1, private_key_2);
  EXPECT_NE(public_key_1, public_key_2);
}

TEST(FakeEasyUnlockClientTest, IsEcP256KeyPair) {
  ASSERT_TRUE(ash::FakeEasyUnlockClient::IsEcP256KeyPair(
      "{\"ec_p256_private_key\": 12}", "{\"ec_p256_public_key\": 12}"));
}

TEST(FakeEasyUnlockClientTest, IsEcP256KeyPair_KeysFromDiffrentPairs) {
  ASSERT_FALSE(ash::FakeEasyUnlockClient::IsEcP256KeyPair(
      "{\"ec_p256_private_key\": 12}", "{\"ec_p256_public_key\": 34}"));
}

TEST(FakeEasyUnlockClientTest, IsEcP256KeyPair_KeyOrderSwitched) {
  ASSERT_FALSE(ash::FakeEasyUnlockClient::IsEcP256KeyPair(
      "{\"ec_p256_public_key\": 34}", "{\"ec_p256_private_key\": 34}"));
}

TEST(FakeEasyUnlockClientTest, IsEcP256KeyPair_PrivateKeyInvalidFormat) {
  ASSERT_FALSE(ash::FakeEasyUnlockClient::IsEcP256KeyPair(
      "\"ec_p256_private_key\": 12", "{\"ec_p256_public_key\": 12}"));
}

TEST(FakeEasyUnlockClientTest, IsEcP256KeyPair_PublicKeyInvalidFormat) {
  ASSERT_FALSE(ash::FakeEasyUnlockClient::IsEcP256KeyPair(
      "{\"ec_p256_private_key\": 12}", "\"ec_p256_public_key\": 12"));
}

TEST(FakeEasyUnlockClientTest, IsEcP256KeyPair_PrivateKeyInvalidDictKey) {
  ASSERT_FALSE(ash::FakeEasyUnlockClient::IsEcP256KeyPair(
      "{\"invalid\": 12}", "{\"ec_p256_public_key\": 12}"));
}

TEST(FakeEasyUnlockClientTest, IsEcP256KeyPair_PublicKeyInvalidDictKey) {
  ASSERT_FALSE(ash::FakeEasyUnlockClient::IsEcP256KeyPair(
      "{\"ec_p256_private_key\": 12}", "{\"invalid\": 12}"));
}

TEST(FakeEasyUnlockClientTest, IsEcP256KeyPair_InvalidDictValues) {
  ASSERT_FALSE(ash::FakeEasyUnlockClient::IsEcP256KeyPair(
      "{\"ec_p256_private_key\": \"12\"}", "{\"ec_p256_public_key\": \"12\"}"));
}

// Verifies the fake |PerformECDHKeyAgreement| method is symetric in respect to
// key pairs from which private and public key used in the key agreement
// originate.
TEST(FakeEasyUnlockClientTest, ECDHKeyAgreementSuccess) {
  ash::FakeEasyUnlockClient client;

  // (Fake) key pairs used in the test to generate fake shared keys.
  const std::string private_key_1 = "{\"ec_p256_private_key\": 32}";
  const std::string public_key_1 = "{\"ec_p256_public_key\": 32}";

  const std::string private_key_2 = "{\"ec_p256_private_key\": 352}";
  const std::string public_key_2 = "{\"ec_p256_public_key\": 352}";

  const std::string private_key_3 = "{\"ec_p256_private_key\": 432}";
  const std::string public_key_3 = "{\"ec_p256_public_key\": 432}";

  // Generate shared key for key pairs 1 and 2, using private key from the
  // second key pair and public key from the first key pair.
  std::string shared_key_1;
  client.PerformECDHKeyAgreement(private_key_2, public_key_1,
                                 base::BindOnce(&RecordData, &shared_key_1));
  EXPECT_FALSE(shared_key_1.empty());

  // Generate shared key for key pairs 1 and 2, using private key from the
  // first key pair and public key from the second key pair.
  std::string shared_key_2;
  client.PerformECDHKeyAgreement(private_key_1, public_key_2,
                                 base::BindOnce(&RecordData, &shared_key_2));
  EXPECT_FALSE(shared_key_2.empty());

  // The generated keys should be equal. They were generated using keys from
  // the same key pairs, even though key pairs from which private and public key
  // originate were switched.
  EXPECT_EQ(shared_key_1, shared_key_2);

  // Generate a key using key pairs 1 and 3.
  std::string shared_key_3;
  client.PerformECDHKeyAgreement(private_key_1, public_key_3,
                                 base::BindOnce(&RecordData, &shared_key_3));
  EXPECT_FALSE(shared_key_3.empty());

  // The new key should be different from the previously generated ones, since
  // the used key pairs are different.
  EXPECT_NE(shared_key_3, shared_key_1);
  EXPECT_NE(shared_key_3, shared_key_1);
}

TEST(FakeEasyUnlockClientTest, ECDHKeyAgreementFailsIfKeyOrderSwitched) {
  ash::FakeEasyUnlockClient client;

  const std::string private_key = "{\"ec_p256_private_key\": 415}";
  const std::string public_key = "{\"ec_p256_public_key\": 345}";

  std::string shared_key;
  client.PerformECDHKeyAgreement(public_key, private_key,
                                 base::BindOnce(&RecordData, &shared_key));
  EXPECT_TRUE(shared_key.empty());
}

TEST(FakeEasyUnlockClientTest, ECDHKeyAgreementFailsIfKeyDictKeyInvalid) {
  ash::FakeEasyUnlockClient client;

  const std::string private_key = "{\"ec_p256_private_key_invalid\": 415}";
  const std::string public_key = "{\"ec_p256_public_key_invalid\": 345}";

  std::string shared_key;
  client.PerformECDHKeyAgreement(private_key, public_key,
                                 base::BindOnce(&RecordData, &shared_key));
  EXPECT_TRUE(shared_key.empty());
}

TEST(FakeEasyUnlockClientTest, ECDHKeyAgreementFailsIfKeyDictValueInvalid) {
  ash::FakeEasyUnlockClient client;

  const std::string private_key = "{\"ec_p256_private_key\": 415}";
  const std::string public_key = "{\"ec_p256_public_key\": \"345__\"}";

  std::string shared_key;
  client.PerformECDHKeyAgreement(private_key, public_key,
                                 base::BindOnce(&RecordData, &shared_key));
  EXPECT_TRUE(shared_key.empty());
}

TEST(FakeEasyUnlockClientTest, ECDHKeyAgreementFailsIfKeyFormatInvalid) {
  ash::FakeEasyUnlockClient client;

  const std::string private_key = "invalid";
  const std::string public_key = "{\"ec_p256_public_key\": 345}";

  std::string shared_key;
  client.PerformECDHKeyAgreement(private_key, public_key,
                                 base::BindOnce(&RecordData, &shared_key));
  EXPECT_TRUE(shared_key.empty());
}

TEST(FakeEasyUnlockClientTest, CreateSecureMessage) {
  ash::FakeEasyUnlockClient client;

  std::string message;

  ash::EasyUnlockClient::CreateSecureMessageOptions options;
  options.key = "KEY";
  options.associated_data = "ASSOCIATED_DATA";
  options.public_metadata = "PUBLIC_METADATA";
  options.verification_key_id = "VERIFICATION_KEY_ID";
  options.decryption_key_id = "DECRYPTION_KEY_ID";
  options.encryption_type = "ENCRYPTION_TYPE";
  options.signature_type = "SIGNATURE_TYPE";

  client.CreateSecureMessage("PAYLOAD", options,
                             base::BindOnce(&RecordData, &message));

  const std::string expected_message(
      "{\"securemessage\": {"
      "\"payload\": \"PAYLOAD\","
      "\"key\": \"KEY\","
      "\"associated_data\": \"ASSOCIATED_DATA\","
      "\"public_metadata\": \"PUBLIC_METADATA\","
      "\"verification_key_id\": \"VERIFICATION_KEY_ID\","
      "\"decryption_key_id\": \"DECRYPTION_KEY_ID\","
      "\"encryption_type\": \"ENCRYPTION_TYPE\","
      "\"signature_type\": \"SIGNATURE_TYPE\"}"
      "}");
  ASSERT_EQ(expected_message, message);
}

TEST(FakeEasyUnlockClientTest, UnwrapSecureMessage) {
  ash::FakeEasyUnlockClient client;

  std::string message;

  ash::EasyUnlockClient::UnwrapSecureMessageOptions options;
  options.key = "KEY";
  options.associated_data = "ASSOCIATED_DATA";
  options.encryption_type = "ENCRYPTION_TYPE";
  options.signature_type = "SIGNATURE_TYPE";

  client.UnwrapSecureMessage("MESSAGE", options,
                             base::BindOnce(&RecordData, &message));

  const std::string expected_message(
      "{\"unwrapped_securemessage\": {"
      "\"message\": \"MESSAGE\","
      "\"key\": \"KEY\","
      "\"associated_data\": \"ASSOCIATED_DATA\","
      "\"encryption_type\": \"ENCRYPTION_TYPE\","
      "\"signature_type\": \"SIGNATURE_TYPE\"}"
      "}");
  ASSERT_EQ(expected_message, message);
}

}  // namespace