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;
     }