Merge "Implement GenerateUniqueId for PureSoftKeymasterContext"
diff --git a/android_keymaster/android_keymaster_utils.cpp b/android_keymaster/android_keymaster_utils.cpp
index b966a61..5a07424 100644
--- a/android_keymaster/android_keymaster_utils.cpp
+++ b/android_keymaster/android_keymaster_utils.cpp
@@ -40,6 +40,31 @@
return result == 0 ? 0 : 1;
}
+keymaster_error_t EllipticKeySizeToCurve(uint32_t key_size_bits, keymaster_ec_curve_t* curve) {
+ switch (key_size_bits) {
+ default:
+ return KM_ERROR_UNSUPPORTED_KEY_SIZE;
+
+ case 224:
+ *curve = KM_EC_CURVE_P_224;
+ break;
+
+ case 256:
+ // 256 bits could be P-256 or Curve25519
+ return KM_ERROR_UNSUPPORTED_KEY_SIZE;
+
+ case 384:
+ *curve = KM_EC_CURVE_P_384;
+ break;
+
+ case 521:
+ *curve = KM_EC_CURVE_P_521;
+ break;
+ }
+
+ return KM_ERROR_OK;
+}
+
keymaster_error_t EcKeySizeToCurve(uint32_t key_size_bits, keymaster_ec_curve_t* curve) {
switch (key_size_bits) {
default:
@@ -85,6 +110,10 @@
case KM_EC_CURVE_P_521:
*key_size_bits = 521;
break;
+
+ case KM_EC_CURVE_CURVE_25519:
+ *key_size_bits = 256;
+ break;
}
return KM_ERROR_OK;
diff --git a/include/keymaster/android_keymaster_utils.h b/include/keymaster/android_keymaster_utils.h
index b0a4052..a23694b 100644
--- a/include/keymaster/android_keymaster_utils.h
+++ b/include/keymaster/android_keymaster_utils.h
@@ -237,7 +237,18 @@
}
};
+/**
+ * Attempt to determine an arbitrary elliptic curve from a key size. If the key size
+ * is ambiguous, KM_ERROR_UNSUPPORTED_KEY_SIZE is returned.
+ */
+keymaster_error_t EllipticKeySizeToCurve(uint32_t key_size_bits, keymaster_ec_curve_t* curve);
+
+/**
+ * Attempt to determine an ECDSA curve from a key size, where the key is know to be
+ * an ECDSA (i.e. non-Edwards) specifically.
+ */
keymaster_error_t EcKeySizeToCurve(uint32_t key_size_bits, keymaster_ec_curve_t* curve);
+
keymaster_error_t EcCurveToKeySize(keymaster_ec_curve_t curve, uint32_t* key_size_bits);
template <class F> class final_action {
diff --git a/include/keymaster/km_openssl/ec_key_factory.h b/include/keymaster/km_openssl/ec_key_factory.h
index 8d3501f..1b7c52a 100644
--- a/include/keymaster/km_openssl/ec_key_factory.h
+++ b/include/keymaster/km_openssl/ec_key_factory.h
@@ -48,6 +48,15 @@
AuthorizationSet* sw_enforced,
CertificateChain* cert_chain) const override;
+ keymaster_error_t ImportRawKey(const AuthorizationSet& key_description,
+ const KeymasterKeyBlob& input_key_material,
+ UniquePtr<Key> attest_key, //
+ const KeymasterBlob& issuer_subject,
+ KeymasterKeyBlob* output_key_blob, //
+ AuthorizationSet* hw_enforced, //
+ AuthorizationSet* sw_enforced,
+ CertificateChain* cert_chain) const;
+
keymaster_error_t CreateEmptyKey(AuthorizationSet&& hw_enforced, AuthorizationSet&& sw_enforced,
UniquePtr<AsymmetricKey>* key) const override;
diff --git a/include/keymaster/km_openssl/ecdh_operation.h b/include/keymaster/km_openssl/ecdh_operation.h
index 4f85afa..6a96009 100644
--- a/include/keymaster/km_openssl/ecdh_operation.h
+++ b/include/keymaster/km_openssl/ecdh_operation.h
@@ -48,6 +48,16 @@
EVP_PKEY_Ptr ecdh_key_;
};
+class X25519Operation : public EcdhOperation {
+ public:
+ X25519Operation(AuthorizationSet&& hw_enforced, AuthorizationSet&& sw_enforced, EVP_PKEY* key)
+ : EcdhOperation(move(hw_enforced), move(sw_enforced), key) {}
+
+ keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& input,
+ const Buffer& signature, AuthorizationSet* output_params,
+ Buffer* output) override;
+};
+
class EcdhOperationFactory : public OperationFactory {
private:
KeyType registry_key() const override { return KeyType(KM_ALGORITHM_EC, KM_PURPOSE_AGREE_KEY); }
diff --git a/include/keymaster/km_openssl/ecdsa_operation.h b/include/keymaster/km_openssl/ecdsa_operation.h
index 6050784..457c381 100644
--- a/include/keymaster/km_openssl/ecdsa_operation.h
+++ b/include/keymaster/km_openssl/ecdsa_operation.h
@@ -23,6 +23,7 @@
#include <keymaster/UniquePtr.h>
#include <keymaster/key.h>
+#include <keymaster/km_openssl/curve25519_key.h>
#include <keymaster/operation.h>
namespace keymaster {
@@ -80,6 +81,24 @@
Buffer* output) override;
};
+class Ed25519SignOperation : public EcdsaSignOperation {
+ public:
+ Ed25519SignOperation(AuthorizationSet&& hw_enforced, AuthorizationSet&& sw_enforced,
+ keymaster_digest_t digest, EVP_PKEY* key)
+ : EcdsaSignOperation(move(hw_enforced), move(sw_enforced), digest, key) {}
+ keymaster_error_t Begin(const AuthorizationSet& input_params,
+ AuthorizationSet* output_params) override;
+ keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
+ AuthorizationSet* output_params, Buffer* output,
+ size_t* input_consumed) override;
+ keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& input,
+ const Buffer& signature, AuthorizationSet* output_params,
+ Buffer* output) override;
+
+ protected:
+ keymaster_error_t StoreAllData(const Buffer& input, size_t* input_consumed);
+};
+
class EcdsaOperationFactory : public OperationFactory {
private:
KeyType registry_key() const override { return KeyType(KM_ALGORITHM_EC, purpose()); }
@@ -98,8 +117,13 @@
keymaster_purpose_t purpose() const override { return KM_PURPOSE_SIGN; }
Operation* InstantiateOperation(AuthorizationSet&& hw_enforced, AuthorizationSet&& sw_enforced,
keymaster_digest_t digest, EVP_PKEY* key) override {
- return new (std::nothrow)
- EcdsaSignOperation(move(hw_enforced), move(sw_enforced), digest, key);
+ if (IsEd25519Key(hw_enforced, sw_enforced)) {
+ return new (std::nothrow)
+ Ed25519SignOperation(move(hw_enforced), move(sw_enforced), digest, key);
+ } else {
+ return new (std::nothrow)
+ EcdsaSignOperation(move(hw_enforced), move(sw_enforced), digest, key);
+ }
}
};
diff --git a/km_openssl/attestation_record.cpp b/km_openssl/attestation_record.cpp
index 3162e4a..f413064 100644
--- a/km_openssl/attestation_record.cpp
+++ b/km_openssl/attestation_record.cpp
@@ -774,7 +774,8 @@
if (auth_list.Contains(TAG_ALGORITHM, KM_ALGORITHM_EC) && //
!auth_list.Contains(TAG_EC_CURVE) && //
auth_list.GetTagValue(TAG_KEY_SIZE, &key_size)) {
- // This must be a keymaster1 key. It's an EC key with no curve. Insert the curve.
+ // This must be a keymaster1 key. It's an EC key with no curve. Insert the curve if we
+ // can unambiguously figure it out.
keymaster_error_t error = EcKeySizeToCurve(key_size, &ec_curve);
if (error != KM_ERROR_OK) return error;
diff --git a/km_openssl/certificate_utils.cpp b/km_openssl/certificate_utils.cpp
index 676b30b..9118c68 100644
--- a/km_openssl/certificate_utils.cpp
+++ b/km_openssl/certificate_utils.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <iostream>
+
#include <openssl/asn1.h>
#include <openssl/evp.h>
#include <openssl/x509v3.h>
@@ -324,7 +326,10 @@
// mistake that hasn't yet been corrected.
auto sk = const_cast<EVP_PKEY*>(signing_key);
- if (!X509_sign(certificate, sk, EVP_sha256())) {
+ // Ed25519 has an internal digest so needs to have no digest fed into X509_sign.
+ const EVP_MD* digest = (EVP_PKEY_id(signing_key) == EVP_PKEY_ED25519) ? nullptr : EVP_sha256();
+
+ if (!X509_sign(certificate, sk, digest)) {
return TranslateLastOpenSslError();
}
return KM_ERROR_OK;
diff --git a/km_openssl/ec_key_factory.cpp b/km_openssl/ec_key_factory.cpp
index 8bf25dd..2e0e2f1 100644
--- a/km_openssl/ec_key_factory.cpp
+++ b/km_openssl/ec_key_factory.cpp
@@ -16,9 +16,11 @@
#include <keymaster/km_openssl/ec_key_factory.h>
+#include <openssl/curve25519.h>
#include <openssl/evp.h>
#include <keymaster/keymaster_context.h>
+#include <keymaster/km_openssl/curve25519_key.h>
#include <keymaster/km_openssl/ec_key.h>
#include <keymaster/km_openssl/ecdh_operation.h>
#include <keymaster/km_openssl/ecdsa_operation.h>
@@ -55,7 +57,7 @@
LOG_E("%s", "No curve or key size specified for EC key generation");
return KM_ERROR_UNSUPPORTED_KEY_SIZE;
}
- keymaster_error_t error = EcKeySizeToCurve(*key_size_bits, curve);
+ keymaster_error_t error = EllipticKeySizeToCurve(*key_size_bits, curve);
if (error != KM_ERROR_OK) {
return KM_ERROR_UNSUPPORTED_KEY_SIZE;
}
@@ -98,29 +100,66 @@
authorizations.push_back(TAG_EC_CURVE, ec_curve);
}
+ bool is_ed25519 = false;
+ bool is_x25519 = false;
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey;
UniquePtr<EC_KEY, EC_KEY_Delete> ec_key(EC_KEY_new());
- UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new());
- if (ec_key.get() == nullptr || pkey.get() == nullptr) return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ KeymasterKeyBlob key_material;
+ if (ec_curve == KM_EC_CURVE_CURVE_25519) {
+ // Curve 25519 keys do not fall under OpenSSL's EC_KEY category.
+ is_ed25519 = (key_description.Contains(TAG_PURPOSE, KM_PURPOSE_SIGN) ||
+ key_description.Contains(TAG_PURPOSE, KM_PURPOSE_ATTEST_KEY));
+ is_x25519 = key_description.Contains(TAG_PURPOSE, KM_PURPOSE_AGREE_KEY);
+ if (is_ed25519 && is_x25519) {
+ // Cannot have both SIGN (Ed25519) and AGREE_KEY (X25519).
+ return KM_ERROR_INCOMPATIBLE_PURPOSE;
+ }
- UniquePtr<EC_GROUP, EC_GROUP_Delete> group(ChooseGroup(ec_curve));
- if (group.get() == nullptr) {
- LOG_E("Unable to get EC group for curve %d", ec_curve);
- return KM_ERROR_UNSUPPORTED_KEY_SIZE;
- }
+ if (is_ed25519) {
+ uint8_t priv_key[ED25519_PRIVATE_KEY_LEN];
+ uint8_t pub_key[ED25519_PUBLIC_KEY_LEN];
+ ED25519_keypair(pub_key, priv_key);
+
+ // Only feed in the first 32 bytes of the generated private key.
+ pkey.reset(EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, priv_key,
+ ED25519_SEED_LEN));
+ } else if (is_x25519) {
+ uint8_t priv_key[X25519_PRIVATE_KEY_LEN];
+ uint8_t pub_key[X25519_PUBLIC_VALUE_LEN];
+ X25519_keypair(pub_key, priv_key);
+
+ pkey.reset(EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, nullptr, priv_key,
+ X25519_PRIVATE_KEY_LEN));
+ } else {
+ return KM_ERROR_UNSUPPORTED_PURPOSE;
+ }
+ if (pkey.get() == nullptr) {
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ } else {
+ pkey.reset(EVP_PKEY_new());
+ if (ec_key.get() == nullptr || pkey.get() == nullptr)
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ UniquePtr<EC_GROUP, EC_GROUP_Delete> group(ChooseGroup(ec_curve));
+ if (group.get() == nullptr) {
+ LOG_E("Unable to get EC group for curve %d", ec_curve);
+ return KM_ERROR_UNSUPPORTED_KEY_SIZE;
+ }
#if !defined(OPENSSL_IS_BORINGSSL)
- EC_GROUP_set_point_conversion_form(group.get(), POINT_CONVERSION_UNCOMPRESSED);
- EC_GROUP_set_asn1_flag(group.get(), OPENSSL_EC_NAMED_CURVE);
+ EC_GROUP_set_point_conversion_form(group.get(), POINT_CONVERSION_UNCOMPRESSED);
+ EC_GROUP_set_asn1_flag(group.get(), OPENSSL_EC_NAMED_CURVE);
#endif
- if (EC_KEY_set_group(ec_key.get(), group.get()) != 1 ||
- EC_KEY_generate_key(ec_key.get()) != 1 || EC_KEY_check_key(ec_key.get()) < 0) {
- return TranslateLastOpenSslError();
+ if (EC_KEY_set_group(ec_key.get(), group.get()) != 1 ||
+ EC_KEY_generate_key(ec_key.get()) != 1 || EC_KEY_check_key(ec_key.get()) < 0) {
+ return TranslateLastOpenSslError();
+ }
+
+ if (EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()) != 1) return TranslateLastOpenSslError();
}
- if (EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()) != 1) return TranslateLastOpenSslError();
-
- KeymasterKeyBlob key_material;
error = EvpKeyToKeyMaterial(pkey.get(), &key_material);
if (error != KM_ERROR_OK) return error;
@@ -128,18 +167,31 @@
hw_enforced, sw_enforced);
if (error != KM_ERROR_OK) return error;
+ // Only generate attestation certificates for KeyMint (KeyMaster uses an attestKey()
+ // entrypoint that is separate from generateKey()).
if (context_.GetKmVersion() < KmVersion::KEYMINT_1) return KM_ERROR_OK;
if (!cert_chain) return KM_ERROR_UNEXPECTED_NULL_POINTER;
- EcKey key(*hw_enforced, *sw_enforced, this, move(ec_key));
+ std::unique_ptr<AsymmetricKey> key;
+ if (is_ed25519) {
+ key.reset(new (std::nothrow) Ed25519Key(*hw_enforced, *sw_enforced, this, key_material));
+ } else if (is_x25519) {
+ key.reset(new (std::nothrow) X25519Key(*hw_enforced, *sw_enforced, this, key_material));
+ } else {
+ key.reset(new (std::nothrow) EcKey(*hw_enforced, *sw_enforced, this, move(ec_key)));
+ }
+ if (key == nullptr) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
if (key_description.Contains(TAG_ATTESTATION_CHALLENGE)) {
- *cert_chain = context_.GenerateAttestation(key, key_description, move(attest_key),
+ *cert_chain = context_.GenerateAttestation(*key, key_description, move(attest_key),
issuer_subject, &error);
} else if (attest_key.get() != nullptr) {
return KM_ERROR_ATTESTATION_CHALLENGE_MISSING;
} else {
*cert_chain = context_.GenerateSelfSignedCertificate(
- key, key_description, !IsCertSigningKey(key_description) /* fake_signature */, &error);
+ *key, key_description, !IsCertSigningKey(key_description) /* fake_signature */, &error);
}
return error;
@@ -154,6 +206,11 @@
AuthorizationSet* hw_enforced,
AuthorizationSet* sw_enforced,
CertificateChain* cert_chain) const {
+ if (input_key_material_format == KM_KEY_FORMAT_RAW) {
+ return ImportRawKey(key_description, input_key_material, move(attest_key), issuer_subject,
+ output_key_blob, hw_enforced, sw_enforced, cert_chain);
+ }
+
if (!output_key_blob || !hw_enforced || !sw_enforced) return KM_ERROR_OUTPUT_PARAMETER_NULL;
AuthorizationSet authorizations;
@@ -173,18 +230,133 @@
error = KeyMaterialToEvpKey(KM_KEY_FORMAT_PKCS8, input_key_material, KM_ALGORITHM_EC, &pkey);
if (error != KM_ERROR_OK) return error;
- EC_KEY_Ptr ec_key(EVP_PKEY_get1_EC_KEY(pkey.get()));
- if (!ec_key.get()) return KM_ERROR_INVALID_ARGUMENT;
+ std::unique_ptr<AsymmetricKey> key;
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_ED25519:
+ key.reset(new (std::nothrow) Ed25519Key(*hw_enforced, *sw_enforced, this));
+ if (!key->EvpToInternal(pkey.get())) {
+ return KM_ERROR_UNSUPPORTED_KEY_FORMAT;
+ }
+ break;
+ case EVP_PKEY_X25519:
+ key.reset(new (std::nothrow) X25519Key(*hw_enforced, *sw_enforced, this));
+ if (!key->EvpToInternal(pkey.get())) {
+ return KM_ERROR_UNSUPPORTED_KEY_FORMAT;
+ }
+ break;
+ case EVP_PKEY_EC: {
+ EC_KEY_Ptr ec_key(EVP_PKEY_get1_EC_KEY(pkey.get()));
+ if (!ec_key.get()) return KM_ERROR_INVALID_ARGUMENT;
- EcKey key(*hw_enforced, *sw_enforced, this, move(ec_key));
+ key.reset(new (std::nothrow) EcKey(*hw_enforced, *sw_enforced, this, move(ec_key)));
+ break;
+ }
+ default:
+ return KM_ERROR_UNSUPPORTED_KEY_FORMAT;
+ }
+ if (key == nullptr) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
if (key_description.Contains(KM_TAG_ATTESTATION_CHALLENGE)) {
- *cert_chain = context_.GenerateAttestation(key, key_description, move(attest_key),
+ *cert_chain = context_.GenerateAttestation(*key, key_description, move(attest_key),
issuer_subject, &error);
} else if (attest_key.get() != nullptr) {
return KM_ERROR_ATTESTATION_CHALLENGE_MISSING;
} else {
*cert_chain = context_.GenerateSelfSignedCertificate(
- key, key_description, !IsCertSigningKey(key_description) /* fake_signature */, &error);
+ *key, key_description, !IsCertSigningKey(key_description) /* fake_signature */, &error);
+ }
+
+ return error;
+}
+
+keymaster_error_t EcKeyFactory::ImportRawKey(const AuthorizationSet& key_description, //
+ const KeymasterKeyBlob& input_key_material,
+ UniquePtr<Key> attest_key, //
+ const KeymasterBlob& issuer_subject,
+ KeymasterKeyBlob* output_key_blob,
+ AuthorizationSet* hw_enforced,
+ AuthorizationSet* sw_enforced,
+ CertificateChain* cert_chain) const {
+ if (!output_key_blob || !hw_enforced || !sw_enforced) return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+ // Curve 25519 keys may arrive in raw form, but if they do the key_description must include
+ // enough information to allow the key material to be identified. This means that the
+ // following tags must already be present in key_description:
+ // - TAG_ALGORITHM: KM_ALGORITHM_EC
+ // - TAG_EC_CURVE: KM_EC_CURVE_CURVE_25519
+ // - TAG_PURPOSE: exactly one of:
+ // - KM_SIGN (Ed25519)
+ // - KM_ATTEST_KEY (Ed25519)
+ // - KM_AGREE (X25519)
+ keymaster_ec_curve_t curve;
+ if (!key_description.GetTagValue(TAG_EC_CURVE, &curve) || curve != KM_EC_CURVE_CURVE_25519) {
+ return KM_ERROR_UNSUPPORTED_KEY_FORMAT;
+ }
+ bool is_ed25519 = (key_description.Contains(TAG_PURPOSE, KM_PURPOSE_SIGN) ||
+ key_description.Contains(TAG_PURPOSE, KM_PURPOSE_ATTEST_KEY));
+ bool is_x25519 = key_description.Contains(TAG_PURPOSE, KM_PURPOSE_AGREE_KEY);
+ if (is_ed25519 && is_x25519) {
+ // Cannot have both SIGN (Ed25519) and AGREE_KEY (X25519).
+ return KM_ERROR_INCOMPATIBLE_PURPOSE;
+ }
+ if (key_description.Contains(TAG_PURPOSE, KM_PURPOSE_ATTEST_KEY) &&
+ key_description.GetTagCount(TAG_PURPOSE) > 1) {
+ // ATTEST_KEY cannot be combined with another purpose.
+ return KM_ERROR_INCOMPATIBLE_PURPOSE;
+ }
+
+ // First convert the raw key data into an EVP_PKEY.
+ EVP_PKEY_Ptr pkey;
+ if (is_ed25519) {
+ pkey.reset(EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, /* unused*/ nullptr,
+ input_key_material.key_material,
+ input_key_material.key_material_size));
+ } else if (is_x25519) {
+ pkey.reset(EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, /* unused*/ nullptr,
+ input_key_material.key_material,
+ input_key_material.key_material_size));
+ } else {
+ return KM_ERROR_UNSUPPORTED_KEY_FORMAT;
+ }
+ if (pkey.get() == nullptr) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ // Now extract PKCS#8 formatted private key material from the EVP_PKEY.
+ KeymasterKeyBlob pkcs8_key_material;
+ keymaster_error_t error = EvpKeyToKeyMaterial(pkey.get(), &pkcs8_key_material);
+ if (error != KM_ERROR_OK) return error;
+
+ // Store the PKCS#8 private key material in the key blob.
+ error = blob_maker_.CreateKeyBlob(key_description, KM_ORIGIN_IMPORTED, pkcs8_key_material,
+ output_key_blob, hw_enforced, sw_enforced);
+ if (error != KM_ERROR_OK) return error;
+
+ if (context_.GetKmVersion() < KmVersion::KEYMINT_1) return KM_ERROR_OK;
+ if (!cert_chain) return KM_ERROR_UNEXPECTED_NULL_POINTER;
+
+ std::unique_ptr<AsymmetricKey> key;
+ if (is_ed25519) {
+ key.reset(new (std::nothrow)
+ Ed25519Key(*hw_enforced, *sw_enforced, this, pkcs8_key_material));
+ } else /* is_x25519 */ {
+ key.reset(new (std::nothrow)
+ X25519Key(*hw_enforced, *sw_enforced, this, pkcs8_key_material));
+ }
+ if (key == nullptr) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ if (key_description.Contains(KM_TAG_ATTESTATION_CHALLENGE)) {
+ *cert_chain = context_.GenerateAttestation(*key, key_description, move(attest_key),
+ issuer_subject, &error);
+ } else if (attest_key.get() != nullptr) {
+ return KM_ERROR_ATTESTATION_CHALLENGE_MISSING;
+ } else {
+ *cert_chain = context_.GenerateSelfSignedCertificate(
+ *key, key_description, !IsCertSigningKey(key_description) /* fake_signature */, &error);
}
return error;
@@ -202,32 +374,8 @@
KeyMaterialToEvpKey(key_format, key_material, keymaster_key_type(), &pkey);
if (error != KM_ERROR_OK) return error;
- UniquePtr<EC_KEY, EC_KEY_Delete> ec_key(EVP_PKEY_get1_EC_KEY(pkey.get()));
- if (!ec_key.get()) return TranslateLastOpenSslError();
-
updated_description->Reinitialize(key_description);
- size_t extracted_key_size_bits;
- error = ec_get_group_size(EC_KEY_get0_group(ec_key.get()), &extracted_key_size_bits);
- if (error != KM_ERROR_OK) return error;
-
- *key_size_bits = extracted_key_size_bits;
- if (!updated_description->GetTagValue(TAG_KEY_SIZE, key_size_bits)) {
- updated_description->push_back(TAG_KEY_SIZE, extracted_key_size_bits);
- } else if (*key_size_bits != extracted_key_size_bits) {
- return KM_ERROR_IMPORT_PARAMETER_MISMATCH;
- }
-
- keymaster_ec_curve_t curve_from_size;
- error = EcKeySizeToCurve(*key_size_bits, &curve_from_size);
- if (error != KM_ERROR_OK) return error;
- keymaster_ec_curve_t curve;
- if (!updated_description->GetTagValue(TAG_EC_CURVE, &curve)) {
- updated_description->push_back(TAG_EC_CURVE, curve_from_size);
- } else if (curve_from_size != curve) {
- return KM_ERROR_IMPORT_PARAMETER_MISMATCH;
- }
-
keymaster_algorithm_t algorithm = KM_ALGORITHM_EC;
if (!updated_description->GetTagValue(TAG_ALGORITHM, &algorithm)) {
updated_description->push_back(TAG_ALGORITHM, KM_ALGORITHM_EC);
@@ -235,6 +383,69 @@
return KM_ERROR_IMPORT_PARAMETER_MISMATCH;
}
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_EC: {
+ UniquePtr<EC_KEY, EC_KEY_Delete> ec_key(EVP_PKEY_get1_EC_KEY(pkey.get()));
+ if (!ec_key.get()) return TranslateLastOpenSslError();
+
+ size_t extracted_key_size_bits;
+ error = ec_get_group_size(EC_KEY_get0_group(ec_key.get()), &extracted_key_size_bits);
+ if (error != KM_ERROR_OK) return error;
+
+ *key_size_bits = extracted_key_size_bits;
+ if (!updated_description->GetTagValue(TAG_KEY_SIZE, key_size_bits)) {
+ updated_description->push_back(TAG_KEY_SIZE, extracted_key_size_bits);
+ } else if (*key_size_bits != extracted_key_size_bits) {
+ return KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ }
+
+ keymaster_ec_curve_t curve_from_size;
+ error = EcKeySizeToCurve(*key_size_bits, &curve_from_size);
+ if (error != KM_ERROR_OK) return error;
+ keymaster_ec_curve_t curve;
+ if (!updated_description->GetTagValue(TAG_EC_CURVE, &curve)) {
+ updated_description->push_back(TAG_EC_CURVE, curve_from_size);
+ } else if (curve_from_size != curve) {
+ return KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ }
+ break;
+ }
+ case EVP_PKEY_ED25519: {
+ keymaster_ec_curve_t curve;
+ if (!updated_description->GetTagValue(TAG_EC_CURVE, &curve)) {
+ updated_description->push_back(TAG_EC_CURVE, KM_EC_CURVE_CURVE_25519);
+ } else if (curve != KM_EC_CURVE_CURVE_25519) {
+ return KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ }
+ if (updated_description->Contains(TAG_PURPOSE, KM_PURPOSE_AGREE_KEY)) {
+ // Purpose is for X25519, key is Ed25519.
+ return KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ }
+ if (updated_description->Contains(TAG_PURPOSE, KM_PURPOSE_ATTEST_KEY) &&
+ updated_description->GetTagCount(TAG_PURPOSE) > 1) {
+ // ATTEST_KEY cannot be combined with another purpose.
+ return KM_ERROR_INCOMPATIBLE_PURPOSE;
+ }
+ break;
+ }
+ case EVP_PKEY_X25519: {
+ keymaster_ec_curve_t curve;
+ if (!updated_description->GetTagValue(TAG_EC_CURVE, &curve)) {
+ updated_description->push_back(TAG_EC_CURVE, KM_EC_CURVE_CURVE_25519);
+ } else if (curve != KM_EC_CURVE_CURVE_25519) {
+ return KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ }
+ if (updated_description->Contains(TAG_PURPOSE, KM_PURPOSE_SIGN) ||
+ updated_description->Contains(TAG_PURPOSE, KM_PURPOSE_ATTEST_KEY)) {
+ // Purpose is for Ed25519, key is X25519.
+ return KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ }
+ break;
+ }
+ default:
+ return KM_ERROR_INVALID_KEY_BLOB;
+ }
+
return KM_ERROR_OK;
}
@@ -283,7 +494,18 @@
keymaster_error_t EcKeyFactory::CreateEmptyKey(AuthorizationSet&& hw_enforced,
AuthorizationSet&& sw_enforced,
UniquePtr<AsymmetricKey>* key) const {
- key->reset(new (std::nothrow) EcKey(move(hw_enforced), move(sw_enforced), this));
+ bool is_ed25519 = IsEd25519Key(hw_enforced, sw_enforced);
+ bool is_x25519 = IsX25519Key(hw_enforced, sw_enforced);
+ if (is_ed25519) {
+ if (is_x25519) {
+ return KM_ERROR_INCOMPATIBLE_PURPOSE;
+ }
+ key->reset(new (std::nothrow) Ed25519Key(move(hw_enforced), move(sw_enforced), this));
+ } else if (is_x25519) {
+ key->reset(new (std::nothrow) X25519Key(move(hw_enforced), move(sw_enforced), this));
+ } else {
+ key->reset(new (std::nothrow) EcKey(move(hw_enforced), move(sw_enforced), this));
+ }
if (!(*key)) return KM_ERROR_MEMORY_ALLOCATION_FAILED;
return KM_ERROR_OK;
}
diff --git a/km_openssl/ecdh_operation.cpp b/km_openssl/ecdh_operation.cpp
index 85db7ed..13f6d6f 100644
--- a/km_openssl/ecdh_operation.cpp
+++ b/km_openssl/ecdh_operation.cpp
@@ -20,6 +20,7 @@
#include <keymaster/km_openssl/openssl_err.h>
#include <keymaster/km_openssl/openssl_utils.h>
#include <keymaster/logger.h>
+#include <openssl/curve25519.h>
#include <openssl/err.h>
#include <vector>
@@ -84,10 +85,38 @@
return KM_ERROR_OK;
}
+keymaster_error_t X25519Operation::Finish(const AuthorizationSet& /*additional_params*/,
+ const Buffer& input, const Buffer& /*signature*/,
+ AuthorizationSet* /*output_params*/, Buffer* output) {
+ if (input.available_read() != X25519_PUBLIC_VALUE_LEN) {
+ LOG_E("Invalid length %d of peer key", input.available_read());
+ return KM_ERROR_INVALID_ARGUMENT;
+ }
+ size_t key_len = X25519_PRIVATE_KEY_LEN;
+ uint8_t priv_key[X25519_PRIVATE_KEY_LEN];
+ if (EVP_PKEY_get_raw_private_key(ecdh_key_.get(), priv_key, &key_len) == 0) {
+ return TranslateLastOpenSslError();
+ }
+ if (key_len != X25519_PRIVATE_KEY_LEN) {
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ if (!output->reserve(X25519_SHARED_KEY_LEN)) {
+ LOG_E("Error reserving data in output buffer", 0);
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ if (X25519(output->peek_write(), priv_key, input.begin()) != 1) {
+ LOG_E("Error deriving key", 0);
+ return TranslateLastOpenSslError();
+ }
+ output->advance_write(X25519_SHARED_KEY_LEN);
+
+ return KM_ERROR_OK;
+}
+
OperationPtr EcdhOperationFactory::CreateOperation(Key&& key,
const AuthorizationSet& /*begin_params*/,
keymaster_error_t* error) {
- const EcKey& ecdh_key = static_cast<EcKey&>(key);
+ const AsymmetricKey& ecdh_key = static_cast<AsymmetricKey&>(key);
EVP_PKEY_Ptr pkey(ecdh_key.InternalToEvp());
if (pkey.get() == nullptr) {
@@ -96,8 +125,22 @@
}
*error = KM_ERROR_OK;
- auto op = new (std::nothrow)
- EcdhOperation(move(key.hw_enforced_move()), move(key.sw_enforced_move()), pkey.release());
+
+ EcdhOperation* op = nullptr;
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_X25519:
+ op = new (std::nothrow) X25519Operation(move(key.hw_enforced_move()),
+ move(key.sw_enforced_move()), pkey.release());
+ break;
+ case EVP_PKEY_EC:
+ op = new (std::nothrow) EcdhOperation(move(key.hw_enforced_move()),
+ move(key.sw_enforced_move()), pkey.release());
+ break;
+ default:
+ *error = KM_ERROR_UNKNOWN_ERROR;
+ return nullptr;
+ }
+
if (!op) {
*error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
return nullptr;
diff --git a/km_openssl/ecdsa_operation.cpp b/km_openssl/ecdsa_operation.cpp
index a1177a6..795a6e9 100644
--- a/km_openssl/ecdsa_operation.cpp
+++ b/km_openssl/ecdsa_operation.cpp
@@ -16,6 +16,7 @@
#include <keymaster/km_openssl/ecdsa_operation.h>
+#include <openssl/curve25519.h>
#include <openssl/ecdsa.h>
#include <keymaster/km_openssl/ec_key.h>
@@ -24,13 +25,16 @@
namespace keymaster {
+// Message size limit for Ed25519 messages, which are not pre-digested.
+static const size_t MAX_ED25519_MSG_SIZE = 16 * 1024;
+
static const keymaster_digest_t supported_digests[] = {KM_DIGEST_NONE, KM_DIGEST_SHA1,
KM_DIGEST_SHA_2_224, KM_DIGEST_SHA_2_256,
KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512};
OperationPtr EcdsaOperationFactory::CreateOperation(Key&& key, const AuthorizationSet& begin_params,
keymaster_error_t* error) {
- const EcKey& ecdsa_key = static_cast<EcKey&>(key);
+ const AsymmetricKey& ecdsa_key = static_cast<AsymmetricKey&>(key);
EVP_PKEY_Ptr pkey(ecdsa_key.InternalToEvp());
if (pkey.get() == nullptr) {
@@ -162,6 +166,69 @@
return KM_ERROR_OK;
}
+keymaster_error_t Ed25519SignOperation::Begin(const AuthorizationSet& /* input_params */,
+ AuthorizationSet* /* output_params */) {
+ if (digest_ != KM_DIGEST_NONE) {
+ // Ed25519 includes an internal digest, so no pre-digesting is supported.
+ return KM_ERROR_UNSUPPORTED_DIGEST;
+ }
+ return GenerateRandom(reinterpret_cast<uint8_t*>(&operation_handle_),
+ (size_t)sizeof(operation_handle_));
+}
+
+keymaster_error_t Ed25519SignOperation::Update(const AuthorizationSet& /* additional_params */,
+ const Buffer& input,
+ AuthorizationSet* /* output_params */,
+ Buffer* /* output */, size_t* input_consumed) {
+ return StoreAllData(input, input_consumed);
+}
+
+keymaster_error_t Ed25519SignOperation::Finish(const AuthorizationSet& additional_params,
+ const Buffer& input, const Buffer& /* signature */,
+ AuthorizationSet* /* output_params */,
+ Buffer* output) {
+ if (!output) return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+ keymaster_error_t error = UpdateForFinish(additional_params, input);
+ if (error != KM_ERROR_OK) return error;
+
+ if (!output->Reinitialize(ED25519_SIGNATURE_LEN)) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ EVP_MD_CTX ctx;
+ EVP_MD_CTX_init(&ctx);
+ if (!EVP_DigestSignInit(&ctx, /* pctx */ nullptr, /* digest */ nullptr, /* engine */ nullptr,
+ ecdsa_key_)) {
+ EVP_MD_CTX_cleanup(&ctx);
+ return TranslateLastOpenSslError();
+ }
+ size_t out_len = ED25519_SIGNATURE_LEN;
+ if (!EVP_DigestSign(&ctx, output->peek_write(), &out_len, data_.peek_read(),
+ data_.available_read())) {
+ EVP_MD_CTX_cleanup(&ctx);
+ return TranslateLastOpenSslError();
+ }
+ EVP_MD_CTX_cleanup(&ctx);
+ output->advance_write(out_len);
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t Ed25519SignOperation::StoreAllData(const Buffer& input, size_t* input_consumed) {
+ if ((data_.available_read() + input.available_read()) > MAX_ED25519_MSG_SIZE) {
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ }
+ if (!data_.reserve(input.available_read())) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ if (!data_.write(input.peek_read(), input.available_read())) {
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+
+ *input_consumed = input.available_read();
+ return KM_ERROR_OK;
+}
+
keymaster_error_t EcdsaVerifyOperation::Begin(const AuthorizationSet& /* input_params */,
AuthorizationSet* /* output_params */) {
auto rc = GenerateRandom(reinterpret_cast<uint8_t*>(&operation_handle_),
diff --git a/km_openssl/openssl_utils.cpp b/km_openssl/openssl_utils.cpp
index 82b54e7..2672cdc 100644
--- a/km_openssl/openssl_utils.cpp
+++ b/km_openssl/openssl_utils.cpp
@@ -66,17 +66,6 @@
}
}
-static int convert_to_evp(keymaster_algorithm_t algorithm) {
- switch (algorithm) {
- case KM_ALGORITHM_RSA:
- return EVP_PKEY_RSA;
- case KM_ALGORITHM_EC:
- return EVP_PKEY_EC;
- default:
- return -1;
- };
-}
-
keymaster_error_t convert_pkcs8_blob_to_evp(const uint8_t* key_data, size_t key_length,
keymaster_algorithm_t expected_algorithm,
UniquePtr<EVP_PKEY, EVP_PKEY_Delete>* pkey) {
@@ -89,9 +78,26 @@
pkey->reset(EVP_PKCS82PKEY(pkcs8.get()));
if (!pkey->get()) return TranslateLastOpenSslError(true /* log_message */);
- if (EVP_PKEY_type((*pkey)->type) != convert_to_evp(expected_algorithm)) {
- LOG_E("EVP key algorithm was %d, not the expected %d", EVP_PKEY_type((*pkey)->type),
- convert_to_evp(expected_algorithm));
+ // Check the key type detected from the PKCS8 blob matches the KM algorithm we expect.
+ keymaster_algorithm_t got_algorithm;
+ switch (EVP_PKEY_type((*pkey)->type)) {
+ case EVP_PKEY_RSA:
+ got_algorithm = KM_ALGORITHM_RSA;
+ break;
+ case EVP_PKEY_EC:
+ case EVP_PKEY_ED25519:
+ case EVP_PKEY_X25519:
+ got_algorithm = KM_ALGORITHM_EC;
+ break;
+ default:
+ LOG_E("EVP key algorithm was unknown (type %d), not the expected %d",
+ EVP_PKEY_type((*pkey)->type), expected_algorithm);
+ return KM_ERROR_INVALID_KEY_BLOB;
+ }
+
+ if (expected_algorithm != got_algorithm) {
+ LOG_E("EVP key algorithm was %d (from type %d), not the expected %d", got_algorithm,
+ EVP_PKEY_type((*pkey)->type), expected_algorithm);
return KM_ERROR_INVALID_KEY_BLOB;
}