| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/dbus/cryptohome_key_delegate_service_provider.h" |
| |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/containers/contains.h" |
| #include "base/containers/span.h" |
| #include "base/functional/bind.h" |
| #include "base/logging.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/certificate_provider/certificate_provider_service.h" |
| #include "chrome/browser/certificate_provider/certificate_provider_service_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h" |
| #include "chromeos/ash/components/dbus/cryptohome/key.pb.h" |
| #include "chromeos/ash/components/dbus/cryptohome/rpc.pb.h" |
| #include "components/account_id/account_id.h" |
| #include "components/user_manager/common_types.h" |
| #include "components/user_manager/known_user.h" |
| #include "dbus/message.h" |
| #include "extensions/common/extension_id.h" |
| #include "net/base/net_errors.h" |
| #include "third_party/boringssl/src/include/openssl/ssl.h" |
| #include "third_party/cros_system_api/dbus/cryptohome/dbus-constants.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Converts the cryptohome challenge algorithm enum into the TLS 1.3 |
| // SignatureScheme. |
| bool ChallengeSignatureAlgorithmToSslAlgorithm( |
| cryptohome::ChallengeSignatureAlgorithm challenge_algorithm, |
| uint16_t* ssl_algorithm) { |
| switch (challenge_algorithm) { |
| case cryptohome::CHALLENGE_RSASSA_PKCS1_V1_5_SHA1: |
| *ssl_algorithm = SSL_SIGN_RSA_PKCS1_SHA1; |
| return true; |
| case cryptohome::CHALLENGE_RSASSA_PKCS1_V1_5_SHA256: |
| *ssl_algorithm = SSL_SIGN_RSA_PKCS1_SHA256; |
| return true; |
| case cryptohome::CHALLENGE_RSASSA_PKCS1_V1_5_SHA384: |
| *ssl_algorithm = SSL_SIGN_RSA_PKCS1_SHA384; |
| return true; |
| case cryptohome::CHALLENGE_RSASSA_PKCS1_V1_5_SHA512: |
| *ssl_algorithm = SSL_SIGN_RSA_PKCS1_SHA512; |
| return true; |
| default: |
| LOG(ERROR) << "Unknown cryptohome key challenge algorithm: " |
| << challenge_algorithm; |
| return false; |
| } |
| } |
| |
| // Completes the "ChallengeKey" D-Bus call of the |CHALLENGE_TYPE_SIGNATURE| |
| // type with the given signature, or with an error if the signature wasn't |
| // successfully generated. |
| void CompleteSignatureKeyChallenge( |
| dbus::MethodCall* method_call, |
| dbus::ExportedObject::ResponseSender response_sender, |
| net::Error error, |
| const std::vector<uint8_t>& signature) { |
| if (error != net::OK || signature.empty()) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_FAILED, |
| "Failed to generate the signature")); |
| return; |
| } |
| |
| cryptohome::KeyChallengeResponse challenge_response; |
| challenge_response.mutable_signature_response_data()->set_signature( |
| signature.data(), signature.size()); |
| |
| std::unique_ptr<dbus::Response> response = |
| dbus::Response::FromMethodCall(method_call); |
| dbus::MessageWriter writer(response.get()); |
| writer.AppendProtoAsArrayOfBytes(challenge_response); |
| |
| std::move(response_sender).Run(std::move(response)); |
| } |
| |
| // Handles the "ChallengeKey" D-Bus call for the request of the |
| // |CHALLENGE_TYPE_SIGNATURE| type. |
| void HandleSignatureKeyChallenge( |
| dbus::MethodCall* method_call, |
| const cryptohome::SignatureKeyChallengeRequestData& challenge_request_data, |
| const AccountId& account_id, |
| dbus::ExportedObject::ResponseSender response_sender) { |
| if (challenge_request_data.data_to_sign().empty()) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_INVALID_ARGS, "Missing data to sign")); |
| return; |
| } |
| if (challenge_request_data.public_key_spki_der().empty()) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_INVALID_ARGS, "Missing public key")); |
| return; |
| } |
| if (!challenge_request_data.has_signature_algorithm()) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_INVALID_ARGS, |
| "Missing signature algorithm")); |
| return; |
| } |
| |
| uint16_t ssl_algorithm = 0; |
| if (!ChallengeSignatureAlgorithmToSslAlgorithm( |
| challenge_request_data.signature_algorithm(), &ssl_algorithm)) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_FAILED, "Unknown signature algorithm")); |
| return; |
| } |
| |
| // Handle the challenge request by delivering it to one of the sign-in |
| // certificateProvider subscribers (e.g., smart card middleware extensions). |
| // The sign-in profile is used since it's where the needed extensions are |
| // installed (e.g., for the smart card based login they are force-installed |
| // via the DeviceLoginScreenExtensions admin policy). |
| Profile* signin_profile = ProfileHelper::GetSigninProfile(); |
| chromeos::CertificateProviderService* certificate_provider_service = |
| chromeos::CertificateProviderServiceFactory::GetForBrowserContext( |
| signin_profile); |
| if (!certificate_provider_service) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_FAILED, |
| "Missing certificate provider service")); |
| return; |
| } |
| |
| std::vector<uint16_t> supported_ssl_algorithms; |
| extensions::ExtensionId extension_id_ignored; |
| if (!certificate_provider_service->LookUpSpki( |
| challenge_request_data.public_key_spki_der(), |
| &supported_ssl_algorithms, &extension_id_ignored)) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED, |
| "Key is unavailable")); |
| return; |
| } |
| if (!base::Contains(supported_ssl_algorithms, ssl_algorithm)) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED, |
| "Unsupported algorithm")); |
| return; |
| } |
| |
| certificate_provider_service->RequestSignatureBySpki( |
| challenge_request_data.public_key_spki_der(), ssl_algorithm, |
| base::as_byte_span(challenge_request_data.data_to_sign()), account_id, |
| base::BindOnce(&CompleteSignatureKeyChallenge, |
| base::Unretained(method_call), |
| std::move(response_sender))); |
| } |
| |
| } // namespace |
| |
| CryptohomeKeyDelegateServiceProvider::CryptohomeKeyDelegateServiceProvider() = |
| default; |
| |
| CryptohomeKeyDelegateServiceProvider::~CryptohomeKeyDelegateServiceProvider() = |
| default; |
| |
| void CryptohomeKeyDelegateServiceProvider::Start( |
| scoped_refptr<dbus::ExportedObject> exported_object) { |
| exported_object->ExportMethod( |
| cryptohome::kCryptohomeKeyDelegateInterface, |
| cryptohome::kCryptohomeKeyDelegateChallengeKey, |
| base::BindRepeating( |
| &CryptohomeKeyDelegateServiceProvider::HandleChallengeKey, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce([](const std::string& interface_name, |
| const std::string& method_name, bool success) { |
| LOG_IF(ERROR, !success) |
| << "Failed to export " << interface_name << "." << method_name; |
| })); |
| } |
| |
| void CryptohomeKeyDelegateServiceProvider::HandleChallengeKey( |
| dbus::MethodCall* method_call, |
| dbus::ExportedObject::ResponseSender response_sender) { |
| dbus::MessageReader reader(method_call); |
| |
| cryptohome::AccountIdentifier account_identifier; |
| if (!reader.PopArrayOfBytesAsProto(&account_identifier)) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_INVALID_ARGS, |
| "Unable to parse AccountIdentifier from request")); |
| return; |
| } |
| user_manager::KnownUser known_user(g_browser_process->local_state()); |
| user_manager::CryptohomeId cryptohome_id(account_identifier.account_id()); |
| const AccountId account_id = |
| known_user.GetAccountIdByCryptohomeId(cryptohome_id); |
| if (!account_id.is_valid() || |
| account_id.GetAccountType() == AccountType::UNKNOWN) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED, |
| "Unknown account")); |
| return; |
| } |
| |
| cryptohome::KeyChallengeRequest request; |
| if (!reader.PopArrayOfBytesAsProto(&request)) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_INVALID_ARGS, |
| "Unable to parse KeyChallengeRequest from request")); |
| return; |
| } |
| |
| if (!request.has_challenge_type()) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_INVALID_ARGS, "Missing challenge type")); |
| return; |
| } |
| |
| if (request.challenge_type() == |
| cryptohome::KeyChallengeRequest::CHALLENGE_TYPE_SIGNATURE) { |
| if (!request.has_signature_request_data()) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_INVALID_ARGS, |
| "Missing signature request data")); |
| return; |
| } |
| HandleSignatureKeyChallenge(method_call, request.signature_request_data(), |
| account_id, std::move(response_sender)); |
| return; |
| } |
| |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED, |
| "Unknown challenge type")); |
| } |
| |
| } // namespace ash |