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

lacros: Implement platform_keys_internal.idl:sign()

This CL implements the sign() method from the platform_keys_internal
extension API. It adds a new Sign() method to the crosapi
KeystoreService that simply forwards the call to ash.

Change-Id: If696709159d2e8d97afd3133534f325f019344fb
Bug: 1164523, 1166105
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2626086
Commit-Queue: Erik Chen <erikchen@chromium.org>
Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Reviewed-by: default avatarMichael Ershov <miersh@google.com>
Cr-Commit-Position: refs/heads/master@{#843385}
parent b6027333
......@@ -9,6 +9,8 @@
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "chrome/browser/chromeos/attestation/tpm_challenge_key.h"
#include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h"
#include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
......@@ -28,7 +30,7 @@ namespace {
const char kEnterprisePlatformErrorInvalidX509Cert[] =
"Certificate is not a valid X.509 certificate.";
const char kUnsupportedKeystoreType[] = "Keystore type is not supported.";
const char kUnsupportedKeystoreType[] = "The token is not valid.";
const char kUnsupportedAlgorithmType[] = "Algorithm type is not supported.";
const char kErrorAlgorithmNotPermittedByCertificate[] =
"The requested Algorithm is not permitted by the certificate.";
......@@ -271,6 +273,77 @@ void KeystoreServiceAsh::GetPublicKey(
std::move(callback).Run(std::move(result_ptr));
}
void KeystoreServiceAsh::Sign(KeystoreType keystore,
const std::vector<uint8_t>& public_key,
SigningScheme scheme,
const std::vector<uint8_t>& data,
const std::string& extension_id,
SignCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
base::Optional<TokenId> token_id = KeystoreToToken(keystore);
if (!token_id) {
std::move(callback).Run(
mojom::KeystoreBinaryResult::NewErrorMessage(kUnsupportedKeystoreType));
return;
}
chromeos::ExtensionPlatformKeysService* service =
chromeos::ExtensionPlatformKeysServiceFactory::GetForBrowserContext(
ProfileManager::GetActiveUserProfile());
chromeos::platform_keys::HashAlgorithm hash_algorithm;
chromeos::platform_keys::KeyType key_type;
switch (scheme) {
case SigningScheme::kUnknown:
std::move(callback).Run(mojom::KeystoreBinaryResult::NewErrorMessage(
kUnsupportedAlgorithmType));
return;
case SigningScheme::kRsassaPkcs1V15None:
service->SignRSAPKCS1Raw(
token_id, std::string(data.begin(), data.end()),
std::string(public_key.begin(), public_key.end()), extension_id,
base::BindOnce(&KeystoreServiceAsh::OnDidSign, std::move(callback)));
return;
case SigningScheme::kRsassaPkcs1V15Sha1:
key_type = chromeos::platform_keys::KeyType::kRsassaPkcs1V15;
hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA1;
break;
case SigningScheme::kRsassaPkcs1V15Sha256:
key_type = chromeos::platform_keys::KeyType::kRsassaPkcs1V15;
hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA256;
break;
case SigningScheme::kRsassaPkcs1V15Sha384:
key_type = chromeos::platform_keys::KeyType::kRsassaPkcs1V15;
hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA384;
break;
case SigningScheme::kRsassaPkcs1V15Sha512:
key_type = chromeos::platform_keys::KeyType::kRsassaPkcs1V15;
hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA512;
break;
case SigningScheme::kEcdsaSha1:
key_type = chromeos::platform_keys::KeyType::kEcdsa;
hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA1;
break;
case SigningScheme::kEcdsaSha256:
key_type = chromeos::platform_keys::KeyType::kEcdsa;
hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA256;
break;
case SigningScheme::kEcdsaSha384:
key_type = chromeos::platform_keys::KeyType::kEcdsa;
hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA384;
break;
case SigningScheme::kEcdsaSha512:
key_type = chromeos::platform_keys::KeyType::kEcdsa;
hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA512;
break;
}
service->SignDigest(
token_id, std::string(data.begin(), data.end()),
std::string(public_key.begin(), public_key.end()), key_type,
hash_algorithm, extension_id,
base::BindOnce(&KeystoreServiceAsh::OnDidSign, std::move(callback)));
}
// static
void KeystoreServiceAsh::OnGetTokens(
GetKeyStoresCallback callback,
......@@ -366,6 +439,21 @@ void KeystoreServiceAsh::OnRemoveCertificate(
std::move(callback).Run(chromeos::platform_keys::StatusToString(status));
}
// static
void KeystoreServiceAsh::OnDidSign(SignCallback callback,
const std::string& signature,
chromeos::platform_keys::Status status) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (status == chromeos::platform_keys::Status::kSuccess) {
std::move(callback).Run(mojom::KeystoreBinaryResult::NewBlob(
std::vector<uint8_t>(signature.begin(), signature.end())));
} else {
std::move(callback).Run(mojom::KeystoreBinaryResult::NewErrorMessage(
chromeos::platform_keys::StatusToString(status)));
}
}
void KeystoreServiceAsh::DidChallengeAttestationOnlyKeystore(
ChallengeAttestationOnlyKeystoreCallback callback,
void* challenge_key_ptr,
......
......@@ -36,6 +36,7 @@ class KeystoreServiceAsh : public mojom::KeystoreService {
// mojom::KeystoreService:
using KeystoreType = mojom::KeystoreType;
using SigningScheme = mojom::KeystoreSigningScheme;
void ChallengeAttestationOnlyKeystore(
const std::string& challenge,
mojom::KeystoreType type,
......@@ -56,6 +57,12 @@ class KeystoreServiceAsh : public mojom::KeystoreService {
void GetPublicKey(const std::vector<uint8_t>& certificate,
mojom::KeystoreSigningAlgorithmName algorithm_name,
GetPublicKeyCallback callback) override;
void Sign(KeystoreType keystore,
const std::vector<uint8_t>& public_key,
SigningScheme scheme,
const std::vector<uint8_t>& data,
const std::string& extension_id,
SignCallback callback) override;
private:
static void OnGetTokens(
......@@ -73,6 +80,9 @@ class KeystoreServiceAsh : public mojom::KeystoreService {
chromeos::platform_keys::Status status);
static void OnRemoveCertificate(RemoveCertificateCallback callback,
chromeos::platform_keys::Status status);
static void OnDidSign(SignCallback callback,
const std::string& signature,
chromeos::platform_keys::Status status);
// |challenge| is used as a opaque identifier to match against the unique_ptr
// in outstanding_challenges_. It should not be dereferenced.
......
......@@ -289,7 +289,7 @@ class ExtensionPlatformKeysService::SignTask : public Task {
platform_keys::KeyType key_type,
platform_keys::HashAlgorithm hash_algorithm,
const std::string& extension_id,
const SignCallback& callback,
SignCallback callback,
ExtensionPlatformKeysService* service)
: token_id_(token_id),
data_(data),
......@@ -298,7 +298,7 @@ class ExtensionPlatformKeysService::SignTask : public Task {
key_type_(key_type),
hash_algorithm_(hash_algorithm),
extension_id_(extension_id),
callback_(callback),
callback_(std::move(callback)),
service_(service) {}
~SignTask() override {}
......@@ -370,8 +370,9 @@ class ExtensionPlatformKeysService::SignTask : public Task {
void OnCanUseKeyForSigningKnown(bool allowed) {
if (!allowed) {
callback_.Run(std::string() /* no signature */,
platform_keys::Status::kErrorKeyNotAllowedForSigning);
std::move(callback_).Run(
std::string() /* no signature */,
platform_keys::Status::kErrorKeyNotAllowedForSigning);
next_step_ = Step::DONE;
DoStep();
return;
......@@ -393,7 +394,7 @@ class ExtensionPlatformKeysService::SignTask : public Task {
LOG(ERROR) << "Marking a key used for signing failed: "
<< platform_keys::StatusToString(status);
next_step_ = Step::DONE;
callback_.Run(std::string() /* no signature */, status);
std::move(callback_).Run(std::string() /* no signature */, status);
DoStep();
return;
}
......@@ -428,7 +429,7 @@ class ExtensionPlatformKeysService::SignTask : public Task {
}
void DidSign(const std::string& signature, platform_keys::Status status) {
callback_.Run(signature, status);
std::move(callback_).Run(signature, status);
DoStep();
}
......@@ -445,7 +446,7 @@ class ExtensionPlatformKeysService::SignTask : public Task {
const platform_keys::KeyType key_type_;
const platform_keys::HashAlgorithm hash_algorithm_;
const std::string extension_id_;
const SignCallback callback_;
SignCallback callback_;
std::unique_ptr<platform_keys::ExtensionKeyPermissionsService>
extension_key_permissions_service_;
ExtensionPlatformKeysService* const service_;
......@@ -815,12 +816,12 @@ void ExtensionPlatformKeysService::SignDigest(
platform_keys::KeyType key_type,
platform_keys::HashAlgorithm hash_algorithm,
const std::string& extension_id,
const SignCallback& callback) {
SignCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
StartOrQueueTask(
std::make_unique<SignTask>(token_id, data, public_key_spki_der,
/*raw_pkcs1=*/false, key_type, hash_algorithm,
extension_id, callback, this));
extension_id, std::move(callback), this));
}
void ExtensionPlatformKeysService::SignRSAPKCS1Raw(
......@@ -828,12 +829,13 @@ void ExtensionPlatformKeysService::SignRSAPKCS1Raw(
const std::string& data,
const std::string& public_key_spki_der,
const std::string& extension_id,
const SignCallback& callback) {
SignCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
StartOrQueueTask(std::make_unique<SignTask>(
token_id, data, public_key_spki_der,
/*raw_pkcs1=*/true, /*key_type=*/platform_keys::KeyType::kRsassaPkcs1V15,
platform_keys::HASH_ALGORITHM_NONE, extension_id, callback, this));
platform_keys::HASH_ALGORITHM_NONE, extension_id, std::move(callback),
this));
}
void ExtensionPlatformKeysService::SelectClientCertificates(
......
......@@ -126,8 +126,8 @@ class ExtensionPlatformKeysService : public KeyedService {
// If signing was successful, |signature| will contain the signature. If it
// failed, |signature| will be empty.
using SignCallback = base::Callback<void(const std::string& signature,
platform_keys::Status status)>;
using SignCallback = base::OnceCallback<void(const std::string& signature,
platform_keys::Status status)>;
// Digests |data|, applies PKCS1 padding if specified by |hash_algorithm| and
// chooses the signature algorithm according to |key_type| and signs the data
......@@ -147,7 +147,7 @@ class ExtensionPlatformKeysService : public KeyedService {
platform_keys::KeyType key_type,
platform_keys::HashAlgorithm hash_algorithm,
const std::string& extension_id,
const SignCallback& callback);
SignCallback callback);
// Applies PKCS1 padding and afterwards signs the data with the private key
// matching |public_key_spki_der|. |data| is not digested. If a |token_id|
......@@ -165,7 +165,7 @@ class ExtensionPlatformKeysService : public KeyedService {
const std::string& data,
const std::string& public_key_spki_der,
const std::string& extension_id,
const SignCallback& callback);
SignCallback callback);
// If the certificate request could be processed successfully, |matches| will
// contain the list of matching certificates (maybe empty). If an error
......
......@@ -4,6 +4,8 @@
#include "chrome/browser/extensions/api/platform_keys/platform_keys_api_lacros.h"
#include <string>
#include "base/optional.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
......@@ -14,6 +16,7 @@
namespace extensions {
namespace api_pki = api::platform_keys_internal;
using SigningScheme = crosapi::mojom::KeystoreSigningScheme;
using SigningAlgorithmName = crosapi::mojom::KeystoreSigningAlgorithmName;
namespace {
......@@ -21,6 +24,8 @@ const char kUnsupportedByAsh[] = "Not implemented.";
const char kUnsupportedProfile[] = "Not available.";
const char kErrorAlgorithmNotPermittedByCertificate[] =
"The requested Algorithm is not permitted by the certificate.";
const char kErrorAlgorithmNotSupported[] = "Algorithm not supported.";
const char kErrorInvalidToken[] = "The token is not valid.";
using crosapi::keystore_service_util::kWebCryptoEcdsa;
using crosapi::keystore_service_util::kWebCryptoRsassaPkcs1v15;
......@@ -34,6 +39,50 @@ base::Optional<SigningAlgorithmName> SigningAlgorithmNameFromString(
return base::nullopt;
}
base::Optional<crosapi::mojom::KeystoreType> KeystoreTypeFromString(
const std::string& input) {
if (input == "user")
return crosapi::mojom::KeystoreType::kUser;
if (input == "system")
return crosapi::mojom::KeystoreType::kDevice;
return base::nullopt;
}
base::Optional<SigningScheme> SigningSchemeFromStrings(
const std::string& hashing,
const std::string& signing) {
if (hashing == "none") {
if (signing == kWebCryptoRsassaPkcs1v15)
return SigningScheme::kRsassaPkcs1V15None;
return base::nullopt;
}
if (hashing == "SHA-1") {
if (signing == kWebCryptoRsassaPkcs1v15)
return SigningScheme::kRsassaPkcs1V15Sha1;
if (signing == kWebCryptoEcdsa)
return SigningScheme::kEcdsaSha1;
}
if (hashing == "SHA-256") {
if (signing == kWebCryptoRsassaPkcs1v15)
return SigningScheme::kRsassaPkcs1V15Sha256;
if (signing == kWebCryptoEcdsa)
return SigningScheme::kEcdsaSha256;
}
if (hashing == "SHA-384") {
if (signing == kWebCryptoRsassaPkcs1v15)
return SigningScheme::kRsassaPkcs1V15Sha384;
if (signing == kWebCryptoEcdsa)
return SigningScheme::kEcdsaSha384;
}
if (hashing == "SHA-512") {
if (signing == kWebCryptoRsassaPkcs1v15)
return SigningScheme::kRsassaPkcs1V15Sha512;
if (signing == kWebCryptoEcdsa)
return SigningScheme::kEcdsaSha512;
}
return base::nullopt;
}
} // namespace
PlatformKeysInternalSelectClientCertificatesFunction::
......@@ -114,7 +163,51 @@ PlatformKeysInternalGetPublicKeyBySpkiFunction::Run() {
PlatformKeysInternalSignFunction::~PlatformKeysInternalSignFunction() {}
ExtensionFunction::ResponseAction PlatformKeysInternalSignFunction::Run() {
return RespondNow(Error("Not implemented."));
std::unique_ptr<api_pki::Sign::Params> params(
api_pki::Sign::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
if (chromeos::LacrosChromeServiceImpl::Get()->GetInterfaceVersion(
crosapi::mojom::KeystoreService::Uuid_) < 4) {
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<crosapi::mojom::KeystoreType> keystore_type =
KeystoreTypeFromString(params->token_id);
if (!keystore_type) {
return RespondNow(Error(kErrorInvalidToken));
}
base::Optional<SigningScheme> scheme = SigningSchemeFromStrings(
params->hash_algorithm_name, params->algorithm_name);
if (!scheme) {
return RespondNow(Error(kErrorAlgorithmNotSupported));
}
auto cb = base::BindOnce(&PlatformKeysInternalSignFunction::OnSign, this);
chromeos::LacrosChromeServiceImpl::Get()->keystore_service_remote()->Sign(
keystore_type.value(), params->public_key, scheme.value(), params->data,
extension_id(), std::move(cb));
return RespondLater();
}
void PlatformKeysInternalSignFunction::OnSign(ResultPtr result) {
using Result = crosapi::mojom::KeystoreBinaryResult;
switch (result->which()) {
case Result::Tag::ERROR_MESSAGE:
Respond(Error(result->get_error_message()));
return;
case Result::Tag::BLOB:
Respond(ArgumentList(api_pki::Sign::Results::Create(result->get_blob())));
return;
}
}
PlatformKeysVerifyTLSServerCertificateFunction::
......
......@@ -47,6 +47,9 @@ class PlatformKeysInternalSignFunction : public ExtensionFunction {
~PlatformKeysInternalSignFunction() override;
ResponseAction Run() override;
using ResultPtr = crosapi::mojom::KeystoreBinaryResultPtr;
void OnSign(ResultPtr result_ptr);
DECLARE_EXTENSION_FUNCTION("platformKeysInternal.sign",
PLATFORMKEYSINTERNAL_SIGN)
};
......
......@@ -61,6 +61,21 @@ enum KeystoreSigningAlgorithmName {
kEcdsa = 2,
};
// Recognized WebCrypto signing schemes.
[Stable, Extensible]
enum KeystoreSigningScheme {
kUnknown = 0,
kRsassaPkcs1V15None = 1, // The data is PKCS#1 v1.5 padded but not hashed.
kRsassaPkcs1V15Sha1 = 2,
kRsassaPkcs1V15Sha256 = 3,
kRsassaPkcs1V15Sha384 = 4,
kRsassaPkcs1V15Sha512 = 5,
kEcdsaSha1 = 6,
kEcdsaSha256 = 7,
kEcdsaSha384 = 8,
kEcdsaSha512 = 9,
};
// Returned by methods that either return a string, or an error.
[Stable, RenamedFrom="crosapi.mojom.ChallengeAttestationOnlyKeystoreResult"]
union KeystoreStringResult {
......@@ -176,5 +191,15 @@ interface KeystoreService {
GetPublicKey@6(array<uint8> certificate,
KeystoreSigningAlgorithmName algorithm_name) =>
(GetPublicKeyResult result);
// Signs some data using a previously generated key, indicated with
// |public_key|. |scheme| is the WebCrypto signing scheme. |extension_id| is
// needed to determine if the extension is allowed to use the key.
// TODO(https://crbug.com/1166105): In the long term it doesn't make sense to
// pass |extension_id|. We should find a better solution.
[MinVersion=4]
Sign@7(KeystoreType keystore, array<uint8> public_key,
KeystoreSigningScheme scheme,
array<uint8> data, string extension_id) => (KeystoreBinaryResult 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