Commit 3f958afb authored by Erik Chen's avatar Erik Chen Committed by Chromium LUCI CQ

lacros: Implement platformKeysInternal.getPublicKey.

This CL refactors the ash implementation of
platformKeysInternal.getPublicKey into chromeos::platform_keys. This has
no intended behavior change for ash. This CL hooks up the crosapi
KeystoreService::GetPublicKey by forwarding to the same
chromeos::platform_keys implementation.

Change-Id: I19daa05727353c65ca072e365297c0e51f6d8b85
Bug: 1164523
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2626044Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Reviewed-by: default avatarMichael Ershov <miersh@google.com>
Commit-Queue: Erik Chen <erikchen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#843373}
parent 0fef84b8
......@@ -4518,6 +4518,7 @@ static_library("browser") {
"//chromeos/components/cdm_factory_daemon:cdm_factory_daemon_browser",
"//chromeos/components/quick_answers",
"//chromeos/components/sync_wifi",
"//chromeos/crosapi/cpp",
"//chromeos/crosapi/mojom",
"//chromeos/dbus",
"//chromeos/geolocation",
......@@ -4637,6 +4638,7 @@ static_library("browser") {
"platform_util_lacros.cc",
]
deps += [
"//chromeos/crosapi/cpp",
"//chromeos/crosapi/mojom",
"//chromeos/lacros",
"//chromeos/ui/frame",
......
......@@ -13,6 +13,7 @@
#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chromeos/crosapi/cpp/keystore_service_util.h"
#include "content/public/browser/browser_thread.h"
#include "net/cert/x509_certificate.h"
#include "third_party/boringssl/src/include/openssl/pool.h"
......@@ -21,6 +22,7 @@ namespace crosapi {
using PlatformKeysService = chromeos::platform_keys::PlatformKeysService;
using TokenId = chromeos::platform_keys::TokenId;
using SigningAlgorithmName = crosapi::mojom::KeystoreSigningAlgorithmName;
namespace {
......@@ -28,6 +30,8 @@ const char kEnterprisePlatformErrorInvalidX509Cert[] =
"Certificate is not a valid X.509 certificate.";
const char kUnsupportedKeystoreType[] = "Keystore type is not supported.";
const char kUnsupportedAlgorithmType[] = "Algorithm type is not supported.";
const char kErrorAlgorithmNotPermittedByCertificate[] =
"The requested Algorithm is not permitted by the certificate.";
// Converts a binary blob to a certificate.
scoped_refptr<net::X509Certificate> ParseCertificate(
......@@ -60,6 +64,18 @@ base::Optional<TokenId> KeystoreToToken(mojom::KeystoreType type) {
}
}
base::Optional<std::string> StringFromSigningAlgorithmName(
SigningAlgorithmName name) {
switch (name) {
case SigningAlgorithmName::kRsassaPkcs115:
return crosapi::keystore_service_util::kWebCryptoRsassaPkcs1v15;
case SigningAlgorithmName::kEcdsa:
return crosapi::keystore_service_util::kWebCryptoEcdsa;
case SigningAlgorithmName::kUnknown:
return base::nullopt;
}
}
} // namespace
KeystoreServiceAsh::KeystoreServiceAsh(
......@@ -216,6 +232,45 @@ void KeystoreServiceAsh::RemoveCertificate(
std::move(callback)));
}
void KeystoreServiceAsh::GetPublicKey(
const std::vector<uint8_t>& certificate,
mojom::KeystoreSigningAlgorithmName algorithm_name,
GetPublicKeyCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
base::Optional<std::string> name =
StringFromSigningAlgorithmName(algorithm_name);
if (!name) {
std::move(callback).Run(mojom::GetPublicKeyResult::NewErrorMessage(
kErrorAlgorithmNotPermittedByCertificate));
return;
}
chromeos::platform_keys::GetPublicKeyAndAlgorithmOutput output =
chromeos::platform_keys::GetPublicKeyAndAlgorithm(certificate,
name.value());
mojom::GetPublicKeyResultPtr result_ptr = mojom::GetPublicKeyResult::New();
if (output.error.empty()) {
base::Optional<crosapi::mojom::KeystoreSigningAlgorithmPtr>
signing_algorithm =
crosapi::keystore_service_util::SigningAlgorithmFromDictionary(
output.algorithm);
if (signing_algorithm) {
mojom::GetPublicKeySuccessResultPtr success_result_ptr =
mojom::GetPublicKeySuccessResult::New();
success_result_ptr->public_key = std::move(output.public_key);
success_result_ptr->algorithm_properties =
std::move(signing_algorithm.value());
result_ptr->set_success_result(std::move(success_result_ptr));
} else {
result_ptr->set_error_message(kUnsupportedAlgorithmType);
}
} else {
result_ptr->set_error_message(output.error);
}
std::move(callback).Run(std::move(result_ptr));
}
// static
void KeystoreServiceAsh::OnGetTokens(
GetKeyStoresCallback callback,
......
......@@ -53,6 +53,9 @@ class KeystoreServiceAsh : public mojom::KeystoreService {
void RemoveCertificate(mojom::KeystoreType keystore,
const std::vector<uint8_t>& certificate,
RemoveCertificateCallback callback) override;
void GetPublicKey(const std::vector<uint8_t>& certificate,
mojom::KeystoreSigningAlgorithmName algorithm_name,
GetPublicKeyCallback callback) override;
private:
static void OnGetTokens(
......
......@@ -9,14 +9,35 @@
#include <string>
#include "base/callback.h"
#include "base/check_op.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chromeos/crosapi/cpp/keystore_service_util.h"
#include "net/base/hash_value.h"
#include "net/base/net_errors.h"
#include "net/cert/x509_certificate.h"
namespace {
const char kErrorAlgorithmNotPermittedByCertificate[] =
"The requested Algorithm is not permitted by the certificate.";
const char kErrorInvalidX509Cert[] =
"Certificate is not a valid X.509 certificate.";
using crosapi::keystore_service_util::kWebCryptoEcdsa;
using crosapi::keystore_service_util::kWebCryptoNamedCurveP256;
using crosapi::keystore_service_util::kWebCryptoRsassaPkcs1v15;
void BuildWebCryptoEcdsaAlgorithmDictionary(
const chromeos::platform_keys::PublicKeyInfo& key_info,
base::DictionaryValue* algorithm) {
CHECK_EQ(net::X509Certificate::kPublicKeyTypeECDSA, key_info.key_type);
algorithm->SetStringKey("name", kWebCryptoEcdsa);
// Only P-256 named curve is supported.
algorithm->SetStringKey("namedCurve", kWebCryptoNamedCurveP256);
}
void IntersectOnWorkerThread(const net::CertificateList& certs1,
const net::CertificateList& certs2,
net::CertificateList* intersection) {
......@@ -102,6 +123,96 @@ void IntersectCertificates(
base::BindOnce(callback, base::Passed(&intersection)));
}
GetPublicKeyAndAlgorithmOutput::GetPublicKeyAndAlgorithmOutput() = default;
GetPublicKeyAndAlgorithmOutput::GetPublicKeyAndAlgorithmOutput(
GetPublicKeyAndAlgorithmOutput&&) = default;
GetPublicKeyAndAlgorithmOutput::~GetPublicKeyAndAlgorithmOutput() = default;
GetPublicKeyAndAlgorithmOutput GetPublicKeyAndAlgorithm(
const std::vector<uint8_t>& possibly_invalid_cert_der,
const std::string& algorithm_name) {
GetPublicKeyAndAlgorithmOutput output;
if (possibly_invalid_cert_der.empty()) {
output.error = kErrorInvalidX509Cert;
return output;
}
// Allow UTF-8 inside PrintableStrings in client certificates. See
// crbug.com/770323 and crbug.com/788655.
net::X509Certificate::UnsafeCreateOptions options;
options.printable_string_is_utf8 = true;
scoped_refptr<net::X509Certificate> cert_x509 =
net::X509Certificate::CreateFromBytesUnsafeOptions(
reinterpret_cast<const char*>(possibly_invalid_cert_der.data()),
possibly_invalid_cert_der.size(), options);
if (!cert_x509) {
output.error = kErrorInvalidX509Cert;
return output;
}
PublicKeyInfo key_info;
key_info.public_key_spki_der =
chromeos::platform_keys::GetSubjectPublicKeyInfo(cert_x509);
if (!chromeos::platform_keys::GetPublicKey(cert_x509, &key_info.key_type,
&key_info.key_size_bits) ||
(key_info.key_type != net::X509Certificate::kPublicKeyTypeRSA &&
key_info.key_type != net::X509Certificate::kPublicKeyTypeECDSA)) {
output.error = StatusToString(Status::kErrorAlgorithmNotSupported);
return output;
}
// Currently, the only supported combinations are:
// 1- A certificate declaring rsaEncryption in the SubjectPublicKeyInfo used
// with the RSASSA-PKCS1-v1.5 algorithm.
// 2- A certificate declaring id-ecPublicKey in the SubjectPublicKeyInfo used
// with the ECDSA algorithm.
if (algorithm_name == kWebCryptoRsassaPkcs1v15) {
if (key_info.key_type != net::X509Certificate::kPublicKeyTypeRSA) {
output.error = kErrorAlgorithmNotPermittedByCertificate;
return output;
}
BuildWebCryptoRSAAlgorithmDictionary(key_info, &output.algorithm);
output.public_key =
std::vector<uint8_t>(key_info.public_key_spki_der.begin(),
key_info.public_key_spki_der.end());
return output;
}
if (algorithm_name == kWebCryptoEcdsa) {
if (key_info.key_type != net::X509Certificate::kPublicKeyTypeECDSA) {
output.error = kErrorAlgorithmNotPermittedByCertificate;
return output;
}
BuildWebCryptoEcdsaAlgorithmDictionary(key_info, &output.algorithm);
output.public_key =
std::vector<uint8_t>(key_info.public_key_spki_der.begin(),
key_info.public_key_spki_der.end());
return output;
}
output.error = kErrorAlgorithmNotPermittedByCertificate;
return output;
}
PublicKeyInfo::PublicKeyInfo() = default;
PublicKeyInfo::~PublicKeyInfo() = default;
void BuildWebCryptoRSAAlgorithmDictionary(const PublicKeyInfo& key_info,
base::DictionaryValue* algorithm) {
CHECK_EQ(net::X509Certificate::kPublicKeyTypeRSA, key_info.key_type);
algorithm->SetStringKey("name", kWebCryptoRsassaPkcs1v15);
algorithm->SetKey("modulusLength",
base::Value(static_cast<int>(key_info.key_size_bits)));
// Equals 65537.
static constexpr uint8_t kDefaultPublicExponent[] = {0x01, 0x00, 0x01};
algorithm->SetKey("publicExponent",
base::Value(base::make_span(kDefaultPublicExponent)));
}
ClientCertificateRequest::ClientCertificateRequest() = default;
ClientCertificateRequest::ClientCertificateRequest(
......
......@@ -5,11 +5,14 @@
#ifndef CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_PLATFORM_KEYS_H_
#define CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_PLATFORM_KEYS_H_
#include <stdint.h>
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/values.h"
#include "net/cert/x509_certificate.h"
namespace chromeos {
......@@ -76,6 +79,45 @@ void IntersectCertificates(
const base::Callback<void(std::unique_ptr<net::CertificateList>)>&
callback);
// The output for GetPublicKeyAndAlgorithm.
struct GetPublicKeyAndAlgorithmOutput {
GetPublicKeyAndAlgorithmOutput();
GetPublicKeyAndAlgorithmOutput(GetPublicKeyAndAlgorithmOutput&&);
~GetPublicKeyAndAlgorithmOutput();
std::string error; // Only set on error.
std::vector<uint8_t> public_key; // Only set on success.
base::DictionaryValue algorithm; // Only set on success.
};
// This is a convenient wrapper around GetPublicKey which also builds a
// WebCrypto algorithm dictionary and performs error checking.
GetPublicKeyAndAlgorithmOutput GetPublicKeyAndAlgorithm(
const std::vector<uint8_t>& possibly_invalid_cert_der,
const std::string& algorithm_name);
struct PublicKeyInfo {
PublicKeyInfo();
~PublicKeyInfo();
// The X.509 Subject Public Key Info of the key in DER encoding.
std::string public_key_spki_der;
// The type of the key.
net::X509Certificate::PublicKeyType key_type =
net::X509Certificate::kPublicKeyTypeUnknown;
// The size of the key in bits.
size_t key_size_bits = 0;
};
// Builds a partial WebCrypto Algorithm object from the parameters available in
// |key_info|, which must be the info of an RSA key. This doesn't include
// sign/hash parameters and thus isn't complete. platform_keys::GetPublicKey()
// enforced the public exponent 65537.
void BuildWebCryptoRSAAlgorithmDictionary(const PublicKeyInfo& key_info,
base::DictionaryValue* algorithm);
// Obtains information about the public key in |certificate|.
// If |certificate| contains an RSA key, sets |key_size_bits| to the modulus
// length, and |key_type| to type RSA and returns true.
......
......@@ -944,7 +944,10 @@ static_library("extensions") {
"api/enterprise_platform_keys/enterprise_platform_keys_api.h",
"api/platform_keys/platform_keys_api.h",
]
deps += [ "//chromeos/crosapi/mojom" ]
deps += [
"//chromeos/crosapi/cpp",
"//chromeos/crosapi/mojom",
]
if (is_chromeos_lacros) {
sources += [
"api/enterprise_device_attributes/enterprise_device_attributes_api_lacros.cc",
......
......@@ -20,12 +20,15 @@
#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
#include "chrome/browser/extensions/api/platform_keys/verify_trust_api.h"
#include "chrome/common/extensions/api/platform_keys_internal.h"
#include "chromeos/crosapi/cpp/keystore_service_util.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/net_errors.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
using PublicKeyInfo = chromeos::platform_keys::PublicKeyInfo;
namespace extensions {
namespace api_pk = api::platform_keys;
......@@ -33,53 +36,12 @@ namespace api_pki = api::platform_keys_internal;
namespace {
const char kErrorAlgorithmNotPermittedByCertificate[] =
"The requested Algorithm is not permitted by the certificate.";
const char kErrorInteractiveCallFromBackground[] =
"Interactive calls must happen in the context of a browser tab or a "
"window.";
const char kWebCryptoEcdsa[] = "ECDSA";
const char kWebCryptoNamedCurveP256[] = "P-256";
const char kWebCryptoRSASSA_PKCS1_v1_5[] = "RSASSA-PKCS1-v1_5";
struct PublicKeyInfo {
// The X.509 Subject Public Key Info of the key in DER encoding.
std::string public_key_spki_der;
// The type of the key.
net::X509Certificate::PublicKeyType key_type =
net::X509Certificate::kPublicKeyTypeUnknown;
// The size of the key in bits.
size_t key_size_bits = 0;
};
// Builds a partial WebCrypto Algorithm object from the parameters available in
// |key_info|, which must the info of an RSA key. This doesn't include sign/hash
// parameters and thus isn't complete.
// platform_keys::GetPublicKey() enforced the public exponent 65537.
void BuildWebCryptoRSAAlgorithmDictionary(const PublicKeyInfo& key_info,
base::DictionaryValue* algorithm) {
CHECK_EQ(net::X509Certificate::kPublicKeyTypeRSA, key_info.key_type);
algorithm->SetStringKey("name", kWebCryptoRSASSA_PKCS1_v1_5);
algorithm->SetKey("modulusLength",
base::Value(static_cast<int>(key_info.key_size_bits)));
// Equals 65537.
static constexpr uint8_t kDefaultPublicExponent[] = {0x01, 0x00, 0x01};
algorithm->SetKey("publicExponent",
base::Value(base::make_span(kDefaultPublicExponent)));
}
void BuildWebCryptoEcdsaAlgorithmDictionary(const PublicKeyInfo& key_info,
base::DictionaryValue* algorithm) {
CHECK_EQ(net::X509Certificate::kPublicKeyTypeECDSA, key_info.key_type);
algorithm->SetStringKey("name", kWebCryptoEcdsa);
// Only P-256 named curve is supported.
algorithm->SetStringKey("namedCurve", kWebCryptoNamedCurveP256);
}
using crosapi::keystore_service_util::kWebCryptoEcdsa;
using crosapi::keystore_service_util::kWebCryptoRsassaPkcs1v15;
const struct NameValuePair {
const char* const name;
......@@ -133,67 +95,17 @@ PlatformKeysInternalGetPublicKeyFunction::Run() {
api_pki::GetPublicKey::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
const std::vector<uint8_t>& cert_der = params->certificate;
if (cert_der.empty())
return RespondNow(Error(platform_keys::kErrorInvalidX509Cert));
// Allow UTF-8 inside PrintableStrings in client certificates. See
// crbug.com/770323 and crbug.com/788655.
net::X509Certificate::UnsafeCreateOptions options;
options.printable_string_is_utf8 = true;
scoped_refptr<net::X509Certificate> cert_x509 =
net::X509Certificate::CreateFromBytesUnsafeOptions(
reinterpret_cast<const char*>(cert_der.data()), cert_der.size(),
options);
if (!cert_x509)
return RespondNow(Error(platform_keys::kErrorInvalidX509Cert));
PublicKeyInfo key_info;
key_info.public_key_spki_der =
chromeos::platform_keys::GetSubjectPublicKeyInfo(cert_x509);
if (!chromeos::platform_keys::GetPublicKey(cert_x509, &key_info.key_type,
&key_info.key_size_bits) ||
(key_info.key_type != net::X509Certificate::kPublicKeyTypeRSA &&
key_info.key_type != net::X509Certificate::kPublicKeyTypeECDSA)) {
return RespondNow(Error(StatusToString(
chromeos::platform_keys::Status::kErrorAlgorithmNotSupported)));
}
// Currently, the only supported combinations are:
// 1- A certificate declaring rsaEncryption in the SubjectPublicKeyInfo used
// with the RSASSA-PKCS1-v1.5 algorithm.
// 2- A certificate declaring id-ecPublicKey in the SubjectPublicKeyInfo used
// with the ECDSA algorithm.
if (params->algorithm_name == kWebCryptoRSASSA_PKCS1_v1_5) {
if (key_info.key_type != net::X509Certificate::kPublicKeyTypeRSA) {
return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate));
}
api_pki::GetPublicKey::Results::Algorithm algorithm;
BuildWebCryptoRSAAlgorithmDictionary(key_info,
&algorithm.additional_properties);
return RespondNow(ArgumentList(api_pki::GetPublicKey::Results::Create(
std::vector<uint8_t>(key_info.public_key_spki_der.begin(),
key_info.public_key_spki_der.end()),
algorithm)));
}
if (params->algorithm_name == kWebCryptoEcdsa) {
if (key_info.key_type != net::X509Certificate::kPublicKeyTypeECDSA) {
return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate));
chromeos::platform_keys::GetPublicKeyAndAlgorithmOutput output =
chromeos::platform_keys::GetPublicKeyAndAlgorithm(params->certificate,
params->algorithm_name);
if (!output.error.empty()) {
return RespondNow(Error(output.error));
}
api_pki::GetPublicKey::Results::Algorithm algorithm;
BuildWebCryptoEcdsaAlgorithmDictionary(key_info,
&algorithm.additional_properties);
algorithm.additional_properties = std::move(output.algorithm);
return RespondNow(ArgumentList(api_pki::GetPublicKey::Results::Create(
std::vector<uint8_t>(key_info.public_key_spki_der.begin(),
key_info.public_key_spki_der.end()),
algorithm)));
}
return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate));
std::move(output.public_key), std::move(algorithm))));
}
PlatformKeysInternalGetPublicKeyBySpkiFunction::
......@@ -223,14 +135,14 @@ PlatformKeysInternalGetPublicKeyBySpkiFunction::Run() {
// Currently, the only supported combination is:
// A SPKI declaring rsaEncryption used with the RSASSA-PKCS1-v1.5 algorithm.
if (params->algorithm_name != kWebCryptoRSASSA_PKCS1_v1_5) {
if (params->algorithm_name != kWebCryptoRsassaPkcs1v15) {
return RespondNow(Error(StatusToString(
chromeos::platform_keys::Status::kErrorAlgorithmNotSupported)));
}
api_pki::GetPublicKeyBySpki::Results::Algorithm algorithm;
BuildWebCryptoRSAAlgorithmDictionary(key_info,
&algorithm.additional_properties);
chromeos::platform_keys::BuildWebCryptoRSAAlgorithmDictionary(
key_info, &algorithm.additional_properties);
return RespondNow(ArgumentList(api_pki::GetPublicKeyBySpki::Results::Create(
public_key_spki_der, algorithm)));
......@@ -347,7 +259,7 @@ void PlatformKeysInternalSelectClientCertificatesFunction::
result_match.certificate.assign(der_encoded_cert.begin(),
der_encoded_cert.end());
BuildWebCryptoRSAAlgorithmDictionary(
chromeos::platform_keys::BuildWebCryptoRSAAlgorithmDictionary(
key_info, &result_match.key_algorithm.additional_properties);
result_matches.push_back(std::move(result_match));
}
......@@ -380,7 +292,7 @@ ExtensionFunction::ResponseAction PlatformKeysInternalSignFunction::Run() {
if (params->hash_algorithm_name == "none") {
// Signing without digesting is only supported for RSASSA-PKCS1-v1_5.
if (params->algorithm_name != kWebCryptoRSASSA_PKCS1_v1_5)
if (params->algorithm_name != kWebCryptoRsassaPkcs1v15)
return RespondNow(Error(StatusToString(
chromeos::platform_keys::Status::kErrorAlgorithmNotSupported)));
......@@ -406,7 +318,7 @@ ExtensionFunction::ResponseAction PlatformKeysInternalSignFunction::Run() {
}
chromeos::platform_keys::KeyType key_type;
if (params->algorithm_name == kWebCryptoRSASSA_PKCS1_v1_5) {
if (params->algorithm_name == kWebCryptoRsassaPkcs1v15) {
key_type = chromeos::platform_keys::KeyType::kRsassaPkcs1V15;
} else if (params->algorithm_name == kWebCryptoEcdsa) {
key_type = chromeos::platform_keys::KeyType::kEcdsa;
......
......@@ -4,14 +4,44 @@
#include "chrome/browser/extensions/api/platform_keys/platform_keys_api_lacros.h"
#include "base/optional.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/platform_keys_internal.h"
#include "chromeos/crosapi/cpp/keystore_service_util.h"
#include "chromeos/lacros/lacros_chrome_service_impl.h"
namespace extensions {
namespace api_pki = api::platform_keys_internal;
using SigningAlgorithmName = crosapi::mojom::KeystoreSigningAlgorithmName;
namespace {
const char kUnsupportedByAsh[] = "Not implemented.";
const char kUnsupportedProfile[] = "Not available.";
const char kErrorAlgorithmNotPermittedByCertificate[] =
"The requested Algorithm is not permitted by the certificate.";
using crosapi::keystore_service_util::kWebCryptoEcdsa;
using crosapi::keystore_service_util::kWebCryptoRsassaPkcs1v15;
base::Optional<SigningAlgorithmName> SigningAlgorithmNameFromString(
const std::string& input) {
if (input == kWebCryptoRsassaPkcs1v15)
return SigningAlgorithmName::kRsassaPkcs115;
if (input == kWebCryptoEcdsa)
return SigningAlgorithmName::kEcdsa;
return base::nullopt;
}
} // namespace
PlatformKeysInternalSelectClientCertificatesFunction::
~PlatformKeysInternalSelectClientCertificatesFunction() {}
ExtensionFunction::ResponseAction
PlatformKeysInternalSelectClientCertificatesFunction::Run() {
return RespondNow(Error("Not implemented."));
return RespondNow(Error(kUnsupportedByAsh));
}
PlatformKeysInternalGetPublicKeyFunction::
......@@ -19,7 +49,58 @@ PlatformKeysInternalGetPublicKeyFunction::
ExtensionFunction::ResponseAction
PlatformKeysInternalGetPublicKeyFunction::Run() {
return RespondNow(Error("Not implemented."));
std::unique_ptr<api_pki::GetPublicKey::Params> params(
api_pki::GetPublicKey::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
if (chromeos::LacrosChromeServiceImpl::Get()->GetInterfaceVersion(
crosapi::mojom::KeystoreService::Uuid_) < 3) {
return RespondNow(Error(kUnsupportedByAsh));
}
// These APIs are used in security-sensitive contexts. We need to ensure that
// the user for ash is the same as the user for lacros. We do this by
// restricting the API to the default profile, which is guaranteed to be the
// same user.
if (!Profile::FromBrowserContext(browser_context())->IsMainProfile())
return RespondNow(Error(kUnsupportedProfile));
base::Optional<SigningAlgorithmName> algorithm_name =
SigningAlgorithmNameFromString(params->algorithm_name);
if (!algorithm_name) {
return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate));
}
auto cb = base::BindOnce(
&PlatformKeysInternalGetPublicKeyFunction::OnGetPublicKey, this);
chromeos::LacrosChromeServiceImpl::Get()
->keystore_service_remote()
->GetPublicKey(params->certificate, algorithm_name.value(),
std::move(cb));
return RespondLater();
}
void PlatformKeysInternalGetPublicKeyFunction::OnGetPublicKey(
ResultPtr result) {
using Result = crosapi::mojom::GetPublicKeyResult;
switch (result->which()) {
case Result::Tag::ERROR_MESSAGE:
Respond(Error(result->get_error_message()));
return;
case Result::Tag::SUCCESS_RESULT:
api_pki::GetPublicKey::Results::Algorithm algorithm;
base::Optional<base::DictionaryValue> dict =
crosapi::keystore_service_util::DictionaryFromSigningAlgorithm(
result->get_success_result()->algorithm_properties);
if (!dict) {
Respond(Error(kUnsupportedByAsh));
return;
}
algorithm.additional_properties = std::move(dict.value());
Respond(ArgumentList(api_pki::GetPublicKey::Results::Create(
result->get_success_result()->public_key, std::move(algorithm))));
return;
}
}
PlatformKeysInternalGetPublicKeyBySpkiFunction::
......
......@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_EXTENSIONS_API_PLATFORM_KEYS_PLATFORM_KEYS_API_LACROS_H_
#define CHROME_BROWSER_EXTENSIONS_API_PLATFORM_KEYS_PLATFORM_KEYS_API_LACROS_H_
#include "chromeos/crosapi/mojom/keystore_service.mojom.h"
#include "extensions/browser/extension_function.h"
namespace extensions {
......@@ -24,6 +25,9 @@ class PlatformKeysInternalGetPublicKeyFunction : public ExtensionFunction {
~PlatformKeysInternalGetPublicKeyFunction() override;
ResponseAction Run() override;
using ResultPtr = crosapi::mojom::GetPublicKeyResultPtr;
void OnGetPublicKey(ResultPtr result_ptr);
DECLARE_EXTENSION_FUNCTION("platformKeysInternal.getPublicKey",
PLATFORMKEYSINTERNAL_GETPUBLICKEY)
};
......
......@@ -155,6 +155,7 @@ test("chromeos_unittests") {
"//build:chromeos_buildflags",
"//chromeos/attestation:unit_tests",
"//chromeos/audio:unit_tests",
"//chromeos/crosapi/cpp:unit_tests",
"//chromeos/cryptohome:unit_tests",
"//chromeos/dbus:test_support",
"//chromeos/dbus:unit_tests",
......
......@@ -10,19 +10,42 @@ config("crosapi_implementation") {
component("cpp") {
output_name = "crosapi_public_cpp"
sources = [
"bitmap.cc",
"bitmap.h",
"bitmap_util.cc",
"bitmap_util.h",
"crosapi_constants.cc",
"crosapi_constants.h",
"keystore_service_util.cc",
"keystore_service_util.h",
"scoped_allow_sync_call.cc",
"scoped_allow_sync_call.h",
]
configs += [ ":crosapi_implementation" ]
deps = [
"//base",
"//chromeos/crosapi/mojom",
"//mojo/public/cpp/bindings",
"//skia",
]
}
# chromeos/crosapi/mojom depends on this component.
component("cpp_internal") {
output_name = "crosapi_private_cpp"
sources = [
"bitmap.cc",
"bitmap.h",
]
configs += [ ":crosapi_implementation" ]
deps = [ "//base" ]
}
source_set("unit_tests") {
testonly = true
deps = [
":cpp",
"//base/test:test_support",
"//chromeos/crosapi/mojom",
"//testing/gtest",
]
sources = [ "keystore_service_util_unittest.cc" ]
}
// Copyright 2021 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/crosapi/cpp/keystore_service_util.h"
#include "base/numerics/safe_math.h"
#include "base/optional.h"
#include "base/values.h"
#include "chromeos/crosapi/mojom/keystore_service.mojom.h"
namespace crosapi {
namespace keystore_service_util {
const char kWebCryptoEcdsa[] = "ECDSA";
const char kWebCryptoRsassaPkcs1v15[] = "RSASSA-PKCS1-v1_5";
const char kWebCryptoNamedCurveP256[] = "P-256";
// Converts a signing algorithm into a WebCrypto dictionary.
base::Optional<base::DictionaryValue> DictionaryFromSigningAlgorithm(
const crosapi::mojom::KeystoreSigningAlgorithmPtr& algorithm) {
base::DictionaryValue value;
switch (algorithm->which()) {
case crosapi::mojom::KeystoreSigningAlgorithm::Tag::kPkcs115:
value.SetStringKey("name", kWebCryptoRsassaPkcs1v15);
if (!base::IsValueInRangeForNumericType<int>(
algorithm->get_pkcs115()->modulus_length)) {
return base::nullopt;
}
value.SetKey("modulusLength",
base::Value(base::checked_cast<int>(
algorithm->get_pkcs115()->modulus_length)));
if (!algorithm->get_pkcs115()->public_exponent) {
return base::nullopt;
}
value.SetKey(
"publicExponent",
base::Value(algorithm->get_pkcs115()->public_exponent.value()));
break;
case crosapi::mojom::KeystoreSigningAlgorithm::Tag::kEcdsa:
value.SetStringKey("name", kWebCryptoEcdsa);
value.SetStringKey("namedCurve", algorithm->get_ecdsa()->named_curve);
break;
}
return value;
}
base::Optional<crosapi::mojom::KeystoreSigningAlgorithmPtr>
SigningAlgorithmFromDictionary(const base::DictionaryValue& dictionary) {
crosapi::mojom::KeystoreSigningAlgorithmPtr algorithm =
crosapi::mojom::KeystoreSigningAlgorithm::New();
const std::string* name = dictionary.FindStringKey("name");
if (!name)
return base::nullopt;
if (*name == kWebCryptoRsassaPkcs1v15) {
base::Optional<int> modulus_length = dictionary.FindIntKey("modulusLength");
const std::vector<uint8_t>* public_exponent =
dictionary.FindBlobKey("publicExponent");
if (!modulus_length || !public_exponent)
return base::nullopt;
if (!base::IsValueInRangeForNumericType<uint32_t>(modulus_length.value()))
return base::nullopt;
crosapi::mojom::KeystorePKCS115ParamsPtr params =
crosapi::mojom::KeystorePKCS115Params::New();
params->modulus_length =
base::checked_cast<uint32_t>(modulus_length.value());
params->public_exponent = *public_exponent;
algorithm->set_pkcs115(std::move(params));
return algorithm;
}
if (*name == kWebCryptoEcdsa) {
const std::string* named_curve = dictionary.FindStringKey("namedCurve");
if (!named_curve)
return base::nullopt;
crosapi::mojom::KeystoreECDSAParamsPtr params =
crosapi::mojom::KeystoreECDSAParams::New();
params->named_curve = *named_curve;
algorithm->set_ecdsa(std::move(params));
return algorithm;
}
return base::nullopt;
}
} // namespace keystore_service_util
} // namespace crosapi
// Copyright 2021 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.
#ifndef CHROMEOS_CROSAPI_CPP_KEYSTORE_SERVICE_UTIL_H_
#define CHROMEOS_CROSAPI_CPP_KEYSTORE_SERVICE_UTIL_H_
#include "base/component_export.h"
#include "base/optional.h"
#include "base/values.h"
#include "chromeos/crosapi/mojom/keystore_service.mojom.h"
namespace crosapi {
namespace keystore_service_util {
// The WebCrypto string for ECDSA.
COMPONENT_EXPORT(CROSAPI)
extern const char kWebCryptoEcdsa[];
// The WebCrypto string for PKCS1.
COMPONENT_EXPORT(CROSAPI)
extern const char kWebCryptoRsassaPkcs1v15[];
// The WebCrypto string for the P-256 named curve.
COMPONENT_EXPORT(CROSAPI)
extern const char kWebCryptoNamedCurveP256[];
// Converts a crosapi signing algorithm into a WebCrypto dictionary. Returns
// base::nullopt on error.
COMPONENT_EXPORT(CROSAPI)
base::Optional<base::DictionaryValue> DictionaryFromSigningAlgorithm(
const crosapi::mojom::KeystoreSigningAlgorithmPtr& algorithm);
// Converts a WebCrypto dictionary into a crosapi signing algorithm. Returns
// base::nullopt on error.
COMPONENT_EXPORT(CROSAPI)
base::Optional<crosapi::mojom::KeystoreSigningAlgorithmPtr>
SigningAlgorithmFromDictionary(const base::DictionaryValue& dictionary);
} // namespace keystore_service_util
} // namespace crosapi
#endif // CHROMEOS_CROSAPI_CPP_KEYSTORE_SERVICE_UTIL_H_
// Copyright 2021 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/crosapi/cpp/keystore_service_util.h"
#include "base/optional.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace crosapi {
namespace keystore_service_util {
TEST(KeystoreServiceUtil, ECDSA) {
base::DictionaryValue value;
value.SetStringKey("name", kWebCryptoEcdsa);
value.SetStringKey("namedCurve", kWebCryptoNamedCurveP256);
base::Optional<crosapi::mojom::KeystoreSigningAlgorithmPtr> ptr =
SigningAlgorithmFromDictionary(value);
ASSERT_TRUE(ptr);
base::Optional<base::DictionaryValue> value2 =
DictionaryFromSigningAlgorithm(ptr.value());
ASSERT_TRUE(value2);
EXPECT_EQ(value, value2);
}
TEST(KeystoreServiceUtil, PKCS) {
base::DictionaryValue value;
value.SetStringKey("name", kWebCryptoRsassaPkcs1v15);
value.SetKey("modulusLength", base::Value(5));
// Equals 65537.
static constexpr uint8_t kDefaultPublicExponent[] = {0x01, 0x00, 0x01};
value.SetKey("publicExponent",
base::Value(base::make_span(kDefaultPublicExponent)));
base::Optional<crosapi::mojom::KeystoreSigningAlgorithmPtr> ptr =
SigningAlgorithmFromDictionary(value);
ASSERT_TRUE(ptr);
base::Optional<base::DictionaryValue> value2 =
DictionaryFromSigningAlgorithm(ptr.value());
ASSERT_TRUE(value2);
EXPECT_EQ(value, value2);
}
} // namespace keystore_service_util
} // namespace crosapi
......@@ -37,7 +37,7 @@ mojom("mojom") {
traits_headers = [ "//chromeos/crosapi/mojom/bitmap_mojom_traits.h" ]
traits_public_deps = [
":mojom_traits",
"//chromeos/crosapi/cpp",
"//chromeos/crosapi/cpp:cpp_internal",
]
},
]
......@@ -63,7 +63,7 @@ component("mojom_traits") {
public_deps = [
":mojom_shared",
"//chromeos/crosapi/cpp",
"//chromeos/crosapi/cpp:cpp_internal",
"//mojo/public/cpp/base:shared_typemap_traits",
]
}
......@@ -30,10 +30,13 @@ enum KeystoreType {
};
// Input parameters for RSASSA-PKCS1-v1_5. Parameters othan than modulus_length
// are currently not supported.
// are currently not supported when used as inputs to GenerateKey().
[Stable, Extensible]
struct KeystorePKCS115Params {
uint32 modulus_length;
[MinVersion=0]
uint32 modulus_length@0;
[MinVersion=1]
array<uint8>? public_exponent@1;
};
// Input parameters for ECDSA. |named_curve| uses WebCrypto nomenclature.
......@@ -50,6 +53,13 @@ union KeystoreSigningAlgorithm {
KeystoreECDSAParams ecdsa;
};
// The name of a WebCrypto signing algorithm.
[Stable, Extensible]
enum KeystoreSigningAlgorithmName {
kUnknown = 0,
kRsassaPkcs115 = 1,
kEcdsa = 2,
};
// Returned by methods that either return a string, or an error.
[Stable, RenamedFrom="crosapi.mojom.ChallengeAttestationOnlyKeystoreResult"]
......@@ -90,6 +100,26 @@ union GetKeyStoresResult {
array<KeystoreType> key_stores;
};
// Returned by GetPublicKey() on success.
[Stable]
struct GetPublicKeySuccessResult {
// The public key of the matching certificate.
array<uint8> public_key;
// Provides details about the signing algorithm.
KeystoreSigningAlgorithm algorithm_properties;
};
// Returned by GetPublicKey().
[Stable]
union GetPublicKeyResult {
// Implies failure.
string error_message;
// Implies success.
GetPublicKeySuccessResult success_result;
};
// This interface is implemented by ash-chrome. It provides lacros-chrome a
// mechanism to modify and query the attestation-only and generate purpose
// keystores.
......@@ -137,5 +167,14 @@ interface KeystoreService {
[MinVersion=2]
RemoveCertificate@5(KeystoreType keystore, array<uint8> certificate) =>
(string error);
// Checks whether |certificate| certifies a key that allows usage of the
// WebCrypto algorithm |algorithm_name|. If so, returns the key info and
// details about the signing algorithm. |certificate| must be a DER encoded
// X.509 certificate.
[MinVersion=3]
GetPublicKey@6(array<uint8> certificate,
KeystoreSigningAlgorithmName algorithm_name) =>
(GetPublicKeyResult result);
};
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