blob: d326904c00b62f6e38726155788f9213b2d7154c [file] [log] [blame]
// Copyright 2017 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 "chromeos/cryptohome/cryptohome_util.h"
#include <string>
#include "base/logging.h"
#include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/dbus/cryptohome/key.pb.h"
#include "chromeos/dbus/cryptohome/rpc.pb.h"
#include "components/device_event_log/device_event_log.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
using chromeos::ChallengeResponseKey;
using google::protobuf::RepeatedPtrField;
namespace cryptohome {
namespace {
bool IsEmpty(const base::Optional<BaseReply>& reply) {
if (!reply.has_value()) {
LOGIN_LOG(ERROR) << "Cryptohome call failed with empty reply.";
return true;
}
return false;
}
ChallengeSignatureAlgorithm ChallengeSignatureAlgorithmToProtoEnum(
ChallengeResponseKey::SignatureAlgorithm algorithm) {
using Algorithm = ChallengeResponseKey::SignatureAlgorithm;
switch (algorithm) {
case Algorithm::kRsassaPkcs1V15Sha1:
return CHALLENGE_RSASSA_PKCS1_V1_5_SHA1;
case Algorithm::kRsassaPkcs1V15Sha256:
return CHALLENGE_RSASSA_PKCS1_V1_5_SHA256;
case Algorithm::kRsassaPkcs1V15Sha384:
return CHALLENGE_RSASSA_PKCS1_V1_5_SHA384;
case Algorithm::kRsassaPkcs1V15Sha512:
return CHALLENGE_RSASSA_PKCS1_V1_5_SHA512;
}
NOTREACHED();
}
void ChallengeResponseKeyToPublicKeyInfo(
const ChallengeResponseKey& challenge_response_key,
ChallengePublicKeyInfo* public_key_info) {
public_key_info->set_public_key_spki_der(
challenge_response_key.public_key_spki_der());
for (ChallengeResponseKey::SignatureAlgorithm algorithm :
challenge_response_key.signature_algorithms()) {
public_key_info->add_signature_algorithm(
ChallengeSignatureAlgorithmToProtoEnum(algorithm));
}
}
void KeyDefPrivilegesToKeyPrivileges(int key_def_privileges,
KeyPrivileges* privileges) {
privileges->set_mount(key_def_privileges & PRIV_MOUNT);
privileges->set_add(key_def_privileges & PRIV_ADD);
privileges->set_remove(key_def_privileges & PRIV_REMOVE);
privileges->set_update(key_def_privileges & PRIV_MIGRATE);
privileges->set_authorized_update(key_def_privileges &
PRIV_AUTHORIZED_UPDATE);
}
// TODO(crbug.com/797848): Add tests that cover this logic.
void KeyDefSecretToKeyAuthorizationSecret(
const KeyDefinition::AuthorizationData::Secret& key_def_secret,
KeyAuthorizationSecret* secret) {
secret->mutable_usage()->set_encrypt(key_def_secret.encrypt);
secret->mutable_usage()->set_sign(key_def_secret.sign);
secret->set_wrapped(key_def_secret.wrapped);
if (!key_def_secret.symmetric_key.empty())
secret->set_symmetric_key(key_def_secret.symmetric_key);
if (!key_def_secret.public_key.empty())
secret->set_public_key(key_def_secret.public_key);
}
// TODO(crbug.com/797848): Add tests that cover this logic.
void KeyDefProviderDataToKeyProviderDataEntry(
const KeyDefinition::ProviderData& provider_data,
KeyProviderData::Entry* entry) {
entry->set_name(provider_data.name);
if (provider_data.number)
entry->set_number(*provider_data.number);
if (provider_data.bytes)
entry->set_bytes(*provider_data.bytes);
}
// TODO(crbug.com/797848): Add tests that cover this logic.
KeyAuthorizationData::KeyAuthorizationType GetKeyAuthDataType(
KeyDefinition::AuthorizationData::Type key_def_auth_data_type) {
switch (key_def_auth_data_type) {
case KeyDefinition::AuthorizationData::TYPE_HMACSHA256:
return KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_HMACSHA256;
case KeyDefinition::AuthorizationData::TYPE_AES256CBC_HMACSHA256:
return KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_AES256CBC_HMACSHA256;
}
}
} // namespace
MountError MountExReplyToMountError(const base::Optional<BaseReply>& reply) {
if (IsEmpty(reply))
return MOUNT_ERROR_FATAL;
if (!reply->HasExtension(MountReply::reply)) {
LOGIN_LOG(ERROR) << "MountEx failed with no MountReply extension in reply.";
return MOUNT_ERROR_FATAL;
}
return CryptohomeErrorToMountError(reply->error());
}
MountError BaseReplyToMountError(const base::Optional<BaseReply>& reply) {
if (IsEmpty(reply))
return MOUNT_ERROR_FATAL;
return CryptohomeErrorToMountError(reply->error());
}
MountError GetKeyDataReplyToMountError(const base::Optional<BaseReply>& reply) {
if (IsEmpty(reply))
return MOUNT_ERROR_FATAL;
if (!reply->HasExtension(GetKeyDataReply::reply)) {
LOGIN_LOG(ERROR)
<< "GetKeyDataEx failed with no GetKeyDataReply extension in reply.";
return MOUNT_ERROR_FATAL;
}
return CryptohomeErrorToMountError(reply->error());
}
// TODO(crbug.com/797848): Finish testing this method.
std::vector<KeyDefinition> GetKeyDataReplyToKeyDefinitions(
const base::Optional<BaseReply>& reply) {
const RepeatedPtrField<KeyData>& key_data =
reply->GetExtension(GetKeyDataReply::reply).key_data();
std::vector<KeyDefinition> key_definitions;
for (RepeatedPtrField<KeyData>::const_iterator it = key_data.begin();
it != key_data.end(); ++it) {
// Extract |type|, |label| and |revision|.
KeyDefinition key_definition;
CHECK(it->has_type());
switch (it->type()) {
case KeyData::KEY_TYPE_PASSWORD:
key_definition.type = KeyDefinition::TYPE_PASSWORD;
break;
case KeyData::KEY_TYPE_CHALLENGE_RESPONSE:
key_definition.type = KeyDefinition::TYPE_CHALLENGE_RESPONSE;
break;
}
key_definition.label = it->label();
key_definition.revision = it->revision();
// Extract |privileges|.
const KeyPrivileges& privileges = it->privileges();
if (privileges.mount())
key_definition.privileges |= PRIV_MOUNT;
if (privileges.add())
key_definition.privileges |= PRIV_ADD;
if (privileges.remove())
key_definition.privileges |= PRIV_REMOVE;
if (privileges.update())
key_definition.privileges |= PRIV_MIGRATE;
if (privileges.authorized_update())
key_definition.privileges |= PRIV_AUTHORIZED_UPDATE;
// Extract |policy|.
key_definition.policy.low_entropy_credential =
it->policy().low_entropy_credential();
key_definition.policy.auth_locked = it->policy().auth_locked();
// Extract |authorization_data|.
for (RepeatedPtrField<KeyAuthorizationData>::const_iterator auth_it =
it->authorization_data().begin();
auth_it != it->authorization_data().end(); ++auth_it) {
key_definition.authorization_data.push_back(
KeyDefinition::AuthorizationData());
KeyAuthorizationDataToAuthorizationData(
*auth_it, &key_definition.authorization_data.back());
}
// Extract |provider_data|.
for (RepeatedPtrField<KeyProviderData::Entry>::const_iterator
provider_data_it = it->provider_data().entry().begin();
provider_data_it != it->provider_data().entry().end();
++provider_data_it) {
// Extract |name|.
key_definition.provider_data.push_back(
KeyDefinition::ProviderData(provider_data_it->name()));
KeyDefinition::ProviderData& provider_data =
key_definition.provider_data.back();
int data_items = 0;
// Extract |number|.
if (provider_data_it->has_number()) {
provider_data.number.reset(new int64_t(provider_data_it->number()));
++data_items;
}
// Extract |bytes|.
if (provider_data_it->has_bytes()) {
provider_data.bytes.reset(new std::string(provider_data_it->bytes()));
++data_items;
}
DCHECK_EQ(1, data_items);
}
key_definitions.push_back(std::move(key_definition));
}
return key_definitions;
}
int64_t AccountDiskUsageReplyToUsageSize(
const base::Optional<BaseReply>& reply) {
if (IsEmpty(reply))
return -1;
if (reply->has_error() && reply->error() != CRYPTOHOME_ERROR_NOT_SET) {
LOGIN_LOG(ERROR) << "GetAccountDiskUsage failed with error: "
<< reply->error();
return -1;
}
if (!reply->HasExtension(GetAccountDiskUsageReply::reply)) {
LOGIN_LOG(ERROR) << "GetAccountDiskUsage failed with no "
"GetAccountDiskUsageReply extension in reply.";
return -1;
}
return reply->GetExtension(GetAccountDiskUsageReply::reply).size();
}
const std::string& MountExReplyToMountHash(const BaseReply& reply) {
return reply.GetExtension(MountReply::reply).sanitized_username();
}
AuthorizationRequest CreateAuthorizationRequest(const std::string& label,
const std::string& secret) {
cryptohome::AuthorizationRequest auth_request;
Key* key = auth_request.mutable_key();
if (!label.empty())
key->mutable_data()->set_label(label);
key->set_secret(secret);
return auth_request;
}
// TODO(crbug.com/797848): Finish testing this method.
void KeyDefinitionToKey(const KeyDefinition& key_def, Key* key) {
KeyData* data = key->mutable_data();
data->set_label(key_def.label);
switch (key_def.type) {
case KeyDefinition::TYPE_PASSWORD:
data->set_type(KeyData::KEY_TYPE_PASSWORD);
key->set_secret(key_def.secret);
break;
case KeyDefinition::TYPE_CHALLENGE_RESPONSE:
data->set_type(KeyData::KEY_TYPE_CHALLENGE_RESPONSE);
for (const auto& challenge_response_key :
key_def.challenge_response_keys) {
ChallengeResponseKeyToPublicKeyInfo(challenge_response_key,
data->add_challenge_response_key());
}
break;
}
if (key_def.revision > 0)
data->set_revision(key_def.revision);
if (key_def.privileges != 0) {
KeyDefPrivilegesToKeyPrivileges(key_def.privileges,
data->mutable_privileges());
}
for (const auto& key_def_auth_data : key_def.authorization_data) {
KeyAuthorizationData* auth_data = data->add_authorization_data();
auth_data->set_type(GetKeyAuthDataType(key_def_auth_data.type));
for (const auto& key_def_secret : key_def_auth_data.secrets) {
KeyDefSecretToKeyAuthorizationSecret(key_def_secret,
auth_data->add_secrets());
}
}
for (const auto& provider_data : key_def.provider_data) {
KeyDefProviderDataToKeyProviderDataEntry(
provider_data, data->mutable_provider_data()->add_entry());
}
}
// TODO(crbug.com/797848): Finish testing this method.
MountError CryptohomeErrorToMountError(CryptohomeErrorCode code) {
switch (code) {
case CRYPTOHOME_ERROR_NOT_SET:
return MOUNT_ERROR_NONE;
case CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND:
return MOUNT_ERROR_USER_DOES_NOT_EXIST;
case CRYPTOHOME_ERROR_NOT_IMPLEMENTED:
case CRYPTOHOME_ERROR_MOUNT_FATAL:
case CRYPTOHOME_ERROR_KEY_QUOTA_EXCEEDED:
case CRYPTOHOME_ERROR_BACKING_STORE_FAILURE:
return MOUNT_ERROR_FATAL;
case CRYPTOHOME_ERROR_AUTHORIZATION_KEY_NOT_FOUND:
case CRYPTOHOME_ERROR_KEY_NOT_FOUND:
case CRYPTOHOME_ERROR_MIGRATE_KEY_FAILED:
case CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED:
return MOUNT_ERROR_KEY_FAILURE;
case CRYPTOHOME_ERROR_TPM_COMM_ERROR:
return MOUNT_ERROR_TPM_COMM_ERROR;
case CRYPTOHOME_ERROR_TPM_DEFEND_LOCK:
return MOUNT_ERROR_TPM_DEFEND_LOCK;
case CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY:
return MOUNT_ERROR_MOUNT_POINT_BUSY;
case CRYPTOHOME_ERROR_TPM_NEEDS_REBOOT:
return MOUNT_ERROR_TPM_NEEDS_REBOOT;
case CRYPTOHOME_ERROR_AUTHORIZATION_KEY_DENIED:
case CRYPTOHOME_ERROR_KEY_LABEL_EXISTS:
case CRYPTOHOME_ERROR_UPDATE_SIGNATURE_INVALID:
return MOUNT_ERROR_KEY_FAILURE;
case CRYPTOHOME_ERROR_MOUNT_OLD_ENCRYPTION:
return MOUNT_ERROR_OLD_ENCRYPTION;
case CRYPTOHOME_ERROR_MOUNT_PREVIOUS_MIGRATION_INCOMPLETE:
return MOUNT_ERROR_PREVIOUS_MIGRATION_INCOMPLETE;
case CRYPTOHOME_ERROR_REMOVE_FAILED:
return MOUNT_ERROR_REMOVE_FAILED;
// TODO(crbug.com/797563): Split the error space and/or handle everything.
case CRYPTOHOME_ERROR_LOCKBOX_SIGNATURE_INVALID:
case CRYPTOHOME_ERROR_LOCKBOX_CANNOT_SIGN:
case CRYPTOHOME_ERROR_BOOT_ATTRIBUTE_NOT_FOUND:
case CRYPTOHOME_ERROR_BOOT_ATTRIBUTES_CANNOT_SIGN:
case CRYPTOHOME_ERROR_TPM_EK_NOT_AVAILABLE:
case CRYPTOHOME_ERROR_ATTESTATION_NOT_READY:
case CRYPTOHOME_ERROR_CANNOT_CONNECT_TO_CA:
case CRYPTOHOME_ERROR_CA_REFUSED_ENROLLMENT:
case CRYPTOHOME_ERROR_CA_REFUSED_CERTIFICATE:
case CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR:
case CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_INVALID:
case CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_CANNOT_STORE:
case CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_CANNOT_REMOVE:
NOTREACHED();
return MOUNT_ERROR_FATAL;
}
}
void KeyAuthorizationDataToAuthorizationData(
const KeyAuthorizationData& authorization_data_proto,
KeyDefinition::AuthorizationData* authorization_data) {
switch (authorization_data_proto.type()) {
case KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_HMACSHA256:
authorization_data->type =
KeyDefinition::AuthorizationData::TYPE_HMACSHA256;
break;
case KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_AES256CBC_HMACSHA256:
authorization_data->type =
KeyDefinition::AuthorizationData::TYPE_AES256CBC_HMACSHA256;
break;
}
for (const auto& secret : authorization_data_proto.secrets()) {
authorization_data->secrets.push_back(
KeyDefinition::AuthorizationData::Secret(
secret.usage().encrypt(), secret.usage().sign(),
secret.symmetric_key(), secret.public_key(), secret.wrapped()));
}
}
} // namespace cryptohome