Move EC key conversion to crypto 78/317078/3
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Tue, 3 Sep 2024 18:56:02 +0000 (20:56 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Fri, 6 Sep 2024 09:25:17 +0000 (11:25 +0200)
Change-Id: Ia848d4df6032d9721a897a019eb4b79349d89770

srcs/crypto/ec_key.cpp
srcs/crypto/ec_key.h
srcs/crypto/ecdh.cpp
srcs/crypto/ecdh.h
srcs/handshake.cpp
srcs/message.cpp
srcs/qr_transaction.cpp
tests/crypto/ec_key_unittest.cpp
tests/handshake_tests.cpp

index e10d03967c49486c6f46aabb0e24e0e8eb16e9f4..ced8c6ee5e4abc6ef61b33386b47400a2f3d6a8b 100644 (file)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 
+#include "../common.h"
 #include "crypto/common.h"
 #include "crypto/ec_key.h"
 #include "crypto/openssl_error.h"
+#include "exception.h"
 #include "log/log.h"
 
+#include <cassert>
 #include <cstring>
+#include <openssl/bn.h>
 #include <openssl/ec.h>
 #include <openssl/evp.h>
 #include <openssl/obj_mac.h> // for NID_X9_62_prime256v1
+#include <openssl/ossl_typ.h>
+#include <openssl/x509.h>
+#include <unordered_map>
 
 namespace Crypto {
 
-X9_62_P_256_Key X9_62_P_256_Key::Create()
+namespace {
+int Curve2NID(ECKey::Curve curve)
+{
+    static const std::unordered_map<ECKey::Curve, int> CURVE2NID = {
+        {ECKey::Curve::X9_62_PRIME256V1, NID_X9_62_prime256v1},
+        {ECKey::Curve::SECP384R1,        NID_secp384r1       },
+        {ECKey::Curve::SECP521R1,        NID_secp521r1       },
+    };
+    auto nid = CURVE2NID.find(curve);
+    if (nid == CURVE2NID.end())
+        THROW_UNKNOWN("Unsupported curve");
+    return nid->second;
+}
+} // namespace
+
+ECKey::~ECKey() { EVP_PKEY_free(m_key); }
+
+ECKey ECKey::Create(Curve curve)
 {
     EVP_PKEY_CTX *ctx = nullptr;
     EVP_PKEY *pkey = nullptr;
@@ -43,7 +67,7 @@ X9_62_P_256_Key X9_62_P_256_Key::Create()
         TRY_LOG_ERROR("EVP_PKEY_keygen_init() error");
         goto opensslError;
     }
-    if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, NID_X9_62_prime256v1) != 1) {
+    if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, Curve2NID(curve)) != 1) {
         TRY_LOG_ERROR("EVP_PKEY_CTX_set_ec_paramgen_curve_nid() error");
         goto opensslError;
     }
@@ -53,14 +77,14 @@ X9_62_P_256_Key X9_62_P_256_Key::Create()
     }
 
     EVP_PKEY_CTX_free(ctx);
-    return X9_62_P_256_Key{pkey};
+    return ECKey{pkey};
 
 opensslError:
     EVP_PKEY_CTX_free(ctx);
     throw OpensslError{};
 }
 
-X9_62_P_256_Key X9_62_P_256_Key::ImportPrivateKey(const CryptoBuffer &input)
+ECKey ECKey::ImportPrivateKey(Curve curve, const CryptoBuffer &input)
 {
     EVP_PKEY *pkey = nullptr;
     EC_KEY *ec = nullptr;
@@ -78,7 +102,7 @@ X9_62_P_256_Key X9_62_P_256_Key::ImportPrivateKey(const CryptoBuffer &input)
         TRY_LOG_ERROR("EC_KEY_new() error");
         goto opensslError;
     }
-    group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+    group = EC_GROUP_new_by_curve_name(Curve2NID(curve));
     if (!group) {
         TRY_LOG_ERROR("EC_GROUP_new_by_curve_name() error");
         goto opensslError;
@@ -117,7 +141,7 @@ X9_62_P_256_Key X9_62_P_256_Key::ImportPrivateKey(const CryptoBuffer &input)
 
     EC_GROUP_clear_free(group);
     EC_POINT_clear_free(point);
-    return X9_62_P_256_Key{pkey};
+    return ECKey{pkey};
 
 opensslError:
     EVP_PKEY_free(pkey);
@@ -127,7 +151,7 @@ opensslError:
     throw OpensslError{};
 }
 
-X9_62_P_256_Key X9_62_P_256_Key::ImportPublicKey(const CryptoBuffer &input)
+ECKey ECKey::ImportPublicKey(Curve curve, const CryptoBuffer &input)
 {
     EVP_PKEY *pkey = nullptr;
     EC_KEY *ec = nullptr;
@@ -143,7 +167,7 @@ X9_62_P_256_Key X9_62_P_256_Key::ImportPublicKey(const CryptoBuffer &input)
         TRY_LOG_ERROR("EC_KEY_new() error");
         goto opensslError;
     }
-    group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+    group = EC_GROUP_new_by_curve_name(Curve2NID(curve));
     if (!group) {
         TRY_LOG_ERROR("EC_GROUP_new_by_curve_name() error");
         goto opensslError;
@@ -163,7 +187,7 @@ X9_62_P_256_Key X9_62_P_256_Key::ImportPublicKey(const CryptoBuffer &input)
     }
 
     EC_GROUP_clear_free(group);
-    return X9_62_P_256_Key{pkey};
+    return ECKey{pkey};
 
 opensslError:
     EVP_PKEY_free(pkey);
@@ -172,44 +196,106 @@ opensslError:
     throw OpensslError{};
 }
 
-CryptoBuffer X9_62_P_256_Key::ExportPrivateKey() const
+ECKey ECKey::ImportPublicKey(Curve curve, const CryptoBuffer &x, const CryptoBuffer &y)
+{
+    BIGNUM *bx = BN_new();
+    if (!bx)
+        THROW_MEMORY();
+    auto cleanupBx = OnScopeExit([&bx] { BN_free(bx); });
+
+    BIGNUM *by = BN_new();
+    if (!by)
+        THROW_MEMORY();
+    auto cleanupBy = OnScopeExit([&by] { BN_free(by); });
+
+    // TODO compressed format
+    if (BN_bin2bn(x.data(), static_cast<int>(x.size()), bx) == nullptr)
+        THROW_UNKNOWN("Bignum x creation failed");
+    if (BN_bin2bn(y.data(), static_cast<int>(y.size()), by) == nullptr)
+        THROW_UNKNOWN("Bignum y creation failed");
+
+    auto ecKey = EC_KEY_new_by_curve_name(Curve2NID(curve));
+    if (!ecKey)
+        THROW_UNKNOWN("EC_KEY_new_by_curve_name() failed");
+
+    auto cleanupEcKey = OnScopeExit([&ecKey] { EC_KEY_free(ecKey); });
+
+    if (EC_KEY_set_public_key_affine_coordinates(ecKey, bx, by) != 1)
+        THROW_UNKNOWN("EC_KEY_set_public_key_affine_coordinates() failed");
+
+    auto evpKey = EVP_PKEY_new();
+    if (!evpKey)
+        THROW_MEMORY();
+
+    auto cleanupEvpKey = OnScopeExit([&evpKey] { EVP_PKEY_free(evpKey); });
+
+    if (EVP_PKEY_assign_EC_KEY(evpKey, ecKey) != 1)
+        THROW_UNKNOWN("EVP_PKEY_assign_EC_KEY() failed");
+    ecKey = nullptr;
+
+    auto ret = ECKey{evpKey};
+    evpKey = nullptr;
+
+    return ret;
+}
+
+CryptoBuffer ECKey::ExportPrivateKey() const
 {
-    CryptoBuffer res(32);
     size_t buffLen;
     unsigned char *buff = nullptr;
 
     const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(m_key);
     buffLen = EC_KEY_priv2buf(ec, &buff);
-    if (buffLen == 0 || buffLen != res.size()) {
+    if (buffLen == 0) {
         TRY_LOG_ERROR("EC_KEY_priv2buf() error");
         OPENSSL_free(buff);
         throw OpensslError{};
     }
 
+    CryptoBuffer res(buffLen);
     std::memcpy(res.data(), buff, buffLen);
     OPENSSL_free(buff);
     return res;
 }
 
-CryptoBuffer X9_62_P_256_Key::ExportPublicKey(bool compressed) const
+CryptoBuffer ECKey::ExportPublicKey(Format format) const
 {
-    CryptoBuffer res(compressed ? 33 : 65);
-    const point_conversion_form_t form =
-        compressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED;
-    size_t buffLen;
-    unsigned char *buff = nullptr;
+    const EC_KEY *ecKey = EVP_PKEY_get0_EC_KEY(m_key);
 
-    const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(m_key);
-    buffLen = EC_KEY_key2buf(ec, form, &buff, nullptr);
-    if (buffLen == 0 || buffLen != res.size()) {
-        TRY_LOG_ERROR("EC_KEY_key2buf() error");
-        OPENSSL_free(buff);
-        throw OpensslError{};
+    switch (format) {
+    case Format::DER: {
+        CryptoBuffer der(200);
+        auto derData = der.data();
+
+        const int ret = i2d_EC_PUBKEY(ecKey, &derData); // deprecated in 3.0
+        if (ret < 0)
+            THROW_UNKNOWN("i2d_EC_PUBKEY() failed");
+
+        der.resize(ret);
+        return der;
     }
+    case Format::OCTET_COMPRESSED:
+    case Format::OCTET_UNCOMPRESSED: {
+        const point_conversion_form_t form = format == Format::OCTET_COMPRESSED
+            ? POINT_CONVERSION_COMPRESSED
+            : POINT_CONVERSION_UNCOMPRESSED;
+        size_t buffLen;
+        unsigned char *buff = nullptr;
 
-    std::memcpy(res.data(), buff, buffLen);
-    OPENSSL_free(buff);
-    return res;
+        buffLen = EC_KEY_key2buf(ecKey, form, &buff, nullptr);
+        auto cleanupBuff = OnScopeExit([&buff] { OPENSSL_free(buff); });
+        if (buffLen == 0) {
+            TRY_LOG_ERROR("EC_KEY_key2buf() error");
+            throw OpensslError{};
+        }
+
+        CryptoBuffer res(buffLen);
+        std::memcpy(res.data(), buff, buffLen);
+        return res;
+    }
+    }
+    assert(false);
+    THROW_UNKNOWN("Unexpected format " << static_cast<int>(format));
 }
 
 } // namespace Crypto
index 442a2850dccd812420133a6ce6cba35b7423684e..7469cf841633589d890bae0d55a544d885b56d7d 100644 (file)
 
 #include "crypto/common.h"
 
-#include <openssl/evp.h>
+#include <webauthn-types.h>
+
+using EVP_PKEY = struct evp_pkey_st;
 
 namespace Crypto {
 
-class X9_62_P_256_Key {
+class ECKey {
 public:
-    X9_62_P_256_Key(const X9_62_P_256_Key &) = delete;
+    enum class Curve {
+        X9_62_PRIME256V1,
+        SECP384R1,
+        SECP521R1
+    };
 
-    X9_62_P_256_Key(X9_62_P_256_Key &&other) noexcept : m_key(other.m_key)
-    {
-        other.m_key = nullptr;
-    }
+    enum class Format {
+        OCTET_UNCOMPRESSED,
+        OCTET_COMPRESSED,
+        DER
+    };
+
+    ECKey(const ECKey &) = delete;
+
+    ECKey(ECKey &&other) noexcept : m_key(other.m_key) { other.m_key = nullptr; }
 
-    X9_62_P_256_Key &operator=(const X9_62_P_256_Key &) = delete;
-    X9_62_P_256_Key &operator=(X9_62_P_256_Key &&) = delete;
+    ECKey &operator=(const ECKey &) = delete;
+    ECKey &operator=(ECKey &&) = delete;
 
-    ~X9_62_P_256_Key() { EVP_PKEY_free(m_key); }
+    ~ECKey();
 
-    static X9_62_P_256_Key Create();
-    static X9_62_P_256_Key ImportPrivateKey(const CryptoBuffer &input);
-    static X9_62_P_256_Key ImportPublicKey(const CryptoBuffer &input);
+    static ECKey Create(Curve curve);
+    static ECKey ImportPrivateKey(Curve curve, const CryptoBuffer &input);
+    static ECKey ImportPublicKey(Curve curve, const CryptoBuffer &input);
+    static ECKey ImportPublicKey(Curve curve, const CryptoBuffer &x, const CryptoBuffer &y);
 
     [[nodiscard]] CryptoBuffer ExportPrivateKey() const;
-    [[nodiscard]] CryptoBuffer ExportPublicKey(bool compressed) const;
+    [[nodiscard]] CryptoBuffer ExportPublicKey(Format format) const;
 
     [[nodiscard]] EVP_PKEY *get() const noexcept { return m_key; }
 
 private:
-    explicit X9_62_P_256_Key(EVP_PKEY *key) noexcept : m_key{key} {}
+    explicit ECKey(EVP_PKEY *key) noexcept : m_key{key} {}
 
     EVP_PKEY *m_key = nullptr;
 };
 
+template <ECKey::Curve C>
+class ECKeyHelper : public ECKey {
+private:
+    // NOLINTNEXTLINE(google-explicit-constructor)
+    ECKeyHelper(ECKey &&other) noexcept : ECKey(std::move(other)) {}
+
+public:
+    static ECKeyHelper Create() { return ECKey::Create(C); }
+
+    static ECKeyHelper ImportPrivateKey(const CryptoBuffer &input)
+    {
+        return ECKey::ImportPrivateKey(C, input);
+    }
+
+    static ECKeyHelper ImportPublicKey(const CryptoBuffer &input)
+    {
+        return ECKey::ImportPublicKey(C, input);
+    }
+
+    static ECKeyHelper ImportPublicKey(const CryptoBuffer &x, const CryptoBuffer &y)
+    {
+        return ECKey::ImportPublicKey(C, x, y);
+    }
+};
+
+using X9_62_P_256_Key = ECKeyHelper<ECKey::Curve::X9_62_PRIME256V1>;
+
 } // namespace Crypto
index 71aae49c3fa037ab4e7ff18df8311f6818fb70a4..c0c2ed7678038604695673c8d30473262c83ceac 100644 (file)
@@ -24,8 +24,7 @@
 
 namespace Crypto {
 
-CryptoBuffer deriveECDHSharedSecret(const X9_62_P_256_Key &myPrivateKey,
-                                    const X9_62_P_256_Key &peerPublicKey)
+CryptoBuffer deriveECDHSharedSecret(const ECKey &myPrivateKey, const ECKey &peerPublicKey)
 {
     CryptoBuffer secret;
     size_t len = 0;
index dce14d8e41e1630152ce9819253755ab92a3f7f8..c631143b4feec9f895cd0abf83f6a2f664227429 100644 (file)
@@ -20,7 +20,6 @@
 
 namespace Crypto {
 
-CryptoBuffer deriveECDHSharedSecret(const X9_62_P_256_Key &myPrivateKey,
-                                    const X9_62_P_256_Key &peerPublicKey);
+CryptoBuffer deriveECDHSharedSecret(const ECKey &myPrivateKey, const ECKey &peerPublicKey);
 
 } // namespace Crypto
index 782843538af5c9878415ff07e280feedb1fe7720..c14f0f5d7d5affc29254d776f034aa7a8d38ba54 100644 (file)
@@ -49,15 +49,16 @@ InitialHandshakeMessage initialHandshakeMessage(const CryptoBuffer &psk,
     };
     if (peerPubKey) {
         res.noise.MixHash(CryptoBuffer{0});
-        res.noise.MixHash(peerPubKey->ExportPublicKey(false));
+        res.noise.MixHash(peerPubKey->ExportPublicKey(Crypto::ECKey::Format::OCTET_UNCOMPRESSED));
     } else {
         res.noise.MixHash(CryptoBuffer{1});
-        res.noise.MixHash(privKey->ExportPublicKey(false));
+        res.noise.MixHash(privKey->ExportPublicKey(Crypto::ECKey::Format::OCTET_UNCOMPRESSED));
     }
 
     res.noise.MixKeyAndHash(psk);
 
-    auto exportedEphemeralKeyPublic = res.ephemeralKey.ExportPublicKey(false);
+    auto exportedEphemeralKeyPublic =
+        res.ephemeralKey.ExportPublicKey(Crypto::ECKey::Format::OCTET_UNCOMPRESSED);
     res.noise.MixHash(exportedEphemeralKeyPublic);
     res.noise.MixKey(exportedEphemeralKeyPublic);
 
index 45dc71d4d8f52477284858bb6b44bc559592381b..05ad9e8285e584d11bc88dde5486b38bddb3e3ee 100644 (file)
 
 #include "cbor_encoding.h"
 #include "cbor_parsing.h"
+#include "crypto/ec_key.h"
 #include "crypto/sha2.h"
 #include "exception.h"
 #include "message.h"
 
-#include <openssl/bn.h> // TODO move it to Crypto?
-#include <openssl/ec.h>
-#include <openssl/ossl_typ.h>
-#include <openssl/x509.h>
 #include <regex>
 
 namespace {
@@ -624,7 +621,6 @@ CtapResponse::AuthData::AttestationData::AttestationData(BufferView &attestation
     auto publicKeyParser =
         CborParsing::Parser::Create(attestationDataView.data(), attestationDataView.size());
     auto credentialPublicKeyMap = publicKeyParser.EnterMap();
-
     // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
     auto kty = credentialPublicKeyMap.GetInt64At(KEY_KTY).value(); // Key Type
     // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
@@ -639,55 +635,30 @@ CtapResponse::AuthData::AttestationData::AttestationData(BufferView &attestation
     if (static_cast<KeyType>(kty) == KeyType::EC2) {
         // https://datatracker.ietf.org/doc/html/rfc8152#section-13.1
         struct AlgInfo {
-            int nid;
-            wauthn_cose_algorithm_e alg;
+            wauthn_cose_algorithm_e algo;
+            Crypto::ECKey::Curve curve;
         };
 
         static const std::unordered_map<Curve, AlgInfo> NAME2ALG = {
-            {Curve::P256, {NID_X9_62_prime256v1, WAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256}},
-            {Curve::P384, {NID_secp384r1, WAUTHN_COSE_ALGORITHM_ECDSA_P384_WITH_SHA384}       },
-            {Curve::P521, {NID_secp521r1, WAUTHN_COSE_ALGORITHM_ECDSA_P521_WITH_SHA512}       },
+            {Curve::P256,
+             {WAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256,
+              Crypto::ECKey::Curve::X9_62_PRIME256V1}                                       },
+            {Curve::P384,
+             {WAUTHN_COSE_ALGORITHM_ECDSA_P384_WITH_SHA384, Crypto::ECKey::Curve::SECP384R1}},
+            {Curve::P521,
+             {WAUTHN_COSE_ALGORITHM_ECDSA_P521_WITH_SHA512, Crypto::ECKey::Curve::SECP521R1}},
         };
         auto algInfo = NAME2ALG.find(static_cast<Curve>(crv));
         if (algInfo == NAME2ALG.end())
             THROW_UNKNOWN("Unsupported 'crv'");
-        if (alg != algInfo->second.alg)
+        if (alg != algInfo->second.algo)
             THROW_UNKNOWN("'Alg' doesn't match 'crv'");
 
-        // TODO move it to crypto?
-        BIGNUM *bx = BN_new();
-        if (!bx)
-            THROW_MEMORY();
-        auto cleanupBx = OnScopeExit([&bx] { BN_free(bx); });
-
-        BIGNUM *by = BN_new();
-        if (!by)
-            THROW_MEMORY();
-        auto cleanupBy = OnScopeExit([&by] { BN_free(by); });
-
-        // TODO compressed format
-        if (BN_bin2bn(x.data(), static_cast<int>(x.size()), bx) == nullptr)
-            THROW_UNKNOWN("Bignum x creation failed");
-        if (BN_bin2bn(y.data(), static_cast<int>(y.size()), by) == nullptr)
-            THROW_UNKNOWN("Bignum y creation failed");
-
-        auto ecKey = EC_KEY_new_by_curve_name(algInfo->second.nid);
-        if (!ecKey)
-            THROW_UNKNOWN("EC_KEY_new_by_curve_name() failed");
-        auto cleanupEcKey = OnScopeExit([&ecKey] { EC_KEY_free(ecKey); });
-
-        if (EC_KEY_set_public_key_affine_coordinates(ecKey, bx, by) != 1)
-            THROW_UNKNOWN("EC_KEY_set_public_key_affine_coordinates() failed");
-
-        m_publicKeyDer.resize(200);
-        auto publicKeyDerData = m_publicKeyDer.data();
-        const int ret = i2d_EC_PUBKEY(ecKey, &publicKeyDerData); // deprecated in 3.0
-        if (ret < 0)
-            THROW_UNKNOWN("i2d_EC_PUBKEY() failed");
-
-        m_publicKeyDer.resize(ret);
+        // TODO compressed format?
+        m_publicKeyDer = Crypto::ECKey::ImportPublicKey(algInfo->second.curve, x, y)
+                             .ExportPublicKey(Crypto::ECKey::Format::DER);
 
-        m_alg = algInfo->second.alg;
+        m_alg = algInfo->second.algo;
     } else {
         // TODO other formats?
         THROW_UNKNOWN("Invalid 'kty' value " << kty);
index 1cecd47671d17cc62addc5d3a4b9a2a03bf40686..9eb70c3c0dfd27b45ad541f097162386e1f5d9cf 100644 (file)
@@ -69,9 +69,12 @@ Result QrTransaction::DoPerformTransaction(
     UpdateStateAndCheckForCancel(State::SHOWING_QR_CODE);
 
     const CryptoBuffer qrSecret = Crypto::RandomBytes(QR_SECRET_LEN);
-    const Crypto::X9_62_P_256_Key identityKey = Crypto::X9_62_P_256_Key::Create();
-    m_qrCodeShower->ShowQrCode(
-        qrSecret, identityKey.ExportPublicKey(true), m_hint, true, std::move(m_qrCodeCallback));
+    const auto identityKey = Crypto::X9_62_P_256_Key::Create();
+    m_qrCodeShower->ShowQrCode(qrSecret,
+                               identityKey.ExportPublicKey(Crypto::ECKey::Format::OCTET_COMPRESSED),
+                               m_hint,
+                               true,
+                               std::move(m_qrCodeCallback));
 
     UpdateStateAndCheckForCancel(State::AWAITING_BLE_ADVERT);
 
index 6779bab0962ea579acf0ea069d5eb70d5407bde2..84c1b47f85912957a49735dc63d9299ddab8ec5e 100644 (file)
 #include "crypto/ec_key.h"
 
 #include <gtest/gtest.h>
+#include <unordered_map>
 
-TEST(X9_62_P_256_KeyTest, CreateExportImportTest)
+using Crypto::ECKey;
+using Crypto::ECKeyHelper;
+using Crypto::X9_62_P_256_Key;
+
+namespace {
+struct Sizes {
+    size_t priv;
+    size_t pub;
+    size_t pubCompressed;
+};
+
+const std::unordered_map<ECKey::Curve, Sizes> SIZES = {
+    {ECKey::Curve::X9_62_PRIME256V1, {32, 65, 33} },
+    {ECKey::Curve::SECP384R1,        {48, 97, 49} },
+    {ECKey::Curve::SECP521R1,        {66, 133, 67}},
+};
+} // namespace
+
+template <ECKey::Curve C>
+void CreateExportImportTest()
 {
-    auto key1 = Crypto::X9_62_P_256_Key::Create();
+    auto key1 = ECKeyHelper<C>::Create();
     auto privKey1 = key1.ExportPrivateKey();
-    auto pubKey1 = key1.ExportPublicKey(false);
-    auto pubKeyCompressed1 = key1.ExportPublicKey(true);
+    auto pubKey1 = key1.ExportPublicKey(ECKey::Format::OCTET_UNCOMPRESSED);
+    auto pubKeyCompressed1 = key1.ExportPublicKey(ECKey::Format::OCTET_COMPRESSED);
+    auto pubKeyDer1 = key1.ExportPublicKey(ECKey::Format::DER);
 
-    EXPECT_EQ(privKey1.size(), 32);
-    EXPECT_EQ(pubKey1.size(), 65);
-    EXPECT_EQ(pubKeyCompressed1.size(), 33);
+    EXPECT_EQ(privKey1.size(), SIZES.at(C).priv);
+    EXPECT_EQ(pubKey1.size(), SIZES.at(C).pub);
+    EXPECT_EQ(pubKeyCompressed1.size(), SIZES.at(C).pubCompressed);
 
-    auto key2 = Crypto::X9_62_P_256_Key::ImportPrivateKey(privKey1);
+    auto key2 = ECKeyHelper<C>::ImportPrivateKey(privKey1);
     auto privKey2 = key2.ExportPrivateKey();
-    auto pubKey2 = key2.ExportPublicKey(false);
-    auto pubKeyCompressed2 = key2.ExportPublicKey(true);
+    auto pubKey2 = key2.ExportPublicKey(ECKey::Format::OCTET_UNCOMPRESSED);
+    auto pubKeyCompressed2 = key2.ExportPublicKey(ECKey::Format::OCTET_COMPRESSED);
+    auto pubKeyDer2 = key2.ExportPublicKey(ECKey::Format::DER);
 
     EXPECT_EQ(privKey1, privKey2);
     EXPECT_EQ(pubKey1, pubKey2);
     EXPECT_EQ(pubKeyCompressed1, pubKeyCompressed2);
+    EXPECT_EQ(pubKeyDer1, pubKeyDer2);
 
-    auto key3 = Crypto::X9_62_P_256_Key::ImportPublicKey(pubKey1);
-    auto pubKey3 = key3.ExportPublicKey(false);
-    auto pubKeyCompressed3 = key3.ExportPublicKey(true);
+    auto key3 = ECKeyHelper<C>::ImportPublicKey(pubKey1);
+    auto pubKey3 = key3.ExportPublicKey(ECKey::Format::OCTET_UNCOMPRESSED);
+    auto pubKeyCompressed3 = key3.ExportPublicKey(ECKey::Format::OCTET_COMPRESSED);
+    auto pubKeyDer3 = key3.ExportPublicKey(ECKey::Format::DER);
     EXPECT_EQ(pubKey1, pubKey3);
     EXPECT_EQ(pubKeyCompressed1, pubKeyCompressed3);
+    EXPECT_EQ(pubKeyDer1, pubKeyDer3);
 
-    auto key4 = Crypto::X9_62_P_256_Key::ImportPublicKey(pubKeyCompressed1);
-    auto pubKey4 = key4.ExportPublicKey(false);
-    auto pubKeyCompressed4 = key4.ExportPublicKey(true);
+    auto key4 = ECKeyHelper<C>::ImportPublicKey(pubKeyCompressed1);
+    auto pubKey4 = key4.ExportPublicKey(ECKey::Format::OCTET_UNCOMPRESSED);
+    auto pubKeyCompressed4 = key4.ExportPublicKey(ECKey::Format::OCTET_COMPRESSED);
+    auto pubKeyDer4 = key3.ExportPublicKey(ECKey::Format::DER);
     EXPECT_EQ(pubKey1, pubKey4);
     EXPECT_EQ(pubKeyCompressed1, pubKeyCompressed4);
+    EXPECT_EQ(pubKeyDer1, pubKeyDer4);
 }
 
-TEST(X9_62_P_256_KeyTest, CreateExportRandomTest)
+TEST(ECKeyTest, CreateExportImportTest)
 {
-    auto key1 = Crypto::X9_62_P_256_Key::Create();
-    auto key2 = Crypto::X9_62_P_256_Key::Create();
+    CreateExportImportTest<ECKey::Curve::X9_62_PRIME256V1>();
+    CreateExportImportTest<ECKey::Curve::SECP384R1>();
+    CreateExportImportTest<ECKey::Curve::SECP521R1>();
+}
+
+template <ECKey::Curve C>
+void CreateExportRandomTest()
+{
+    auto key1 = ECKeyHelper<C>::Create();
+    auto key2 = ECKeyHelper<C>::Create();
 
     EXPECT_NE(key1.ExportPrivateKey(), key2.ExportPrivateKey());
-    EXPECT_NE(key1.ExportPublicKey(false), key2.ExportPublicKey(false));
-    EXPECT_NE(key1.ExportPublicKey(true), key2.ExportPublicKey(true));
+    EXPECT_NE(key1.ExportPublicKey(ECKey::Format::OCTET_UNCOMPRESSED),
+              key2.ExportPublicKey(ECKey::Format::OCTET_UNCOMPRESSED));
+    EXPECT_NE(key1.ExportPublicKey(ECKey::Format::OCTET_COMPRESSED),
+              key2.ExportPublicKey(ECKey::Format::OCTET_COMPRESSED));
+    EXPECT_NE(key1.ExportPublicKey(ECKey::Format::DER), key2.ExportPublicKey(ECKey::Format::DER));
+}
+
+TEST(ECKeyTest, CreateExportRandomTest)
+{
+    CreateExportRandomTest<ECKey::Curve::X9_62_PRIME256V1>();
+    CreateExportRandomTest<ECKey::Curve::SECP384R1>();
+    CreateExportRandomTest<ECKey::Curve::SECP521R1>();
 }
 
-TEST(X9_62_P_256_KeyTest, ImportExportTestVector)
+template <ECKey::Curve C>
+void ImportExportTestVector(const CryptoBuffer &priv,
+                            const CryptoBuffer &pub,
+                            const CryptoBuffer &pubCompressed,
+                            const CryptoBuffer &x,
+                            const CryptoBuffer &y,
+                            const CryptoBuffer &der)
 {
-    const CryptoBuffer PRIV_KEY = {0xcb, 0x6f, 0xc4, 0xd8, 0x99, 0x34, 0x03, 0x6d, 0x35, 0xa3, 0x60,
-                                   0x5a, 0xae, 0x36, 0x63, 0x6d, 0xe4, 0x8f, 0x62, 0xa7, 0x64, 0xf7,
-                                   0xfd, 0xdc, 0x61, 0xc1, 0x12, 0x36, 0xa6, 0xf7, 0x79, 0x76};
-
-    const CryptoBuffer PUB_KEY = {0x04, 0x52, 0x6a, 0x35, 0xdf, 0x37, 0x0f, 0xf3, 0x5e, 0x56, 0x22,
-                                  0x96, 0xc9, 0x55, 0xbf, 0x33, 0xdb, 0x75, 0x52, 0x24, 0xad, 0x63,
-                                  0x20, 0xfc, 0x8c, 0xa9, 0x14, 0x97, 0xe3, 0x85, 0x14, 0x3e, 0xed,
-                                  0xbd, 0x1d, 0x73, 0x10, 0xe8, 0x20, 0xb8, 0xfb, 0xe0, 0xc3, 0x5f,
-                                  0x97, 0x36, 0x69, 0xa3, 0x40, 0x83, 0x4c, 0xae, 0xf2, 0xf0, 0x89,
-                                  0xfa, 0x8a, 0xb4, 0xc1, 0x9e, 0xca, 0xf6, 0xb9, 0x11, 0x39};
-
-    const CryptoBuffer PUB_KEY_COMPRESSED = {0x03, 0x52, 0x6a, 0x35, 0xdf, 0x37, 0x0f, 0xf3, 0x5e,
-                                             0x56, 0x22, 0x96, 0xc9, 0x55, 0xbf, 0x33, 0xdb, 0x75,
-                                             0x52, 0x24, 0xad, 0x63, 0x20, 0xfc, 0x8c, 0xa9, 0x14,
-                                             0x97, 0xe3, 0x85, 0x14, 0x3e, 0xed};
-
-    auto key = Crypto::X9_62_P_256_Key::ImportPrivateKey(PRIV_KEY);
+    auto key = ECKeyHelper<C>::ImportPrivateKey(priv);
     auto privKey = key.ExportPrivateKey();
-    auto pubKey = key.ExportPublicKey(false);
-    auto pubKeyCompressed = key.ExportPublicKey(true);
+    auto pubKey = key.ExportPublicKey(ECKey::Format::OCTET_UNCOMPRESSED);
+    auto pubKeyCompressed = key.ExportPublicKey(ECKey::Format::OCTET_COMPRESSED);
+    auto pubDer = ECKeyHelper<C>::ImportPublicKey(x, y).ExportPublicKey(ECKey::Format::DER);
+
+    EXPECT_EQ(privKey, priv);
+    EXPECT_EQ(pubKey, pub);
+    EXPECT_EQ(pubKeyCompressed, pubCompressed);
+    EXPECT_EQ(pubDer, der);
+}
+
+TEST(ECKeyTest, ImportExportTestVectorPrime256v1)
+{
+    // Test data generated using openssl
+    const CryptoBuffer PRIV_KEY = {0xf7, 0xa8, 0x51, 0x19, 0xac, 0x44, 0x77, 0x3f, 0x59, 0xed, 0x27,
+                                   0x23, 0x91, 0x1f, 0x4e, 0xb3, 0xcb, 0x3f, 0xb5, 0x2f, 0x4d, 0x41,
+                                   0x42, 0x82, 0x43, 0x00, 0xa6, 0x57, 0x18, 0x66, 0xff, 0x71};
+
+    const CryptoBuffer PUB_KEY = {0x04, 0xab, 0x21, 0xe6, 0x63, 0xeb, 0x92, 0xc4, 0x56, 0x3c, 0xed,
+                                  0x1d, 0xa8, 0xad, 0x24, 0x75, 0xf6, 0x67, 0xd1, 0x41, 0xec, 0x3e,
+                                  0x38, 0xc6, 0x22, 0x8a, 0x0d, 0x14, 0xee, 0xa4, 0xeb, 0x3e, 0xc7,
+                                  0xd3, 0x3a, 0x7a, 0x4a, 0xe5, 0x34, 0x44, 0x61, 0x1f, 0x48, 0x3a,
+                                  0x4f, 0x14, 0x7e, 0x70, 0x98, 0x39, 0xe4, 0x69, 0xa6, 0x18, 0x1b,
+                                  0x3d, 0xf5, 0x6b, 0xdd, 0x9f, 0x37, 0x95, 0x8d, 0x47, 0x24};
+
+    const CryptoBuffer PUB_KEY_COMPRESSED = {0x02, 0xab, 0x21, 0xe6, 0x63, 0xeb, 0x92, 0xc4, 0x56,
+                                             0x3c, 0xed, 0x1d, 0xa8, 0xad, 0x24, 0x75, 0xf6, 0x67,
+                                             0xd1, 0x41, 0xec, 0x3e, 0x38, 0xc6, 0x22, 0x8a, 0x0d,
+                                             0x14, 0xee, 0xa4, 0xeb, 0x3e, 0xc7};
+
+    const CryptoBuffer X = {0xab, 0x21, 0xe6, 0x63, 0xeb, 0x92, 0xc4, 0x56, 0x3c, 0xed, 0x1d,
+                            0xa8, 0xad, 0x24, 0x75, 0xf6, 0x67, 0xd1, 0x41, 0xec, 0x3e, 0x38,
+                            0xc6, 0x22, 0x8a, 0x0d, 0x14, 0xee, 0xa4, 0xeb, 0x3e, 0xc7};
+
+    const CryptoBuffer Y = {0xd3, 0x3a, 0x7a, 0x4a, 0xe5, 0x34, 0x44, 0x61, 0x1f, 0x48, 0x3a,
+                            0x4f, 0x14, 0x7e, 0x70, 0x98, 0x39, 0xe4, 0x69, 0xa6, 0x18, 0x1b,
+                            0x3d, 0xf5, 0x6b, 0xdd, 0x9f, 0x37, 0x95, 0x8d, 0x47, 0x24};
+
+    const CryptoBuffer PUB_KEY_DER = {
+        0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
+        0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
+        0x04, 0xab, 0x21, 0xe6, 0x63, 0xeb, 0x92, 0xc4, 0x56, 0x3c, 0xed, 0x1d, 0xa8,
+        0xad, 0x24, 0x75, 0xf6, 0x67, 0xd1, 0x41, 0xec, 0x3e, 0x38, 0xc6, 0x22, 0x8a,
+        0x0d, 0x14, 0xee, 0xa4, 0xeb, 0x3e, 0xc7, 0xd3, 0x3a, 0x7a, 0x4a, 0xe5, 0x34,
+        0x44, 0x61, 0x1f, 0x48, 0x3a, 0x4f, 0x14, 0x7e, 0x70, 0x98, 0x39, 0xe4, 0x69,
+        0xa6, 0x18, 0x1b, 0x3d, 0xf5, 0x6b, 0xdd, 0x9f, 0x37, 0x95, 0x8d, 0x47, 0x24};
+
+    ImportExportTestVector<ECKey::Curve::X9_62_PRIME256V1>(
+        PRIV_KEY, PUB_KEY, PUB_KEY_COMPRESSED, X, Y, PUB_KEY_DER);
+}
+
+TEST(ECKeyTest, ImportExportTestVectorSecp384r1)
+{
+    const CryptoBuffer PRIV_KEY = {0xa6, 0x0e, 0xbc, 0xa4, 0xa8, 0x8c, 0x66, 0x83, 0xb2, 0xe3,
+                                   0x3f, 0x22, 0x78, 0x3b, 0xe3, 0xbc, 0x2a, 0xb5, 0x19, 0xeb,
+                                   0x21, 0x02, 0xe7, 0x0c, 0x36, 0xc4, 0xd3, 0x10, 0xa1, 0x66,
+                                   0x34, 0xb4, 0x89, 0x88, 0xe8, 0x6d, 0xd7, 0x5d, 0x3b, 0xe6,
+                                   0x67, 0x65, 0x9e, 0xc8, 0x33, 0xd1, 0x73, 0xc7};
+
+    const CryptoBuffer PUB_KEY = {
+        0x04, 0x6b, 0x39, 0xb6, 0x58, 0x37, 0x07, 0x79, 0xd2, 0x8d, 0x3d, 0x45, 0x22, 0x41,
+        0x3a, 0xbf, 0x78, 0x07, 0xa4, 0x5a, 0x13, 0x95, 0x9f, 0xc2, 0x3e, 0xbf, 0x50, 0xd3,
+        0xf0, 0x30, 0x34, 0xc4, 0x91, 0x36, 0xfb, 0xd6, 0x8d, 0x84, 0x0a, 0xce, 0x7e, 0x54,
+        0xf3, 0x0d, 0x57, 0xd4, 0x3c, 0x5b, 0xb2, 0x1b, 0x7a, 0x00, 0x6b, 0xc1, 0x36, 0x50,
+        0xef, 0xe7, 0x38, 0xf9, 0x13, 0xf0, 0x49, 0xfc, 0xe9, 0x94, 0xc0, 0x1e, 0xe0, 0x7d,
+        0xb6, 0xdc, 0xc3, 0x2b, 0x25, 0xde, 0x47, 0xfd, 0x5b, 0x51, 0xec, 0xd1, 0xc1, 0x0d,
+        0x39, 0x92, 0x7d, 0x1b, 0xee, 0x89, 0x80, 0x2e, 0x60, 0x73, 0x0a, 0x5a, 0xc0};
+
+    const CryptoBuffer PUB_KEY_COMPRESSED = {
+        0x02, 0x6b, 0x39, 0xb6, 0x58, 0x37, 0x07, 0x79, 0xd2, 0x8d, 0x3d, 0x45, 0x22,
+        0x41, 0x3a, 0xbf, 0x78, 0x07, 0xa4, 0x5a, 0x13, 0x95, 0x9f, 0xc2, 0x3e, 0xbf,
+        0x50, 0xd3, 0xf0, 0x30, 0x34, 0xc4, 0x91, 0x36, 0xfb, 0xd6, 0x8d, 0x84, 0x0a,
+        0xce, 0x7e, 0x54, 0xf3, 0x0d, 0x57, 0xd4, 0x3c, 0x5b, 0xb2};
+
+    const CryptoBuffer X = {0x6b, 0x39, 0xb6, 0x58, 0x37, 0x07, 0x79, 0xd2, 0x8d, 0x3d, 0x45, 0x22,
+                            0x41, 0x3a, 0xbf, 0x78, 0x07, 0xa4, 0x5a, 0x13, 0x95, 0x9f, 0xc2, 0x3e,
+                            0xbf, 0x50, 0xd3, 0xf0, 0x30, 0x34, 0xc4, 0x91, 0x36, 0xfb, 0xd6, 0x8d,
+                            0x84, 0x0a, 0xce, 0x7e, 0x54, 0xf3, 0x0d, 0x57, 0xd4, 0x3c, 0x5b, 0xb2};
+
+    const CryptoBuffer Y = {0x1b, 0x7a, 0x00, 0x6b, 0xc1, 0x36, 0x50, 0xef, 0xe7, 0x38, 0xf9, 0x13,
+                            0xf0, 0x49, 0xfc, 0xe9, 0x94, 0xc0, 0x1e, 0xe0, 0x7d, 0xb6, 0xdc, 0xc3,
+                            0x2b, 0x25, 0xde, 0x47, 0xfd, 0x5b, 0x51, 0xec, 0xd1, 0xc1, 0x0d, 0x39,
+                            0x92, 0x7d, 0x1b, 0xee, 0x89, 0x80, 0x2e, 0x60, 0x73, 0x0a, 0x5a, 0xc0};
+
+    const CryptoBuffer PUB_KEY_DER = {
+        0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05,
+        0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0x6b, 0x39, 0xb6, 0x58, 0x37, 0x07,
+        0x79, 0xd2, 0x8d, 0x3d, 0x45, 0x22, 0x41, 0x3a, 0xbf, 0x78, 0x07, 0xa4, 0x5a, 0x13, 0x95,
+        0x9f, 0xc2, 0x3e, 0xbf, 0x50, 0xd3, 0xf0, 0x30, 0x34, 0xc4, 0x91, 0x36, 0xfb, 0xd6, 0x8d,
+        0x84, 0x0a, 0xce, 0x7e, 0x54, 0xf3, 0x0d, 0x57, 0xd4, 0x3c, 0x5b, 0xb2, 0x1b, 0x7a, 0x00,
+        0x6b, 0xc1, 0x36, 0x50, 0xef, 0xe7, 0x38, 0xf9, 0x13, 0xf0, 0x49, 0xfc, 0xe9, 0x94, 0xc0,
+        0x1e, 0xe0, 0x7d, 0xb6, 0xdc, 0xc3, 0x2b, 0x25, 0xde, 0x47, 0xfd, 0x5b, 0x51, 0xec, 0xd1,
+        0xc1, 0x0d, 0x39, 0x92, 0x7d, 0x1b, 0xee, 0x89, 0x80, 0x2e, 0x60, 0x73, 0x0a, 0x5a, 0xc0};
+
+    ImportExportTestVector<ECKey::Curve::SECP384R1>(
+        PRIV_KEY, PUB_KEY, PUB_KEY_COMPRESSED, X, Y, PUB_KEY_DER);
+}
+
+TEST(ECKeyTest, ImportExportTestVectorSecp521r1)
+{
+    // Test data generated using openssl
+    const CryptoBuffer PRIV_KEY = {
+        0x00, 0xa9, 0x58, 0x7c, 0xfb, 0xb8, 0xa9, 0xae, 0x39, 0xfe, 0x85, 0x2f, 0x3f, 0x21,
+        0x9f, 0xc6, 0x7d, 0x4a, 0xc6, 0x2e, 0x50, 0xc8, 0x14, 0xaa, 0x26, 0x4c, 0xf2, 0x4d,
+        0x6f, 0xa2, 0xe4, 0xcb, 0xe2, 0x49, 0x24, 0xa0, 0x05, 0xbe, 0x40, 0x8b, 0x5c, 0xdd,
+        0x16, 0xff, 0x7c, 0xf0, 0x32, 0xf8, 0x26, 0x71, 0x1c, 0x97, 0x2f, 0xe2, 0xe6, 0x60,
+        0xad, 0x28, 0x04, 0x7e, 0xe9, 0xdc, 0x95, 0x9d, 0x1b, 0xdb};
+
+    const CryptoBuffer PUB_KEY = {
+        0x04, 0x01, 0xf9, 0x0f, 0x05, 0x7b, 0xf1, 0x37, 0x46, 0xc2, 0x0f, 0x89, 0x38, 0x8b, 0xdd,
+        0x56, 0xe1, 0x6d, 0x79, 0xca, 0xc2, 0x1f, 0x8a, 0xef, 0x59, 0x53, 0x0b, 0xb8, 0x18, 0x21,
+        0x3a, 0x23, 0x9f, 0xa5, 0x35, 0xb4, 0xc2, 0x98, 0x84, 0x9f, 0x53, 0x13, 0x73, 0x3c, 0x58,
+        0x2d, 0xbe, 0x42, 0x44, 0x77, 0xe0, 0xae, 0xb9, 0xa0, 0x49, 0x03, 0x19, 0xff, 0x71, 0x2a,
+        0xba, 0x09, 0x03, 0x17, 0x7e, 0x6b, 0x56, 0x01, 0x3a, 0xab, 0xb9, 0xc1, 0x80, 0x8d, 0x08,
+        0xa5, 0x50, 0xf1, 0x7d, 0xf3, 0x9f, 0xaf, 0xe2, 0x03, 0xbc, 0x05, 0xa1, 0x0b, 0xf7, 0x8a,
+        0xfd, 0x8b, 0x0f, 0x22, 0xc1, 0x48, 0xee, 0x50, 0xa8, 0xe5, 0x0e, 0x02, 0x47, 0x2b, 0x21,
+        0xfb, 0xaa, 0x39, 0x3f, 0x2f, 0x75, 0x1b, 0x9e, 0x3d, 0x97, 0x63, 0x82, 0x8b, 0xf1, 0xe2,
+        0x97, 0xa8, 0x71, 0xe6, 0x15, 0x7e, 0x4b, 0x64, 0xff, 0xaa, 0xeb, 0xb7, 0x95};
+
+    const CryptoBuffer PUB_KEY_COMPRESSED = {
+        0x03, 0x01, 0xf9, 0x0f, 0x05, 0x7b, 0xf1, 0x37, 0x46, 0xc2, 0x0f, 0x89, 0x38, 0x8b,
+        0xdd, 0x56, 0xe1, 0x6d, 0x79, 0xca, 0xc2, 0x1f, 0x8a, 0xef, 0x59, 0x53, 0x0b, 0xb8,
+        0x18, 0x21, 0x3a, 0x23, 0x9f, 0xa5, 0x35, 0xb4, 0xc2, 0x98, 0x84, 0x9f, 0x53, 0x13,
+        0x73, 0x3c, 0x58, 0x2d, 0xbe, 0x42, 0x44, 0x77, 0xe0, 0xae, 0xb9, 0xa0, 0x49, 0x03,
+        0x19, 0xff, 0x71, 0x2a, 0xba, 0x09, 0x03, 0x17, 0x7e, 0x6b, 0x56};
+
+    const CryptoBuffer X = {0x01, 0xf9, 0x0f, 0x05, 0x7b, 0xf1, 0x37, 0x46, 0xc2, 0x0f, 0x89,
+                            0x38, 0x8b, 0xdd, 0x56, 0xe1, 0x6d, 0x79, 0xca, 0xc2, 0x1f, 0x8a,
+                            0xef, 0x59, 0x53, 0x0b, 0xb8, 0x18, 0x21, 0x3a, 0x23, 0x9f, 0xa5,
+                            0x35, 0xb4, 0xc2, 0x98, 0x84, 0x9f, 0x53, 0x13, 0x73, 0x3c, 0x58,
+                            0x2d, 0xbe, 0x42, 0x44, 0x77, 0xe0, 0xae, 0xb9, 0xa0, 0x49, 0x03,
+                            0x19, 0xff, 0x71, 0x2a, 0xba, 0x09, 0x03, 0x17, 0x7e, 0x6b, 0x56};
+
+    const CryptoBuffer Y = {0x01, 0x3a, 0xab, 0xb9, 0xc1, 0x80, 0x8d, 0x08, 0xa5, 0x50, 0xf1,
+                            0x7d, 0xf3, 0x9f, 0xaf, 0xe2, 0x03, 0xbc, 0x05, 0xa1, 0x0b, 0xf7,
+                            0x8a, 0xfd, 0x8b, 0x0f, 0x22, 0xc1, 0x48, 0xee, 0x50, 0xa8, 0xe5,
+                            0x0e, 0x02, 0x47, 0x2b, 0x21, 0xfb, 0xaa, 0x39, 0x3f, 0x2f, 0x75,
+                            0x1b, 0x9e, 0x3d, 0x97, 0x63, 0x82, 0x8b, 0xf1, 0xe2, 0x97, 0xa8,
+                            0x71, 0xe6, 0x15, 0x7e, 0x4b, 0x64, 0xff, 0xaa, 0xeb, 0xb7, 0x95};
+
+    const CryptoBuffer PUB_KEY_DER = {
+        0x30, 0x81, 0x9b, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
+        0x05, 0x2b, 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86, 0x00, 0x04, 0x01, 0xf9, 0x0f, 0x05,
+        0x7b, 0xf1, 0x37, 0x46, 0xc2, 0x0f, 0x89, 0x38, 0x8b, 0xdd, 0x56, 0xe1, 0x6d, 0x79, 0xca,
+        0xc2, 0x1f, 0x8a, 0xef, 0x59, 0x53, 0x0b, 0xb8, 0x18, 0x21, 0x3a, 0x23, 0x9f, 0xa5, 0x35,
+        0xb4, 0xc2, 0x98, 0x84, 0x9f, 0x53, 0x13, 0x73, 0x3c, 0x58, 0x2d, 0xbe, 0x42, 0x44, 0x77,
+        0xe0, 0xae, 0xb9, 0xa0, 0x49, 0x03, 0x19, 0xff, 0x71, 0x2a, 0xba, 0x09, 0x03, 0x17, 0x7e,
+        0x6b, 0x56, 0x01, 0x3a, 0xab, 0xb9, 0xc1, 0x80, 0x8d, 0x08, 0xa5, 0x50, 0xf1, 0x7d, 0xf3,
+        0x9f, 0xaf, 0xe2, 0x03, 0xbc, 0x05, 0xa1, 0x0b, 0xf7, 0x8a, 0xfd, 0x8b, 0x0f, 0x22, 0xc1,
+        0x48, 0xee, 0x50, 0xa8, 0xe5, 0x0e, 0x02, 0x47, 0x2b, 0x21, 0xfb, 0xaa, 0x39, 0x3f, 0x2f,
+        0x75, 0x1b, 0x9e, 0x3d, 0x97, 0x63, 0x82, 0x8b, 0xf1, 0xe2, 0x97, 0xa8, 0x71, 0xe6, 0x15,
+        0x7e, 0x4b, 0x64, 0xff, 0xaa, 0xeb, 0xb7, 0x95};
 
-    EXPECT_EQ(privKey, PRIV_KEY);
-    EXPECT_EQ(pubKey, PUB_KEY);
-    EXPECT_EQ(pubKeyCompressed, PUB_KEY_COMPRESSED);
+    ImportExportTestVector<ECKey::Curve::SECP521R1>(
+        PRIV_KEY, PUB_KEY, PUB_KEY_COMPRESSED, X, Y, PUB_KEY_DER);
 }
index 0b4cee88bc90306ce07a49b34cf063d4216de118..affba4b51a81cbe3422969c5a41a689f598d6256 100644 (file)
@@ -163,17 +163,19 @@ public:
                 : Crypto::Noise::SymmetricState::HandshakeKind::KNpsk0_P256_AESGCM_SHA256};
         if (m_authenticatorPrivKey) {
             noise.MixHash(CryptoBuffer{0});
-            noise.MixHash(m_authenticatorPrivKey->ExportPublicKey(false));
+            noise.MixHash(
+                m_authenticatorPrivKey->ExportPublicKey(Crypto::ECKey::Format::OCTET_UNCOMPRESSED));
         } else {
             noise.MixHash(CryptoBuffer{1});
-            noise.MixHash(m_platformPubKey->ExportPublicKey(false));
+            noise.MixHash(
+                m_platformPubKey->ExportPublicKey(Crypto::ECKey::Format::OCTET_UNCOMPRESSED));
         }
 
         noise.MixKeyAndHash(m_psk);
 
         auto exportedPlatformEphemeralPubKeyLen =
             (m_authenticatorPrivKey ? m_authenticatorPrivKey : m_platformPubKey)
-                ->ExportPublicKey(false)
+                ->ExportPublicKey(Crypto::ECKey::Format::OCTET_UNCOMPRESSED)
                 .size();
         ASSERT_GE(msg.size(), exportedPlatformEphemeralPubKeyLen);
         auto platformEphemeralPubKeyBytes =
@@ -195,7 +197,8 @@ public:
 
         // Prepare response
         auto authenticatorEphemeralPrivKey = Crypto::X9_62_P_256_Key::Create();
-        auto response = authenticatorEphemeralPrivKey.ExportPublicKey(false);
+        auto response = authenticatorEphemeralPrivKey.ExportPublicKey(
+            Crypto::ECKey::Format::OCTET_UNCOMPRESSED);
         noise.MixHash(response);
         noise.MixKey(response);
 
@@ -258,8 +261,8 @@ public:
 private:
     CryptoBuffer m_psk = Crypto::RandomBytes(PSK_LEN);
     Crypto::X9_62_P_256_Key m_identityKey = Crypto::X9_62_P_256_Key::Create();
-    Crypto::X9_62_P_256_Key m_identityPubKey =
-        Crypto::X9_62_P_256_Key::ImportPublicKey(m_identityKey.ExportPublicKey(false));
+    Crypto::X9_62_P_256_Key m_identityPubKey = Crypto::X9_62_P_256_Key::ImportPublicKey(
+        m_identityKey.ExportPublicKey(Crypto::ECKey::Format::OCTET_UNCOMPRESSED));
     CryptoBuffer m_getInfoMsg = Crypto::RandomBytes(get_random(0, 77));
 };
 
@@ -274,7 +277,8 @@ public:
 
     void RunAndTestHandshake(IHandshake &handshake)
     {
-        const CryptoBuffer authenticatorPubKey = m_authenticatorPrivKey.ExportPublicKey(false);
+        const CryptoBuffer authenticatorPubKey =
+            m_authenticatorPrivKey.ExportPublicKey(Crypto::ECKey::Format::OCTET_UNCOMPRESSED);
         auto res = handshake.DoStateAssistedHandshake(m_psk, authenticatorPubKey);
         EXPECT_EQ(res.encryptedTunnel->ReadBinary(), m_getInfoMsg);
     }