kmsp11/object.cc (233 lines of code) (raw):

// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "kmsp11/object.h" #include "absl/strings/str_split.h" #include "common/openssl.h" #include "common/status_macros.h" #include "kmsp11/kmsp11.h" #include "kmsp11/util/crypto_utils.h" #include "kmsp11/util/errors.h" #include "kmsp11/util/string_utils.h" namespace cloud_kms::kmsp11 { namespace { absl::Status AddStorageAttributes(AttributeMap* attrs, const kms_v1::CryptoKeyVersion& ckv) { ASSIGN_OR_RETURN(std::string key_id, ExtractKeyId(ckv.name())); // 4.4 Storage objects attrs->PutBool(CKA_TOKEN, true); attrs->PutBool(CKA_PRIVATE, false); attrs->PutBool(CKA_MODIFIABLE, false); attrs->Put(CKA_LABEL, key_id); attrs->PutBool(CKA_COPYABLE, false); attrs->PutBool(CKA_DESTROYABLE, false); // Custom attributes attrs->PutULong(CKA_KMS_ALGORITHM, ckv.algorithm()); return absl::OkStatus(); }; absl::Status AddKeyAttributes(AttributeMap* attrs, const kms_v1::CryptoKeyVersion& ckv) { ASSIGN_OR_RETURN(AlgorithmDetails algorithm, GetDetails(ckv.algorithm())); // 4.7 Key objects attrs->PutULong(CKA_KEY_TYPE, algorithm.key_type); attrs->Put(CKA_ID, ckv.name()); attrs->Put(CKA_START_DATE, ""); attrs->Put(CKA_END_DATE, ""); attrs->PutBool(CKA_DERIVE, false); attrs->PutBool(CKA_LOCAL, ckv.import_job().empty()); attrs->PutULong(CKA_KEY_GEN_MECHANISM, ckv.import_job().empty() ? algorithm.key_gen_mechanism : CK_UNAVAILABLE_INFORMATION); attrs->PutULongList(CKA_ALLOWED_MECHANISMS, algorithm.allowed_mechanisms); return absl::OkStatus(); } absl::Status AddPublicKeyAttributes(AttributeMap* attrs, const kms_v1::CryptoKeyVersion& ckv, std::string_view public_key_der) { ASSIGN_OR_RETURN(AlgorithmDetails algorithm, GetDetails(ckv.algorithm())); // 4.8 Public key objects attrs->Put(CKA_SUBJECT, ""); attrs->PutBool(CKA_ENCRYPT, algorithm.purpose == kms_v1::CryptoKey::ASYMMETRIC_DECRYPT); attrs->PutBool(CKA_VERIFY, algorithm.purpose == kms_v1::CryptoKey::ASYMMETRIC_SIGN); attrs->PutBool(CKA_VERIFY_RECOVER, false); attrs->PutBool(CKA_WRAP, false); attrs->PutBool(CKA_TRUSTED, false); attrs->Put(CKA_WRAP_TEMPLATE, ""); attrs->Put(CKA_PUBLIC_KEY_INFO, public_key_der); return absl::OkStatus(); } absl::Status AddPrivateKeyAttributes(AttributeMap* attrs, const kms_v1::CryptoKeyVersion& ckv, std::string_view public_key_der) { ASSIGN_OR_RETURN(AlgorithmDetails algorithm, GetDetails(ckv.algorithm())); // Override CKA_DESTROYABLE (from 4.4 Storage Objects) attrs->PutBool(CKA_DESTROYABLE, true); // 4.9 Private key objects attrs->Put(CKA_SUBJECT, ""); attrs->PutBool(CKA_SENSITIVE, true); attrs->PutBool(CKA_DECRYPT, algorithm.purpose == kms_v1::CryptoKey::ASYMMETRIC_DECRYPT); attrs->PutBool(CKA_SIGN, algorithm.purpose == kms_v1::CryptoKey::ASYMMETRIC_SIGN); attrs->PutBool(CKA_SIGN_RECOVER, false); attrs->PutBool(CKA_UNWRAP, false); attrs->PutBool(CKA_EXTRACTABLE, false); attrs->PutBool(CKA_ALWAYS_SENSITIVE, ckv.import_job().empty()); attrs->PutBool(CKA_NEVER_EXTRACTABLE, ckv.import_job().empty()); attrs->PutBool(CKA_WRAP_WITH_TRUSTED, false); attrs->Put(CKA_UNWRAP_TEMPLATE, ""); attrs->PutBool(CKA_ALWAYS_AUTHENTICATE, false); attrs->Put(CKA_PUBLIC_KEY_INFO, public_key_der); return absl::OkStatus(); } absl::Status AddEcPublicKeyAttributes(AttributeMap* attrs, BSSL_CONST EC_KEY* public_key) { ASSIGN_OR_RETURN(std::string params, MarshalEcParametersDer(public_key)); ASSIGN_OR_RETURN(std::string ec_point, MarshalEcPointToAsn1OctetStringDer(public_key)); // 2.3.3 ECDSA public key objects attrs->Put(CKA_EC_PARAMS, params); attrs->Put(CKA_EC_POINT, ec_point); return absl::OkStatus(); } absl::Status AddEcPrivateKeyAttributes(AttributeMap* attrs, BSSL_CONST EC_KEY* public_key) { ASSIGN_OR_RETURN(std::string params, MarshalEcParametersDer(public_key)); // Some implementations seem to expect that EC private keys contain the EC // public key attributes as well. RETURN_IF_ERROR(AddEcPublicKeyAttributes(attrs, public_key)); // 2.3.4 Elliptic curve private key objects attrs->PutSensitive(CKA_VALUE); return absl::OkStatus(); } absl::Status AddRsaPublicKeyAttributes(AttributeMap* attrs, BSSL_CONST RSA* public_key) { const BIGNUM *n, *e; RSA_get0_key(public_key, &n, &e, /*d=*/nullptr); // 2.1.2 RSA public key objects attrs->PutBigNum(CKA_MODULUS, n); attrs->PutULong(CKA_MODULUS_BITS, RSA_bits(public_key)); attrs->PutBigNum(CKA_PUBLIC_EXPONENT, e); return absl::OkStatus(); } absl::Status AddRsaPrivateKeyAttributes(AttributeMap* attrs, BSSL_CONST RSA* public_key) { // Some implementations seem to expect that RSA private keys contain the RSA // public key attributes as well. RETURN_IF_ERROR(AddRsaPublicKeyAttributes(attrs, public_key)); // 2.1.3 RSA private key objects attrs->PutSensitive(CKA_PRIVATE_EXPONENT); attrs->PutSensitive(CKA_PRIME_1); attrs->PutSensitive(CKA_PRIME_2); attrs->PutSensitive(CKA_EXPONENT_1); attrs->PutSensitive(CKA_EXPONENT_2); attrs->PutSensitive(CKA_COEFFICIENT); return absl::OkStatus(); } absl::Status AddSecretKeyAttributes(AttributeMap* attrs, const kms_v1::CryptoKeyVersion& ckv) { ASSIGN_OR_RETURN(AlgorithmDetails algorithm, GetDetails(ckv.algorithm())); // CKA_VALUE and CKA_VALUE_LEN are tied to the mechanisms in the spec, but // in practice they are the same for all secret keys. // eg. // http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/errata01/os/pkcs11-curr-v2.40-errata01-os-complete.html#_Toc228894691 attrs->PutSensitive(CKA_VALUE); // CKA_VALUE_LEN = key size in bytes. attrs->PutULong(CKA_VALUE_LEN, algorithm.key_bit_length / 8); // Override CKA_DESTROYABLE (from 4.4 Storage Objects) attrs->PutBool(CKA_DESTROYABLE, true); // 4.10 Secret key objects attrs->Put(CKA_SUBJECT, ""); attrs->PutBool(CKA_SENSITIVE, true); attrs->PutBool(CKA_ENCRYPT, algorithm.purpose == kms_v1::CryptoKey::RAW_ENCRYPT_DECRYPT); attrs->PutBool(CKA_DECRYPT, algorithm.purpose == kms_v1::CryptoKey::RAW_ENCRYPT_DECRYPT); attrs->PutBool(CKA_SIGN, algorithm.purpose == kms_v1::CryptoKey::MAC); attrs->PutBool(CKA_VERIFY, algorithm.purpose == kms_v1::CryptoKey::MAC); attrs->PutBool(CKA_WRAP, false); attrs->PutBool(CKA_UNWRAP, false); attrs->PutBool(CKA_EXTRACTABLE, false); attrs->PutBool(CKA_ALWAYS_SENSITIVE, ckv.import_job().empty()); attrs->PutBool(CKA_NEVER_EXTRACTABLE, ckv.import_job().empty()); // CKA_CHECK_VALUE is intentionally skipped, as permitted by the spec. // http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html#_Ref320515101 attrs->PutBool(CKA_WRAP_WITH_TRUSTED, false); attrs->PutBool(CKA_TRUSTED, false); attrs->Put(CKA_WRAP_TEMPLATE, ""); attrs->Put(CKA_UNWRAP_TEMPLATE, ""); return absl::OkStatus(); } absl::Status AddX509CertificateAttributes(AttributeMap* attrs, const kms_v1::CryptoKeyVersion& ckv, X509* cert) { ASSIGN_OR_RETURN(absl::Time not_before, Asn1TimeToAbsl(X509_get_notBefore(cert))); ASSIGN_OR_RETURN(absl::Time not_after, Asn1TimeToAbsl(X509_get_notAfter(cert))); bssl::UniquePtr<EVP_PKEY> pub(X509_get_pubkey(cert)); ASSIGN_OR_RETURN(std::string public_key_info, MarshalX509PublicKeyDer(pub.get())); ASSIGN_OR_RETURN(std::string subject_der, MarshalX509Name(X509_get_subject_name(cert))); ASSIGN_OR_RETURN(std::string issuer_der, MarshalX509Name(X509_get_issuer_name(cert))); ASSIGN_OR_RETURN(std::string serial, MarshalAsn1Integer(X509_get_serialNumber(cert))); ASSIGN_OR_RETURN(std::string cert_der, MarshalX509CertificateDer(cert)); char cert_der_sha1[20]; SHA1(reinterpret_cast<const uint8_t*>(cert_der.data()), cert_der.size(), reinterpret_cast<uint8_t*>(cert_der_sha1)); // 4.6.2 Certificate objects attrs->PutULong(CKA_CERTIFICATE_TYPE, CKC_X_509); attrs->PutBool(CKA_TRUSTED, false); attrs->PutULong(CKA_CERTIFICATE_CATEGORY, CK_CERTIFICATE_CATEGORY_UNSPECIFIED); attrs->Put(CKA_CHECK_VALUE, std::string_view(cert_der_sha1, 3)); attrs->PutDate(CKA_START_DATE, not_before); attrs->PutDate(CKA_END_DATE, not_after); attrs->Put(CKA_PUBLIC_KEY_INFO, public_key_info); // 4.6.3 X.509 public key certificate objects attrs->Put(CKA_SUBJECT, subject_der); attrs->Put(CKA_ID, ckv.name()); attrs->Put(CKA_ISSUER, issuer_der); attrs->Put(CKA_SERIAL_NUMBER, serial); attrs->Put(CKA_VALUE, cert_der); attrs->Put(CKA_URL, ""); attrs->Put(CKA_HASH_OF_SUBJECT_PUBLIC_KEY, ""); attrs->Put(CKA_HASH_OF_ISSUER_PUBLIC_KEY, ""); attrs->PutULong(CKA_JAVA_MIDP_SECURITY_DOMAIN, CK_SECURITY_DOMAIN_UNSPECIFIED); return absl::OkStatus(); } } // namespace absl::StatusOr<KeyPair> Object::NewKeyPair(const kms_v1::CryptoKeyVersion& ckv, BSSL_CONST EVP_PKEY* public_key) { ASSIGN_OR_RETURN(std::string pub_der, MarshalX509PublicKeyDer(public_key)); AttributeMap pub_attrs; pub_attrs.PutULong(CKA_CLASS, CKO_PUBLIC_KEY); RETURN_IF_ERROR(AddStorageAttributes(&pub_attrs, ckv)); RETURN_IF_ERROR(AddKeyAttributes(&pub_attrs, ckv)); RETURN_IF_ERROR(AddPublicKeyAttributes(&pub_attrs, ckv, pub_der)); AttributeMap prv_attrs; prv_attrs.PutULong(CKA_CLASS, CKO_PRIVATE_KEY); RETURN_IF_ERROR(AddStorageAttributes(&prv_attrs, ckv)); RETURN_IF_ERROR(AddKeyAttributes(&prv_attrs, ckv)); RETURN_IF_ERROR(AddPrivateKeyAttributes(&prv_attrs, ckv, pub_der)); int pkey_id = EVP_PKEY_id(public_key); switch (pkey_id) { case EVP_PKEY_EC: { BSSL_CONST EC_KEY* ec_public_key = EVP_PKEY_get0_EC_KEY(public_key); RETURN_IF_ERROR(AddEcPublicKeyAttributes(&pub_attrs, ec_public_key)); RETURN_IF_ERROR(AddEcPrivateKeyAttributes(&prv_attrs, ec_public_key)); break; } case EVP_PKEY_RSA: { BSSL_CONST RSA* rsa_public_key = EVP_PKEY_get0_RSA(public_key); RETURN_IF_ERROR(AddRsaPublicKeyAttributes(&pub_attrs, rsa_public_key)); RETURN_IF_ERROR(AddRsaPrivateKeyAttributes(&prv_attrs, rsa_public_key)); break; } default: return NewError(absl::StatusCode::kUnimplemented, absl::StrFormat("unsupported EVP_PKEY type: %d", pkey_id), CKR_GENERAL_ERROR, SOURCE_LOCATION); } ASSIGN_OR_RETURN(AlgorithmDetails algorithm, GetDetails(ckv.algorithm())); return KeyPair{Object(ckv.name(), CKO_PUBLIC_KEY, algorithm, pub_attrs), Object(ckv.name(), CKO_PRIVATE_KEY, algorithm, prv_attrs)}; } absl::StatusOr<Object> Object::NewSecretKey( const kms_v1::CryptoKeyVersion& ckv) { AttributeMap attrs; attrs.PutULong(CKA_CLASS, CKO_SECRET_KEY); RETURN_IF_ERROR(AddStorageAttributes(&attrs, ckv)); RETURN_IF_ERROR(AddKeyAttributes(&attrs, ckv)); RETURN_IF_ERROR(AddSecretKeyAttributes(&attrs, ckv)); ASSIGN_OR_RETURN(AlgorithmDetails algorithm, GetDetails(ckv.algorithm())); return Object(ckv.name(), CKO_SECRET_KEY, algorithm, attrs); } absl::StatusOr<Object> Object::NewCertificate( const kms_v1::CryptoKeyVersion& ckv, X509* certificate) { ASSIGN_OR_RETURN(AlgorithmDetails algorithm, GetDetails(ckv.algorithm())); AttributeMap cert_attrs; cert_attrs.PutULong(CKA_CLASS, CKO_CERTIFICATE); RETURN_IF_ERROR(AddStorageAttributes(&cert_attrs, ckv)); RETURN_IF_ERROR(AddX509CertificateAttributes(&cert_attrs, ckv, certificate)); return Object(ckv.name(), CKO_CERTIFICATE, algorithm, cert_attrs); } } // namespace cloud_kms::kmsp11