Commit 280a69e4 authored by Maksim Moskvitin's avatar Maksim Moskvitin Committed by Commit Bot

Support transitions from trusted vault passphrase

This CL supports transitions from trusted vault passphrase to keystore
and custom passphrases. To avoid data corruptions by buggy clients the
bridge verifies that the remote keybag contains the last trusted vault
key, which was used as the default key. Since browser restart can
happen in between of receiving the remote update and decryption of the
new remote keybag, |last_trusted_vault_key| stored in NigoriLocalData.

Bug: 1020084
Change-Id: Ica654898a3499f599258bf9e65eb8d91580fcc2f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2087766
Commit-Queue: Maksim Moskvitin <mmoskvitin@google.com>
Reviewed-by: default avatarMikel Astiz <mastiz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#747697}
parent e09f4624
......@@ -152,6 +152,12 @@ NigoriState NigoriState::CreateFromLocalProto(
state.pending_keystore_decryptor_token =
proto.pending_keystore_decryptor_token();
}
if (proto.has_last_default_trusted_vault_key_name()) {
state.last_default_trusted_vault_key_name =
proto.last_default_trusted_vault_key_name();
}
return state;
}
......@@ -210,6 +216,10 @@ sync_pb::NigoriModel NigoriState::ToLocalProto() const {
*proto.mutable_pending_keystore_decryptor_token() =
*pending_keystore_decryptor_token;
}
if (last_default_trusted_vault_key_name.has_value()) {
proto.set_last_default_trusted_vault_key_name(
*last_default_trusted_vault_key_name);
}
return proto;
}
......@@ -236,6 +246,8 @@ sync_pb::NigoriSpecifics NigoriState::ToSpecificsProto() const {
UpdateSpecificsFromKeyDerivationParams(
*custom_passphrase_key_derivation_params, &specifics);
}
// TODO(crbug.com/1020084): populate |keystore_decryptor_token| for trusted
// vault passphrase to allow rollbacks.
if (passphrase_type == sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE) {
// TODO(crbug.com/922900): it seems possible to have corrupted
// |pending_keystore_decryptor_token| and an ability to recover it in case
......@@ -277,6 +289,8 @@ NigoriState NigoriState::Clone() const {
result.encrypt_everything = encrypt_everything;
result.keystore_keys_cryptographer = keystore_keys_cryptographer->Clone();
result.pending_keystore_decryptor_token = pending_keystore_decryptor_token;
result.last_default_trusted_vault_key_name =
last_default_trusted_vault_key_name;
return result;
}
......
......@@ -82,6 +82,10 @@ struct NigoriState {
// can't be decrypted right after remote update arrival due to lack of
// keystore keys. May be set only for keystore Nigori.
base::Optional<sync_pb::EncryptedData> pending_keystore_decryptor_token;
// The name of the latest available trusted vault key that was used as the
// default encryption key.
base::Optional<std::string> last_default_trusted_vault_key_name;
};
} // namespace syncer
......
......@@ -198,13 +198,8 @@ bool IsValidPassphraseTransition(
case NigoriSpecifics::CUSTOM_PASSPHRASE:
return false;
case NigoriSpecifics::TRUSTED_VAULT_PASSPHRASE:
// TODO(crbug.com/984940): The below should be allowed for
// CUSTOM_PASSPHRASE and KEYSTORE_PASSPHRASE but it requires carefully
// verifying that the client triggering the transition already had access
// to the trusted vault passphrase (e.g. the new keybag must be a
// superset of the old and the default key must have changed).
NOTIMPLEMENTED();
return false;
return new_passphrase_type == NigoriSpecifics::CUSTOM_PASSPHRASE ||
new_passphrase_type == NigoriSpecifics::KEYSTORE_PASSPHRASE;
}
NOTREACHED();
return false;
......@@ -653,6 +648,9 @@ void NigoriSyncBridgeImpl::AddTrustedVaultDecryptionKeys(
return;
}
state_.last_default_trusted_vault_key_name =
state_.cryptographer->GetDefaultEncryptionKeyName();
storage_->StoreData(SerializeAsNigoriLocalData());
broadcasting_observer_->OnCryptographerStateChanged(
state_.cryptographer.get(), state_.pending_keys.has_value());
......@@ -995,6 +993,17 @@ base::Optional<ModelError> NigoriSyncBridgeImpl::TryDecryptPendingKeysWith(
"Received keybag is missing the new default key.");
}
if (state_.last_default_trusted_vault_key_name.has_value() &&
!new_key_bag.HasKey(*state_.last_default_trusted_vault_key_name)) {
// Protocol violation.
return ModelError(FROM_HERE,
"Received keybag is missing the last trusted vault key.");
}
// Reset |last_default_trusted_vault_key_name| as |state_| might go out of
// TRUSTED_VAULT passphrase type. The callers are responsible to set it again
// if needed.
state_.last_default_trusted_vault_key_name = base::nullopt;
state_.cryptographer->EmplaceKeysFrom(new_key_bag);
state_.cryptographer->SelectDefaultEncryptionKey(new_default_key_name);
state_.pending_keys.reset();
......@@ -1054,6 +1063,7 @@ void NigoriSyncBridgeImpl::ApplyDisableSyncChanges() {
state_.custom_passphrase_time = base::Time();
state_.keystore_migration_time = base::Time();
state_.custom_passphrase_key_derivation_params = base::nullopt;
state_.last_default_trusted_vault_key_name = base::nullopt;
broadcasting_observer_->OnCryptographerStateChanged(
state_.cryptographer.get(),
/*has_pending_keys=*/false);
......
......@@ -90,6 +90,11 @@ message NigoriModel {
// Encryptor keystore decryptor token. Used for decryption of keystore Nigori
// in case keystore keys arrived after NigoriSpecifics.
optional EncryptedData pending_keystore_decryptor_token = 11;
// Contains the name of the latest available trusted vault key that was used
// as the default encryption key. Resets when the client go out of pending
// decryption state and transits to other passphrase types.
optional string last_default_trusted_vault_key_name = 12;
}
// Sync proto to store Nigori data in storage. Proto should be encrypted with
......
......@@ -606,6 +606,7 @@ VISIT_PROTO_FIELDS(const sync_pb::NigoriModel& proto) {
VISIT_REP(encrypted_types_specifics_field_number);
VISIT_REP(keystore_key);
VISIT(pending_keystore_decryptor_token);
VISIT(last_default_trusted_vault_key_name);
}
VISIT_PROTO_FIELDS(const sync_pb::NigoriLocalData& proto) {
......
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