blob: 59f6912108919ddae8eb29fb1a5ca0e1b95f61d4 [file] [log] [blame]
// Copyright (c) 2012 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 "chaps/session_impl.h"
#include <limits>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <base/check.h>
#include <base/check_op.h>
#include <base/logging.h>
#include <base/notreached.h>
#include <brillo/secure_blob.h>
#include <crypto/libcrypto-compat.h>
#include <crypto/scoped_openssl_types.h>
#include <openssl/bio.h>
#include <openssl/des.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/ec.h>
#include "chaps/chaps.h"
#include "chaps/chaps_factory.h"
#include "chaps/chaps_utility.h"
#include "chaps/object.h"
#include "chaps/object_pool.h"
#include "chaps/tpm_utility.h"
#include "pkcs11/cryptoki.h"
using brillo::SecureBlob;
using ScopedASN1_OCTET_STRING =
crypto::ScopedOpenSSL<ASN1_OCTET_STRING, ASN1_OCTET_STRING_free>;
using std::hex;
using std::map;
using std::set;
using std::string;
using std::vector;
namespace chaps {
namespace {
using chaps::OperationType::kDecrypt;
using chaps::OperationType::kDigest;
using chaps::OperationType::kEncrypt;
using chaps::OperationType::kSign;
using chaps::OperationType::kVerify;
const int kDefaultAuthDataBytes = 20;
const int kMaxCipherBlockBytes = 16;
const int kMaxRSAOutputBytes = 2048;
const int kMaxDigestOutputBytes = EVP_MAX_MD_SIZE;
const int kMinRSAKeyBits = 512;
const int kMaxRSAKeyBits = kMaxRSAOutputBytes * 8;
CK_RV ResultToRV(chaps::ObjectPool::Result result, CK_RV fail_rv) {
switch (result) {
case chaps::ObjectPool::Result::Success:
return CKR_OK;
case chaps::ObjectPool::Result::Failure:
return fail_rv;
case chaps::ObjectPool::Result::WaitForPrivateObjects:
return CKR_WOULD_BLOCK_FOR_PRIVATE_OBJECTS;
}
}
bool IsSuccess(chaps::ObjectPool::Result result) {
return result == chaps::ObjectPool::Result::Success;
}
class MechanismInfo {
public:
explicit MechanismInfo(CK_MECHANISM_TYPE mechanism);
bool IsSupported() const;
bool IsOperationValid(chaps::OperationType op) const;
bool IsForKeyType(CK_KEY_TYPE keytype) const;
private:
struct MechanismInfoData {
bool is_supported;
set<chaps::OperationType> operation;
CK_KEY_TYPE key_type;
};
static MechanismInfoData GetSupportedMechanismInfo(
CK_MECHANISM_TYPE mechanism);
MechanismInfoData data_;
};
MechanismInfo::MechanismInfo(CK_MECHANISM_TYPE mechanism)
: data_(GetSupportedMechanismInfo(mechanism)) {}
bool MechanismInfo::IsSupported() const {
return data_.is_supported;
}
bool MechanismInfo::IsOperationValid(chaps::OperationType op) const {
return IsSupported() && data_.operation.count(op) > 0;
}
bool MechanismInfo::IsForKeyType(CK_KEY_TYPE keytype) const {
return IsSupported() && data_.key_type == keytype;
}
MechanismInfo::MechanismInfoData MechanismInfo::GetSupportedMechanismInfo(
CK_MECHANISM_TYPE mechanism) {
switch (mechanism) {
// DES
case CKM_DES_ECB:
case CKM_DES_CBC:
case CKM_DES_CBC_PAD:
return {true, {kEncrypt, kDecrypt}, CKK_DES};
// DES3
case CKM_DES3_ECB:
case CKM_DES3_CBC:
case CKM_DES3_CBC_PAD:
return {true, {kEncrypt, kDecrypt}, CKK_DES3};
// AES
case CKM_AES_ECB:
case CKM_AES_CBC:
case CKM_AES_CBC_PAD:
return {true, {kEncrypt, kDecrypt}, CKK_AES};
// RSA PKCS v1.5
case CKM_RSA_PKCS:
return {true, {kEncrypt, kDecrypt, kSign, kVerify}, CKK_RSA};
case CKM_MD5_RSA_PKCS:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
return {true, {kSign, kVerify}, CKK_RSA};
// RSA RSS
case CKM_RSA_PKCS_PSS:
case CKM_SHA1_RSA_PKCS_PSS:
case CKM_SHA256_RSA_PKCS_PSS:
case CKM_SHA384_RSA_PKCS_PSS:
case CKM_SHA512_RSA_PKCS_PSS:
case CKM_SHA224_RSA_PKCS_PSS:
return {true, {kSign, kVerify}, CKK_RSA};
// ECC
case CKM_ECDSA:
case CKM_ECDSA_SHA1:
return {true, {kSign, kVerify}, CKK_EC};
// HMAC
case CKM_MD5_HMAC:
case CKM_SHA_1_HMAC:
case CKM_SHA256_HMAC:
case CKM_SHA384_HMAC:
case CKM_SHA512_HMAC:
return {true, {kSign, kVerify}, CKK_GENERIC_SECRET};
// Digest
case CKM_MD5:
case CKM_SHA_1:
case CKM_SHA256:
case CKM_SHA384:
case CKM_SHA512:
return {true, {kDigest}, CKK_INVALID_KEY_TYPE};
default:
return {false, {}, CKK_INVALID_KEY_TYPE};
}
}
bool IsHMAC(CK_MECHANISM_TYPE mechanism) {
return MechanismInfo(mechanism).IsForKeyType(CKK_GENERIC_SECRET);
}
// Returns true if the given block cipher (AES/DES) mechanism uses padding.
bool IsPaddingEnabled(CK_MECHANISM_TYPE mechanism) {
switch (mechanism) {
case CKM_DES_CBC_PAD:
case CKM_DES3_CBC_PAD:
case CKM_AES_CBC_PAD:
return true;
default:
return false;
}
}
bool IsRSA(CK_MECHANISM_TYPE mechanism) {
return MechanismInfo(mechanism).IsForKeyType(CKK_RSA);
}
bool IsECC(CK_MECHANISM_TYPE mechanism) {
return MechanismInfo(mechanism).IsForKeyType(CKK_EC);
}
bool IsMechanismValidForOperation(chaps::OperationType operation,
CK_MECHANISM_TYPE mechanism) {
return MechanismInfo(mechanism).IsOperationValid(operation);
}
CK_OBJECT_CLASS GetExpectedObjectClass(chaps::OperationType operation,
CK_KEY_TYPE key_type) {
bool use_private_key = operation == kSign || operation == kDecrypt;
switch (key_type) {
case CKK_DES:
case CKK_DES3:
case CKK_AES:
return CKO_SECRET_KEY;
case CKK_RSA:
case CKK_EC:
return use_private_key ? CKO_PRIVATE_KEY : CKO_PUBLIC_KEY;
case CKK_GENERIC_SECRET:
return CKO_SECRET_KEY;
default:
// Never used
NOTREACHED();
return -1;
}
}
// Check |object_class| and |key_type| is valid for |mechanism| and |operation|
bool IsValidKeyType(chaps::OperationType operation,
CK_MECHANISM_TYPE mechanism,
CK_OBJECT_CLASS object_class,
CK_KEY_TYPE key_type) {
return MechanismInfo(mechanism).IsForKeyType(key_type) &&
object_class == GetExpectedObjectClass(operation, key_type);
}
const EVP_CIPHER* GetOpenSSLCipher(CK_MECHANISM_TYPE mechanism,
size_t key_size) {
switch (mechanism) {
case CKM_DES_ECB:
return EVP_des_ecb();
case CKM_DES_CBC:
case CKM_DES_CBC_PAD:
return EVP_des_cbc();
case CKM_DES3_ECB:
return EVP_des_ede3();
case CKM_DES3_CBC:
case CKM_DES3_CBC_PAD:
return EVP_des_ede3_cbc();
case CKM_AES_ECB:
switch (key_size) {
case 16:
return EVP_aes_128_ecb();
case 24:
return EVP_aes_192_ecb();
default:
return EVP_aes_256_ecb();
}
break;
case CKM_AES_CBC:
case CKM_AES_CBC_PAD:
switch (key_size) {
case 16:
return EVP_aes_128_cbc();
case 24:
return EVP_aes_192_cbc();
default:
return EVP_aes_256_cbc();
}
break;
}
return nullptr;
}
string GetDERDigestInfo(CK_MECHANISM_TYPE mechanism) {
return GetDigestAlgorithmEncoding(chaps::GetDigestAlgorithm(mechanism));
}
// TODO(menghuan): Move Create*KeyFromObject to the member function of object.
crypto::ScopedEC_KEY CreateECCPublicKeyFromObject(const Object* key_object) {
// Start parsing EC_PARAMS
string ec_params = key_object->GetAttributeString(CKA_EC_PARAMS);
crypto::ScopedEC_KEY key = chaps::CreateECCKeyFromEC_PARAMS(ec_params);
if (key == nullptr)
return nullptr;
// Start parsing EC_POINT
// DER decode EC_POINT to OCT_STRING
string pub_data = key_object->GetAttributeString(CKA_EC_POINT);
const unsigned char* buf = chaps::ConvertStringToByteBuffer(pub_data.data());
ScopedASN1_OCTET_STRING os(
d2i_ASN1_OCTET_STRING(nullptr, &buf, pub_data.size()));
if (os == nullptr)
return nullptr;
// Convert OCT_STRING to *EC_KEY
buf = os->data;
EC_KEY* key_ptr = key.get();
key_ptr = o2i_ECPublicKey(&key_ptr, &buf, os->length);
if (key_ptr == nullptr)
return nullptr;
CHECK_EQ(key_ptr, key.get());
if (!EC_KEY_check_key(key.get())) {
LOG(ERROR) << __func__
<< ": Bad key created from object. OpenSSL key check fail.";
return nullptr;
}
return key;
}
crypto::ScopedEC_KEY CreateECCPrivateKeyFromObject(const Object* key_object) {
// Parse EC_PARAMS
string ec_params = key_object->GetAttributeString(CKA_EC_PARAMS);
crypto::ScopedEC_KEY key = chaps::CreateECCKeyFromEC_PARAMS(ec_params);
if (key == nullptr)
return nullptr;
crypto::ScopedBIGNUM d(BN_new());
if (!d) {
LOG(ERROR) << "Failed to allocate BIGNUM.";
return nullptr;
}
if (!chaps::ConvertToBIGNUM(key_object->GetAttributeString(CKA_VALUE),
d.get())) {
LOG(ERROR) << "Failed to convert CKA_VALUE to BIGNUM.";
return nullptr;
}
if (!EC_KEY_set_private_key(key.get(), d.get()))
return nullptr;
// OpenSSL will not set public key field. Need to manually compute.
const EC_GROUP* group = EC_KEY_get0_group(key.get());
if (group == nullptr)
return nullptr;
crypto::ScopedEC_POINT pub_key(EC_POINT_new(group));
EC_POINT_mul(group, pub_key.get(), d.get(), nullptr, nullptr, nullptr);
if (!EC_KEY_set_public_key(key.get(), pub_key.get()))
return nullptr;
if (!EC_KEY_check_key(key.get())) {
LOG(ERROR) << __func__
<< ": Bad key created from object. OpenSSL key check fail.";
return nullptr;
}
return key;
}
crypto::ScopedRSA CreateRSAKeyFromObject(const chaps::Object* key_object) {
crypto::ScopedRSA rsa(RSA_new());
if (!rsa) {
LOG(ERROR) << "Failed to allocate RSA or BIGNUM for key.";
return nullptr;
}
if (key_object->GetObjectClass() == CKO_PUBLIC_KEY) {
crypto::ScopedBIGNUM rsa_n(BN_new()), rsa_e(BN_new());
if (!rsa_n || !rsa_e) {
LOG(ERROR) << "Failed to allocate RSA or BIGNUM for key.";
return nullptr;
}
string n = key_object->GetAttributeString(CKA_MODULUS);
string e = key_object->GetAttributeString(CKA_PUBLIC_EXPONENT);
if (!chaps::ConvertToBIGNUM(n, rsa_n.get()) ||
!chaps::ConvertToBIGNUM(e, rsa_e.get())) {
LOG(ERROR) << "Failed to convert modulus or exponent for key.";
return nullptr;
}
if (!RSA_set0_key(rsa.get(), rsa_n.release(), rsa_e.release(), nullptr)) {
LOG(ERROR) << "Failed to set modulus or exponent for RSA.";
return nullptr;
}
} else { // key_object->GetObjectClass() == CKO_PRIVATE_KEY
crypto::ScopedBIGNUM rsa_n(BN_new()), rsa_e(BN_new()), rsa_d(BN_new()),
rsa_p(BN_new()), rsa_q(BN_new()), rsa_dmp1(BN_new()),
rsa_dmq1(BN_new()), rsa_iqmp(BN_new());
if (!rsa_n || !rsa_e || !rsa_d || !rsa_p || !rsa_q || !rsa_dmp1 ||
!rsa_dmq1 || !rsa_iqmp) {
LOG(ERROR) << "Failed to allocate BIGNUM for private key.";
return nullptr;
}
string n = key_object->GetAttributeString(CKA_MODULUS);
string e = key_object->GetAttributeString(CKA_PUBLIC_EXPONENT);
string d = key_object->GetAttributeString(CKA_PRIVATE_EXPONENT);
string p = key_object->GetAttributeString(CKA_PRIME_1);
string q = key_object->GetAttributeString(CKA_PRIME_2);
string dmp1 = key_object->GetAttributeString(CKA_EXPONENT_1);
string dmq1 = key_object->GetAttributeString(CKA_EXPONENT_2);
string iqmp = key_object->GetAttributeString(CKA_COEFFICIENT);
if (!chaps::ConvertToBIGNUM(n, rsa_n.get()) ||
!chaps::ConvertToBIGNUM(e, rsa_e.get()) ||
!chaps::ConvertToBIGNUM(d, rsa_d.get()) ||
!chaps::ConvertToBIGNUM(p, rsa_p.get()) ||
!chaps::ConvertToBIGNUM(q, rsa_q.get()) ||
!chaps::ConvertToBIGNUM(dmp1, rsa_dmp1.get()) ||
!chaps::ConvertToBIGNUM(dmq1, rsa_dmq1.get()) ||
!chaps::ConvertToBIGNUM(iqmp, rsa_iqmp.get())) {
LOG(ERROR) << "Failed to convert parameters for private key.";
return nullptr;
}
if (!RSA_set0_key(rsa.get(), rsa_n.release(), rsa_e.release(),
rsa_d.release()) ||
!RSA_set0_factors(rsa.get(), rsa_p.release(), rsa_q.release()) ||
!RSA_set0_crt_params(rsa.get(), rsa_dmp1.release(), rsa_dmq1.release(),
rsa_iqmp.release())) {
LOG(ERROR) << "Failed to set parameters for private key RSA.";
return nullptr;
}
}
return rsa;
}
// TODO(crbug/916023): Move OpenSSL utility to cross daemon library.
// Return the length (in bytes) of group order of the EC key |key| or 0 on error
// which is aligned with OpenSSL design.
size_t GetGroupOrderLengthFromEcKey(const crypto::ScopedEC_KEY& key) {
crypto::ScopedBIGNUM order(BN_new());
if (!order) {
LOG(ERROR) << "Failed to allocate BIGNUM for EC key order.";
return 0;
}
const EC_GROUP* group = EC_KEY_get0_group(key.get());
if (group == nullptr)
return 0;
if (!EC_GROUP_get_order(group, order.get(), nullptr))
return 0;
return BN_num_bytes(order.get());
}
// RSA Sign/Verify Helper
class RSASignerVerifier {
public:
// Just a default destructor, nothing more, nothing less.
// Note: It's here because this is a base class, and we want to avoid having
// non-virtual destructor in base class.
virtual ~RSASignerVerifier() = default;
// Sign |context->data_| with |rsa|.
virtual bool Sign(crypto::ScopedRSA rsa,
SessionImpl::OperationContext* context) = 0;
// Verify |signature| of |digest| against |rsa|.
virtual CK_RV Verify(crypto::ScopedRSA rsa,
SessionImpl::OperationContext* context,
const string& digest,
const string& signature) = 0;
static std::unique_ptr<RSASignerVerifier> GetForMechanism(
CK_MECHANISM_TYPE mechanism);
};
// Sign/Verify helper for PKCS#1 v1.5
class RSASignerVerifierImplPKCS115 : public RSASignerVerifier {
public:
virtual ~RSASignerVerifierImplPKCS115() = default;
bool Sign(crypto::ScopedRSA rsa,
SessionImpl::OperationContext* context) final {
if (RSA_size(rsa.get()) > kMaxRSAOutputBytes) {
LOG(ERROR) << __func__ << ": RSA Key size is too large for PKCS#1 v1.5.";
return false;
}
uint8_t buffer[kMaxRSAOutputBytes];
// Emulate RSASSA by performing raw RSA (decrypting) with RSA_PKCS1_PADDING
string input = GetDERDigestInfo(context->mechanism_) + context->data_;
int length = RSA_private_encrypt(
input.length(), ConvertStringToByteBuffer(input.data()), buffer,
rsa.get(), RSA_PKCS1_PADDING); // Adds PKCS #1 type 1 padding.
if (length == -1) {
LOG(ERROR) << __func__ << ": RSA_private_encrypt failed for PKCS#1 v1.5: "
<< GetOpenSSLError();
return false;
}
// Set the signature in context->data_.
context->data_ = string(reinterpret_cast<char*>(buffer), length);
return true;
};
CK_RV Verify(crypto::ScopedRSA rsa,
SessionImpl::OperationContext* context,
const string& digest,
const string& signature) final {
if (RSA_size(rsa.get()) > kMaxRSAOutputBytes) {
LOG(ERROR) << __func__ << ": RSA Key size is too large for PKCS#1 v1.5.";
return CKR_KEY_SIZE_RANGE;
}
uint8_t buffer[kMaxRSAOutputBytes];
int length = RSA_public_decrypt(
signature.length(), ConvertStringToByteBuffer(signature.data()), buffer,
rsa.get(), RSA_PKCS1_PADDING); // Strips PKCS #1 type 1 padding.
if (length == -1) {
LOG(ERROR) << __func__ << ": RSA_public_decrypt failed for PKCS#1 v1.5: "
<< GetOpenSSLError();
return CKR_SIGNATURE_INVALID;
}
string signed_data = GetDERDigestInfo(context->mechanism_) + digest;
if (static_cast<size_t>(length) != signed_data.length() ||
0 != brillo::SecureMemcmp(buffer, signed_data.data(), length)) {
return CKR_SIGNATURE_INVALID;
}
return CKR_OK;
};
};
// Sign/Verify helper for RSA PSS
class RSASignerVerifierImplPSS : public RSASignerVerifier {
public:
virtual ~RSASignerVerifierImplPSS() = default;
bool Sign(crypto::ScopedRSA rsa,
SessionImpl::OperationContext* context) final {
if (RSA_size(rsa.get()) > kMaxRSAOutputBytes) {
LOG(ERROR) << __func__ << ": RSA Key size is too large for RSA PSS.";
return false;
}
uint8_t buffer[kMaxRSAOutputBytes];
DigestAlgorithm digest_algorithm = GetDigestAlgorithm(context->mechanism_);
// Parse the RSA PSS Parameters.
const CK_RSA_PKCS_PSS_PARAMS* pss_params = nullptr;
const EVP_MD* mgf1_hash = nullptr;
if (!ParseRSAPSSParams(context->mechanism_, context->parameter_,
&pss_params, &mgf1_hash, &digest_algorithm)) {
LOG(ERROR) << __func__ << ": Failed to parse RSA PSS parameters.";
return false;
}
string padded_data(RSA_size(rsa.get()), 0);
if (RSA_padding_add_PKCS1_PSS_mgf1(
rsa.get(),
reinterpret_cast<unsigned char*>(base::data(padded_data)),
reinterpret_cast<const unsigned char*>(base::data(context->data_)),
GetOpenSSLDigest(digest_algorithm), mgf1_hash,
pss_params->sLen) != 1) {
LOG(ERROR) << __func__ << ": Failed to produce the PSA PSS paddings.";
return false;
}
int length = RSA_private_encrypt(
padded_data.length(), ConvertStringToByteBuffer(padded_data.data()),
buffer, rsa.get(), RSA_NO_PADDING);
if (length == -1) {
LOG(ERROR) << __func__
<< ": RSA_private_encrypt failed: " << GetOpenSSLError();
return false;
}
// Set the signature in context->data_.
context->data_ = string(reinterpret_cast<char*>(buffer), length);
return true;
};
CK_RV Verify(crypto::ScopedRSA rsa,
SessionImpl::OperationContext* context,
const string& digest,
const string& signature) final {
if (RSA_size(rsa.get()) > kMaxRSAOutputBytes) {
LOG(ERROR) << __func__ << ": RSA Key size is too large for RSA PSS.";
return CKR_KEY_SIZE_RANGE;
}
DigestAlgorithm digest_algorithm = GetDigestAlgorithm(context->mechanism_);
uint8_t buffer[kMaxRSAOutputBytes];
// Parse the RSA PSS Parameters.
const CK_RSA_PKCS_PSS_PARAMS* pss_params = nullptr;
const EVP_MD* mgf1_hash = nullptr;
if (!ParseRSAPSSParams(context->mechanism_, context->parameter_,
&pss_params, &mgf1_hash, &digest_algorithm)) {
LOG(ERROR) << __func__ << ": Failed to parse RSA PSS parameters.";
return CKR_SIGNATURE_INVALID;
}
int expected_size = EVP_MD_size(GetOpenSSLDigest(digest_algorithm));
if (digest.size() != expected_size) {
LOG(ERROR) << __func__ << ": Size mismatch with RSAPSS, expected "
<< expected_size << ", actual " << digest.size();
return CKR_SIGNATURE_INVALID;
}
int length = RSA_public_decrypt(signature.length(),
ConvertStringToByteBuffer(signature.data()),
buffer, rsa.get(), RSA_NO_PADDING);
if (length == -1) {
LOG(ERROR) << __func__
<< ": RSA_public_decrypt failed: " << GetOpenSSLError();
return CKR_SIGNATURE_INVALID;
}
if (RSA_verify_PKCS1_PSS_mgf1(
rsa.get(), reinterpret_cast<const unsigned char*>(digest.data()),
GetOpenSSLDigest(digest_algorithm), mgf1_hash, buffer,
pss_params->sLen) != 1) {
LOG(ERROR) << __func__ << ": Incorrect PSS padding.";
return CKR_SIGNATURE_INVALID;
}
return CKR_OK;
};
};
std::unique_ptr<RSASignerVerifier> RSASignerVerifier::GetForMechanism(
CK_MECHANISM_TYPE mechanism) {
auto scheme = GetSigningSchemeForMechanism(mechanism);
switch (scheme) {
case RsaPaddingScheme::RSASSA_PKCS1_V1_5:
return std::make_unique<RSASignerVerifierImplPKCS115>();
case RsaPaddingScheme::RSASSA_PSS:
return std::make_unique<RSASignerVerifierImplPSS>();
default:
LOG(ERROR) << __func__ << ": Invalid mechanism "
<< static_cast<int64_t>(mechanism);
return nullptr;
}
}
} // namespace
SessionImpl::SessionImpl(int slot_id,
ObjectPool* token_object_pool,
TPMUtility* tpm_utility,
ChapsFactory* factory,
HandleGenerator* handle_generator,
bool is_read_only)
: factory_(factory),
find_results_valid_(false),
is_read_only_(is_read_only),
slot_id_(slot_id),
token_object_pool_(token_object_pool),
tpm_utility_(tpm_utility),
is_legacy_loaded_(false),
private_root_key_(0),
public_root_key_(0) {
CHECK(token_object_pool_);
CHECK(tpm_utility_);
CHECK(factory_);
session_object_pool_.reset(
factory_->CreateObjectPool(handle_generator, nullptr, nullptr, nullptr));
CHECK(session_object_pool_.get());
}
SessionImpl::~SessionImpl() {}
int SessionImpl::GetSlot() const {
return slot_id_;
}
CK_STATE SessionImpl::GetState() const {
return is_read_only_ ? CKS_RO_USER_FUNCTIONS : CKS_RW_USER_FUNCTIONS;
}
bool SessionImpl::IsReadOnly() const {
return is_read_only_;
}
bool SessionImpl::IsOperationActive(OperationType type) const {
CHECK(type < kNumOperationTypes);
return operation_context_[type].is_valid_;
}
CK_RV SessionImpl::CreateObject(const CK_ATTRIBUTE_PTR attributes,
int num_attributes,
int* new_object_handle) {
return CreateObjectInternal(attributes, num_attributes, nullptr,
new_object_handle);
}
CK_RV SessionImpl::CopyObject(const CK_ATTRIBUTE_PTR attributes,
int num_attributes,
int object_handle,
int* new_object_handle) {
const Object* orig_object = nullptr;
if (!GetObject(object_handle, &orig_object))
return CKR_OBJECT_HANDLE_INVALID;
CHECK(orig_object);
return CreateObjectInternal(attributes, num_attributes, orig_object,
new_object_handle);
}
CK_RV SessionImpl::DestroyObject(int object_handle) {
const Object* object = nullptr;
if (!GetObject(object_handle, &object))
return CKR_OBJECT_HANDLE_INVALID;
CHECK(object);
ObjectPool* pool =
object->IsTokenObject() ? token_object_pool_ : session_object_pool_.get();
return ResultToRV(pool->Delete(object), CKR_GENERAL_ERROR);
}
bool SessionImpl::GetObject(int object_handle, const Object** object) {
CHECK(object);
if (token_object_pool_->FindByHandle(object_handle, object) ==
ObjectPool::Result::Success)
return true;
return session_object_pool_->FindByHandle(object_handle, object) ==
ObjectPool::Result::Success;
}
bool SessionImpl::GetModifiableObject(int object_handle, Object** object) {
CHECK(object);
const Object* const_object;
if (!GetObject(object_handle, &const_object))
return false;
ObjectPool* pool = const_object->IsTokenObject() ? token_object_pool_
: session_object_pool_.get();
*object = pool->GetModifiableObject(const_object);
return true;
}
CK_RV SessionImpl::FlushModifiableObject(Object* object) {
CHECK(object);
ObjectPool* pool =
object->IsTokenObject() ? token_object_pool_ : session_object_pool_.get();
return ResultToRV(pool->Flush(object), CKR_FUNCTION_FAILED);
}
CK_RV SessionImpl::FindObjectsInit(const CK_ATTRIBUTE_PTR attributes,
int num_attributes) {
if (find_results_valid_)
return CKR_OPERATION_ACTIVE;
std::unique_ptr<Object> search_template(factory_->CreateObject());
CHECK(search_template.get());
search_template->SetAttributes(attributes, num_attributes);
vector<const Object*> objects;
if (!search_template->IsAttributePresent(CKA_TOKEN) ||
search_template->IsTokenObject()) {
auto res = token_object_pool_->Find(search_template.get(), &objects);
if (!IsSuccess(res))
return ResultToRV(res, CKR_GENERAL_ERROR);
}
if (!search_template->IsAttributePresent(CKA_TOKEN) ||
!search_template->IsTokenObject()) {
auto res = session_object_pool_->Find(search_template.get(), &objects);
if (!IsSuccess(res))
return ResultToRV(res, CKR_GENERAL_ERROR);
}
find_results_.clear();
find_results_offset_ = 0;
find_results_valid_ = true;
for (size_t i = 0; i < objects.size(); ++i) {
find_results_.push_back(objects[i]->handle());
}
return CKR_OK;
}
CK_RV SessionImpl::FindObjects(int max_object_count,
vector<int>* object_handles) {
CHECK(object_handles);
if (!find_results_valid_)
return CKR_OPERATION_NOT_INITIALIZED;
size_t end_offset =
find_results_offset_ + static_cast<size_t>(max_object_count);
if (end_offset > find_results_.size())
end_offset = find_results_.size();
for (size_t i = find_results_offset_; i < end_offset; ++i) {
object_handles->push_back(find_results_[i]);
}
find_results_offset_ += object_handles->size();
return CKR_OK;
}
CK_RV SessionImpl::FindObjectsFinal() {
if (!find_results_valid_)
return CKR_OPERATION_NOT_INITIALIZED;
find_results_valid_ = false;
return CKR_OK;
}
CK_RV SessionImpl::OperationInit(OperationType operation,
CK_MECHANISM_TYPE mechanism,
const string& mechanism_parameter,
const Object* key) {
CHECK(operation < kNumOperationTypes);
OperationContext* context = &operation_context_[operation];
if (context->is_valid_) {
LOG(ERROR) << "Operation is already active.";
return CKR_OPERATION_ACTIVE;
}
context->Clear();
context->mechanism_ = mechanism;
context->parameter_ = mechanism_parameter;
if (!IsMechanismValidForOperation(operation, mechanism)) {
LOG(ERROR) << "Mechanism not supported: 0x" << hex << mechanism;
return CKR_MECHANISM_INVALID;
}
if (operation == kSign || operation == kVerify || operation == kEncrypt ||
operation == kDecrypt) {
// Make sure the key is valid for the mechanism.
CHECK(key);
if (!IsValidKeyType(
operation, mechanism, key->GetObjectClass(),
key->GetAttributeInt(CKA_KEY_TYPE, CK_UNAVAILABLE_INFORMATION))) {
LOG(ERROR) << "Key type mismatch.";
return CKR_KEY_TYPE_INCONSISTENT;
}
if (!key->GetAttributeBool(GetRequiredKeyUsage(operation), false)) {
LOG(ERROR) << "Key function not permitted.";
return CKR_KEY_FUNCTION_NOT_PERMITTED;
}
if (IsRSA(mechanism)) {
// Refuse to use RSA keys with unsupported sizes that may have been
// created in an earlier version of chaps.
int key_size = key->GetAttributeString(CKA_MODULUS).length() * 8;
if (key_size < kMinRSAKeyBits || key_size > kMaxRSAKeyBits) {
LOG(ERROR) << "Key size not supported: " << key_size;
return CKR_KEY_SIZE_RANGE;
}
}
}
if (operation == kEncrypt || operation == kDecrypt) {
if (mechanism == CKM_RSA_PKCS) {
context->key_ = key;
context->is_valid_ = true;
} else {
return CipherInit((operation == kEncrypt), mechanism, mechanism_parameter,
key);
}
} else if (operation == kSign || operation == kVerify ||
operation == kDigest) {
// It is valid for GetOpenSSLDigestForMechanism to return NULL (e.g.
// CKM_RSA_PKCS).
const EVP_MD* digest = GetOpenSSLDigestForMechanism(mechanism);
if (IsHMAC(mechanism)) {
string key_material = key->GetAttributeString(CKA_VALUE);
context->hmac_context_.reset(HMAC_CTX_new());
if (!context->hmac_context_) {
LOG(ERROR) << "Failed to allocate HMAC context";
return CKR_FUNCTION_FAILED;
}
HMAC_Init_ex(context->hmac_context_.get(), key_material.data(),
key_material.length(), digest, nullptr);
context->is_hmac_ = true;
} else if (digest) {
context->digest_context_.reset(EVP_MD_CTX_new());
if (!context->digest_context_) {
LOG(ERROR) << "Failed to allocate EVP_MD context";
return CKR_FUNCTION_FAILED;
}
EVP_DigestInit_ex(context->digest_context_.get(), digest, nullptr);
context->is_digest_ = true;
}
if (IsRSA(mechanism) || IsECC(mechanism))
context->key_ = key;
context->is_valid_ = true;
} else {
NOTREACHED();
return CKR_FUNCTION_FAILED;
}
return CKR_OK;
}
CK_RV SessionImpl::OperationUpdate(OperationType operation,
const string& data_in,
int* required_out_length,
string* data_out) {
CHECK(operation < kNumOperationTypes);
OperationContext* context = &operation_context_[operation];
if (!context->is_valid_) {
LOG(ERROR) << "Operation is not initialized.";
return CKR_OPERATION_NOT_INITIALIZED;
}
if (context->is_finished_) {
LOG(ERROR) << "Operation is finished.";
OperationCancel(operation);
return CKR_OPERATION_ACTIVE;
}
context->is_incremental_ = true;
return OperationUpdateInternal(operation, data_in, required_out_length,
data_out);
}
CK_RV SessionImpl::OperationUpdateInternal(OperationType operation,
const string& data_in,
int* required_out_length,
string* data_out) {
CHECK(operation < kNumOperationTypes);
OperationContext* context = &operation_context_[operation];
if (context->is_cipher_) {
CK_RV rv = CipherUpdate(context, data_in, required_out_length, data_out);
if ((rv != CKR_OK) && (rv != CKR_BUFFER_TOO_SMALL))
OperationCancel(operation);
return rv;
} else if (context->is_digest_) {
EVP_DigestUpdate(context->digest_context_.get(), data_in.data(),
data_in.length());
} else if (context->is_hmac_) {
HMAC_Update(context->hmac_context_.get(),
ConvertStringToByteBuffer(data_in.c_str()), data_in.length());
} else {
// We don't need to process now; just queue the data.
context->data_ += data_in;
}
if (required_out_length)
*required_out_length = 0;
return CKR_OK;
}
void SessionImpl::OperationCancel(OperationType operation) {
CHECK(operation < kNumOperationTypes);
OperationContext* context = &operation_context_[operation];
if (!context->is_valid_) {
LOG(ERROR) << "Operation is not initialized.";
return;
}
// Drop the context and any associated data.
context->Clear();
}
CK_RV SessionImpl::OperationFinal(OperationType operation,
int* required_out_length,
string* data_out) {
CHECK(required_out_length);
CHECK(data_out);
CHECK(operation < kNumOperationTypes);
OperationContext* context = &operation_context_[operation];
if (!context->is_valid_) {
LOG(ERROR) << "Operation is not initialized.";
return CKR_OPERATION_NOT_INITIALIZED;
}
if (!context->is_incremental_ && context->is_finished_) {
LOG(ERROR) << "Operation is not incremental.";
OperationCancel(operation);
return CKR_OPERATION_ACTIVE;
}
context->is_incremental_ = true;
return OperationFinalInternal(operation, required_out_length, data_out);
}
CK_RV SessionImpl::OperationFinalInternal(OperationType operation,
int* required_out_length,
string* data_out) {
CHECK(operation < kNumOperationTypes);
OperationContext* context = &operation_context_[operation];
context->is_valid_ = false;
// Complete the operation if it has not already been done.
if (!context->is_finished_) {
if (context->is_cipher_) {
CK_RV result = CipherFinal(context);
if (result != CKR_OK)
return result;
} else if (context->is_digest_) {
unsigned char buffer[kMaxDigestOutputBytes];
unsigned int out_length = 0;
EVP_DigestFinal_ex(context->digest_context_.get(), buffer, &out_length);
context->data_ = string(reinterpret_cast<char*>(buffer), out_length);
} else if (context->is_hmac_) {
unsigned char buffer[kMaxDigestOutputBytes];
unsigned int out_length = 0;
HMAC_Final(context->hmac_context_.get(), buffer, &out_length);
context->data_ = string(reinterpret_cast<char*>(buffer), out_length);
}
// Some RSA/ECC mechanisms use a digest so it's important to finish the
// digest before finishing the RSA/ECC computation.
if (IsRSA(context->mechanism_)) {
if (operation == kEncrypt) {
if (!RSAEncrypt(context))
return CKR_FUNCTION_FAILED;
} else if (operation == kDecrypt) {
if (!RSADecrypt(context))
return CKR_FUNCTION_FAILED;
} else if (operation == kSign) {
if (!RSASign(context))
return CKR_FUNCTION_FAILED;
}
} else if (IsECC(context->mechanism_)) {
if (operation == kSign) {
if (!ECCSign(context))
return CKR_FUNCTION_FAILED;
}
}
context->is_finished_ = true;
}
CK_RV result = GetOperationOutput(context, required_out_length, data_out);
if (result == CKR_BUFFER_TOO_SMALL) {
// We'll keep the context valid so a subsequent call can pick up the data.
context->is_valid_ = true;
}
return result;
}
CK_RV SessionImpl::VerifyFinal(const string& signature) {
OperationContext* context = &operation_context_[kVerify];
// Call the generic OperationFinal so any digest or HMAC computation gets
// finalized.
int max_out_length = std::numeric_limits<int>::max();
string data_out;
CK_RV result = OperationFinal(kVerify, &max_out_length, &data_out);
if (result != CKR_OK)
return result;
// We only support 3 Verify mechanisms, HMAC, RSA and ECC.
if (context->is_hmac_) {
// The data_out contents will be the computed HMAC. To verify an HMAC, it is
// recomputed and literally compared.
if (signature.length() != data_out.length())
return CKR_SIGNATURE_LEN_RANGE;
if (0 != brillo::SecureMemcmp(signature.data(), data_out.data(),
signature.length()))
return CKR_SIGNATURE_INVALID;
return CKR_OK;
} else if (IsRSA(context->mechanism_)) {
// The data_out contents will be the computed digest.
return RSAVerify(context, data_out, signature);
} else if (IsECC(context->mechanism_)) {
// The data_out contents will be the computed digest.
return ECCVerify(context, data_out, signature);
} else {
NOTREACHED();
return false;
}
}
CK_RV SessionImpl::OperationSinglePart(OperationType operation,
const string& data_in,
int* required_out_length,
string* data_out) {
CHECK(operation < kNumOperationTypes);
OperationContext* context = &operation_context_[operation];
if (!context->is_valid_) {
LOG(ERROR) << "Operation is not initialized.";
return CKR_OPERATION_NOT_INITIALIZED;
}
if (context->is_incremental_) {
LOG(ERROR) << "Operation is incremental.";
OperationCancel(operation);
return CKR_OPERATION_ACTIVE;
}
CK_RV result = CKR_OK;
if (!context->is_finished_) {
string update, final;
int max = std::numeric_limits<int>::max();
result = OperationUpdateInternal(operation, data_in, &max, &update);
if (result != CKR_OK)
return result;
max = std::numeric_limits<int>::max();
result = OperationFinalInternal(operation, &max, &final);
if (result != CKR_OK)
return result;
context->data_ = update + final;
context->is_finished_ = true;
}
context->is_valid_ = false;
result = GetOperationOutput(context, required_out_length, data_out);
if (result == CKR_BUFFER_TOO_SMALL) {
// We'll keep the context valid so a subsequent call can pick up the data.
context->is_valid_ = true;
}
return result;
}
CK_RV SessionImpl::GenerateKey(CK_MECHANISM_TYPE mechanism,
const string& mechanism_parameter,
const CK_ATTRIBUTE_PTR attributes,
int num_attributes,
int* new_key_handle) {
CHECK(new_key_handle);
std::unique_ptr<Object> object(factory_->CreateObject());
CHECK(object.get());
CK_RV result = object->SetAttributes(attributes, num_attributes);
if (result != CKR_OK)
return result;
CK_KEY_TYPE key_type = 0;
string key_material;
switch (mechanism) {
case CKM_DES_KEY_GEN: {
key_type = CKK_DES;
if (!GenerateDESKey(&key_material))
return CKR_FUNCTION_FAILED;
break;
}
case CKM_DES3_KEY_GEN: {
key_type = CKK_DES3;
string des[3];
for (int i = 0; i < 3; ++i) {
if (!GenerateDESKey(&des[i]))
return CKR_FUNCTION_FAILED;
}
key_material = des[0] + des[1] + des[2];
break;
}
case CKM_AES_KEY_GEN: {
key_type = CKK_AES;
if (!object->IsAttributePresent(CKA_VALUE_LEN))
return CKR_TEMPLATE_INCOMPLETE;
CK_ULONG key_length = object->GetAttributeInt(CKA_VALUE_LEN, 0);
if (key_length != 16 && key_length != 24 && key_length != 32)
return CKR_KEY_SIZE_RANGE;
key_material = GenerateRandomSoftware(key_length);
break;
}
case CKM_GENERIC_SECRET_KEY_GEN: {
key_type = CKK_GENERIC_SECRET;
if (!object->IsAttributePresent(CKA_VALUE_LEN))
return CKR_TEMPLATE_INCOMPLETE;
CK_ULONG key_length = object->GetAttributeInt(CKA_VALUE_LEN, 0);
if (key_length < 1)
return CKR_KEY_SIZE_RANGE;
key_material = GenerateRandomSoftware(key_length);
break;
}
default: {
LOG(ERROR) << "GenerateKey: Mechanism not supported: " << hex
<< mechanism;
return CKR_MECHANISM_INVALID;
}
}
object->SetAttributeInt(CKA_CLASS, CKO_SECRET_KEY);
object->SetAttributeInt(CKA_KEY_TYPE, key_type);
object->SetAttributeString(CKA_VALUE, key_material);
object->SetAttributeBool(CKA_LOCAL, true);
object->SetAttributeInt(CKA_KEY_GEN_MECHANISM, mechanism);
result = object->FinalizeNewObject();
if (result != CKR_OK)
return result;
ObjectPool* pool =
object->IsTokenObject() ? token_object_pool_ : session_object_pool_.get();
auto pool_res = pool->Insert(object.get());
if (!IsSuccess(pool_res))
return ResultToRV(pool_res, CKR_FUNCTION_FAILED);
*new_key_handle = object.release()->handle();
return CKR_OK;
}
CK_RV SessionImpl::GenerateKeyPair(CK_MECHANISM_TYPE mechanism,
const string& mechanism_parameter,
const CK_ATTRIBUTE_PTR public_attributes,
int num_public_attributes,
const CK_ATTRIBUTE_PTR private_attributes,
int num_private_attributes,
int* new_public_key_handle,
int* new_private_key_handle) {
CHECK(new_public_key_handle);
CHECK(new_private_key_handle);
// Create public/private key objects
std::unique_ptr<Object> public_object(factory_->CreateObject());
CHECK(public_object.get());
std::unique_ptr<Object> private_object(factory_->CreateObject());
CHECK(private_object.get());
// copy attributes
// TODO(menghuan): don't copy the attribute that doesn't support
CK_RV result =
public_object->SetAttributes(public_attributes, num_public_attributes);
if (result != CKR_OK)
return result;
result =
private_object->SetAttributes(private_attributes, num_private_attributes);
if (result != CKR_OK)
return result;
// Get the object pool
ObjectPool* public_pool =
(public_object->IsTokenObject() ? token_object_pool_
: session_object_pool_.get());
ObjectPool* private_pool =
(private_object->IsTokenObject() ? token_object_pool_
: session_object_pool_.get());
switch (mechanism) {
case CKM_RSA_PKCS_KEY_PAIR_GEN:
result = GenerateRSAKeyPair(public_object.get(), private_object.get());
break;
case CKM_EC_KEY_PAIR_GEN:
result = GenerateECCKeyPair(public_object.get(), private_object.get());
break;
default:
LOG(ERROR) << __func__ << ": Mechanism not supported: " << hex
<< mechanism;
return CKR_MECHANISM_INVALID;
}
if (result != CKR_OK) {
return result;
}
// Set the general attributes for public / private key
public_object->SetAttributeInt(CKA_CLASS, CKO_PUBLIC_KEY);
private_object->SetAttributeInt(CKA_CLASS, CKO_PRIVATE_KEY);
// The CKA_KEY_GEN_MECHANISM attribute identifies the key generation mechanism
// used to generate the key material. It contains a valid value only if the
// CKA_LOCAL attribute has the value CK_TRUE. If CKA_LOCAL has the value
// CK_FALSE, the value of the attribute is CK_UNAVAILABLE_INFORMATION.
public_object->SetAttributeBool(CKA_LOCAL, true);
private_object->SetAttributeBool(CKA_LOCAL, true);
public_object->SetAttributeInt(CKA_KEY_GEN_MECHANISM, mechanism);
private_object->SetAttributeInt(CKA_KEY_GEN_MECHANISM, mechanism);
// Finalize the objects
result = public_object->FinalizeNewObject();
if (result != CKR_OK) {
LOG(ERROR) << __func__ << ": Fail to finalize public object.";
return result;
}
result = private_object->FinalizeNewObject();
if (result != CKR_OK) {
LOG(ERROR) << __func__ << ": Fail to finalize private object.";
return result;
}
auto pool_res = public_pool->Insert(public_object.get());
if (!IsSuccess(pool_res)) {
LOG(ERROR) << __func__ << ": Fail to insert public object to public pool.";
return ResultToRV(pool_res, CKR_FUNCTION_FAILED);
}
pool_res = private_pool->Insert(private_object.get());
if (!IsSuccess(pool_res)) {
LOG(ERROR) << __func__
<< ": Fail to insert private object to private pool.";
// Remove inserted public object.
// The object will be destroy in Delete(), we should release uniptr.
public_pool->Delete(public_object.release());
return ResultToRV(pool_res, CKR_FUNCTION_FAILED);
}
*new_public_key_handle = public_object.release()->handle();
*new_private_key_handle = private_object.release()->handle();
return CKR_OK;
}
CK_RV SessionImpl::SeedRandom(const string& seed) {
RAND_seed(seed.data(), seed.length());
return CKR_OK;
}
CK_RV SessionImpl::GenerateRandom(int num_bytes, string* random_data) {
*random_data = GenerateRandomSoftware(num_bytes);
return CKR_OK;
}
bool SessionImpl::IsPrivateLoaded() {
return token_object_pool_->IsPrivateLoaded();
}
CK_RV SessionImpl::CipherInit(bool is_encrypt,
CK_MECHANISM_TYPE mechanism,
const string& mechanism_parameter,
const Object* key) {
string key_material = key->GetAttributeString(CKA_VALUE);
const EVP_CIPHER* cipher_type =
GetOpenSSLCipher(mechanism, key_material.size());
if (!cipher_type) {
LOG(ERROR) << "Mechanism not supported: 0x" << hex << mechanism;
return CKR_MECHANISM_INVALID;
}
// The mechanism parameter is the IV for cipher modes which require an IV,
// otherwise it is expected to be empty.
if (static_cast<int>(mechanism_parameter.size()) !=
EVP_CIPHER_iv_length(cipher_type)) {
LOG(ERROR) << "IV length is invalid: " << mechanism_parameter.size();
return CKR_MECHANISM_PARAM_INVALID;
}
if (static_cast<int>(key_material.size()) !=
EVP_CIPHER_key_length(cipher_type)) {
LOG(ERROR) << "Key size not supported: " << key_material.size();
return CKR_KEY_SIZE_RANGE;
}
OperationType operation = is_encrypt ? kEncrypt : kDecrypt;
OperationContext* context = &operation_context_[operation];
context->cipher_context_.reset(EVP_CIPHER_CTX_new());
if (!context->cipher_context_) {
LOG(ERROR) << "Failed to allocate EVP_CIPHER context";
return CKR_FUNCTION_FAILED;
}
if (!EVP_CipherInit_ex(context->cipher_context_.get(), cipher_type, nullptr,
ConvertStringToByteBuffer(key_material.c_str()),
ConvertStringToByteBuffer(mechanism_parameter.c_str()),
is_encrypt)) {
LOG(ERROR) << "EVP_CipherInit failed: " << GetOpenSSLError();
return CKR_FUNCTION_FAILED;
}
EVP_CIPHER_CTX_set_padding(context->cipher_context_.get(),
IsPaddingEnabled(mechanism));
context->is_valid_ = true;
context->is_cipher_ = true;
return CKR_OK;
}
CK_RV SessionImpl::CipherUpdate(OperationContext* context,
const string& data_in,
int* required_out_length,
string* data_out) {
CHECK(required_out_length);
CHECK(data_out);
// If we have output already waiting, we don't need to process input.
if (context->data_.empty()) {
int in_length = data_in.length();
int out_length = in_length + kMaxCipherBlockBytes;
context->data_.resize(out_length);
if (!EVP_CipherUpdate(
context->cipher_context_.get(),
ConvertStringToByteBuffer(context->data_.c_str()), &out_length,
ConvertStringToByteBuffer(data_in.c_str()), in_length)) {
context->is_valid_ = false;
LOG(ERROR) << "EVP_CipherUpdate failed: " << GetOpenSSLError();
return CKR_FUNCTION_FAILED;
}
context->data_.resize(out_length);
}
return GetOperationOutput(context, required_out_length, data_out);
}
CK_RV SessionImpl::CipherFinal(OperationContext* context) {
if (context->data_.empty()) {
int out_length = kMaxCipherBlockBytes * 2;
context->data_.resize(out_length);
if (!EVP_CipherFinal_ex(context->cipher_context_.get(),
ConvertStringToByteBuffer(context->data_.c_str()),
&out_length)) {
LOG(ERROR) << "EVP_CipherFinal failed: " << GetOpenSSLError();
return CKR_FUNCTION_FAILED;
}
context->data_.resize(out_length);
}
return CKR_OK;
}
CK_RV SessionImpl::CreateObjectInternal(const CK_ATTRIBUTE_PTR attributes,
int num_attributes,
const Object* copy_from_object,
int* new_object_handle) {
CHECK(new_object_handle);
CHECK(attributes || num_attributes == 0);
std::unique_ptr<Object> object(factory_->CreateObject());
CHECK(object.get());
CK_RV result = CKR_OK;
if (copy_from_object) {
result = object->Copy(copy_from_object);
if (result != CKR_OK)
return result;
}
result = object->SetAttributes(attributes, num_attributes);
if (result != CKR_OK)
return result;
bool is_token_object = object->IsTokenObject();
if (is_token_object) {
result = WrapPrivateKey(object.get());
if (result != CKR_OK)
return result;
}
// Finalize the object, whether it's new or copied.
if (copy_from_object) {
result = object->FinalizeCopyObject();
} else {
result = object->FinalizeNewObject();
}
if (result != CKR_OK) {
return result;
}
ObjectPool* pool =
is_token_object ? token_object_pool_ : session_object_pool_.get();
auto pool_res = pool->Insert(object.get());
if (!IsSuccess(pool_res))
return ResultToRV(pool_res, CKR_GENERAL_ERROR);
*new_object_handle = object.release()->handle();
return CKR_OK;
}
bool SessionImpl::GenerateDESKey(string* key_material) {
static const int kDESKeySizeBytes = 8;
bool done = false;
while (!done) {
string tmp = GenerateRandomSoftware(kDESKeySizeBytes);
DES_cblock des;
memcpy(&des, tmp.data(), kDESKeySizeBytes);
if (!DES_is_weak_key(&des)) {
DES_set_odd_parity(&des);
*key_material = string(reinterpret_cast<char*>(des), kDESKeySizeBytes);
done = true;
}
}
return true;
}
CK_RV SessionImpl::GenerateRSAKeyPair(Object* public_object,
Object* private_object) {
// CKA_PUBLIC_EXPONENT is optional. The default is 65537 (0x10001).
string public_exponent("\x01\x00\x01", 3);
if (public_object->IsAttributePresent(CKA_PUBLIC_EXPONENT))
public_exponent = public_object->GetAttributeString(CKA_PUBLIC_EXPONENT);
public_object->SetAttributeString(CKA_PUBLIC_EXPONENT, public_exponent);
private_object->SetAttributeString(CKA_PUBLIC_EXPONENT, public_exponent);
// CKA_MODULUS_BITS is requried
if (!public_object->IsAttributePresent(CKA_MODULUS_BITS))
return CKR_TEMPLATE_INCOMPLETE;
CK_ULONG modulus_bits = public_object->GetAttributeInt(CKA_MODULUS_BITS, 0);
if (modulus_bits < kMinRSAKeyBits || modulus_bits > kMaxRSAKeyBits)
return CKR_KEY_SIZE_RANGE;
// Set CKA_KEY_TYPE
public_object->SetAttributeInt(CKA_KEY_TYPE, CKK_RSA);
private_object->SetAttributeInt(CKA_KEY_TYPE, CKK_RSA);
// Check if we are able to back this key with the TPM.
if (tpm_utility_->IsTPMAvailable() && private_object->IsTokenObject() &&
modulus_bits >= tpm_utility_->MinRSAKeyBits() &&
modulus_bits <= tpm_utility_->MaxRSAKeyBits() &&
!private_object->GetAttributeBool(kForceSoftwareAttribute, false)) {
// Use TPM to generate RSA key
if (!GenerateRSAKeyPairTPM(modulus_bits, public_exponent, public_object,
private_object))
return CKR_FUNCTION_FAILED;
} else {
// Use software to generate RSA key
if (!GenerateRSAKeyPairSoftware(modulus_bits, public_exponent,
public_object, private_object))
return CKR_FUNCTION_FAILED;
}
return CKR_OK;
}
bool SessionImpl::GenerateRSAKeyPairSoftware(int modulus_bits,
const string& public_exponent,
Object* public_object,
Object* private_object) {
if (public_exponent.length() > sizeof(uint32_t) || public_exponent.empty())
return false;
crypto::ScopedRSA key(RSA_new());
crypto::ScopedBIGNUM e(BN_new());
if (!key || !e) {
LOG(ERROR) << "Failed to allocate RSA or BIGNUM for exponent.";
return false;
}
if (!ConvertToBIGNUM(public_exponent, e.get())) {
LOG(ERROR) << "Failed to convert exponent to BIGNUM.";
return false;
}
if (!RSA_generate_key_ex(key.get(), modulus_bits, e.get(), nullptr)) {
LOG(ERROR) << "Failed to generate key pair.";
return false;
}
const BIGNUM* key_n;
const BIGNUM* key_d;
const BIGNUM* key_p;
const BIGNUM* key_q;
const BIGNUM* key_dmp1;
const BIGNUM* key_dmq1;
const BIGNUM* key_iqmp;
RSA_get0_key(key.get(), &key_n, nullptr, &key_d);
RSA_get0_factors(key.get(), &key_p, &key_q);
RSA_get0_crt_params(key.get(), &key_dmp1, &key_dmq1, &key_iqmp);
string n = ConvertFromBIGNUM(key_n);
string d = ConvertFromBIGNUM(key_d);
string p = ConvertFromBIGNUM(key_p);
string q = ConvertFromBIGNUM(key_q);
string dmp1 = ConvertFromBIGNUM(key_dmp1);
string dmq1 = ConvertFromBIGNUM(key_dmq1);
string iqmp = ConvertFromBIGNUM(key_iqmp);
public_object->SetAttributeString(CKA_MODULUS, n);
private_object->SetAttributeString(CKA_MODULUS, n);
private_object->SetAttributeString(CKA_PRIVATE_EXPONENT, d);
private_object->SetAttributeString(CKA_PRIME_1, p);
private_object->SetAttributeString(CKA_PRIME_2, q);
private_object->SetAttributeString(CKA_EXPONENT_1, dmp1);
private_object->SetAttributeString(CKA_EXPONENT_2, dmq1);
private_object->SetAttributeString(CKA_COEFFICIENT, iqmp);
return true;
}
bool SessionImpl::GenerateRSAKeyPairTPM(int modulus_bits,
const string& public_exponent,
Object* public_object,
Object* private_object) {
string auth_data = GenerateRandomSoftware(kDefaultAuthDataBytes);
string key_blob;
int tpm_key_handle;
if (!tpm_utility_->GenerateRSAKey(
slot_id_, modulus_bits, public_exponent,
SecureBlob(auth_data.begin(), auth_data.end()), &key_blob,
&tpm_key_handle))
return false;
// Get public key information from TPM
string modulus;
string exponent;
if (!tpm_utility_->GetRSAPublicKey(tpm_key_handle, &exponent, &modulus))
return false;
public_object->SetAttributeString(CKA_MODULUS, modulus);
private_object->SetAttributeString(CKA_MODULUS, modulus);
private_object->SetAttributeString(kAuthDataAttribute, auth_data);
private_object->SetAttributeString(kKeyBlobAttribute, key_blob);
return true;
}
CK_RV SessionImpl::GenerateECCKeyPair(Object* public_object,
Object* private_object) {
// CKA_EC_PARAMS is requried
if (!public_object->IsAttributePresent(CKA_EC_PARAMS))
return CKR_TEMPLATE_INCOMPLETE;
crypto::ScopedEC_KEY key = CreateECCKeyFromEC_PARAMS(
public_object->GetAttributeString(CKA_EC_PARAMS));
if (key == nullptr) {
LOG(ERROR) << __func__ << ": CKA_EC_PARAMS parse fail.";
return CKR_DOMAIN_PARAMS_INVALID;
}
// Set CKA_KEY_TYPE
public_object->SetAttributeInt(CKA_KEY_TYPE, CKK_EC);
private_object->SetAttributeInt(CKA_KEY_TYPE, CKK_EC);
// reset CKA_EC_PARAMS for both key
const string ec_params = GetECParametersAsString(key.get());
if (ec_params.empty()) {
LOG(ERROR) << __func__ << ": Fail to dump CKA_EC_PARAMS";
return CKR_FUNCTION_FAILED;
}
public_object->SetAttributeString(CKA_EC_PARAMS, ec_params);
private_object->SetAttributeString(CKA_EC_PARAMS, ec_params);
// Get NID from key
const EC_GROUP* group = EC_KEY_get0_group(key.get());
if (group == nullptr)
return CKR_FUNCTION_FAILED;
int curve_nid = EC_GROUP_get_curve_name(group);
bool is_using_tpm =
tpm_utility_->IsTPMAvailable() && private_object->IsTokenObject() &&
tpm_utility_->IsECCurveSupported(curve_nid) &&
!private_object->GetAttributeBool(kForceSoftwareAttribute, false);
bool result = false;
if (is_using_tpm) {
result =
GenerateECCKeyPairTPM(key, curve_nid, public_object, private_object);
} else {
result = GenerateECCKeyPairSoftware(key, public_object, private_object);
}
return result ? CKR_OK : CKR_FUNCTION_FAILED;
}
bool SessionImpl::GenerateECCKeyPairTPM(const crypto::ScopedEC_KEY& key,
int curve_nid,
Object* public_object,
Object* private_object) {
string auth_data = GenerateRandomSoftware(kDefaultAuthDataBytes);
string key_blob;
int tpm_key_handle;
if (!tpm_utility_->GenerateECCKey(slot_id_, curve_nid, SecureBlob(auth_data),
&key_blob, &tpm_key_handle)) {
LOG(ERROR) << __func__ << ": Fail to generate ECC key in TPM.";
return false;
}
// Get public key information from TPM
string ec_point;
if (!tpm_utility_->GetECCPublicKey(tpm_key_handle, &ec_point)) {
LOG(ERROR) << __func__ << ": Fail to get ECC public key from TPM.";
return false;
}
// Set CKA_EC_POINT for public key
public_object->SetAttributeString(CKA_EC_POINT, ec_point);
// Set TPM information for private key
private_object->SetAttributeString(kAuthDataAttribute, auth_data);
private_object->SetAttributeString(kKeyBlobAttribute, key_blob);
return true;
}
bool SessionImpl::GenerateECCKeyPairSoftware(const crypto::ScopedEC_KEY& key,
Object* public_object,
Object* private_object) {
if (!EC_KEY_generate_key(key.get())) {
LOG(ERROR) << __func__
<< ": Software generate key fail. Perhaps it is not supported "
"by OpenSSL.";
return false;
}
// Set CKA_EC_POINT for public key
const string ec_point = GetECPointAsString(key.get());
if (ec_point.empty()) {
LOG(ERROR) << __func__ << ": Fail to dump EC_POINT.";
return false;
}
public_object->SetAttributeString(CKA_EC_POINT, ec_point);
// Set CKA_VALUE for private key
const BIGNUM* privkey = EC_KEY_get0_private_key(key.get());
private_object->SetAttributeString(CKA_VALUE, ConvertFromBIGNUM(privkey));
return true;
}
string SessionImpl::GenerateRandomSoftware(int num_bytes) {
string random(num_bytes, 0);
RAND_bytes(ConvertStringToByteBuffer(random.data()), num_bytes);
return random;
}
CK_RV SessionImpl::GetOperationOutput(OperationContext* context,
int* required_out_length,
string* data_out) {
int out_length = context->data_.length();
int max_length = *required_out_length;
*required_out_length = out_length;
if (max_length < out_length)
return CKR_BUFFER_TOO_SMALL;
*data_out = context->data_;
context->data_.clear();
return CKR_OK;
}
CK_ATTRIBUTE_TYPE SessionImpl::GetRequiredKeyUsage(OperationType operation) {
switch (operation) {
case kEncrypt:
return CKA_ENCRYPT;
case kDecrypt:
return CKA_DECRYPT;
case kSign:
return CKA_SIGN;
case kVerify:
return CKA_VERIFY;
default:
break;
}
return 0;
}
bool SessionImpl::GetTPMKeyHandle(const Object* key, int* key_handle) {
map<const Object*, int>::iterator it = object_tpm_handle_map_.find(key);
if (it == object_tpm_handle_map_.end()) {
// Only private keys are loaded into the TPM. All public key operations do
// not use the TPM (and use OpenSSL instead).
if (key->GetObjectClass() == CKO_PRIVATE_KEY) {
string auth_data = key->GetAttributeString(kAuthDataAttribute);
if (key->GetAttributeBool(kLegacyAttribute, false)) {
// This is a legacy key and it needs to be loaded with the legacy root
// key.
if (!LoadLegacyRootKeys())
return false;
bool is_private = key->GetAttributeBool(CKA_PRIVATE, true);
int root_key_handle = is_private ? private_root_key_ : public_root_key_;
if (!tpm_utility_->LoadKeyWithParent(
slot_id_, key->GetAttributeString(kKeyBlobAttribute),
SecureBlob(auth_data.begin(), auth_data.end()), root_key_handle,
key_handle))
return false;
} else {
if (!tpm_utility_->LoadKey(
slot_id_, key->GetAttributeString(kKeyBlobAttribute),
SecureBlob(auth_data.begin(), auth_data.end()), key_handle))
return false;
}
} else {
LOG(ERROR) << "Invalid object class for loading into TPM.";
return false;
}
object_tpm_handle_map_[key] = *key_handle;
} else {
*key_handle = it->second;
}
return true;
}
bool SessionImpl::LoadLegacyRootKeys() {
if (is_legacy_loaded_)
return true;
// Load the legacy root keys. See http://trousers.sourceforge.net/pkcs11.html
// for details on where these come from.
string private_blob;
if (!token_object_pool_->GetInternalBlob(kLegacyPrivateRootKey,
&private_blob)) {
LOG(ERROR) << "Failed to read legacy private root key blob.";
return false;
}
if (!tpm_utility_->LoadKey(slot_id_, private_blob, SecureBlob(),
&private_root_key_)) {
LOG(ERROR) << "Failed to load legacy private root key.";
return false;
}
string public_blob;
if (!token_object_pool_->GetInternalBlob(kLegacyPublicRootKey,
&public_blob)) {
LOG(ERROR) << "Failed to read legacy public root key blob.";
return false;
}
if (!tpm_utility_->LoadKey(slot_id_, public_blob, SecureBlob(),
&public_root_key_)) {
LOG(ERROR) << "Failed to load legacy public root key.";
return false;
}
is_legacy_loaded_ = true;
return true;
}
bool SessionImpl::RSADecrypt(OperationContext* context) {
if (context->key_->IsTokenObject() &&
context->key_->IsAttributePresent(kKeyBlobAttribute)) {
int tpm_key_handle = 0;
if (!GetTPMKeyHandle(context->key_, &tpm_key_handle))
return false;
string encrypted_data = context->data_;
context->data_.clear();
if (!tpm_utility_->Unbind(tpm_key_handle, encrypted_data, &context->data_))
return false;
} else {
crypto::ScopedRSA rsa = CreateRSAKeyFromObject(context->key_);
if (!rsa) {
LOG(ERROR) << "Failed to create RSA key for decryption.";
return false;
}
uint8_t buffer[kMaxRSAOutputBytes];
CHECK(RSA_size(rsa.get()) <= kMaxRSAOutputBytes);
int length = RSA_private_decrypt(
context->data_.length(),
ConvertStringToByteBuffer(context->data_.data()), buffer, rsa.get(),
RSA_PKCS1_PADDING); // Strips PKCS #1 type 2 padding.
if (length == -1) {
LOG(ERROR) << "RSA_private_decrypt failed: " << GetOpenSSLError();
return false;
}
context->data_ = ConvertByteBufferToString(buffer, length);
}
return true;
}
bool SessionImpl::RSAEncrypt(OperationContext* context) {
crypto::ScopedRSA rsa = CreateRSAKeyFromObject(context->key_);
if (!rsa) {
LOG(ERROR) << "Failed to create RSA key for encryption.";
return false;
}
uint8_t buffer[kMaxRSAOutputBytes];
CHECK(RSA_size(rsa.get()) <= kMaxRSAOutputBytes);
int length = RSA_public_encrypt(
context->data_.length(), ConvertStringToByteBuffer(context->data_.data()),
buffer, rsa.get(),
RSA_PKCS1_PADDING); // Adds PKCS #1 type 2 padding.
if (length == -1) {
LOG(ERROR) << "RSA_public_encrypt failed: " << GetOpenSSLError();
return false;
}
context->data_ = ConvertByteBufferToString(buffer, length);
return true;
}
bool SessionImpl::RSASign(OperationContext* context) {
string signature;
if (context->key_->IsTokenObject() &&
context->key_->IsAttributePresent(kKeyBlobAttribute)) {
int tpm_key_handle = 0;
if (!GetTPMKeyHandle(context->key_, &tpm_key_handle))
return false;
if (!tpm_utility_->Sign(tpm_key_handle, context->mechanism_,
context->parameter_, context->data_, &signature))
return false;
} else {
crypto::ScopedRSA rsa = CreateRSAKeyFromObject(context->key_);
if (!rsa) {
LOG(ERROR) << "Failed to create RSA key for signing.";
return false;
}
std::unique_ptr<RSASignerVerifier> signer =
RSASignerVerifier::GetForMechanism(context->mechanism_);
if (!signer)
return false;
return signer->Sign(std::move(rsa), context);
}
context->data_ = signature;
return true;
}
CK_RV SessionImpl::RSAVerify(OperationContext* context,
const string& digest,
const string& signature) {
if (context->key_->GetAttributeString(CKA_MODULUS).length() !=
signature.length())
return CKR_SIGNATURE_LEN_RANGE;
crypto::ScopedRSA rsa = CreateRSAKeyFromObject(context->key_);
if (!rsa) {
LOG(ERROR) << "Failed to create RSA key for verification.";
return CKR_KEY_HANDLE_INVALID;
}
std::unique_ptr<RSASignerVerifier> verifier =
RSASignerVerifier::GetForMechanism(context->mechanism_);
if (!verifier)
return CKR_ARGUMENTS_BAD;
return verifier->Verify(std::move(rsa), context, digest, signature);
}
bool SessionImpl::ECCSign(OperationContext* context) {
string signature;
if (context->key_->IsTokenObject() &&
context->key_->IsAttributePresent(kKeyBlobAttribute)) {
if (!ECCSignTPM(context->data_, context->mechanism_, context->key_,
&signature))
return false;
} else {
if (!ECCSignSoftware(context->data_, context->key_, &signature))
return false;
}
context->data_ = signature;
return true;
}
bool SessionImpl::ECCSignTPM(const std::string& input,
CK_MECHANISM_TYPE signing_mechanism,
const Object* key_object,
std::string* signature) {
if (!(signing_mechanism == CKM_ECDSA ||
signing_mechanism == CKM_ECDSA_SHA1)) {
LOG(ERROR)
<< "Failed to sign with ECCSignTPM because mechanism is unsupported: "
<< signing_mechanism;
return false;
}
int tpm_key_handle = 0;
if (!GetTPMKeyHandle(key_object, &tpm_key_handle))
return false;
// Note that ECC doesn't require any signing parameters.
if (!tpm_utility_->Sign(tpm_key_handle, signing_mechanism, "", input,
signature))
return false;
return true;
}
bool SessionImpl::ECCSignSoftware(const std::string& input,
const Object* key_object,
std::string* signature) {
crypto::ScopedEC_KEY key = CreateECCPrivateKeyFromObject(key_object);
if (key == nullptr) {
LOG(ERROR) << __func__ << ": Load key failed.";
return false;
}
// We don't use ECDSA_sign here since the output format of PKCS#11 is
// different from OpenSSL's.
crypto::ScopedECDSA_SIG sig(ECDSA_do_sign(
ConvertStringToByteBuffer(input.data()), input.size(), key.get()));
if (sig == nullptr) {
LOG(ERROR) << __func__ << ": ECDSA failed: " << GetOpenSSLError();
return false;
}
// The resulting signature is always of length |2 * nLen|, where nLen is the
// maximum size of the EC group. The first half of the signature is r and the
// second half is s.
int max_length = GetGroupOrderLengthFromEcKey(key);
if (max_length <= 0) {
LOG(ERROR) << __func__ << ": Get the group order fail.";
return false;
}
const BIGNUM* r;
const BIGNUM* s;
ECDSA_SIG_get0(sig.get(), &r, &s);
*signature =
ConvertFromBIGNUM(r, max_length) + ConvertFromBIGNUM(s, max_length);
return true;
}
CK_RV SessionImpl::ECCVerify(OperationContext* context,
const string& signed_data,
const string& signature) {
// Software verify with ECC key
crypto::ScopedEC_KEY key = CreateECCPublicKeyFromObject(context->key_);
if (key == nullptr) {
LOG(ERROR) << __func__ << ": Load key failed.";
return CKR_FUNCTION_FAILED;
}
// Parse signature back to ECDSA_SIG
int sign_size = signature.size();
if (sign_size % 2 != 0) {
return CKR_SIGNATURE_LEN_RANGE;
}
crypto::ScopedECDSA_SIG sig(ECDSA_SIG_new());
crypto::ScopedBIGNUM r(BN_new()), s(BN_new());
if (!sig || !r || !s) {
LOG(ERROR) << "Failed to allocate ECDSA_SIG or BIGNUM.";
return CKR_FUNCTION_FAILED;
}
if (!ConvertToBIGNUM(signature.substr(0, sign_size / 2), r.get()) ||
!ConvertToBIGNUM(signature.substr(sign_size / 2), s.get())) {
LOG(ERROR) << "Failed to convert BIGNUM for ECDSA_SIG.";
return CKR_FUNCTION_FAILED;
}
if (!ECDSA_SIG_set0(sig.get(), r.release(), s.release())) {
LOG(ERROR) << "Failed to set ECDSA_SIG parameters.";
return CKR_FUNCTION_FAILED;
}
// 1 for a valid signature, 0 for an invalid signature and -1 on error.
int result = ECDSA_do_verify(ConvertStringToByteBuffer(signed_data.data()),
signed_data.size(), sig.get(), key.get());
if (result < 0) {
LOG(ERROR) << __func__ << ": ECDSA verify failed: " << GetOpenSSLError();
return CKR_FUNCTION_FAILED;
}
return result ? CKR_OK : CKR_SIGNATURE_INVALID;
}
CK_RV SessionImpl::WrapRSAPrivateKey(Object* object) {
if (!object->IsAttributePresent(CKA_PUBLIC_EXPONENT) ||
!object->IsAttributePresent(CKA_MODULUS) ||
!(object->IsAttributePresent(CKA_PRIME_1) ||
object->IsAttributePresent(CKA_PRIME_2)))
return CKR_TEMPLATE_INCOMPLETE;
// If TPM doesn't support, fall back to software.
int key_size_bits = object->GetAttributeString(CKA_MODULUS).length() * 8;
if (key_size_bits > tpm_utility_->MaxRSAKeyBits() ||
key_size_bits < tpm_utility_->MinRSAKeyBits()) {
LOG(WARNING) << "WARNING: " << key_size_bits
<< "-bit private key cannot be wrapped by the TPM.";
return CKR_OK;
}
// Get prime p or q
string prime = object->IsAttributePresent(CKA_PRIME_1)
? object->GetAttributeString(CKA_PRIME_1)
: object->GetAttributeString(CKA_PRIME_2);
string auth_data = GenerateRandomSoftware(kDefaultAuthDataBytes);
string key_blob;
int tpm_key_handle = 0;
// TODO(menghuan): Use Software key but report and have an auto-rewrapping
// when WrapRSAKey() fail
if (!tpm_utility_->WrapRSAKey(slot_id_,
object->GetAttributeString(CKA_PUBLIC_EXPONENT),
object->GetAttributeString(CKA_MODULUS), prime,
SecureBlob(auth_data.begin(), auth_data.end()),
&key_blob, &tpm_key_handle))
return CKR_FUNCTION_FAILED;
object->SetAttributeString(kAuthDataAttribute, auth_data);
object->SetAttributeString(kKeyBlobAttribute, key_blob);
object->RemoveAttribute(CKA_PRIVATE_EXPONENT);
object->RemoveAttribute(CKA_PRIME_1);
object->RemoveAttribute(CKA_PRIME_2);
object->RemoveAttribute(CKA_EXPONENT_1);
object->RemoveAttribute(CKA_EXPONENT_2);
object->RemoveAttribute(CKA_COEFFICIENT);
return CKR_OK;
}
CK_RV SessionImpl::WrapECCPrivateKey(Object* object) {
if (!object->IsAttributePresent(CKA_EC_PARAMS) ||
!object->IsAttributePresent(CKA_VALUE)) {
return CKR_TEMPLATE_INCOMPLETE;
}
// Get OpenSSL NID
crypto::ScopedEC_Key key = CreateECCPrivateKeyFromObject(object);
const EC_GROUP* group = EC_KEY_get0_group(key.get());
if (group == nullptr) {
return CKR_FUNCTION_FAILED;
}
int curve_nid = EC_GROUP_get_curve_name(group);
// If TPM doesn't support, fall back to software.
if (!tpm_utility_->IsECCurveSupported(curve_nid)) {
return CKR_OK;
}
// Get public key value
crypto::ScopedBIGNUM x(BN_new()), y(BN_new());
if (!x || !y) {
LOG(ERROR) << "Failed to allocate BIGNUM.";
return CKR_FUNCTION_FAILED;
}
const EC_POINT* ec_point = EC_KEY_get0_public_key(key.get());
if (ec_point == nullptr) {
return CKR_FUNCTION_FAILED;
}
if (!EC_POINT_get_affine_coordinates_GF2m(group, ec_point, x.get(), y.get(),
nullptr)) {
return CKR_FUNCTION_FAILED;
}
string auth_data = GenerateRandomSoftware(kDefaultAuthDataBytes);
string key_blob;
int tpm_key_handle = 0;
if (!tpm_utility_->WrapECCKey(slot_id_, curve_nid, ConvertFromBIGNUM(x.get()),
ConvertFromBIGNUM(y.get()),
object->GetAttributeString(CKA_VALUE),
SecureBlob(auth_data.begin(), auth_data.end()),
&key_blob, &tpm_key_handle)) {
return CKR_FUNCTION_FAILED;
}
object->SetAttributeString(kAuthDataAttribute, auth_data);
object->SetAttributeString(kKeyBlobAttribute, key_blob);
object->RemoveAttribute(CKA_VALUE);
return CKR_OK;
}
CK_RV SessionImpl::WrapPrivateKey(Object* object) {
if (!tpm_utility_->IsTPMAvailable() ||
object->GetAttributeBool(kForceSoftwareAttribute, false) ||
object->GetObjectClass() != CKO_PRIVATE_KEY ||
object->IsAttributePresent(kKeyBlobAttribute)) {
// This object does not need to be wrapped.
return CKR_OK;
}
int key_type = object->GetAttributeInt(CKA_KEY_TYPE, 0);
if (key_type == CKK_RSA) {
return WrapRSAPrivateKey(object);
} else if (key_type == CKK_EC) {
return WrapECCPrivateKey(object);
} else {
// If TPM doesn't support, fall back to software.
LOG(WARNING) << __func__ << ": Key type " << key_type
<< " private key cannot be wrapped by the TPM.";
return CKR_OK;
}
}
SessionImpl::OperationContext::OperationContext()
: is_valid_(false),
is_cipher_(false),
is_digest_(false),
is_hmac_(false),
is_finished_(false),
key_(nullptr) {}
SessionImpl::OperationContext::~OperationContext() {
Clear();
}
void SessionImpl::OperationContext::Clear() {
cipher_context_.reset();
digest_context_.reset();
hmac_context_.reset();
is_valid_ = false;
is_cipher_ = false;
is_digest_ = false;
is_hmac_ = false;
is_incremental_ = false;
is_finished_ = false;
key_ = nullptr;
data_.clear();
parameter_.clear();
}
} // namespace chaps