blob: f3e8546ba4304de7d1afd6ab5e3f7d7970e311ee [file] [log] [blame]
// Copyright 2015 The Chromium OS 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 "attestation/server/pkcs11_key_store.h"
#include <memory>
#include <string>
#include <vector>
#include <base/bind.h>
#include <base/callback.h>
#include <base/files/file_path.h>
#include <base/logging.h>
#include <base/stl_util.h>
#include <base/strings/string_util.h>
#include <brillo/cryptohome.h>
#include <chaps/isolate.h>
#include <chaps/pkcs11/cryptoki.h>
#include <chaps/token_manager_client.h>
#include <crypto/libcrypto-compat.h>
#include <crypto/scoped_openssl_types.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/x509.h>
namespace attestation {
namespace {
std::string Sha1(const std::string& input) {
unsigned char output[SHA_DIGEST_LENGTH];
SHA1(reinterpret_cast<const unsigned char*>(input.data()), input.size(),
output);
return std::string(reinterpret_cast<char*>(output), SHA_DIGEST_LENGTH);
}
bool IsSupportedRegisterKeyType(KeyType key_type) {
return key_type == KEY_TYPE_RSA || key_type == KEY_TYPE_ECC;
}
CK_KEY_TYPE ToPkcs11KeyType(KeyType key_type) {
switch (key_type) {
case KEY_TYPE_RSA:
return CKK_RSA;
case KEY_TYPE_ECC:
return CKK_EC;
default:
LOG(DFATAL) << "Unsupported key type input: " << key_type;
return CKK_RSA;
}
}
typedef crypto::ScopedOpenSSL<X509, X509_free> ScopedX509;
} // namespace
// An arbitrary application ID to identify PKCS #11 objects.
const char kApplicationID[] = "CrOS_d5bbc079d2497110feadfc97c40d718ae46f4658";
// A helper class to scope a PKCS #11 session.
class ScopedSession {
public:
explicit ScopedSession(CK_SLOT_ID slot) : handle_(CK_INVALID_HANDLE) {
CK_RV rv = C_Initialize(nullptr);
if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
// This may be normal in a test environment.
LOG(INFO) << "PKCS #11 is not available.";
return;
}
CK_FLAGS flags = CKF_RW_SESSION | CKF_SERIAL_SESSION;
if (C_OpenSession(slot, flags, nullptr, nullptr, &handle_) != CKR_OK) {
LOG(ERROR) << "Failed to open PKCS #11 session.";
return;
}
}
ScopedSession(const ScopedSession&) = delete;
ScopedSession& operator=(const ScopedSession&) = delete;
~ScopedSession() {
if (IsValid() && (C_CloseSession(handle_) != CKR_OK)) {
LOG(WARNING) << "Failed to close PKCS #11 session.";
handle_ = CK_INVALID_HANDLE;
}
}
CK_SESSION_HANDLE handle() const { return handle_; }
bool IsValid() const { return (handle_ != CK_INVALID_HANDLE); }
private:
CK_SESSION_HANDLE handle_;
};
Pkcs11KeyStore::Pkcs11KeyStore(chaps::TokenManagerClient* token_manager)
: token_manager_(token_manager) {}
Pkcs11KeyStore::~Pkcs11KeyStore() {}
bool Pkcs11KeyStore::Read(const std::string& username,
const std::string& key_name,
std::string* key_data) {
CK_SLOT_ID slot;
if (!GetUserSlot(username, &slot)) {
LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
return false;
}
ScopedSession session(slot);
if (!session.IsValid()) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
return false;
}
CK_OBJECT_HANDLE key_handle = FindObject(session.handle(), key_name);
if (key_handle == CK_INVALID_HANDLE) {
LOG(WARNING) << "Pkcs11KeyStore: Key does not exist: " << key_name;
return false;
}
// First get the attribute with a NULL buffer which will give us the length.
CK_ATTRIBUTE attribute = {CKA_VALUE, nullptr, 0};
if (C_GetAttributeValue(session.handle(), key_handle, &attribute, 1) !=
CKR_OK) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to read key data: " << key_name;
return false;
}
key_data->resize(attribute.ulValueLen);
attribute.pValue = base::data(*key_data);
if (C_GetAttributeValue(session.handle(), key_handle, &attribute, 1) !=
CKR_OK) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to read key data: " << key_name;
return false;
}
key_data->resize(attribute.ulValueLen);
return true;
}
bool Pkcs11KeyStore::Write(const std::string& username,
const std::string& key_name,
const std::string& key_data) {
// Delete any existing key with the same name.
if (!Delete(username, key_name)) {
return false;
}
CK_SLOT_ID slot;
if (!GetUserSlot(username, &slot)) {
LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
return false;
}
ScopedSession session(slot);
if (!session.IsValid()) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
return false;
}
std::string mutable_key_name(key_name);
std::string mutable_key_data(key_data);
std::string mutable_application_id(kApplicationID);
// Create a new data object for the key.
CK_OBJECT_CLASS object_class = CKO_DATA;
CK_BBOOL true_value = CK_TRUE;
CK_BBOOL false_value = CK_FALSE;
CK_ATTRIBUTE attributes[] = {
{CKA_CLASS, &object_class, sizeof(object_class)},
{CKA_LABEL, base::data(mutable_key_name), mutable_key_name.size()},
{CKA_VALUE, base::data(mutable_key_data), mutable_key_data.size()},
{CKA_APPLICATION, base::data(mutable_application_id),
mutable_application_id.size()},
{CKA_TOKEN, &true_value, sizeof(true_value)},
{CKA_PRIVATE, &true_value, sizeof(true_value)},
{CKA_MODIFIABLE, &false_value, sizeof(false_value)}};
CK_OBJECT_HANDLE key_handle = CK_INVALID_HANDLE;
if (C_CreateObject(session.handle(), attributes, base::size(attributes),
&key_handle) != CKR_OK) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to write key data: " << key_name;
return false;
}
return true;
}
bool Pkcs11KeyStore::Delete(const std::string& username,
const std::string& key_name) {
CK_SLOT_ID slot;
if (!GetUserSlot(username, &slot)) {
LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
return false;
}
ScopedSession session(slot);
if (!session.IsValid()) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
return false;
}
CK_OBJECT_HANDLE key_handle = FindObject(session.handle(), key_name);
if (key_handle != CK_INVALID_HANDLE) {
if (C_DestroyObject(session.handle(), key_handle) != CKR_OK) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to delete key data.";
return false;
}
}
return true;
}
bool Pkcs11KeyStore::DeleteByPrefix(const std::string& username,
const std::string& key_prefix) {
CK_SLOT_ID slot;
if (!GetUserSlot(username, &slot)) {
LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
return false;
}
ScopedSession session(slot);
if (!session.IsValid()) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
return false;
}
EnumObjectsCallback callback =
base::Bind(&Pkcs11KeyStore::DeleteIfMatchesPrefix, base::Unretained(this),
session.handle(), key_prefix);
if (!EnumObjects(session.handle(), callback)) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to delete key data.";
return false;
}
return true;
}
bool Pkcs11KeyStore::Register(const std::string& username,
const std::string& label,
KeyType key_type,
KeyUsage key_usage,
const std::string& private_key_blob,
const std::string& public_key_der,
const std::string& certificate) {
const CK_ATTRIBUTE_TYPE kKeyBlobAttribute = CKA_VENDOR_DEFINED + 1;
if (!IsSupportedRegisterKeyType(key_type)) {
LOG(ERROR) << "Pkcs11KeyStore: Unsupported key type: " << key_type << ".";
return false;
}
CK_SLOT_ID slot;
if (!GetUserSlot(username, &slot)) {
LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
return false;
}
ScopedSession session(slot);
if (!session.IsValid()) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
return false;
}
// Extract the modulus from the public key if it's RSA; or, extract the ecc
// parameters and the ecc point if it's ECC. We do the parsing here because
// both private and public key objects need them.
std::string modulus;
std::string ecc_params, ecc_point;
const unsigned char* asn1_ptr =
reinterpret_cast<const unsigned char*>(public_key_der.data());
if (key_type == KEY_TYPE_RSA) {
crypto::ScopedRSA public_key(
d2i_RSAPublicKey(nullptr, &asn1_ptr, public_key_der.size()));
if (!public_key.get()) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to decode RSA public key.";
return false;
}
modulus.resize(RSA_size(public_key.get()), 0);
const BIGNUM* n = nullptr;
RSA_get0_key(public_key.get(), &n, nullptr, nullptr);
int length =
BN_bn2bin(n, reinterpret_cast<unsigned char*>(base::data(modulus)));
if (length <= 0) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to extract public key modulus.";
return false;
}
modulus.resize(length);
} else if (key_type == KEY_TYPE_ECC) {
crypto::ScopedEC_KEY public_key(
d2i_EC_PUBKEY(nullptr, &asn1_ptr, public_key_der.size()));
if (!public_key.get()) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to decode ECC public key.";
return false;
}
int output_size = i2d_ECParameters(public_key.get(), nullptr);
if (output_size <= 0) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to call i2d_ECParameters to get "
"output size.";
return false;
}
std::unique_ptr<uint8_t[]> output =
std::make_unique<uint8_t[]>(output_size);
uint8_t* output_buffer = output.get();
if (i2d_ECParameters(public_key.get(), &output_buffer) <= 0) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to call i2d_ECParameters.";
return false;
}
ecc_params.assign(output.get(), output.get() + output_size);
output_size = i2o_ECPublicKey(public_key.get(), nullptr);
if (output_size <= 0) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to call i2o_ECPublicKey to get "
"output size.";
return false;
}
output = std::make_unique<uint8_t[]>(output_size);
output_buffer = output.get();
if (i2o_ECPublicKey(public_key.get(), &output_buffer) <= 0) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to call i2o_ECPublicKey.";
return false;
}
// CKA_EC_POINT is DER-encoded ANSI X9.62 ECPoint value. The format should
// be 04 LEN 04 X Y, where the first 04 is the octet string tag, LEN is the
// the content length, the second 04 identifies the uncompressed form, and X
// and Y are the point coordinates.
//
// i2o_ECPublicKey() returns only the content (04 X Y)
crypto::ScopedOpenSSL<ASN1_OCTET_STRING, ASN1_OCTET_STRING_free>
asn1_oct_string(ASN1_OCTET_STRING_new());
if (!asn1_oct_string) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to call ASN1_OCTET_STRING_new.";
return false;
}
if (!ASN1_OCTET_STRING_set(asn1_oct_string.get(), output.get(),
output_size)) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to call ASN1_OCTET_STRING_set.";
return false;
}
output_size = i2d_ASN1_OCTET_STRING(asn1_oct_string.get(), nullptr);
if (output_size <= 0) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to call i2d_ASN1_OCTET_STRING to "
"get output size.";
return false;
}
output = std::make_unique<uint8_t[]>(output_size);
output_buffer = output.get();
if (i2d_ASN1_OCTET_STRING(asn1_oct_string.get(), &output_buffer) <= 0) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to call i2d_ASN1_OCTET_STRING.";
return false;
}
ecc_point.assign(output.get(), output.get() + output_size);
} else {
NOTREACHED();
}
// Construct a PKCS #11 template for the public key object.
CK_BBOOL true_value = CK_TRUE;
CK_BBOOL false_value = CK_FALSE;
CK_KEY_TYPE p11_key_type = ToPkcs11KeyType(key_type);
CK_OBJECT_CLASS public_key_class = CKO_PUBLIC_KEY;
std::string id = Sha1(modulus);
std::string mutable_label(label);
CK_ULONG modulus_bits = modulus.size() * 8;
CK_BBOOL sign_usage = (key_usage == KEY_USAGE_SIGN);
CK_BBOOL decrypt_usage = (key_usage == KEY_USAGE_DECRYPT);
unsigned char public_exponent[] = {1, 0, 1};
std::vector<CK_ATTRIBUTE> public_key_attributes = {
{CKA_CLASS, &public_key_class, sizeof(public_key_class)},
{CKA_TOKEN, &true_value, sizeof(true_value)},
{CKA_DERIVE, &false_value, sizeof(false_value)},
{CKA_WRAP, &false_value, sizeof(false_value)},
{CKA_VERIFY, &sign_usage, sizeof(sign_usage)},
{CKA_VERIFY_RECOVER, &false_value, sizeof(false_value)},
{CKA_ENCRYPT, &decrypt_usage, sizeof(decrypt_usage)},
{CKA_KEY_TYPE, &p11_key_type, sizeof(p11_key_type)},
{CKA_ID, base::data(id), id.size()},
{CKA_LABEL, base::data(mutable_label), mutable_label.size()},
};
if (key_type == KEY_TYPE_RSA) {
const CK_ATTRIBUTE rsa_key_attributes[] = {
{CKA_MODULUS_BITS, &modulus_bits, sizeof(modulus_bits)},
{CKA_PUBLIC_EXPONENT, public_exponent, base::size(public_exponent)},
{CKA_MODULUS, base::data(modulus), modulus.size()},
};
public_key_attributes.insert(public_key_attributes.end(),
std::begin(rsa_key_attributes),
std::end(rsa_key_attributes));
} else if (key_type == KEY_TYPE_ECC) {
const CK_ATTRIBUTE ecc_key_attributes[] = {
{CKA_EC_PARAMS, const_cast<char*>(ecc_params.c_str()),
ecc_params.length()},
{CKA_EC_POINT, const_cast<char*>(ecc_point.c_str()),
ecc_point.length()},
};
public_key_attributes.insert(public_key_attributes.end(),
std::begin(ecc_key_attributes),
std::end(ecc_key_attributes));
} else {
NOTREACHED();
}
CK_OBJECT_HANDLE object_handle = CK_INVALID_HANDLE;
if (C_CreateObject(session.handle(), public_key_attributes.data(),
public_key_attributes.size(), &object_handle) != CKR_OK) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to create public key object.";
return false;
}
// Construct a PKCS #11 template for the private key object.
std::string mutable_private_key_blob(private_key_blob);
CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY;
std::vector<CK_ATTRIBUTE> private_key_attributes = {
{CKA_CLASS, &private_key_class, sizeof(private_key_class)},
{CKA_TOKEN, &true_value, sizeof(true_value)},
{CKA_PRIVATE, &true_value, sizeof(true_value)},
{CKA_SENSITIVE, &true_value, sizeof(true_value)},
{CKA_EXTRACTABLE, &false_value, sizeof(false_value)},
{CKA_DERIVE, &false_value, sizeof(false_value)},
{CKA_UNWRAP, &false_value, sizeof(false_value)},
{CKA_SIGN, &sign_usage, sizeof(sign_usage)},
{CKA_SIGN_RECOVER, &false_value, sizeof(false_value)},
{CKA_DECRYPT, &decrypt_usage, sizeof(decrypt_usage)},
{CKA_KEY_TYPE, &p11_key_type, sizeof(p11_key_type)},
{CKA_ID, base::data(id), id.size()},
{CKA_LABEL, base::data(mutable_label), mutable_label.size()},
{kKeyBlobAttribute, base::data(mutable_private_key_blob),
mutable_private_key_blob.size()},
};
if (key_type == KEY_TYPE_RSA) {
const CK_ATTRIBUTE rsa_key_attributes[] = {
{CKA_PUBLIC_EXPONENT, public_exponent, base::size(public_exponent)},
{CKA_MODULUS, base::data(modulus), modulus.size()},
};
private_key_attributes.insert(private_key_attributes.end(),
std::begin(rsa_key_attributes),
std::end(rsa_key_attributes));
} else if (key_type == KEY_TYPE_ECC) {
const CK_ATTRIBUTE ecc_key_attributes[] = {
{CKA_EC_PARAMS, const_cast<char*>(ecc_params.c_str()),
ecc_params.length()},
{CKA_EC_POINT, const_cast<char*>(ecc_point.c_str()),
ecc_point.length()},
};
private_key_attributes.insert(private_key_attributes.end(),
std::begin(ecc_key_attributes),
std::end(ecc_key_attributes));
} else {
NOTREACHED();
}
if (C_CreateObject(session.handle(), private_key_attributes.data(),
private_key_attributes.size(), &object_handle) != CKR_OK) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to create private key object.";
return false;
}
if (!certificate.empty()) {
std::string subject;
std::string issuer;
std::string serial_number;
if (!GetCertificateFields(certificate, &subject, &issuer, &serial_number)) {
LOG(WARNING) << "Pkcs11KeyStore: Failed to find certificate fields.";
}
// Construct a PKCS #11 template for a certificate object.
std::string mutable_certificate = certificate;
CK_OBJECT_CLASS certificate_class = CKO_CERTIFICATE;
CK_CERTIFICATE_TYPE certificate_type = CKC_X_509;
CK_ATTRIBUTE certificate_attributes[] = {
{CKA_CLASS, &certificate_class, sizeof(certificate_class)},
{CKA_TOKEN, &true_value, sizeof(true_value)},
{CKA_PRIVATE, &false_value, sizeof(false_value)},
{CKA_ID, base::data(id), id.size()},
{CKA_LABEL, base::data(mutable_label), mutable_label.size()},
{CKA_CERTIFICATE_TYPE, &certificate_type, sizeof(certificate_type)},
{CKA_SUBJECT, base::data(subject), subject.size()},
{CKA_ISSUER, base::data(issuer), issuer.size()},
{CKA_SERIAL_NUMBER, base::data(serial_number), serial_number.size()},
{CKA_VALUE, base::data(mutable_certificate),
mutable_certificate.size()}};
if (C_CreateObject(session.handle(), certificate_attributes,
base::size(certificate_attributes),
&object_handle) != CKR_OK) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to create certificate object.";
return false;
}
}
return true;
}
bool Pkcs11KeyStore::RegisterCertificate(const std::string& username,
const std::string& certificate) {
CK_SLOT_ID slot;
if (!GetUserSlot(username, &slot)) {
LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
return false;
}
ScopedSession session(slot);
if (!session.IsValid()) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
return false;
}
if (DoesCertificateExist(session.handle(), certificate)) {
LOG(INFO) << "Pkcs11KeyStore: Certificate already exists.";
return true;
}
std::string subject;
std::string issuer;
std::string serial_number;
if (!GetCertificateFields(certificate, &subject, &issuer, &serial_number)) {
LOG(WARNING) << "Pkcs11KeyStore: Failed to find certificate fields.";
}
// Construct a PKCS #11 template for a certificate object.
std::string mutable_certificate = certificate;
CK_OBJECT_CLASS certificate_class = CKO_CERTIFICATE;
CK_CERTIFICATE_TYPE certificate_type = CKC_X_509;
CK_BBOOL true_value = CK_TRUE;
CK_BBOOL false_value = CK_FALSE;
CK_ATTRIBUTE certificate_attributes[] = {
{CKA_CLASS, &certificate_class, sizeof(certificate_class)},
{CKA_TOKEN, &true_value, sizeof(true_value)},
{CKA_PRIVATE, &false_value, sizeof(false_value)},
{CKA_CERTIFICATE_TYPE, &certificate_type, sizeof(certificate_type)},
{CKA_SUBJECT, base::data(subject), subject.size()},
{CKA_ISSUER, base::data(issuer), issuer.size()},
{CKA_SERIAL_NUMBER, base::data(serial_number), serial_number.size()},
{CKA_VALUE, base::data(mutable_certificate), mutable_certificate.size()}};
CK_OBJECT_HANDLE object_handle = CK_INVALID_HANDLE;
if (C_CreateObject(session.handle(), certificate_attributes,
base::size(certificate_attributes),
&object_handle) != CKR_OK) {
LOG(ERROR) << "Pkcs11KeyStore: Failed to create certificate object.";
return false;
}
return true;
}
CK_OBJECT_HANDLE Pkcs11KeyStore::FindObject(CK_SESSION_HANDLE session_handle,
const std::string& key_name) {
// Assemble a search template.
std::string mutable_key_name(key_name);
std::string mutable_application_id(kApplicationID);
CK_OBJECT_CLASS object_class = CKO_DATA;
CK_BBOOL true_value = CK_TRUE;
CK_BBOOL false_value = CK_FALSE;
CK_ATTRIBUTE attributes[] = {
{CKA_CLASS, &object_class, sizeof(object_class)},
{CKA_LABEL, base::data(mutable_key_name), mutable_key_name.size()},
{CKA_APPLICATION, base::data(mutable_application_id),
mutable_application_id.size()},
{CKA_TOKEN, &true_value, sizeof(true_value)},
{CKA_PRIVATE, &true_value, sizeof(true_value)},
{CKA_MODIFIABLE, &false_value, sizeof(false_value)}};
CK_OBJECT_HANDLE key_handle = CK_INVALID_HANDLE;
CK_ULONG count = 0;
if ((C_FindObjectsInit(session_handle, attributes, base::size(attributes)) !=
CKR_OK) ||
(C_FindObjects(session_handle, &key_handle, 1, &count) != CKR_OK) ||
(C_FindObjectsFinal(session_handle) != CKR_OK)) {
LOG(ERROR) << "Key search failed: " << key_name;
return CK_INVALID_HANDLE;
}
if (count == 1)
return key_handle;
return CK_INVALID_HANDLE;
}
bool Pkcs11KeyStore::GetUserSlot(const std::string& username,
CK_SLOT_ID_PTR slot) {
const char kChapsDaemonName[] = "chaps";
const char kChapsSystemToken[] = "/var/lib/chaps";
base::FilePath token_path =
username.empty() ? base::FilePath(kChapsSystemToken)
: brillo::cryptohome::home::GetDaemonStorePath(
username, kChapsDaemonName);
CK_RV rv;
rv = C_Initialize(nullptr);
if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
LOG(WARNING) << __func__ << ": C_Initialize failed.";
return false;
}
CK_ULONG num_slots = 0;
rv = C_GetSlotList(CK_TRUE, nullptr, &num_slots);
if (rv != CKR_OK) {
LOG(WARNING) << __func__ << ": C_GetSlotList(nullptr) failed.";
return false;
}
std::unique_ptr<CK_SLOT_ID[]> slot_list(new CK_SLOT_ID[num_slots]);
rv = C_GetSlotList(CK_TRUE, slot_list.get(), &num_slots);
if (rv != CKR_OK) {
LOG(WARNING) << __func__ << ": C_GetSlotList failed.";
return false;
}
// Look through all slots for |token_path|.
for (CK_ULONG i = 0; i < num_slots; ++i) {
base::FilePath slot_path;
if (token_manager_->GetTokenPath(
chaps::IsolateCredentialManager::GetDefaultIsolateCredential(),
slot_list[i], &slot_path) &&
(token_path == slot_path)) {
*slot = slot_list[i];
return true;
}
}
LOG(WARNING) << __func__ << ": Path not found.";
return false;
}
bool Pkcs11KeyStore::EnumObjects(
CK_SESSION_HANDLE session_handle,
const Pkcs11KeyStore::EnumObjectsCallback& callback) {
std::string mutable_application_id(kApplicationID);
// Assemble a search template.
CK_OBJECT_CLASS object_class = CKO_DATA;
CK_BBOOL true_value = CK_TRUE;
CK_BBOOL false_value = CK_FALSE;
CK_ATTRIBUTE attributes[] = {
{CKA_CLASS, &object_class, sizeof(object_class)},
{CKA_APPLICATION, base::data(mutable_application_id),
mutable_application_id.size()},
{CKA_TOKEN, &true_value, sizeof(true_value)},
{CKA_PRIVATE, &true_value, sizeof(true_value)},
{CKA_MODIFIABLE, &false_value, sizeof(false_value)}};
const CK_ULONG kMaxHandles = 100; // Arbitrary.
CK_OBJECT_HANDLE handles[kMaxHandles];
CK_ULONG count = 0;
if ((C_FindObjectsInit(session_handle, attributes, base::size(attributes)) !=
CKR_OK) ||
(C_FindObjects(session_handle, handles, kMaxHandles, &count) != CKR_OK)) {
LOG(ERROR) << "Key search failed.";
return false;
}
while (count > 0) {
for (CK_ULONG i = 0; i < count; ++i) {
std::string key_name;
if (!GetKeyName(session_handle, handles[i], &key_name)) {
LOG(WARNING) << "Found key object but failed to get name.";
continue;
}
if (!callback.Run(key_name, handles[i]))
return false;
}
if (C_FindObjects(session_handle, handles, kMaxHandles, &count) != CKR_OK) {
LOG(ERROR) << "Key search continuation failed.";
return false;
}
}
if (C_FindObjectsFinal(session_handle) != CKR_OK) {
LOG(WARNING) << "Failed to finalize key search.";
}
return true;
}
bool Pkcs11KeyStore::GetKeyName(CK_SESSION_HANDLE session_handle,
CK_OBJECT_HANDLE object_handle,
std::string* key_name) {
CK_ATTRIBUTE attribute = {CKA_LABEL, nullptr, 0};
if (C_GetAttributeValue(session_handle, object_handle, &attribute, 1) !=
CKR_OK) {
LOG(ERROR) << "C_GetAttributeValue(CKA_LABEL) [length] failed.";
return false;
}
key_name->resize(attribute.ulValueLen);
attribute.pValue = base::data(*key_name);
if (C_GetAttributeValue(session_handle, object_handle, &attribute, 1) !=
CKR_OK) {
LOG(ERROR) << "C_GetAttributeValue(CKA_LABEL) failed.";
return false;
}
return true;
}
bool Pkcs11KeyStore::DeleteIfMatchesPrefix(CK_SESSION_HANDLE session_handle,
const std::string& key_prefix,
const std::string& key_name,
CK_OBJECT_HANDLE object_handle) {
if (base::StartsWith(key_name, key_prefix, base::CompareCase::SENSITIVE)) {
if (C_DestroyObject(session_handle, object_handle) != CKR_OK) {
LOG(ERROR) << "C_DestroyObject failed.";
return false;
}
}
return true;
}
bool Pkcs11KeyStore::GetCertificateFields(const std::string& certificate,
std::string* subject,
std::string* issuer,
std::string* serial_number) {
const unsigned char* asn1_ptr =
reinterpret_cast<const unsigned char*>(certificate.data());
ScopedX509 x509(d2i_X509(nullptr, &asn1_ptr, certificate.size()));
if (!x509) {
LOG(WARNING) << "Pkcs11KeyStore: Failed to decode certificate.";
return false;
}
unsigned char* subject_buffer = nullptr;
int length =
i2d_X509_NAME(X509_get_subject_name(x509.get()), &subject_buffer);
crypto::ScopedOpenSSLBytes scoped_subject_buffer(subject_buffer);
if (length <= 0) {
LOG(WARNING) << "Pkcs11KeyStore: Failed to encode certificate subject.";
return false;
}
subject->assign(reinterpret_cast<char*>(subject_buffer), length);
unsigned char* issuer_buffer = nullptr;
length = i2d_X509_NAME(X509_get_issuer_name(x509.get()), &issuer_buffer);
crypto::ScopedOpenSSLBytes scoped_issuer_buffer(issuer_buffer);
if (length <= 0) {
LOG(WARNING) << "Pkcs11KeyStore: Failed to encode certificate issuer.";
return false;
}
issuer->assign(reinterpret_cast<char*>(issuer_buffer), length);
unsigned char* serial_number_buffer = nullptr;
// TODO(djkurtz): Use X509_get0_serialNumber once i2d_ASN1_INTEGER is
// constified.
length = i2d_ASN1_INTEGER(X509_get_serialNumber(x509.get()),
&serial_number_buffer);
crypto::ScopedOpenSSLBytes scoped_serial_number_buffer(serial_number_buffer);
if (length <= 0) {
LOG(WARNING) << "Pkcs11KeyStore: Failed to encode certificate serial "
"number.";
return false;
}
serial_number->assign(reinterpret_cast<char*>(serial_number_buffer), length);
return true;
}
bool Pkcs11KeyStore::DoesCertificateExist(CK_SESSION_HANDLE session_handle,
const std::string& certificate) {
CK_OBJECT_CLASS object_class = CKO_CERTIFICATE;
CK_BBOOL true_value = CK_TRUE;
CK_BBOOL false_value = CK_FALSE;
std::string mutable_certificate = certificate;
CK_ATTRIBUTE attributes[] = {
{CKA_CLASS, &object_class, sizeof(object_class)},
{CKA_TOKEN, &true_value, sizeof(true_value)},
{CKA_PRIVATE, &false_value, sizeof(false_value)},
{CKA_VALUE, base::data(mutable_certificate), mutable_certificate.size()}};
CK_OBJECT_HANDLE object_handle = CK_INVALID_HANDLE;
CK_ULONG count = 0;
if ((C_FindObjectsInit(session_handle, attributes, base::size(attributes)) !=
CKR_OK) ||
(C_FindObjects(session_handle, &object_handle, 1, &count) != CKR_OK) ||
(C_FindObjectsFinal(session_handle) != CKR_OK)) {
return false;
}
return (count > 0);
}
} // namespace attestation