| // 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 |