]> granicus.if.org Git - pdns/commitdiff
Add ECDSA support to DNSSEC infra via mbedTLS
authorRemi Gacogne <rgacogne-github@coredump.fr>
Wed, 23 Dec 2015 11:45:40 +0000 (12:45 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 23 Dec 2015 11:45:40 +0000 (12:45 +0100)
build-scripts/dist-recursor
pdns/Makefile-recursor
pdns/mbedtlscompat.hh
pdns/mbedtlssigners.cc

index 42201519e4c5d2607eed6cb74dc232520a625241..b066ddc9d2fcf9074c3b19a092d13eb2cfa7cc33 100755 (executable)
@@ -74,13 +74,13 @@ mkdir -p $DIRNAME/ext/rapidjson/include/rapidjson/internal
 cp -a ../ext/rapidjson/include/rapidjson/*.h $DIRNAME/ext/rapidjson/include/rapidjson/
 cp -a ../ext/rapidjson/include/rapidjson/internal/*.h $DIRNAME/ext/rapidjson/include/rapidjson/internal
 mkdir -p $DIRNAME/ext/mbedtls/include/mbedtls
-cp -a ../ext/mbedtls/include/mbedtls/{config.h,check_config.h,aes.h,ripemd160.h,sha1.h,md.h,md5.h,sha256.h,sha512.h,md_internal.h} ../ext/mbedtls/include/mbedtls/base64.h ../ext/mbedtls/include/mbedtls/platform.h ../ext/mbedtls/include/mbedtls/version.h $DIRNAME/ext/mbedtls/include/mbedtls
-cp -a ../ext/mbedtls/include/mbedtls/{entropy.h,ctr_drbg.h,rsa.h,ecp.h,bignum.h,oid.h,asn1.h,pk.h,ecdsa.h,cipher.h,x509.h} $DIRNAME/ext/mbedtls/include/mbedtls
+cp -a ../ext/mbedtls/include/mbedtls/{config.h,check_config.h,aes.h,ripemd160.h,sha1.h,md.h,md5.h,sha256.h,sha512.h,ecp.h,ecdsa.h,md_internal.h} ../ext/mbedtls/include/mbedtls/base64.h ../ext/mbedtls/include/mbedtls/platform.h ../ext/mbedtls/include/mbedtls/version.h $DIRNAME/ext/mbedtls/include/mbedtls
+cp -a ../ext/mbedtls/include/mbedtls/{entropy.h,ctr_drbg.h,hmac_drbg.h,rsa.h,ecp.h,bignum.h,oid.h,asn1.h,asn1write.h,pk.h,ecdsa.h,cipher.h,x509.h} $DIRNAME/ext/mbedtls/include/mbedtls
 cp -a ../ext/mbedtls/include/mbedtls/{bn_mul.h,config.h,entropy_poll.h,timing.h} $DIRNAME/ext/mbedtls/include/mbedtls
 
 mkdir -p $DIRNAME/ext/mbedtls/library
 cp -a ../ext/mbedtls/library/{aes.c,base64.c,md.c,md_wrap.c,md5.c,sha1.c,sha256.c,sha512.c,ripemd160.c} $DIRNAME/ext/mbedtls/library
-cp -a ../ext/mbedtls/library/{rsa.c,bignum.c,oid.c,asn1parse.c,ctr_drbg.c,entropy.c,entropy_poll.c,timing.c} $DIRNAME/ext/mbedtls/library
+cp -a ../ext/mbedtls/library/{rsa.c,bignum.c,oid.c,asn1parse.c,ctr_drbg.c,entropy.c,entropy_poll.c,timing.c,ecp.c,ecdsa.c,ecp_curves.c,hmac_drbg.c,asn1write.c} $DIRNAME/ext/mbedtls/library
 
 cp -a ../ext/yahttp/ $DIRNAME/ext/yahttp
 
index e1edefa2f5c96ff9e67328b3ed6401074c6fff42..2a57156d13874c2c48b7be801ff606b4b0dee73c 100644 (file)
@@ -24,6 +24,8 @@ dns_random.o pubsuffix.o ext/mbedtls/library/aes.o ext/mbedtls/library/base64.o
 ext/mbedtls/library/md5.o ext/mbedtls/library/sha1.o ext/mbedtls/library/sha256.o  \
 ext/mbedtls/library/sha512.o ext/mbedtls/library/md.o ext/mbedtls/library/md_wrap.o \
 ext/mbedtls/library/ripemd160.o ext/mbedtls/library/rsa.o \
+ext/mbedtls/library/ecdsa.o ext/mbedtls/library/ecp.o ext/mbedtls/library/ecp_curves.o \
+ext/mbedtls/library/hmac_drbg.o ext/mbedtls/library/asn1write.o \
 ext/mbedtls/library/bignum.o ext/mbedtls/library/oid.o ext/mbedtls/library/asn1parse.o  \
 ext/mbedtls/library/ctr_drbg.o ext/mbedtls/library/entropy.o ext/mbedtls/library/entropy_poll.o\
 ext/mbedtls/library/timing.o \
index 9ed90327ec83a5f00d85482576b94424e96e29a9..075731adbbc3884b09517576526d0f9e227de3b3 100644 (file)
@@ -33,6 +33,10 @@ typedef md_type_t mbedtls_md_type_t;
 
 #define mbedtls_mpi mpi
 
+#define mbedtls_ecdsa_context ecdsa_context
+
+#define mbedtls_ecp_group ecp_group
+#define mbedtls_ecp_group_id ecp_group_id
 
 // Functions macro
 #define mbedtls_aes_crypt_ctr aes_crypt_ctr
@@ -65,10 +69,12 @@ typedef md_type_t mbedtls_md_type_t;
 
 #define mbedtls_entropy_init entropy_init
 #define mbedtls_entropy_func entropy_func
+#define mbedtls_entropy_free entropy_free
 
 #define mbedtls_ctr_drbg_init
 #define mbedtls_ctr_drbg_seed ctr_drbg_init
 #define mbedtls_ctr_drbg_random ctr_drbg_random
+#define mbedtls_ctr_drbg_free ctr_drbg_free
 
 #define mbedtls_rsa_init rsa_init
 #define mbedtls_rsa_gen_key rsa_gen_key
@@ -81,6 +87,20 @@ typedef md_type_t mbedtls_md_type_t;
 #define mbedtls_mpi_write_binary mpi_write_binary
 #define mbedtls_mpi_read_binary mpi_read_binary
 
+#define mbedtls_ecdsa_free ecdsa_free
+#define mbedtls_ecdsa_genkey ecdsa_genkey
+#define mbedtls_ecdsa_init ecdsa_init
+#define mbedtls_ecdsa_sign_det ecdsa_sign_det
+#define mbedtls_ecdsa_verify ecdsa_verify
+
+#define mbedtls_ecp_copy ecp_copy
+#define mbedtls_ecp_group_copy ecp_group_copy
+#define mbedtls_ecp_group_load ecp_group_load
+#define mbedtls_ecp_mul ecp_mul
+#define mbedtls_ecp_point_init ecp_point_init
+#define mbedtls_ecp_point_read_binary ecp_point_read_binary
+#define mbedtls_ecp_point_write_binary ecp_point_write_binary
+#define mbedtls_ecp_group_free ecp_group_free
 
 // Functions
 #ifdef POLARSSL_BASE64_H
index 9fe5d4ad50b27116940952f19d318d6d3b03ecd6..330d0430fc1269b1743ba5d45939749a3ea49a6b 100644 (file)
@@ -4,11 +4,13 @@
 #ifdef HAVE_MBEDTLS2
 #include <mbedtls/entropy.h>
 #include <mbedtls/ctr_drbg.h>
+#include <mbedtls/ecdsa.h>
 #include <mbedtls/rsa.h>
 #include <mbedtls/base64.h>
 #else
 #include <polarssl/entropy.h>
 #include <polarssl/ctr_drbg.h>
+#include <polarssl/ecdsa.h>
 #include <polarssl/rsa.h>
 #include <polarssl/base64.h>
 #include "mbedtlscompat.hh"
@@ -361,6 +363,358 @@ string RSADNSCryptoKeyEngine::getPublicKeyString()  const
   return keystring;
 }
 
+class MbedECDSADNSCryptoKeyEngine : public DNSCryptoKeyEngine
+{
+public:
+  explicit MbedECDSADNSCryptoKeyEngine(unsigned int algo) : DNSCryptoKeyEngine(algo)
+  {
+    static const unsigned char custom[] = "PowerDNS";
+    mbedtls_ecdsa_init(&d_ctx);
+    mbedtls_entropy_init(&d_entropy);
+    mbedtls_ctr_drbg_init(&d_ctr_drbg);
+
+    int ret = mbedtls_ctr_drbg_seed(&d_ctr_drbg, mbedtls_entropy_func, &d_entropy, custom, sizeof(custom) - 1);
+    if (ret != 0) {
+      throw runtime_error("Entropy gathering for key generation failed");
+    }
+
+    mbedtls_ecp_group_id groupId;
+
+    if(d_algorithm == 13) {
+      groupId = MBEDTLS_ECP_DP_SECP256R1;
+    }
+    else if(d_algorithm == 14){
+      groupId = MBEDTLS_ECP_DP_SECP384R1;
+    }
+    else {
+      throw runtime_error("Unknown algo "+std::to_string(d_algorithm)+" from ECDSA class");
+    }
+
+    int res = mbedtls_ecp_group_load(&d_ctx.grp, groupId);
+    if (res != 0) {
+      throw runtime_error("Error loading EC group for algo "+std::to_string(d_algorithm));
+    }
+  }
+
+  MbedECDSADNSCryptoKeyEngine(const MbedECDSADNSCryptoKeyEngine& orig) : MbedECDSADNSCryptoKeyEngine(orig.d_algorithm)
+  {
+    mbedtls_ecp_point_init(&d_ctx.Q);
+    int ret = mbedtls_ecp_copy(&d_ctx.Q, &orig.d_ctx.Q);
+
+    if (ret != 0) {
+      throw runtime_error("EC point copy failed");
+    }
+
+    mbedtls_mpi_init(&d_ctx.d);
+    ret = mbedtls_mpi_copy(&d_ctx.d, &orig.d_ctx.d);
+
+    if (ret != 0) {
+      throw runtime_error("ECDSA key copy failed");
+    }
+  }
+
+  ~MbedECDSADNSCryptoKeyEngine()
+  {
+    mbedtls_ctr_drbg_free(&d_ctr_drbg);
+    mbedtls_entropy_free(&d_entropy);
+    mbedtls_ecdsa_free(&d_ctx);
+  }
+
+  string getName() const { return "mbedTLS ECDSA"; }
+  void create(unsigned int bits);
+  storvector_t convertToISCVector() const;
+  std::string getPubKeyHash() const;
+  std::string sign(const std::string& hash) const;
+  std::string hash(const std::string& hash) const;
+  bool verify(const std::string& hash, const std::string& signature) const;
+  std::string getPublicKeyString() const;
+  int getBits() const;
+  void fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap);
+  void fromPublicKeyString(const std::string& content);
+  void fromPEMString(DNSKEYRecordContent& drc, const std::string& raw)
+  {}
+
+  static DNSCryptoKeyEngine* maker(unsigned int algorithm)
+  {
+    return new MbedECDSADNSCryptoKeyEngine(algorithm);
+  }
+
+private:
+  mbedtls_ecdsa_context d_ctx;
+  mbedtls_entropy_context d_entropy;
+  mbedtls_ctr_drbg_context d_ctr_drbg;
+};
+
+void MbedECDSADNSCryptoKeyEngine::create(unsigned int bits)
+{
+  mbedtls_ecp_group_id groupId;
+
+  if(bits == 256) {
+    groupId = MBEDTLS_ECP_DP_SECP256R1;
+  }
+  else if(bits == 384){
+    groupId = MBEDTLS_ECP_DP_SECP384R1;
+  }
+  else {
+    throw runtime_error("Unknown key length of "+std::to_string(bits)+" bits requested from ECDSA class");
+  }
+
+  int res = mbedtls_ecdsa_genkey(&d_ctx, groupId, &mbedtls_ctr_drbg_random, &d_ctr_drbg);
+  if (res != 0) {
+    throw runtime_error("Key generation failed");
+  }
+}
+
+int MbedECDSADNSCryptoKeyEngine::getBits() const
+{
+  if (d_algorithm == 13) {
+    return 256;
+  }
+  else if (d_algorithm == 14) {
+    return 384;
+  }
+
+  return -1;
+}
+
+DNSCryptoKeyEngine::storvector_t MbedECDSADNSCryptoKeyEngine::convertToISCVector() const
+{
+  storvector_t storvect;
+  string algorithm;
+
+  if(d_algorithm == 13)  {
+    algorithm = "13 (ECDSAP256SHA256)";
+  }
+  else if(d_algorithm == 14) {
+    algorithm ="14 (ECDSAP384SHA384)";
+  }
+  else {
+    algorithm =" ? (?)";
+  }
+
+  storvect.push_back(make_pair("Algorithm", algorithm));
+
+  unsigned char tmp[mbedtls_mpi_size(&d_ctx.d)];
+  int ret = mbedtls_mpi_write_binary(&d_ctx.d, tmp, sizeof(tmp));
+
+  if (ret != 0) {
+    throw runtime_error("Error converting ECDSA Private Key to binary");
+  }
+
+  storvect.push_back(make_pair("PrivateKey", string((char*) tmp, sizeof(tmp))));
+
+  return storvect;
+}
+
+void MbedECDSADNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap)
+{
+  drc.d_algorithm = atoi(stormap["algorithm"].c_str());
+
+  if (drc.d_algorithm != d_algorithm) {
+    throw runtime_error("Tried to feed an algorithm "+std::to_string(drc.d_algorithm)+" to a "+std::to_string(d_algorithm)+" key!");
+  }
+
+  string privateKey = stormap["privatekey"];
+  int ret = mbedtls_mpi_read_binary(&d_ctx.d, (unsigned char*) privateKey.c_str(), privateKey.length());
+  if (ret != 0)  {
+    throw runtime_error("Reading ECDSA private key from binary failed");
+  }
+
+  /* compute the public key */
+  ret = mbedtls_ecp_mul(&d_ctx.grp, &d_ctx.Q, &d_ctx.d, &d_ctx.grp.G, &mbedtls_ctr_drbg_random, &d_ctr_drbg);
+
+  if (ret != 0)  {
+    throw runtime_error("Computing ECDSA public key from private failed");
+  }
+}
+
+std::string MbedECDSADNSCryptoKeyEngine::getPubKeyHash() const
+{
+  unsigned char binaryPoint[MBEDTLS_ECP_MAX_PT_LEN];
+  size_t binaryPointLen = 0;
+  unsigned char hash[20];
+  int ret = mbedtls_ecp_point_write_binary(&d_ctx.grp, &d_ctx.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &binaryPointLen, binaryPoint, sizeof(binaryPoint));
+
+  if (ret != 0) {
+    throw runtime_error("Exporting ECP point to binary failed");
+  }
+
+  mbedtls_sha1_context ctx;
+  mbedtls_sha1_starts(&ctx);
+  /* we skip the first byte as the other backends use
+     raw field elements, as opposed to the format described in
+     SEC1: "2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion" */
+  mbedtls_sha1_update(&ctx, binaryPoint + 1, binaryPointLen - 1);
+  mbedtls_sha1_finish(&ctx, hash);
+
+  return string((char*)hash, sizeof(hash));
+}
+
+std::string MbedECDSADNSCryptoKeyEngine::getPublicKeyString() const
+{
+  unsigned char binaryPoint[MBEDTLS_ECP_MAX_PT_LEN];
+  size_t binaryPointLen = 0;
+  int ret = mbedtls_ecp_point_write_binary(&d_ctx.grp, &d_ctx.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &binaryPointLen, binaryPoint, sizeof(binaryPoint));
+
+  if (ret != 0) {
+    throw runtime_error("Exporting ECP point to binary failed");
+  }
+
+  /* we skip the first byte as the other signers use
+     raw field elements, as opposed to the format described in
+     SEC1: "2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion" */
+  return string((const char *)(binaryPoint + 1), binaryPointLen - 1);
+}
+
+void MbedECDSADNSCryptoKeyEngine::fromPublicKeyString(const std::string&input)
+{
+  /* uncompressed point, from SEC1:
+     "2.3.4 Octet-String-to-Elliptic-Curve-Point Conversion" */
+  static const unsigned char uncompressed[] = { 0x04 };
+  string ecdsaPoint;
+  ecdsaPoint.assign((const char*) uncompressed, sizeof(uncompressed));
+  ecdsaPoint.append(input);
+
+  int ret = mbedtls_ecp_point_read_binary(&d_ctx.grp, &d_ctx.Q, (unsigned char*) ecdsaPoint.c_str(), ecdsaPoint.length());
+
+  if (ret != 0) {
+    throw runtime_error("Reading ECP point from binary failed");
+  }
+}
+
+std::string MbedECDSADNSCryptoKeyEngine::sign(const std::string& msg) const
+{
+  string hash = this->hash(msg);
+  mbedtls_md_type_t hashKind;
+  if (hash.size() == 32) {
+    hashKind = MBEDTLS_MD_SHA256;
+  }
+  else {
+    hashKind = MBEDTLS_MD_SHA384;
+  }
+
+  mbedtls_mpi r, s;
+
+  mbedtls_mpi_init(&r);
+  mbedtls_mpi_init(&s);
+
+  mbedtls_ecp_group tempGroup;
+  mbedtls_ecp_group_init(&tempGroup);
+
+  int ret = mbedtls_ecp_group_copy(&tempGroup, &d_ctx.grp);
+
+  if (ret != 0) {
+    mbedtls_mpi_free(&r);
+    mbedtls_mpi_free(&s);
+    mbedtls_ecp_group_free(&tempGroup);
+    throw runtime_error("Error copying ECDSA group");
+  }
+
+  ret = mbedtls_ecdsa_sign_det(&tempGroup, &r, &s, &d_ctx.d, (const unsigned char*) hash.c_str(), hash.length(), hashKind);
+
+  mbedtls_ecp_group_free(&tempGroup);
+
+  if (ret != 0) {
+    mbedtls_mpi_free(&r);
+    mbedtls_mpi_free(&s);
+    throw runtime_error("ECDSA signature failed");
+  }
+
+  /* SEC1: 4.1.3 Signing Operation */
+  const size_t rSize = mbedtls_mpi_size(&r);
+  const size_t sSize = mbedtls_mpi_size(&s);
+  const size_t sigLen = rSize + sSize;
+
+  unsigned char sig[sigLen];
+
+  ret = mbedtls_mpi_write_binary(&r, sig, rSize);
+
+  if (ret != 0) {
+    mbedtls_mpi_free(&r);
+    mbedtls_mpi_free(&s);
+    throw runtime_error("Error converting ECDSA signature part R to binary");
+  }
+
+  ret = mbedtls_mpi_write_binary(&s, sig + rSize, sSize);
+
+  if (ret != 0) {
+    mbedtls_mpi_free(&r);
+    mbedtls_mpi_free(&s);
+    throw runtime_error("Error converting ECDSA signature part S to binary");
+  }
+
+  mbedtls_mpi_free(&r);
+  mbedtls_mpi_free(&s);
+
+  return string((char *) sig, sigLen);
+}
+
+std::string MbedECDSADNSCryptoKeyEngine::hash(const std::string& orig) const
+{
+  if(getBits() == 256) {
+    unsigned char hash[32];
+    mbedtls_sha256((unsigned char*) orig.c_str(), orig.length(), hash, 0);
+    return string((char*) hash, sizeof(hash));
+  }
+  else if(getBits() == 384) {
+    unsigned char hash[48];
+    // mbedtls_sha512() with the last parameter as 1 computes sha384
+    mbedtls_sha512((unsigned char*) orig.c_str(), orig.length(), hash, 1);
+    return string((char*) hash, sizeof(hash));
+  }
+
+  throw runtime_error("mbedTLS ECDSA does not support hash size of "+std::to_string(getBits()));
+}
+
+bool MbedECDSADNSCryptoKeyEngine::verify(const std::string& msg, const std::string& signature) const
+{
+  string hash = this->hash(msg);
+  const size_t mpiLen = mbedtls_mpi_size(&d_ctx.grp.P);
+  mbedtls_mpi r, s;
+
+  /* SEC1: 4.1.4 Verifying Operation */
+  mbedtls_mpi_init(&r);
+  mbedtls_mpi_init(&s);
+
+  if (signature.length() < (mpiLen * 2)) {
+    throw runtime_error("Invalid ECDSA signature size");
+  }
+
+  int ret = mbedtls_mpi_read_binary(&r, (unsigned char*) signature.c_str(), mpiLen);
+  if (ret != 0)  {
+    mbedtls_mpi_free(&r);
+    mbedtls_mpi_free(&s);
+    throw runtime_error("Reading ECDSA signature part R from binary failed");
+  }
+
+  ret = mbedtls_mpi_read_binary(&s, (unsigned char*) signature.c_str() + mpiLen, mpiLen);
+  if (ret != 0)  {
+    mbedtls_mpi_free(&r);
+    mbedtls_mpi_free(&s);
+    throw runtime_error("Reading ECDSA signature part S from binary failed");
+  }
+
+  mbedtls_ecp_group tempGroup;
+  mbedtls_ecp_group_init(&tempGroup);
+
+  ret = mbedtls_ecp_group_copy(&tempGroup, &d_ctx.grp);
+
+  if (ret != 0) {
+    mbedtls_mpi_free(&r);
+    mbedtls_mpi_free(&s);
+    mbedtls_ecp_group_free(&tempGroup);
+    throw runtime_error("Error copying ECDSA group");
+  }
+
+  ret = mbedtls_ecdsa_verify(&tempGroup, (const unsigned char*) hash.c_str(), hash.length(), &d_ctx.Q, &r, &s);
+
+  mbedtls_ecp_group_free(&tempGroup);
+  mbedtls_mpi_free(&r);
+  mbedtls_mpi_free(&s);
+
+  return (ret == 0);
+}
+
 namespace {
 struct LoaderStruct
 {
@@ -370,7 +724,8 @@ struct LoaderStruct
     DNSCryptoKeyEngine::report(7, &RSADNSCryptoKeyEngine::maker, true);
     DNSCryptoKeyEngine::report(8, &RSADNSCryptoKeyEngine::maker, true);
     DNSCryptoKeyEngine::report(10, &RSADNSCryptoKeyEngine::maker, true);
+    DNSCryptoKeyEngine::report(13, &MbedECDSADNSCryptoKeyEngine::maker, true);
+    DNSCryptoKeyEngine::report(14, &MbedECDSADNSCryptoKeyEngine::maker, true);
   }
 } loaderMbed;
 }
-