| // Copyright 2017 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. |
| // |
| // KeyStore interface classes for cert_provision library. |
| |
| #include <base/logging.h> |
| #include <base/stl_util.h> |
| |
| #include "cryptohome/cert/cert_provision_keystore.h" |
| #include <openssl/sha.h> |
| |
| namespace { |
| |
| // An arbitrary application ID to identify PKCS #11 objects. |
| const char kApplicationID[] = |
| "cert_provision_0caa2ccf131a8a4ff0a2b68f38aa180b504a7f65"; |
| const CK_OBJECT_CLASS kDataClass = CKO_DATA; |
| const CK_BBOOL kTrue = CK_TRUE; |
| const CK_BBOOL kFalse = CK_FALSE; |
| |
| // Max supported signature size. With only RSASSA-PKCS1-v1_5 supported, allows |
| // for the signing keys of up to 16k bits (2k bytes) in size. |
| const CK_ULONG kMaxSignatureSize = 2048; |
| |
| std::vector<CK_ATTRIBUTE> GetProvisionStatusAttributes( |
| const std::string& label) { |
| return std::vector<CK_ATTRIBUTE>({ |
| {CKA_CLASS, const_cast<CK_OBJECT_CLASS*>(&kDataClass), |
| sizeof(kDataClass)}, |
| {CKA_APPLICATION, const_cast<char*>(kApplicationID), |
| base::size(kApplicationID)}, |
| {CKA_TOKEN, const_cast<CK_BBOOL*>(&kTrue), sizeof(kTrue)}, |
| {CKA_PRIVATE, const_cast<CK_BBOOL*>(&kTrue), sizeof(kTrue)}, |
| {CKA_MODIFIABLE, const_cast<CK_BBOOL*>(&kFalse), sizeof(kFalse)}, |
| {CKA_LABEL, const_cast<char*>(label.data()), label.size()}, |
| }); |
| } |
| |
| } // namespace |
| |
| namespace cert_provision { |
| |
| KeyStore* KeyStore::subst_obj = nullptr; |
| |
| Scoped<KeyStore> KeyStore::Create() { |
| return subst_obj ? Scoped<KeyStore>(subst_obj) |
| : Scoped<KeyStore>(GetDefault()); |
| } |
| |
| std::unique_ptr<KeyStore> KeyStore::GetDefault() { |
| return std::unique_ptr<KeyStore>(new KeyStoreImpl()); |
| } |
| |
| KeyStoreImpl::~KeyStoreImpl() { |
| TearDown(); |
| } |
| |
| OpResult KeyStoreImpl::Init() { |
| CK_RV res; |
| if (!initialized_) { |
| res = C_Initialize(NULL /* pInitArgs */); |
| if (res != CKR_OK) { |
| return KeyStoreResError("Failed to initialize keystore", res); |
| } |
| initialized_ = true; |
| } |
| |
| res = C_OpenSession(0 /* slotID */, CKF_SERIAL_SESSION | CKF_RW_SESSION, |
| NULL /* pApplication callback parameter */, |
| NULL /* Notify callback */, &session_); |
| if (res != CKR_OK) { |
| return KeyStoreResError("Failed to open session", res); |
| } |
| |
| return OpResult(); |
| } |
| |
| void KeyStoreImpl::TearDown() { |
| if (session_ != CK_INVALID_HANDLE) { |
| C_CloseSession(session_); |
| session_ = CK_INVALID_HANDLE; |
| } |
| if (initialized_) { |
| C_Finalize(NULL); |
| initialized_ = false; |
| } |
| } |
| |
| bool KeyStoreImpl::GetMechanismType(SignMechanism mechanism, |
| CK_MECHANISM_TYPE* type) { |
| DCHECK(type); |
| switch (mechanism) { |
| case SHA1_RSA_PKCS: |
| *type = CKM_SHA1_RSA_PKCS; |
| break; |
| case SHA256_RSA_PKCS: |
| *type = CKM_SHA256_RSA_PKCS; |
| break; |
| case SHA256_RSA_PSS: |
| *type = CKM_SHA256_RSA_PKCS_PSS; |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| OpResult KeyStoreImpl::Find(CK_ATTRIBUTE* attr, |
| CK_ULONG attr_count, |
| std::vector<CK_OBJECT_HANDLE>* objects) { |
| CK_RV res = C_FindObjectsInit(session_, attr, attr_count); |
| if (res != CKR_OK) { |
| return KeyStoreResError("Failed to initialize object search", res); |
| } |
| |
| CK_OBJECT_HANDLE obj; |
| CK_ULONG found; |
| do { |
| res = C_FindObjects(session_, &obj, 1, &found); |
| if (res != CKR_OK) { |
| CK_RV res_final = C_FindObjectsFinal(session_); |
| if (res_final != CKR_OK) { |
| LOG(WARNING) << "Failed to finalize finding objects: " << res_final; |
| } |
| return KeyStoreResError("Failed to find objects", res); |
| } |
| if (found > 0) { |
| objects->push_back(obj); |
| } |
| } while (found > 0); |
| |
| res = C_FindObjectsFinal(session_); |
| if (res != CKR_OK) { |
| return KeyStoreResError("Failed to finalize object search", res); |
| } |
| |
| return OpResult(); |
| } |
| |
| OpResult KeyStoreImpl::Sign(const std::string& id, |
| const std::string& label, |
| SignMechanism mechanism, |
| const std::string& data, |
| std::string* signature) { |
| CK_OBJECT_CLASS class_value = CKO_PRIVATE_KEY; |
| CK_ATTRIBUTE attributes[] = { |
| {CKA_CLASS, &class_value, sizeof(class_value)}, |
| {CKA_ID, const_cast<char*>(id.c_str()), id.size()}, |
| {CKA_LABEL, const_cast<char*>(label.c_str()), label.size()}, |
| }; |
| std::vector<CK_OBJECT_HANDLE> objects; |
| OpResult result = Find(attributes, base::size(attributes), &objects); |
| if (!result) { |
| return result; |
| } |
| if (objects.size() != 1) { |
| return {Status::KeyStoreError, |
| objects.size() ? "Multiple keys found." : "No key to sign."}; |
| } |
| |
| CK_MECHANISM mech; |
| CK_RSA_PKCS_PSS_PARAMS params; |
| if (!GetMechanismType(mechanism, &mech.mechanism)) { |
| return {Status::KeyStoreError, "Unknown sign mechanism."}; |
| } |
| if (mechanism == SHA256_RSA_PSS) { |
| // Get the length of the RSA key |
| CK_ATTRIBUTE attribute_template[] = { |
| {CKA_MODULUS, nullptr, 0}, |
| }; |
| CK_RV ret = C_GetAttributeValue(session_, objects[0], attribute_template, |
| base::size(attribute_template)); |
| if (ret != CKR_OK) { |
| return KeyStoreResError("Failed to get attribute value", ret); |
| } |
| |
| int rsa_size = attribute_template[0].ulValueLen; |
| CK_ULONG max_sLen = rsa_size - 2 - SHA256_DIGEST_LENGTH; |
| |
| params = {CKM_SHA256_RSA_PKCS_PSS, CKG_MGF1_SHA256, max_sLen}; |
| mech.pParameter = ¶ms; |
| mech.ulParameterLen = sizeof(params); |
| } else { |
| mech.pParameter = NULL; |
| mech.ulParameterLen = 0; |
| } |
| |
| CK_RV res = C_SignInit(session_, &mech, objects[0]); |
| if (res != CKR_OK) { |
| return KeyStoreResError("Failed to initialize signing", res); |
| } |
| |
| CK_ULONG sig_len = kMaxSignatureSize; |
| VLOG(2) << "C_Sign max signature size = " << sig_len; |
| signature->assign(sig_len, 0); |
| res = C_Sign( |
| session_, reinterpret_cast<CK_BYTE_PTR>(const_cast<char*>(data.data())), |
| data.size(), |
| reinterpret_cast<CK_BYTE_PTR>(const_cast<char*>(signature->data())), |
| &sig_len); |
| if (res != CKR_OK) { |
| return KeyStoreResError("Failed to sign", res); |
| } |
| VLOG(2) << "C_Sign resulting signature size = " << sig_len; |
| signature->resize(sig_len); |
| return OpResult(); |
| } |
| |
| OpResult KeyStoreImpl::ReadProvisionStatus( |
| const std::string& label, google::protobuf::MessageLite* provision_status) { |
| auto attributes = GetProvisionStatusAttributes(label); |
| std::vector<CK_OBJECT_HANDLE> objects; |
| OpResult result = Find(attributes.data(), attributes.size(), &objects); |
| if (!result) { |
| return result; |
| } |
| if (objects.size() == 0) { |
| provision_status->Clear(); |
| } else if (objects.size() == 1) { |
| CK_OBJECT_HANDLE object = objects[0]; |
| CK_ATTRIBUTE attribute = {CKA_VALUE, NULL, 0}; |
| CK_RV res = C_GetAttributeValue(session_, object, &attribute, 1); |
| if (res != CKR_OK) { |
| return KeyStoreResError("Failed to get provision status size", res); |
| } |
| |
| std::string value(attribute.ulValueLen, 0); |
| attribute.pValue = const_cast<char*>(value.data()); |
| res = C_GetAttributeValue(session_, object, &attribute, 1); |
| if (res != CKR_OK) { |
| return KeyStoreResError("Failed to get provision status", res); |
| } |
| |
| provision_status->ParseFromString(value); |
| } else { |
| return {Status::KeyStoreError, "Multiple provision statuses found."}; |
| } |
| return OpResult(); |
| } |
| |
| OpResult KeyStoreImpl::WriteProvisionStatus( |
| const std::string& label, |
| const google::protobuf::MessageLite& provision_status) { |
| auto attributes = GetProvisionStatusAttributes(label); |
| std::vector<CK_OBJECT_HANDLE> objects; |
| OpResult result = Find(attributes.data(), attributes.size(), &objects); |
| if (!result) { |
| return result; |
| } |
| CK_RV res; |
| for (auto& object : objects) { |
| res = C_DestroyObject(session_, object); |
| if (res != CKR_OK) { |
| return KeyStoreResError("Failed to delete previous provision status", |
| res); |
| } |
| } |
| |
| std::string value; |
| provision_status.SerializeToString(&value); |
| attributes.push_back( |
| {CKA_VALUE, const_cast<char*>(value.data()), value.size()}); |
| CK_OBJECT_HANDLE object; |
| res = C_CreateObject(session_, attributes.data(), attributes.size(), &object); |
| if (res != CKR_OK) { |
| return KeyStoreResError("Failed to write provision status", res); |
| } |
| return OpResult(); |
| } |
| |
| OpResult KeyStoreImpl::DeleteKeys(const std::string& id, |
| const std::string& label) { |
| CK_ATTRIBUTE attributes[] = { |
| {CKA_ID, const_cast<char*>(id.c_str()), id.size()}, |
| {CKA_LABEL, const_cast<char*>(label.c_str()), label.size()}, |
| }; |
| std::vector<CK_OBJECT_HANDLE> objects; |
| OpResult result = Find(attributes, base::size(attributes), &objects); |
| if (!result) { |
| return result; |
| } |
| CK_RV res; |
| for (auto object : objects) { |
| VLOG(1) << "Deleting old object " << object; |
| res = C_DestroyObject(session_, object); |
| if (res != CKR_OK) { |
| LOG(WARNING) << "Failed to delete old object " << object << " for label " |
| << label; |
| } |
| } |
| return OpResult(); |
| } |
| |
| } // namespace cert_provision |