Commit 657a8c53 authored by Josh Nohle's avatar Josh Nohle Committed by Commit Bot

Use existing kUserKeyPair keys during key creation, if possible.

In CyptAuth v2 Enrollment, the user key pair--also known as
CryptAuthKeyBundle::Name::kUserKeyPair or "PublicKey"--has special
standing in order to 1) accommodate any existing key from v1 Enrollment
and 2) enforce that the key is not rotated. Only one user key pair
should exist in its key bundle, and it should be an active, P-256 key
with handle "device_key".

It is possible that CryptAuth could request the creation of a new user
key pair even if the client sends information about an existing key in
the SyncKeysRequest. If this happens, the client should re-use the
existing user key pair key material when creating a new key. At the end
of the enrollment flow, the existing key will be replaced with this new
key that has the same public/private keys, but possibly with a new key
directive.

It might seem more efficient to simply ignore the key-creation request,
but the method outlined above natually fits into the enrollment flow,
the key directive will be updated, and the client will be able to
provide CryptAuth with an EnrollKeys request, which it might be
expected.

Bug: 899080
Change-Id: I3a4a63aa902090698ecb619bc7af78ff1e790c23
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1504121
Commit-Queue: Josh Nohle <nohle@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#638327}
parent 61d9c1b6
...@@ -116,7 +116,11 @@ std::ostream& operator<<( ...@@ -116,7 +116,11 @@ std::ostream& operator<<(
stream << "[Error: KeyActions do not specify an active key]"; stream << "[Error: KeyActions do not specify an active key]";
break; break;
case ResultCode::kErrorKeyCreationKeyTypeNotSupported: case ResultCode::kErrorKeyCreationKeyTypeNotSupported:
stream << "[Error: KeyCreation instructions specify unsupported KeyType]"; stream << "[Error: Key-creation instructions specify unsupported "
<< "KeyType]";
break;
case ResultCode::kErrorUserKeyPairCreationInstructionsInvalid:
stream << "[Error: Key-creation instructions for user key pair invalid]";
break; break;
case ResultCode::kErrorSymmetricKeyCreationMissingServerDiffieHellman: case ResultCode::kErrorSymmetricKeyCreationMissingServerDiffieHellman:
stream << "[Error: Cannot create symmetric key; missing server " stream << "[Error: Cannot create symmetric key; missing server "
......
...@@ -79,6 +79,9 @@ class CryptAuthEnrollmentResult { ...@@ -79,6 +79,9 @@ class CryptAuthEnrollmentResult {
kErrorKeyActionsDoNotSpecifyAnActiveKey, kErrorKeyActionsDoNotSpecifyAnActiveKey,
// KeyCreation instructions specify an unsupported KeyType. // KeyCreation instructions specify an unsupported KeyType.
kErrorKeyCreationKeyTypeNotSupported, kErrorKeyCreationKeyTypeNotSupported,
// Invalid key-creation instructions for user key pair. It must be P256 and
// active.
kErrorUserKeyPairCreationInstructionsInvalid,
// Cannot create a symmetric key without the server's Diffie-Hellman key. // Cannot create a symmetric key without the server's Diffie-Hellman key.
kErrorSymmetricKeyCreationMissingServerDiffieHellman, kErrorSymmetricKeyCreationMissingServerDiffieHellman,
// Failed to compute at least one key proof. // Failed to compute at least one key proof.
......
...@@ -14,6 +14,22 @@ CryptAuthKeyCreator::CreateKeyData::CreateKeyData( ...@@ -14,6 +14,22 @@ CryptAuthKeyCreator::CreateKeyData::CreateKeyData(
base::Optional<std::string> handle) base::Optional<std::string> handle)
: status(status), type(type), handle(handle) {} : status(status), type(type), handle(handle) {}
CryptAuthKeyCreator::CreateKeyData::CreateKeyData(
CryptAuthKey::Status status,
cryptauthv2::KeyType type,
const std::string& handle,
const std::string& public_key,
const std::string& private_key)
: status(status),
type(type),
handle(handle),
public_key(public_key),
private_key(private_key) {
DCHECK(!handle.empty());
DCHECK(!public_key.empty());
DCHECK(!private_key.empty());
}
CryptAuthKeyCreator::CreateKeyData::~CreateKeyData() = default; CryptAuthKeyCreator::CreateKeyData::~CreateKeyData() = default;
CryptAuthKeyCreator::CreateKeyData::CreateKeyData(const CreateKeyData&) = CryptAuthKeyCreator::CreateKeyData::CreateKeyData(const CreateKeyData&) =
......
...@@ -49,12 +49,27 @@ class CryptAuthKeyCreator { ...@@ -49,12 +49,27 @@ class CryptAuthKeyCreator {
CreateKeyData(CryptAuthKey::Status status, CreateKeyData(CryptAuthKey::Status status,
cryptauthv2::KeyType type, cryptauthv2::KeyType type,
base::Optional<std::string> handle = base::nullopt); base::Optional<std::string> handle = base::nullopt);
// Special constructor needed to handle existing user key pair. The input
// strings cannot be empty.
CreateKeyData(CryptAuthKey::Status status,
cryptauthv2::KeyType type,
const std::string& handle,
const std::string& public_key,
const std::string& private_key);
~CreateKeyData(); ~CreateKeyData();
CreateKeyData(const CreateKeyData&); CreateKeyData(const CreateKeyData&);
CryptAuthKey::Status status; CryptAuthKey::Status status;
cryptauthv2::KeyType type; cryptauthv2::KeyType type;
base::Optional<std::string> handle; base::Optional<std::string> handle;
// Special data needed to handle existing user key pair. If these are both
// non-empty strings and the key type is asymmetric, then the key creator
// will bypass the standard key creation and simply return
// CryptAuthKey(|public_key|, |private_key|, |status|, |type|, |handle|).
base::Optional<std::string> public_key;
base::Optional<std::string> private_key;
}; };
CryptAuthKeyCreator(); CryptAuthKeyCreator();
......
...@@ -4,21 +4,13 @@ ...@@ -4,21 +4,13 @@
#include "chromeos/services/device_sync/cryptauth_key_creator_impl.h" #include "chromeos/services/device_sync/cryptauth_key_creator_impl.h"
#include <memory>
#include <string>
#include <utility>
#include "base/base64.h" #include "base/base64.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/containers/flat_map.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/optional.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "chromeos/components/multidevice/logging/logging.h"
#include "chromeos/components/multidevice/secure_message_delegate_impl.h" #include "chromeos/components/multidevice/secure_message_delegate_impl.h"
#include "chromeos/services/device_sync/cryptauth_constants.h" #include "chromeos/services/device_sync/cryptauth_constants.h"
#include "chromeos/services/device_sync/cryptauth_key.h"
#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h" #include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
#include "crypto/hkdf.h" #include "crypto/hkdf.h"
#include "crypto/random.h" #include "crypto/random.h"
...@@ -165,29 +157,41 @@ void CryptAuthKeyCreatorImpl::OnDiffieHellmanHandshakeSecretDerived( ...@@ -165,29 +157,41 @@ void CryptAuthKeyCreatorImpl::OnDiffieHellmanHandshakeSecretDerived(
void CryptAuthKeyCreatorImpl::StartKeyCreation() { void CryptAuthKeyCreatorImpl::StartKeyCreation() {
for (const auto& key_to_create : keys_to_create_) { for (const auto& key_to_create : keys_to_create_) {
const CryptAuthKeyBundle::Name& bundle_name = key_to_create.first;
const CreateKeyData& key_data = key_to_create.second;
// If the key to create is symmetric, derive a symmetric key from the // If the key to create is symmetric, derive a symmetric key from the
// Diffie-Hellman handshake secrect using HKDF. The CryptAuth v2 // Diffie-Hellman handshake secrect using HKDF. The CryptAuth v2
// Enrollment protocol specifies that the salt should be "CryptAuth // Enrollment protocol specifies that the salt should be "CryptAuth
// Enrollment" and the info should be the key handle. This process is // Enrollment" and the info should be the key handle. This process is
// synchronous, unlike SecureMessageDelegate calls. // synchronous, unlike SecureMessageDelegate calls.
if (IsValidSymmetricKeyType(key_to_create.second.type)) { if (IsValidSymmetricKeyType(key_data.type)) {
std::string handle = key_to_create.second.handle.has_value() std::string handle =
? *key_to_create.second.handle key_data.handle ? *key_data.handle : CreateRandomHandle();
: CreateRandomHandle(); std::string derived_symmetric_key_material =
std::string derived_symmetric_key_material = crypto::HkdfSha256( crypto::HkdfSha256(dh_handshake_secret_->symmetric_key(),
dh_handshake_secret_->symmetric_key(), kCryptAuthSymmetricKeyDerivationSalt, handle,
kCryptAuthSymmetricKeyDerivationSalt, handle, NumBytesForSymmetricKeyType(key_data.type));
NumBytesForSymmetricKeyType(key_to_create.second.type));
OnSymmetricKeyDerived(bundle_name, derived_symmetric_key_material,
OnSymmetricKeyDerived(key_to_create.first, derived_symmetric_key_material,
handle); handle);
} else {
DCHECK(IsValidAsymmetricKeyType(key_to_create.second.type));
secure_message_delegate_->GenerateKeyPair( continue;
base::Bind(&CryptAuthKeyCreatorImpl::OnAsymmetricKeyPairGenerated,
base::Unretained(this), key_to_create.first));
} }
DCHECK(IsValidAsymmetricKeyType(key_data.type));
// If the key material was explicitly set in CreateKeyData, bypass the
// standard key creation.
if (key_data.public_key && key_data.private_key) {
OnAsymmetricKeyPairGenerated(bundle_name, *key_data.public_key,
*key_data.private_key);
continue;
}
secure_message_delegate_->GenerateKeyPair(
base::Bind(&CryptAuthKeyCreatorImpl::OnAsymmetricKeyPairGenerated,
base::Unretained(this), key_to_create.first));
} }
} }
......
...@@ -5,8 +5,6 @@ ...@@ -5,8 +5,6 @@
#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_CREATOR_IMPL_H_ #ifndef CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_CREATOR_IMPL_H_
#define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_CREATOR_IMPL_H_ #define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_CREATOR_IMPL_H_
#include "chromeos/services/device_sync/cryptauth_key_creator.h"
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility> #include <utility>
...@@ -18,6 +16,7 @@ ...@@ -18,6 +16,7 @@
#include "base/optional.h" #include "base/optional.h"
#include "chromeos/services/device_sync/cryptauth_key.h" #include "chromeos/services/device_sync/cryptauth_key.h"
#include "chromeos/services/device_sync/cryptauth_key_bundle.h" #include "chromeos/services/device_sync/cryptauth_key_bundle.h"
#include "chromeos/services/device_sync/cryptauth_key_creator.h"
namespace chromeos { namespace chromeos {
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "chromeos/services/device_sync/cryptauth_key_creator_impl.h"
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility> #include <utility>
...@@ -18,6 +16,7 @@ ...@@ -18,6 +16,7 @@
#include "chromeos/services/device_sync/cryptauth_key.h" #include "chromeos/services/device_sync/cryptauth_key.h"
#include "chromeos/services/device_sync/cryptauth_key_bundle.h" #include "chromeos/services/device_sync/cryptauth_key_bundle.h"
#include "chromeos/services/device_sync/cryptauth_key_creator.h" #include "chromeos/services/device_sync/cryptauth_key_creator.h"
#include "chromeos/services/device_sync/cryptauth_key_creator_impl.h"
#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h" #include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
#include "crypto/hkdf.h" #include "crypto/hkdf.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -33,15 +32,19 @@ const char kFakeClientEphemeralDhPublicKeyMaterial[] = "client_ephemeral_dh"; ...@@ -33,15 +32,19 @@ const char kFakeClientEphemeralDhPublicKeyMaterial[] = "client_ephemeral_dh";
const char kFakeAsymmetricKeyHandle[] = "asymmetric_key_handle"; const char kFakeAsymmetricKeyHandle[] = "asymmetric_key_handle";
const char kFakePublicKeyMaterial[] = "public_key"; const char kFakePublicKeyMaterial[] = "public_key";
const char kFakeSymmetricKeyHandle[] = "symmetric_key_handle"; const char kFakeSymmetricKeyHandle[] = "symmetric_key_handle";
const char kFakeProvidedPublicKeyMaterial[] = "provided_public_key";
void VerifyCreatedKeysCallback( const char kFakeProvidedPrivateKeyMaterial[] = "provided_private_key";
const base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKey>&
expected_new_keys, size_t NumBytesForSymmetricKeyType(cryptauthv2::KeyType key_type) {
const base::Optional<CryptAuthKey>& expected_client_ephemeral_dh, switch (key_type) {
const base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKey>& new_keys, case cryptauthv2::KeyType::RAW128:
const base::Optional<CryptAuthKey>& client_ephemeral_dh) { return 16u;
EXPECT_EQ(expected_client_ephemeral_dh, client_ephemeral_dh); case cryptauthv2::KeyType::RAW256:
EXPECT_EQ(expected_new_keys, new_keys); return 32u;
default:
NOTREACHED();
return 0u;
}
} }
class FakeSecureMessageDelegateFactory class FakeSecureMessageDelegateFactory
...@@ -71,12 +74,19 @@ class FakeSecureMessageDelegateFactory ...@@ -71,12 +74,19 @@ class FakeSecureMessageDelegateFactory
class DeviceSyncCryptAuthKeyCreatorImplTest : public testing::Test { class DeviceSyncCryptAuthKeyCreatorImplTest : public testing::Test {
protected: protected:
DeviceSyncCryptAuthKeyCreatorImplTest() = default; DeviceSyncCryptAuthKeyCreatorImplTest()
: fake_secure_message_delegate_factory_(
std::make_unique<FakeSecureMessageDelegateFactory>()),
fake_server_ephemeral_dh_(CryptAuthKey(
kFakeServerEphemeralDhPublicKeyMaterial,
fake_secure_message_delegate()->GetPrivateKeyForPublicKey(
kFakeServerEphemeralDhPublicKeyMaterial),
CryptAuthKey::Status::kActive,
cryptauthv2::KeyType::P256)) {}
~DeviceSyncCryptAuthKeyCreatorImplTest() override = default; ~DeviceSyncCryptAuthKeyCreatorImplTest() override = default;
void SetUp() override { void SetUp() override {
fake_secure_message_delegate_factory_ =
std::make_unique<FakeSecureMessageDelegateFactory>();
multidevice::SecureMessageDelegateImpl::Factory::SetInstanceForTesting( multidevice::SecureMessageDelegateImpl::Factory::SetInstanceForTesting(
fake_secure_message_delegate_factory_.get()); fake_secure_message_delegate_factory_.get());
...@@ -88,31 +98,81 @@ class DeviceSyncCryptAuthKeyCreatorImplTest : public testing::Test { ...@@ -88,31 +98,81 @@ class DeviceSyncCryptAuthKeyCreatorImplTest : public testing::Test {
nullptr); nullptr);
} }
CryptAuthKey DeriveSecret( void CallCreateKeys(
const base::Optional<CryptAuthKey>& server_ephemeral_dh, const base::flat_map<CryptAuthKeyBundle::Name,
const base::Optional<CryptAuthKey>& client_ephemeral_dh) { CryptAuthKeyCreator::CreateKeyData>& keys_to_create,
const base::Optional<CryptAuthKey>& server_ephemeral_dh) {
key_creator_->CreateKeys(
keys_to_create, server_ephemeral_dh,
base::BindOnce(&DeviceSyncCryptAuthKeyCreatorImplTest::OnKeysCreated,
base::Unretained(this)));
}
void VerifyKeyCreation(
const base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKey>&
expected_new_keys,
const base::Optional<CryptAuthKey>& expected_client_ephemeral_dh) {
EXPECT_TRUE(new_keys_);
EXPECT_TRUE(client_ephemeral_dh_);
EXPECT_EQ(expected_new_keys, *new_keys_);
EXPECT_EQ(expected_client_ephemeral_dh, *client_ephemeral_dh_);
}
std::string DeriveSecret(const CryptAuthKey& server_ephemeral_dh,
const CryptAuthKey& client_ephemeral_dh) {
std::string derived_key; std::string derived_key;
fake_secure_message_delegate()->DeriveKey( fake_secure_message_delegate()->DeriveKey(
client_ephemeral_dh->private_key(), server_ephemeral_dh->public_key(), client_ephemeral_dh.private_key(), server_ephemeral_dh.public_key(),
base::Bind([](std::string* derived_key, base::Bind([](std::string* derived_key,
const std::string& key) { *derived_key = key; }, const std::string& key) { *derived_key = key; },
&derived_key)); &derived_key));
return CryptAuthKey(derived_key, CryptAuthKey::Status::kActive, return derived_key;
cryptauthv2::KeyType::RAW256);
} }
CryptAuthKeyCreator* key_creator() { return key_creator_.get(); } CryptAuthKey DeriveSymmetricKey(
const CryptAuthKeyBundle::Name& bundle_name,
const CryptAuthKeyCreator::CreateKeyData& key_to_create,
const CryptAuthKey& client_ephemeral_dh) {
std::string expected_handshake_secret =
DeriveSecret(fake_server_ephemeral_dh_, client_ephemeral_dh);
std::string expected_symmetric_key_material = crypto::HkdfSha256(
expected_handshake_secret, kCryptAuthSymmetricKeyDerivationSalt,
key_to_create.handle ? *key_to_create.handle : "",
NumBytesForSymmetricKeyType(key_to_create.type));
return CryptAuthKey(expected_symmetric_key_material, key_to_create.status,
key_to_create.type, key_to_create.handle);
}
multidevice::FakeSecureMessageDelegate* fake_secure_message_delegate() { multidevice::FakeSecureMessageDelegate* fake_secure_message_delegate() {
return fake_secure_message_delegate_factory_->instance(); return fake_secure_message_delegate_factory_->instance();
} }
const CryptAuthKey& fake_server_ephemeral_dh() {
return fake_server_ephemeral_dh_;
}
private: private:
void OnKeysCreated(
const base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKey>& new_keys,
const base::Optional<CryptAuthKey>& client_ephemeral_dh) {
new_keys_ = new_keys;
client_ephemeral_dh_ = client_ephemeral_dh;
}
std::unique_ptr<FakeSecureMessageDelegateFactory> std::unique_ptr<FakeSecureMessageDelegateFactory>
fake_secure_message_delegate_factory_; fake_secure_message_delegate_factory_;
std::unique_ptr<CryptAuthKeyCreator> key_creator_; std::unique_ptr<CryptAuthKeyCreator> key_creator_;
CryptAuthKey fake_server_ephemeral_dh_;
// A null value (for the outermost Optional) indicates that OnKeysCreated()
// was not called.
base::Optional<base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKey>>
new_keys_;
base::Optional<base::Optional<CryptAuthKey>> client_ephemeral_dh_;
DISALLOW_COPY_AND_ASSIGN(DeviceSyncCryptAuthKeyCreatorImplTest); DISALLOW_COPY_AND_ASSIGN(DeviceSyncCryptAuthKeyCreatorImplTest);
}; };
...@@ -136,53 +196,82 @@ TEST_F(DeviceSyncCryptAuthKeyCreatorImplTest, AsymmetricKeyCreation) { ...@@ -136,53 +196,82 @@ TEST_F(DeviceSyncCryptAuthKeyCreatorImplTest, AsymmetricKeyCreation) {
fake_secure_message_delegate()->set_next_public_key(kFakePublicKeyMaterial); fake_secure_message_delegate()->set_next_public_key(kFakePublicKeyMaterial);
key_creator()->CreateKeys( CallCreateKeys(keys_to_create, base::nullopt /* fake_server_ephemeral_dh */);
keys_to_create, base::nullopt /* fake_server_ephemeral_dh */, VerifyKeyCreation(expected_new_keys,
base::BindOnce(VerifyCreatedKeysCallback, expected_new_keys, base::nullopt /* expected_client_ephemeral_dh */);
base::nullopt /* expected_client_ephemeral_dh */));
} }
TEST_F(DeviceSyncCryptAuthKeyCreatorImplTest, SymmetricKeyCreation) { TEST_F(DeviceSyncCryptAuthKeyCreatorImplTest, SymmetricKeyCreation) {
CryptAuthKeyCreator::CreateKeyData symmetric_key_to_create(
CryptAuthKey::Status::kActive, cryptauthv2::KeyType::RAW256,
kFakeSymmetricKeyHandle);
base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKeyCreator::CreateKeyData>
keys_to_create = {{CryptAuthKeyBundle::Name::kLegacyMasterKey,
symmetric_key_to_create}};
CryptAuthKey expected_client_ephemeral_dh(
kFakeClientEphemeralDhPublicKeyMaterial,
fake_secure_message_delegate()->GetPrivateKeyForPublicKey(
kFakeClientEphemeralDhPublicKeyMaterial),
CryptAuthKey::Status::kActive, cryptauthv2::KeyType::P256);
CryptAuthKey expected_symmetric_key =
DeriveSymmetricKey(CryptAuthKeyBundle::Name::kLegacyMasterKey,
symmetric_key_to_create, expected_client_ephemeral_dh);
base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKey> expected_new_keys = {
{CryptAuthKeyBundle::Name::kLegacyMasterKey, expected_symmetric_key}};
fake_secure_message_delegate()->set_next_public_key(
kFakeClientEphemeralDhPublicKeyMaterial);
CallCreateKeys(keys_to_create, fake_server_ephemeral_dh());
VerifyKeyCreation(expected_new_keys, expected_client_ephemeral_dh);
}
TEST_F(DeviceSyncCryptAuthKeyCreatorImplTest,
MultipleKeyCreation_KeyMaterialProvidedForAsymmetricKey) {
CryptAuthKeyCreator::CreateKeyData symmetric_key_to_create(
CryptAuthKey::Status::kActive, cryptauthv2::KeyType::RAW256,
kFakeSymmetricKeyHandle);
base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKeyCreator::CreateKeyData> base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKeyCreator::CreateKeyData>
keys_to_create = { keys_to_create = {
{CryptAuthKeyBundle::Name::kUserKeyPair, {CryptAuthKeyBundle::Name::kUserKeyPair,
CryptAuthKeyCreator::CreateKeyData(CryptAuthKey::Status::kActive, CryptAuthKeyCreator::CreateKeyData(
cryptauthv2::KeyType::RAW256, CryptAuthKey::Status::kActive, cryptauthv2::KeyType::P256,
kFakeSymmetricKeyHandle)}}; kFakeAsymmetricKeyHandle, kFakeProvidedPublicKeyMaterial,
kFakeProvidedPrivateKeyMaterial)},
base::Optional<CryptAuthKey> fake_server_ephemeral_dh = {CryptAuthKeyBundle::Name::kLegacyMasterKey,
CryptAuthKey(kFakeServerEphemeralDhPublicKeyMaterial, symmetric_key_to_create}};
fake_secure_message_delegate()->GetPrivateKeyForPublicKey(
kFakeServerEphemeralDhPublicKeyMaterial), CryptAuthKey expected_client_ephemeral_dh(
CryptAuthKey::Status::kActive, cryptauthv2::KeyType::P256); kFakeClientEphemeralDhPublicKeyMaterial,
fake_secure_message_delegate()->GetPrivateKeyForPublicKey(
base::Optional<CryptAuthKey> expected_client_ephemeral_dh = kFakeClientEphemeralDhPublicKeyMaterial),
CryptAuthKey(kFakeClientEphemeralDhPublicKeyMaterial, CryptAuthKey::Status::kActive, cryptauthv2::KeyType::P256);
fake_secure_message_delegate()->GetPrivateKeyForPublicKey(
kFakeClientEphemeralDhPublicKeyMaterial), CryptAuthKey expected_asymmetric_key(
CryptAuthKey::Status::kActive, cryptauthv2::KeyType::P256); kFakeProvidedPublicKeyMaterial, kFakeProvidedPrivateKeyMaterial,
CryptAuthKey::Status::kActive, cryptauthv2::KeyType::P256,
CryptAuthKey expected_handshake_secret = kFakeAsymmetricKeyHandle);
DeriveSecret(fake_server_ephemeral_dh, expected_client_ephemeral_dh);
std::string expected_symmetric_key_material = CryptAuthKey expected_symmetric_key =
crypto::HkdfSha256(expected_handshake_secret.symmetric_key(), DeriveSymmetricKey(CryptAuthKeyBundle::Name::kLegacyMasterKey,
kCryptAuthSymmetricKeyDerivationSalt, symmetric_key_to_create, expected_client_ephemeral_dh);
kFakeSymmetricKeyHandle, 32u /* derived_key_size */);
CryptAuthKey expected_symmetric_key(
expected_symmetric_key_material, CryptAuthKey::Status::kActive,
cryptauthv2::KeyType::RAW256, kFakeSymmetricKeyHandle);
base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKey> expected_new_keys = { base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKey> expected_new_keys = {
{CryptAuthKeyBundle::Name::kUserKeyPair, expected_symmetric_key}}; {CryptAuthKeyBundle::Name::kUserKeyPair, expected_asymmetric_key},
{CryptAuthKeyBundle::Name::kLegacyMasterKey, expected_symmetric_key}};
// There is no need to generate an asymmetric key for kUserKeyPair since we
// passed in the key material to CreateKeyData.
fake_secure_message_delegate()->set_next_public_key( fake_secure_message_delegate()->set_next_public_key(
kFakeClientEphemeralDhPublicKeyMaterial); kFakeClientEphemeralDhPublicKeyMaterial);
key_creator()->CreateKeys( CallCreateKeys(keys_to_create, fake_server_ephemeral_dh());
keys_to_create, fake_server_ephemeral_dh, VerifyKeyCreation(expected_new_keys, expected_client_ephemeral_dh);
base::BindOnce(VerifyCreatedKeysCallback, expected_new_keys,
expected_client_ephemeral_dh));
} }
} // namespace device_sync } // namespace device_sync
......
...@@ -254,48 +254,59 @@ bool IsSupportedKeyType(const cryptauthv2::KeyType& key_type) { ...@@ -254,48 +254,59 @@ bool IsSupportedKeyType(const cryptauthv2::KeyType& key_type) {
key_type == cryptauthv2::KeyType::P256; key_type == cryptauthv2::KeyType::P256;
} }
// The key bundle kUserKeyPair has special standing in order to 1) accommodate
// any existing key from v1 Enrollment and 2) enforce that the key is not
// rotated. As such, only one user key pair should exist in the key bundle, and
// it should be an active, P-256 key with handle
// kCryptAuthFixedUserKeyPairHandle.
//
// It is possible that CryptAuth could request the creation of a new user key
// pair even if the client sends information about an existing key in the
// SyncKeysRequest. If this happens, the client should re-use the existing user
// key pair key material when creating a new key. At the end of the enrollment
// flow, the existing key will be replaced with this new key that has the same
// public/private keys.
//
// Returns an error code if the key-creation instructions are invalid and null // Returns an error code if the key-creation instructions are invalid and null
// otherwise. // otherwise.
base::Optional<CryptAuthEnrollmentResult::ResultCode> base::Optional<CryptAuthEnrollmentResult::ResultCode>
ProcessKeyCreationInstructions( ProcessNewUserKeyPairInstructions(
const CryptAuthKeyBundle::Name& bundle_name, CryptAuthKey::Status status,
const SyncSingleKeyResponse& single_key_response, cryptauthv2::KeyType type,
const std::string& server_ephemeral_dh, const CryptAuthKey* current_active_key,
base::Optional<CryptAuthKeyCreator::CreateKeyData>* new_key_to_create, base::Optional<CryptAuthKeyCreator::CreateKeyData>* new_key_to_create) {
base::Optional<cryptauthv2::KeyDirective>* new_key_directive) { if (type != cryptauthv2::KeyType::P256) {
if (single_key_response.key_creation() == SyncSingleKeyResponse::NONE) PA_LOG(ERROR) << "User key pair must have KeyType P256.";
return base::nullopt;
if (!IsSupportedKeyType(single_key_response.key_type())) {
PA_LOG(ERROR) << "KeyType " << single_key_response.key_type() << " "
<< "not supported.";
return CryptAuthEnrollmentResult::ResultCode:: return CryptAuthEnrollmentResult::ResultCode::
kErrorKeyCreationKeyTypeNotSupported; kErrorUserKeyPairCreationInstructionsInvalid;
} }
// Symmetric keys cannot be created without the server's Diffie-Hellman key. // Because no more than one user key pair can exist in the bundle, the newly
if (server_ephemeral_dh.empty() && // created key must be active.
(single_key_response.key_type() == cryptauthv2::KeyType::RAW128 || if (status != CryptAuthKey::Status::kActive) {
single_key_response.key_type() == cryptauthv2::KeyType::RAW256)) { PA_LOG(ERROR) << "New user key pair must be active.";
PA_LOG(ERROR)
<< "Missing server's Diffie-Hellman key. Cannot create symmetric keys.";
return CryptAuthEnrollmentResult::ResultCode:: return CryptAuthEnrollmentResult::ResultCode::
kErrorSymmetricKeyCreationMissingServerDiffieHellman; kErrorUserKeyPairCreationInstructionsInvalid;
} }
// CryptAuth demands that the key in the kUserKeyPair bundle has a fixed // If a user key pair already exists in the registry, reuse the same key data.
// handle name. For other key bundles, do not specify a handle name; let if (current_active_key && current_active_key->IsAsymmetricKey() &&
// CryptAuthKey generate a handle for us. !current_active_key->private_key().empty()) {
base::Optional<std::string> new_key_handle; PA_LOG(WARNING) << "Received request to create new user key pair while one "
if (bundle_name == CryptAuthKeyBundle::Name::kUserKeyPair) << "already exists in the key registry. Reusing existing "
new_key_handle = kCryptAuthFixedUserKeyPairHandle; << "key material.";
*new_key_to_create = CryptAuthKeyCreator::CreateKeyData( *new_key_to_create = CryptAuthKeyCreator::CreateKeyData(
ConvertKeyCreationToKeyStatus(single_key_response.key_creation()), status, type, kCryptAuthFixedUserKeyPairHandle,
single_key_response.key_type(), new_key_handle); current_active_key->public_key(), current_active_key->private_key());
if (single_key_response.has_key_directive()) return base::nullopt;
*new_key_directive = single_key_response.key_directive(); }
// If there is no user key pair in the registry, then the user has never
// successfully enrolled via v1 or v2 Enrollment. Generate a new key pair.
*new_key_to_create = CryptAuthKeyCreator::CreateKeyData(
status, type, kCryptAuthFixedUserKeyPairHandle);
return base::nullopt; return base::nullopt;
} }
...@@ -620,6 +631,51 @@ CryptAuthV2EnrollerImpl::ProcessSingleKeyResponses( ...@@ -620,6 +631,51 @@ CryptAuthV2EnrollerImpl::ProcessSingleKeyResponses(
return error_code; return error_code;
} }
base::Optional<CryptAuthEnrollmentResult::ResultCode>
CryptAuthV2EnrollerImpl::ProcessKeyCreationInstructions(
const CryptAuthKeyBundle::Name& bundle_name,
const SyncSingleKeyResponse& single_key_response,
const std::string& server_ephemeral_dh,
base::Optional<CryptAuthKeyCreator::CreateKeyData>* new_key_to_create,
base::Optional<cryptauthv2::KeyDirective>* new_key_directive) {
if (single_key_response.key_creation() == SyncSingleKeyResponse::NONE)
return base::nullopt;
CryptAuthKey::Status status =
ConvertKeyCreationToKeyStatus(single_key_response.key_creation());
cryptauthv2::KeyType type = single_key_response.key_type();
if (!IsSupportedKeyType(type)) {
PA_LOG(ERROR) << "KeyType " << type << " not supported.";
return CryptAuthEnrollmentResult::ResultCode::
kErrorKeyCreationKeyTypeNotSupported;
}
// Symmetric keys cannot be created without the server's Diffie-Hellman key.
if (server_ephemeral_dh.empty() && (type == cryptauthv2::KeyType::RAW128 ||
type == cryptauthv2::KeyType::RAW256)) {
PA_LOG(ERROR)
<< "Missing server's Diffie-Hellman key. Cannot create symmetric keys.";
return CryptAuthEnrollmentResult::ResultCode::
kErrorSymmetricKeyCreationMissingServerDiffieHellman;
}
if (single_key_response.has_key_directive())
*new_key_directive = single_key_response.key_directive();
// Handle the user key pair special case separately below.
if (bundle_name != CryptAuthKeyBundle::Name::kUserKeyPair) {
*new_key_to_create = CryptAuthKeyCreator::CreateKeyData(status, type);
return base::nullopt;
}
DCHECK(bundle_name == CryptAuthKeyBundle::Name::kUserKeyPair);
return ProcessNewUserKeyPairInstructions(
status, type, key_registry_->GetActiveKey(bundle_name),
new_key_to_create);
}
void CryptAuthV2EnrollerImpl::OnSyncKeysFailure(NetworkRequestError error) { void CryptAuthV2EnrollerImpl::OnSyncKeysFailure(NetworkRequestError error) {
FinishAttempt(SyncKeysNetworkRequestErrorToResultCode(error)); FinishAttempt(SyncKeysNetworkRequestErrorToResultCode(error));
} }
......
...@@ -120,6 +120,17 @@ class CryptAuthV2EnrollerImpl : public CryptAuthV2Enroller { ...@@ -120,6 +120,17 @@ class CryptAuthV2EnrollerImpl : public CryptAuthV2Enroller {
base::flat_map<CryptAuthKeyBundle::Name, cryptauthv2::KeyDirective>* base::flat_map<CryptAuthKeyBundle::Name, cryptauthv2::KeyDirective>*
new_key_directives); new_key_directives);
// A function to help ProcessSingleKeyResponse() handle the key-creation
// instructions.
base::Optional<CryptAuthEnrollmentResult::ResultCode>
ProcessKeyCreationInstructions(
const CryptAuthKeyBundle::Name& bundle_name,
const cryptauthv2::SyncKeysResponse::SyncSingleKeyResponse&
single_key_response,
const std::string& server_ephemeral_dh,
base::Optional<CryptAuthKeyCreator::CreateKeyData>* new_key_to_create,
base::Optional<cryptauthv2::KeyDirective>* new_key_directive);
void OnSyncKeysFailure(NetworkRequestError error); void OnSyncKeysFailure(NetworkRequestError error);
void OnKeysCreated( void OnKeysCreated(
......
...@@ -75,21 +75,13 @@ const char kRandomSessionId[] = "random_session_id"; ...@@ -75,21 +75,13 @@ const char kRandomSessionId[] = "random_session_id";
const char kOldActivePublicKey[] = "old_active_public_key"; const char kOldActivePublicKey[] = "old_active_public_key";
const char kOldActivePrivateKey[] = "old_active_private_key"; const char kOldActivePrivateKey[] = "old_active_private_key";
const char kOldActiveAsymmetricKeyHandle[] = "old_active_handle";
// User key pair active handle must be kCryptAuthFixedUserKeyPairHandle.
const CryptAuthKey kOldActiveAsymmetricKey(kOldActivePublicKey, const CryptAuthKey kOldActiveAsymmetricKey(kOldActivePublicKey,
kOldActivePrivateKey, kOldActivePrivateKey,
CryptAuthKey::Status::kActive, CryptAuthKey::Status::kActive,
KeyType::P256, KeyType::P256,
kOldActiveAsymmetricKeyHandle); kCryptAuthFixedUserKeyPairHandle);
const char kOldInactivePublicKey[] = "old_inactive_public_key";
const char kOldInactivePrivateKey[] = "old_inactive_private_key";
const char kOldInactiveAsymmetricKeyHandle[] = "old_inactive_handle";
const CryptAuthKey kOldInactiveAsymmetricKey(kOldInactivePublicKey,
kOldInactivePrivateKey,
CryptAuthKey::Status::kInactive,
KeyType::P256,
kOldInactiveAsymmetricKeyHandle);
const char kOldActiveSymmetricKeyMaterial[] = "old_active_symmetric_key"; const char kOldActiveSymmetricKeyMaterial[] = "old_active_symmetric_key";
const char kOldActiveSymmetricKeyHandle[] = "old_active_symmetric_key_handle"; const char kOldActiveSymmetricKeyHandle[] = "old_active_symmetric_key_handle";
...@@ -108,7 +100,6 @@ CryptAuthKey kOldInactiveSymmetricKey(kOldInactiveSymmetricKeyMaterial, ...@@ -108,7 +100,6 @@ CryptAuthKey kOldInactiveSymmetricKey(kOldInactiveSymmetricKeyMaterial,
const char kNewPublicKey[] = "new_public_key"; const char kNewPublicKey[] = "new_public_key";
const char kNewPrivateKey[] = "new_private_key"; const char kNewPrivateKey[] = "new_private_key";
const char kFixedUserKeyPairHandle[] = "device_key";
const char kNewSymmetricKey[] = "new_symmetric_key"; const char kNewSymmetricKey[] = "new_symmetric_key";
const char kNewSymmetricKeyHandle[] = "new_symmetric_key_handle"; const char kNewSymmetricKeyHandle[] = "new_symmetric_key_handle";
...@@ -504,8 +495,26 @@ class DeviceSyncCryptAuthV2EnrollerImplTest ...@@ -504,8 +495,26 @@ class DeviceSyncCryptAuthV2EnrollerImplTest
EXPECT_EQ(key.status(), create_key_data.status); EXPECT_EQ(key.status(), create_key_data.status);
EXPECT_EQ(key.type(), create_key_data.type); EXPECT_EQ(key.type(), create_key_data.type);
if (bundle_name == CryptAuthKeyBundle::Name::kUserKeyPair)
// Special handling for user key pair.
if (bundle_name == CryptAuthKeyBundle::Name::kUserKeyPair) {
EXPECT_EQ(key.handle(), create_key_data.handle); EXPECT_EQ(key.handle(), create_key_data.handle);
const CryptAuthKey* current_active_user_key_pair =
key_registry()->GetActiveKey(
CryptAuthKeyBundle::Name::kUserKeyPair);
if (current_active_user_key_pair) {
EXPECT_EQ(key.public_key(), create_key_data.public_key);
EXPECT_EQ(current_active_user_key_pair->public_key(),
create_key_data.public_key);
EXPECT_EQ(key.private_key(), create_key_data.private_key);
EXPECT_EQ(current_active_user_key_pair->private_key(),
create_key_data.private_key);
EXPECT_EQ(KeyType::P256, create_key_data.type);
EXPECT_EQ(CryptAuthKey::Status::kActive, create_key_data.status);
}
}
} }
ASSERT_TRUE(key_creator()->server_ephemeral_dh()->IsAsymmetricKey()); ASSERT_TRUE(key_creator()->server_ephemeral_dh()->IsAsymmetricKey());
...@@ -514,6 +523,29 @@ class DeviceSyncCryptAuthV2EnrollerImplTest ...@@ -514,6 +523,29 @@ class DeviceSyncCryptAuthV2EnrollerImplTest
EXPECT_EQ(KeyType::P256, key_creator()->server_ephemeral_dh()->type()); EXPECT_EQ(KeyType::P256, key_creator()->server_ephemeral_dh()->type());
} }
void VerifyEnrollSingleKeyRequest(const CryptAuthKeyBundle::Name& bundle_name,
const CryptAuthKey& new_key) {
const EnrollSingleKeyRequest& single_request_user_key_pair =
enroll_keys_request()->enroll_single_key_requests(
GetKeyBundleIndex(bundle_name));
EXPECT_EQ(CryptAuthKeyBundle::KeyBundleNameEnumToString(bundle_name),
single_request_user_key_pair.key_name());
EXPECT_EQ(new_key.handle(), single_request_user_key_pair.new_key_handle());
// No private or symmetric keys should be sent to CryptAuth, so key_material
// should only ever be populated with a public key.
EXPECT_EQ(new_key.IsAsymmetricKey() ? new_key.public_key() : std::string(),
single_request_user_key_pair.key_material());
EXPECT_EQ(CryptAuthKeyProofComputerImpl::Factory::Get()
->BuildInstance()
->ComputeKeyProof(new_key, kRandomSessionId,
kCryptAuthKeyProofSalt),
single_request_user_key_pair.key_proof());
}
CryptAuthV2Enroller* enroller() { return enroller_.get(); } CryptAuthV2Enroller* enroller() { return enroller_.get(); }
CryptAuthKeyRegistry* key_registry() { return key_registry_.get(); } CryptAuthKeyRegistry* key_registry() { return key_registry_.get(); }
...@@ -577,8 +609,6 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, SuccessfulEnrollment) { ...@@ -577,8 +609,6 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, SuccessfulEnrollment) {
// Seed key registry. // Seed key registry.
key_registry()->AddEnrolledKey(CryptAuthKeyBundle::Name::kUserKeyPair, key_registry()->AddEnrolledKey(CryptAuthKeyBundle::Name::kUserKeyPair,
kOldActiveAsymmetricKey); kOldActiveAsymmetricKey);
key_registry()->AddEnrolledKey(CryptAuthKeyBundle::Name::kUserKeyPair,
kOldInactiveAsymmetricKey);
key_registry()->SetKeyDirective(CryptAuthKeyBundle::Name::kUserKeyPair, key_registry()->SetKeyDirective(CryptAuthKeyBundle::Name::kUserKeyPair,
GetSampleOldKeyDirective()); GetSampleOldKeyDirective());
CryptAuthKeyBundle expected_key_bundle_user_key_pair( CryptAuthKeyBundle expected_key_bundle_user_key_pair(
...@@ -601,29 +631,27 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, SuccessfulEnrollment) { ...@@ -601,29 +631,27 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, SuccessfulEnrollment) {
ClientDirective expected_new_client_directive = GetSampleNewClientDirective(); ClientDirective expected_new_client_directive = GetSampleNewClientDirective();
KeyDirective expected_new_key_directive = GetSampleNewKeyDirective(); KeyDirective expected_new_key_directive = GetSampleNewKeyDirective();
// For kUserKeyPair: // For kUserKeyPair (special case):
// - active --> temporarily active during key creation
// - new --> same handle so overwrites active key with same material
// For kMasterLegacyKey:
// - active --> deleted // - active --> deleted
// - inactive --> temporarily active during key creation // - inactive --> temporarily active during key creation
// - new --> active after created // - new --> active after created
// For kMasterLegacyKey:
// - active --> active
// - inactive --> inactive
// - new --> inactive
std::vector<SyncSingleKeyResponseData> sync_single_key_responses_data = { std::vector<SyncSingleKeyResponseData> sync_single_key_responses_data = {
SyncSingleKeyResponseData( SyncSingleKeyResponseData(
CryptAuthKeyBundle::Name::kUserKeyPair, key_registry(), CryptAuthKeyBundle::Name::kUserKeyPair, key_registry(),
{{kOldActiveAsymmetricKeyHandle, SyncSingleKeyResponse::DELETE}, {{kCryptAuthFixedUserKeyPairHandle,
{kOldInactiveAsymmetricKeyHandle,
SyncSingleKeyResponse::ACTIVATE}} /* handle_to_action_map */, SyncSingleKeyResponse::ACTIVATE}} /* handle_to_action_map */,
SyncSingleKeyResponse::ACTIVE /* new_key_creation */, SyncSingleKeyResponse::ACTIVE /* new_key_creation */,
KeyType::P256 /* new_key_type */, KeyType::P256 /* new_key_type */,
expected_new_key_directive /* new_key_directive */), expected_new_key_directive /* new_key_directive */),
SyncSingleKeyResponseData( SyncSingleKeyResponseData(
CryptAuthKeyBundle::Name::kLegacyMasterKey, key_registry(), CryptAuthKeyBundle::Name::kLegacyMasterKey, key_registry(),
{{kOldActiveSymmetricKeyHandle, SyncSingleKeyResponse::ACTIVATE}, {{kOldActiveSymmetricKeyHandle, SyncSingleKeyResponse::DELETE},
{kOldInactiveSymmetricKeyHandle, {kOldInactiveSymmetricKeyHandle,
SyncSingleKeyResponse::DEACTIVATE}} /* handle_to_action_map */, SyncSingleKeyResponse::ACTIVATE}} /* handle_to_action_map */,
SyncSingleKeyResponse::INACTIVE /* new_key_creation */, SyncSingleKeyResponse::ACTIVE /* new_key_creation */,
KeyType::RAW256 /* new_key_type */, KeyType::RAW256 /* new_key_type */,
expected_new_key_directive /* new_key_directive */)}; expected_new_key_directive /* new_key_directive */)};
...@@ -635,25 +663,30 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, SuccessfulEnrollment) { ...@@ -635,25 +663,30 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, SuccessfulEnrollment) {
SendSyncKeysResponse(sync_keys_response); SendSyncKeysResponse(sync_keys_response);
// Verify that the key actions were applied. (Note: New keys not created yet.) // Verify that the key actions were applied. (Note: New keys not created yet.)
expected_key_bundle_user_key_pair.DeleteKey(kOldActiveAsymmetricKeyHandle); // No key actions expected for kUserKeyPair.
expected_key_bundle_user_key_pair.SetActiveKey(
kOldInactiveAsymmetricKeyHandle);
EXPECT_EQ( EXPECT_EQ(
expected_key_bundle_user_key_pair, expected_key_bundle_user_key_pair,
*key_registry()->GetKeyBundle(CryptAuthKeyBundle::Name::kUserKeyPair)); *key_registry()->GetKeyBundle(CryptAuthKeyBundle::Name::kUserKeyPair));
// In kLegacyMasterKey bundle, former active key should have been deleted and
// former inactive key should now be active.
expected_key_bundle_legacy_master_key.DeleteKey(kOldActiveSymmetricKeyHandle);
expected_key_bundle_legacy_master_key.SetActiveKey(
kOldInactiveSymmetricKeyHandle);
EXPECT_EQ(expected_key_bundle_legacy_master_key, EXPECT_EQ(expected_key_bundle_legacy_master_key,
*key_registry()->GetKeyBundle( *key_registry()->GetKeyBundle(
CryptAuthKeyBundle::Name::kLegacyMasterKey)); CryptAuthKeyBundle::Name::kLegacyMasterKey));
// Verify the key creation data, and assume successful key creation. // Verify the key creation data, and assume successful key creation.
// Note: Since an active user key pair already exists, the same key material
// should re-used.
base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKey> expected_new_keys = { base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKey> expected_new_keys = {
{CryptAuthKeyBundle::Name::kUserKeyPair, {CryptAuthKeyBundle::Name::kUserKeyPair,
CryptAuthKey(kNewPublicKey, kNewPrivateKey, CryptAuthKey(kOldActivePublicKey, kOldActivePrivateKey,
CryptAuthKey::Status::kActive, KeyType::P256, CryptAuthKey::Status::kActive, KeyType::P256,
kFixedUserKeyPairHandle)}, kCryptAuthFixedUserKeyPairHandle)},
{CryptAuthKeyBundle::Name::kLegacyMasterKey, {CryptAuthKeyBundle::Name::kLegacyMasterKey,
CryptAuthKey(kNewSymmetricKey, CryptAuthKey::Status::kInactive, CryptAuthKey(kNewSymmetricKey, CryptAuthKey::Status::kActive,
KeyType::RAW256, kNewSymmetricKeyHandle)}}; KeyType::RAW256, kNewSymmetricKeyHandle)}};
VerifyKeyCreatorInputs( VerifyKeyCreatorInputs(
...@@ -666,40 +699,13 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, SuccessfulEnrollment) { ...@@ -666,40 +699,13 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, SuccessfulEnrollment) {
EXPECT_EQ(kRandomSessionId, enroll_keys_request()->random_session_id()); EXPECT_EQ(kRandomSessionId, enroll_keys_request()->random_session_id());
EXPECT_EQ(kClientDhPublicKey, enroll_keys_request()->client_ephemeral_dh()); EXPECT_EQ(kClientDhPublicKey, enroll_keys_request()->client_ephemeral_dh());
EXPECT_EQ(2, enroll_keys_request()->enroll_single_key_requests_size()); EXPECT_EQ(2, enroll_keys_request()->enroll_single_key_requests_size());
VerifyEnrollSingleKeyRequest(
std::unique_ptr<CryptAuthKeyProofComputer> key_proof_computer = CryptAuthKeyBundle::Name::kUserKeyPair,
CryptAuthKeyProofComputerImpl::Factory::Get()->BuildInstance(); expected_new_keys.find(CryptAuthKeyBundle::Name::kUserKeyPair)->second);
VerifyEnrollSingleKeyRequest(
const EnrollSingleKeyRequest& single_request_user_key_pair = CryptAuthKeyBundle::Name::kLegacyMasterKey,
enroll_keys_request()->enroll_single_key_requests( expected_new_keys.find(CryptAuthKeyBundle::Name::kLegacyMasterKey)
GetKeyBundleIndex(CryptAuthKeyBundle::Name::kUserKeyPair)); ->second);
EXPECT_EQ(CryptAuthKeyBundle::KeyBundleNameEnumToString(
CryptAuthKeyBundle::Name::kUserKeyPair),
single_request_user_key_pair.key_name());
EXPECT_EQ(kFixedUserKeyPairHandle,
single_request_user_key_pair.new_key_handle());
EXPECT_EQ(kNewPublicKey, single_request_user_key_pair.key_material());
EXPECT_EQ(key_proof_computer->ComputeKeyProof(
expected_new_keys.find(CryptAuthKeyBundle::Name::kUserKeyPair)
->second,
kRandomSessionId, kCryptAuthKeyProofSalt),
single_request_user_key_pair.key_proof());
const EnrollSingleKeyRequest& single_request_legacy_master_key =
enroll_keys_request()->enroll_single_key_requests(
GetKeyBundleIndex(CryptAuthKeyBundle::Name::kLegacyMasterKey));
EXPECT_EQ(CryptAuthKeyBundle::KeyBundleNameEnumToString(
CryptAuthKeyBundle::Name::kLegacyMasterKey),
single_request_legacy_master_key.key_name());
EXPECT_EQ(kNewSymmetricKeyHandle,
single_request_legacy_master_key.new_key_handle());
EXPECT_TRUE(single_request_legacy_master_key.key_material().empty());
EXPECT_EQ(
key_proof_computer->ComputeKeyProof(
expected_new_keys.find(CryptAuthKeyBundle::Name::kLegacyMasterKey)
->second,
kRandomSessionId, kCryptAuthKeyProofSalt),
single_request_legacy_master_key.key_proof());
// Assume a successful EnrollKeys() call. // Assume a successful EnrollKeys() call.
// Note: No parameters in EnrollKeysResponse are processed by the enroller // Note: No parameters in EnrollKeysResponse are processed by the enroller
...@@ -731,16 +737,71 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, SuccessfulEnrollment) { ...@@ -731,16 +737,71 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, SuccessfulEnrollment) {
*key_registry()->GetKeyBundle(bundle_name)); *key_registry()->GetKeyBundle(bundle_name));
} }
TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest,
SuccessfulEnrollment_CreateUserKeyPair_NoKeyInRegistry) {
CallEnroll(GetSampleClientMetadata(), GetSampleClientAppMetadata(),
GetSamplePreviousClientDirectivePolicyReference());
ClientDirective expected_new_client_directive = GetSampleNewClientDirective();
KeyDirective expected_new_key_directive = GetSampleNewKeyDirective();
SyncKeysResponse sync_keys_response = BuildSyncKeysResponse(
{SyncSingleKeyResponseData(
CryptAuthKeyBundle::Name::kUserKeyPair, key_registry(),
{} /* handle_to_action_map */,
SyncSingleKeyResponse::ACTIVE /* new_key_creation */,
KeyType::P256 /* new_key_type */,
expected_new_key_directive /* new_key_directive */)},
kRandomSessionId, kServerEphemeralDh, expected_new_client_directive);
SendSyncKeysResponse(sync_keys_response);
// Note: Because there is not an existing kUserKeyPair key in registry, a new
// key should be generated. (If there was an existing key, its key material
// would be reused because the kUserKeyPair key should not be rotated.)
base::flat_map<CryptAuthKeyBundle::Name, CryptAuthKey> expected_new_keys = {
{CryptAuthKeyBundle::Name::kUserKeyPair,
CryptAuthKey(kNewPublicKey, kNewPrivateKey,
CryptAuthKey::Status::kActive, KeyType::P256,
kCryptAuthFixedUserKeyPairHandle)}};
VerifyKeyCreatorInputs(
expected_new_keys,
kServerEphemeralDh /* expected_server_ephemeral_dh_public_key */);
RunKeyCreator(expected_new_keys, kClientEphemeralDh);
EXPECT_EQ(1, enroll_keys_request()->enroll_single_key_requests_size());
VerifyEnrollSingleKeyRequest(
CryptAuthKeyBundle::Name::kUserKeyPair,
expected_new_keys.find(CryptAuthKeyBundle::Name::kUserKeyPair)->second);
SendEnrollKeysResponse(EnrollKeysResponse());
EXPECT_EQ(CryptAuthEnrollmentResult(
CryptAuthEnrollmentResult::ResultCode::kSuccessNewKeysEnrolled,
expected_new_client_directive),
enrollment_result());
CryptAuthKeyBundle expected_key_bundle(
CryptAuthKeyBundle::Name::kUserKeyPair);
expected_key_bundle.AddKey(
expected_new_keys.find(CryptAuthKeyBundle::Name::kUserKeyPair)->second);
expected_key_bundle.set_key_directive(expected_new_key_directive);
EXPECT_EQ(expected_key_bundle, *key_registry()->GetKeyBundle(
CryptAuthKeyBundle::Name::kUserKeyPair));
}
TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest,
SuccessfulEnrollment_NoKeysCreated) { SuccessfulEnrollment_NoKeysCreated) {
key_registry()->AddEnrolledKey(CryptAuthKeyBundle::Name::kUserKeyPair, key_registry()->AddEnrolledKey(CryptAuthKeyBundle::Name::kLegacyMasterKey,
kOldActiveAsymmetricKey); kOldActiveSymmetricKey);
key_registry()->AddEnrolledKey(CryptAuthKeyBundle::Name::kUserKeyPair, key_registry()->AddEnrolledKey(CryptAuthKeyBundle::Name::kLegacyMasterKey,
kOldInactiveAsymmetricKey); kOldInactiveSymmetricKey);
key_registry()->SetKeyDirective(CryptAuthKeyBundle::Name::kUserKeyPair, key_registry()->SetKeyDirective(CryptAuthKeyBundle::Name::kLegacyMasterKey,
GetSampleOldKeyDirective()); GetSampleOldKeyDirective());
CryptAuthKeyBundle expected_key_bundle( CryptAuthKeyBundle expected_key_bundle(*key_registry()->GetKeyBundle(
*key_registry()->GetKeyBundle(CryptAuthKeyBundle::Name::kUserKeyPair)); CryptAuthKeyBundle::Name::kLegacyMasterKey));
CallEnroll(GetSampleClientMetadata(), GetSampleClientAppMetadata(), CallEnroll(GetSampleClientMetadata(), GetSampleClientAppMetadata(),
GetSamplePreviousClientDirectivePolicyReference()); GetSamplePreviousClientDirectivePolicyReference());
...@@ -749,18 +810,19 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, ...@@ -749,18 +810,19 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest,
// but not create any new keys. // but not create any new keys.
SyncKeysResponse sync_keys_response = SyncKeysResponse sync_keys_response =
BuildSyncKeysResponse({SyncSingleKeyResponseData( BuildSyncKeysResponse({SyncSingleKeyResponseData(
CryptAuthKeyBundle::Name::kUserKeyPair, key_registry(), CryptAuthKeyBundle::Name::kLegacyMasterKey, key_registry(),
{{kOldActiveAsymmetricKeyHandle, SyncSingleKeyResponse::DEACTIVATE}, {{kOldActiveSymmetricKeyHandle, SyncSingleKeyResponse::DEACTIVATE},
{kOldInactiveAsymmetricKeyHandle, {kOldInactiveSymmetricKeyHandle,
SyncSingleKeyResponse::ACTIVATE}} /* handle_to_action_map */, SyncSingleKeyResponse::ACTIVATE}} /* handle_to_action_map */,
SyncSingleKeyResponse::NONE /* new_key_creation */, SyncSingleKeyResponse::NONE /* new_key_creation */,
base::nullopt /* new_key_type */, base::nullopt /* new_key_type */,
base::nullopt /* new_key_directive */)}); base::nullopt /* new_key_directive */)});
SendSyncKeysResponse(sync_keys_response); SendSyncKeysResponse(sync_keys_response);
expected_key_bundle.SetActiveKey(kOldInactiveAsymmetricKeyHandle); expected_key_bundle.SetActiveKey(kOldInactiveSymmetricKeyHandle);
EXPECT_EQ(expected_key_bundle, *key_registry()->GetKeyBundle( EXPECT_EQ(expected_key_bundle,
CryptAuthKeyBundle::Name::kUserKeyPair)); *key_registry()->GetKeyBundle(
CryptAuthKeyBundle::Name::kLegacyMasterKey));
EXPECT_EQ(CryptAuthEnrollmentResult( EXPECT_EQ(CryptAuthEnrollmentResult(
CryptAuthEnrollmentResult::ResultCode::kSuccessNoNewKeysNeeded, CryptAuthEnrollmentResult::ResultCode::kSuccessNoNewKeysNeeded,
...@@ -853,7 +915,7 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, Failure_InvalidKeyActions_Size) { ...@@ -853,7 +915,7 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, Failure_InvalidKeyActions_Size) {
TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest,
Failure_InvalidKeyActions_NoActiveKey) { Failure_InvalidKeyActions_NoActiveKey) {
key_registry()->AddEnrolledKey(CryptAuthKeyBundle::Name::kUserKeyPair, key_registry()->AddEnrolledKey(CryptAuthKeyBundle::Name::kLegacyMasterKey,
kOldActiveAsymmetricKey); kOldActiveAsymmetricKey);
CallEnroll(GetSampleClientMetadata(), GetSampleClientAppMetadata(), CallEnroll(GetSampleClientMetadata(), GetSampleClientAppMetadata(),
...@@ -862,8 +924,8 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, ...@@ -862,8 +924,8 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest,
// Try to deactivate the only active key. // Try to deactivate the only active key.
SyncKeysResponse sync_keys_response = SyncKeysResponse sync_keys_response =
BuildSyncKeysResponse({SyncSingleKeyResponseData( BuildSyncKeysResponse({SyncSingleKeyResponseData(
CryptAuthKeyBundle::Name::kUserKeyPair, key_registry(), CryptAuthKeyBundle::Name::kLegacyMasterKey, key_registry(),
{{kOldActiveAsymmetricKeyHandle, {{kOldActiveSymmetricKeyHandle,
SyncSingleKeyResponse::DEACTIVATE}} /* handle_to_action_map */, SyncSingleKeyResponse::DEACTIVATE}} /* handle_to_action_map */,
SyncSingleKeyResponse::NONE /* new_key_creation */, SyncSingleKeyResponse::NONE /* new_key_creation */,
base::nullopt /* new_key_type */, base::nullopt /* new_key_type */,
...@@ -899,10 +961,12 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, ...@@ -899,10 +961,12 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest,
} }
TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest,
Failure_InvalidKeyCreationInstructions_NoServerDiffieHellman) { Failure_InvalidKeyCreationInstructions_UnsupportedUserKeyPairKeyType) {
CallEnroll(GetSampleClientMetadata(), GetSampleClientAppMetadata(), CallEnroll(GetSampleClientMetadata(), GetSampleClientAppMetadata(),
GetSamplePreviousClientDirectivePolicyReference()); GetSamplePreviousClientDirectivePolicyReference());
// Instruct client to create a symmetric user key pair. The user key pair is
// heavily protected against anything other than P256.
SyncKeysResponse sync_keys_response = SyncKeysResponse sync_keys_response =
BuildSyncKeysResponse({SyncSingleKeyResponseData( BuildSyncKeysResponse({SyncSingleKeyResponseData(
CryptAuthKeyBundle::Name::kUserKeyPair, key_registry(), CryptAuthKeyBundle::Name::kUserKeyPair, key_registry(),
...@@ -910,6 +974,50 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, ...@@ -910,6 +974,50 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest,
SyncSingleKeyResponse::ACTIVE /* new_key_creation */, SyncSingleKeyResponse::ACTIVE /* new_key_creation */,
KeyType::RAW256 /* new_key_type */, KeyType::RAW256 /* new_key_type */,
base::nullopt /* new_key_directive */)}); base::nullopt /* new_key_directive */)});
SendSyncKeysResponse(sync_keys_response);
EXPECT_EQ(CryptAuthEnrollmentResult(
CryptAuthEnrollmentResult::ResultCode::
kErrorUserKeyPairCreationInstructionsInvalid,
sync_keys_response.client_directive()),
enrollment_result());
}
TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest,
Failure_InvalidKeyCreationInstructions_NewUserKeyPairMustBeActive) {
CallEnroll(GetSampleClientMetadata(), GetSampleClientAppMetadata(),
GetSamplePreviousClientDirectivePolicyReference());
// Instruct client to create a new, inactive user key pair. Since there can
// only be one user key pair in the bundle, a new one must be active.
SyncKeysResponse sync_keys_response =
BuildSyncKeysResponse({SyncSingleKeyResponseData(
CryptAuthKeyBundle::Name::kUserKeyPair, key_registry(),
{} /* handle_to_action_map */,
SyncSingleKeyResponse::INACTIVE /* new_key_creation */,
KeyType::P256 /* new_key_type */,
base::nullopt /* new_key_directive */)});
SendSyncKeysResponse(sync_keys_response);
EXPECT_EQ(CryptAuthEnrollmentResult(
CryptAuthEnrollmentResult::ResultCode::
kErrorUserKeyPairCreationInstructionsInvalid,
sync_keys_response.client_directive()),
enrollment_result());
}
TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest,
Failure_InvalidKeyCreationInstructions_NoServerDiffieHellman) {
CallEnroll(GetSampleClientMetadata(), GetSampleClientAppMetadata(),
GetSamplePreviousClientDirectivePolicyReference());
SyncKeysResponse sync_keys_response =
BuildSyncKeysResponse({SyncSingleKeyResponseData(
CryptAuthKeyBundle::Name::kLegacyMasterKey, key_registry(),
{} /* handle_to_action_map */,
SyncSingleKeyResponse::ACTIVE /* new_key_creation */,
KeyType::RAW256 /* new_key_type */,
base::nullopt /* new_key_directive */)});
sync_keys_response.release_server_ephemeral_dh(); sync_keys_response.release_server_ephemeral_dh();
SendSyncKeysResponse(sync_keys_response); SendSyncKeysResponse(sync_keys_response);
...@@ -941,7 +1049,7 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, ...@@ -941,7 +1049,7 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest,
{CryptAuthKeyBundle::Name::kUserKeyPair, {CryptAuthKeyBundle::Name::kUserKeyPair,
CryptAuthKey(kNewPublicKey, kNewPrivateKey, CryptAuthKey(kNewPublicKey, kNewPrivateKey,
CryptAuthKey::Status::kActive, KeyType::P256, CryptAuthKey::Status::kActive, KeyType::P256,
kFixedUserKeyPairHandle)}}; kCryptAuthFixedUserKeyPairHandle)}};
RunKeyCreator(expected_new_keys, kClientEphemeralDh); RunKeyCreator(expected_new_keys, kClientEphemeralDh);
EXPECT_EQ(CryptAuthEnrollmentResult(CryptAuthEnrollmentResult::ResultCode:: EXPECT_EQ(CryptAuthEnrollmentResult(CryptAuthEnrollmentResult::ResultCode::
...@@ -980,7 +1088,7 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, Failure_EnrollKeysApiCall) { ...@@ -980,7 +1088,7 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, Failure_EnrollKeysApiCall) {
{CryptAuthKeyBundle::Name::kUserKeyPair, {CryptAuthKeyBundle::Name::kUserKeyPair,
CryptAuthKey(kNewPublicKey, kNewPrivateKey, CryptAuthKey(kNewPublicKey, kNewPrivateKey,
CryptAuthKey::Status::kActive, KeyType::P256, CryptAuthKey::Status::kActive, KeyType::P256,
kFixedUserKeyPairHandle)}}; kCryptAuthFixedUserKeyPairHandle)}};
RunKeyCreator(expected_new_keys, kClientEphemeralDh); RunKeyCreator(expected_new_keys, kClientEphemeralDh);
FailEnrollKeysRequest(NetworkRequestError::kBadRequest); FailEnrollKeysRequest(NetworkRequestError::kBadRequest);
...@@ -1049,7 +1157,7 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest, ...@@ -1049,7 +1157,7 @@ TEST_F(DeviceSyncCryptAuthV2EnrollerImplTest,
{CryptAuthKeyBundle::Name::kUserKeyPair, {CryptAuthKeyBundle::Name::kUserKeyPair,
CryptAuthKey(kNewPublicKey, kNewPrivateKey, CryptAuthKey(kNewPublicKey, kNewPrivateKey,
CryptAuthKey::Status::kActive, KeyType::P256, CryptAuthKey::Status::kActive, KeyType::P256,
kFixedUserKeyPairHandle)}}; kCryptAuthFixedUserKeyPairHandle)}};
RunKeyCreator(expected_new_keys, kClientEphemeralDh); RunKeyCreator(expected_new_keys, kClientEphemeralDh);
// Timeout waiting for EnrollKeysResponse. // Timeout waiting for EnrollKeysResponse.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment