security: encode EC public key with named curve
refs #5037
Change-Id: I5d77782e347579fe6dffb188a92178f592bcd492
diff --git a/ndn-cxx/security/transform/private-key.cpp b/ndn-cxx/security/transform/private-key.cpp
index 659de11..f5c9360 100644
--- a/ndn-cxx/security/transform/private-key.cpp
+++ b/ndn-cxx/security/transform/private-key.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -32,6 +32,7 @@
#include "ndn-cxx/util/random.hpp"
#include <boost/lexical_cast.hpp>
+#include <boost/scope_exit.hpp>
#include <cstring>
#define ENSURE_PRIVATE_KEY_LOADED(key) \
@@ -475,42 +476,44 @@
unique_ptr<PrivateKey>
PrivateKey::generateEcKey(uint32_t keySize)
{
- detail::EvpPkeyCtx pctx(EVP_PKEY_EC);
-
- if (EVP_PKEY_paramgen_init(pctx) <= 0)
- NDN_THROW(PrivateKey::Error("Failed to initialize EC paramgen context"));
-
- int ret;
+ EC_KEY* eckey = nullptr;
switch (keySize) {
case 224:
- ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_secp224r1);
+ eckey = EC_KEY_new_by_curve_name(NID_secp224r1);
break;
case 256:
- ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1); // same as secp256r1
+ eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); // same as secp256r1
break;
case 384:
- ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_secp384r1);
+ eckey = EC_KEY_new_by_curve_name(NID_secp384r1);
break;
case 521:
- ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_secp521r1);
+ eckey = EC_KEY_new_by_curve_name(NID_secp521r1);
break;
default:
NDN_THROW(std::invalid_argument("Unsupported EC key length " + to_string(keySize)));
}
- if (ret <= 0)
- NDN_THROW(PrivateKey::Error("Failed to set EC curve"));
+ if (eckey == nullptr)
+ NDN_THROW(Error("Failed to set EC curve"));
- Impl params;
- if (EVP_PKEY_paramgen(pctx, ¶ms.key) <= 0)
- NDN_THROW(PrivateKey::Error("Failed to generate EC parameters"));
+ BOOST_SCOPE_EXIT(&eckey) {
+ EC_KEY_free(eckey);
+ } BOOST_SCOPE_EXIT_END
- detail::EvpPkeyCtx kctx(params.key);
- if (EVP_PKEY_keygen_init(kctx) <= 0)
- NDN_THROW(PrivateKey::Error("Failed to initialize EC keygen context"));
+#if OPENSSL_VERSION_NUMBER < 0x1010000fL
+ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+#endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
+
+ if (EC_KEY_generate_key(eckey) != 1) {
+ NDN_THROW(Error("Failed to generate EC key"));
+ }
auto privateKey = make_unique<PrivateKey>();
- if (EVP_PKEY_keygen(kctx, &privateKey->m_impl->key) <= 0)
- NDN_THROW(PrivateKey::Error("Failed to generate EC key"));
+ privateKey->m_impl->key = EVP_PKEY_new();
+ if (privateKey->m_impl->key == nullptr)
+ NDN_THROW(Error("Failed to create EVP_PKEY"));
+ if (EVP_PKEY_set1_EC_KEY(privateKey->m_impl->key, eckey) != 1)
+ NDN_THROW(Error("Failed to assign EC key"));
return privateKey;
}
diff --git a/tests/unit/security/transform/private-key.t.cpp b/tests/unit/security/transform/private-key.t.cpp
index 2317479..738067a 100644
--- a/tests/unit/security/transform/private-key.t.cpp
+++ b/tests/unit/security/transform/private-key.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -31,6 +31,7 @@
#include "ndn-cxx/security/transform/signer-filter.hpp"
#include "ndn-cxx/security/transform/stream-sink.hpp"
#include "ndn-cxx/security/transform/verifier-filter.hpp"
+#include "ndn-cxx/util/string-helper.hpp"
#include "tests/boost-test.hpp"
@@ -160,8 +161,11 @@
"ywIDAQAB\n";
};
-struct EcKeyTestData
+struct EcKeySpecificCurveTestData
{
+ // EC keys are generated in named curve format only. However, old keys in specific curve format
+ // are still accepted. See https://redmine.named-data.net/issues/5037
+
const size_t keySize = 256;
const std::string privateKeyPkcs1 =
"MIIBaAIBAQQgRxwcbzK9RV6AHYFsDcykI86o3M/a1KlJn0z8PcLMBZOggfowgfcC\n"
@@ -193,7 +197,31 @@
"5EJTDxq6ls5FoYLfThp8HOjuwGSz0qw8ocMqyku1y0V5peQ4rEPd0bwcpZd9svA=\n";
};
-using KeyTestDataSets = boost::mpl::vector<RsaKeyTestData, EcKeyTestData>;
+struct EcKeyNamedCurveTestData
+{
+ // openssl ecparam -name secp384r1 -genkey -out pvt1.pem
+ // openssl pkcs8 -topk8 -passout pass:password -in pvt1.pem -out pvt8.pem
+ // openssl ec -in pvt1.pem -pubout -out pub8.pem
+
+ const size_t keySize = 384;
+ const std::string privateKeyPkcs1 =
+ "MIGkAgEBBDCUsb7NymksCkQAjdLMjUilWhOEyeYmGi79sX1RbsmfnoF/8SesKBhO\n"
+ "or+TZ8g8/8igBwYFK4EEACKhZANiAARWGplLOGdQiXRFQcd0VLPeTt0zXEj5zvSv\n"
+ "aHx9MrzBy57wgz10wTAiR561wuLtFAYxmqL9Ikrzx/BaEg0+v2zQ05NCzMNN8v2c\n"
+ "7/FzOhD7fmZrlJsT6Q2aHGExW0Rj3GE=\n";
+ const std::string privateKeyPkcs8 =
+ "MIHgMBsGCSqGSIb3DQEFAzAOBAjniUjwJfWsMQICCAAEgcCakGTKa49csaPpmtzi\n"
+ "5sTJw+AH8ajUqcDbtp2pJP/Ni6M1p9fai9hOKPElf9uJuYh/S80FAU6WQmZBAxL4\n"
+ "bF598ncLPogpGvz21wLuSc1xnbD829zAsMmh0XZvMZpBWX2g0NnZJx7GOraskTSh\n"
+ "7qGu70B+uKzw+JxIzgBEeMcoBUg8mTEht5zghfLGYkQp6BpTPdpU64udpPAKzFNs\n"
+ "5X+BzBnT5Yy49/Lp4uYIji8qwJFF3VqTn8RFKunFYDDejRU=\n";
+ const std::string publicKeyPkcs8 =
+ "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEVhqZSzhnUIl0RUHHdFSz3k7dM1xI+c70\n"
+ "r2h8fTK8wcue8IM9dMEwIkeetcLi7RQGMZqi/SJK88fwWhINPr9s0NOTQszDTfL9\n"
+ "nO/xczoQ+35ma5SbE+kNmhxhMVtEY9xh\n";
+};
+
+using KeyTestDataSets = boost::mpl::vector<RsaKeyTestData, EcKeySpecificCurveTestData, EcKeyNamedCurveTestData>;
static void
checkPkcs8Encoding(ConstBufferPtr encoding, const std::string& password, ConstBufferPtr pkcs1)
@@ -410,25 +438,48 @@
BOOST_CHECK_THROW(hmacKey->decrypt(os.buf()->data(), os.buf()->size()), PrivateKey::Error);
}
-struct RsaKeyGenParams
+class RsaKeyGenParams
{
+public:
using Params = RsaKeyParams;
using hasPublicKey = std::true_type;
using canSavePkcs1 = std::true_type;
+
+ static void
+ checkPublicKey(const Buffer& bits)
+ {
+ }
};
-struct EcKeyGenParams
+class EcKeyGenParams
{
+public:
using Params = EcKeyParams;
using hasPublicKey = std::true_type;
using canSavePkcs1 = std::true_type;
+
+ static void
+ checkPublicKey(const Buffer& bits)
+ {
+ // EC key generation should use named curve format. See https://redmine.named-data.net/issues/5037
+ // OBJECT IDENTIFIER 1.2.840.10045.3.1.7 prime256v1 (ANSI X9.62 named elliptic curve)
+ const uint8_t oid[] = {0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07};
+ BOOST_CHECK_MESSAGE(std::search(bits.begin(), bits.end(), oid, oid + sizeof(oid)) != bits.end(),
+ "OID not found in " << toHex(bits));
+ }
};
-struct HmacKeyGenParams
+class HmacKeyGenParams
{
+public:
using Params = HmacKeyParams;
using hasPublicKey = std::false_type;
using canSavePkcs1 = std::false_type;
+
+ static void
+ checkPublicKey(const Buffer& bits)
+ {
+ }
};
using KeyGenParams = boost::mpl::vector<RsaKeyGenParams,
@@ -452,6 +503,8 @@
bool result = false;
if (typename T::hasPublicKey()) {
auto pKeyBits = sKey->derivePublicKey();
+ BOOST_REQUIRE(pKeyBits != nullptr);
+ T::checkPublicKey(*pKeyBits);
PublicKey pKey;
pKey.loadPkcs8(pKeyBits->data(), pKeyBits->size());
BOOST_CHECK_NO_THROW(bufferSource(data, sizeof(data)) >>