]> granicus.if.org Git - pdns/commitdiff
API: Make the /cryptokeys endpoint use CryptoKey objects
authorPieter Lexis <pieter.lexis@powerdns.com>
Thu, 16 Nov 2017 13:53:47 +0000 (14:53 +0100)
committerPieter Lexis <pieter.lexis@powerdns.com>
Fri, 17 Nov 2017 09:01:56 +0000 (10:01 +0100)
Add bits and algorithm to the CryptoKey object

docs/http-api/cryptokeyitem.rst
docs/http-api/endpoint-cryptokeys.rst
pdns/ws-auth.cc
regression-tests.api/test_Zones.py
regression-tests.api/test_cryptokeys.py

index 4090ab6e6840218b57821dc75633b3c99685b978..80951d10a55802a638012c8938c8d979589e0812 100644 (file)
@@ -15,3 +15,5 @@ CryptoKey
   :param string dnskey: The DNSKEY record for this key
   :param [string] ds: An array of DS records for this key
   :param string privatekey: The private key in ISC format
+  :param string algorithm: The key's algorithm
+  :param int bit: The bitsize of this key
index ce35115b25d95d4fcdf15993c5c3524283f58ff7..021abb4308090e5a76b7557c8e6e91c2a7241ebb 100644 (file)
@@ -14,21 +14,18 @@ These endpoints allow for the manipulation of DNSSEC crypto material.
 
 .. http:post:: /api/v1/servers/:server_id/zones/:zone_id/cryptokeys
 
-  This method adds a new key to a zone.
-  The key can either be generated or imported by supplying the ``content`` parameter.
+  This method adds a new key to a zone, POST data should be a :json:object:`CryptoKey`.
+  But not all fields needs to be present:
 
-  if ``content``, ``bits`` and ``algo`` are null, a key will be generated based
+  The key can either be generated or imported by supplying the ``privatekey`` parameter.
+
+  if ``privatekey``, ``bits`` and ``algorithm`` are null, a key will be generated based
   on the :ref:`setting-default-ksk-algorithm` and :ref:`setting-default-ksk-size`
   settings for a KSK and the :ref:`setting-default-zsk-algorithm` and :ref:`setting-default-zsk-size`
   options for a ZSK.
 
   :param server_id: The name of the server
   :param zone_id: The id value of the :json:object:`Zone`
-  :reqjson string content: The private key to use (The format used is compatible with BIND and NSD/LDNS)
-  :reqjson string keytype: Either "ksk" or "zsk"
-  :reqjson bool active: If not set the key will not be active by default
-  :reqjson int bits: Number of bits in the key (if ``content`` is not set)
-  :reqjson int,string algo: The DNSSEC algorithm (if ``content`` is not set), see :ref:`dnssec-supported-algos`
   :statuscode 201: Everything was fine, returns all public data as a :json:object:`CryptoKey`.
   :statuscode 422: Returned when something is wrong with the content of the request.
                    Contains an error message
@@ -44,7 +41,8 @@ These endpoints allow for the manipulation of DNSSEC crypto material.
 
 .. http:put:: /api/v1/servers/:server_id/zones/:zone_name/cryptokeys/:cryptokey_id
 
-  This method (de)activates a key from ``zone_name`` specified by ``cryptokey_id``.
+  This method changes a key from ``zone_name`` specified by ``cryptokey_id``.
+  At this time, only changing the ``active`` parameter is supported.
 
   :param string server_id: The name of the server
   :param string zone_id: The id value of the :json:object:`Zone`
index 460952f6cfad6152425371390503a86e80acd547..b5dc9c4a662300cf607ecaa70313395f8c285cc0 100644 (file)
@@ -892,7 +892,9 @@ static void apiZoneCryptokeysGET(DNSName zonename, int inquireKeyId, HttpRespons
         { "active", value.second.active },
         { "keytype", keyType },
         { "flags", (uint16_t)value.first.d_flags },
-        { "dnskey", value.first.getDNSKEY().getZoneRepresentation() }
+        { "dnskey", value.first.getDNSKEY().getZoneRepresentation() },
+        { "algorithm", DNSSECKeeper::algorithm2name(value.first.d_algorithm) },
+        { "bits", value.first.getKey()->getBits() }
     };
 
     if (value.second.keyType == DNSSECKeeper::KSK || value.second.keyType == DNSSECKeeper::CSK) {
@@ -942,10 +944,10 @@ static void apiZoneCryptokeysDELETE(DNSName zonename, int inquireKeyId, HttpRequ
  * This method adds a key to a zone by generate it or content parameter.
  * Parameter:
  *  {
- *  "content" : "key The format used is compatible with BIND and NSD/LDNS" <string>
+ *  "privatekey" : "key The format used is compatible with BIND and NSD/LDNS" <string>
  *  "keytype" : "ksk|zsk" <string>
  *  "active"  : "true|false" <value>
- *  "algo" : "key generation algorithm "name|number" as default"<string> https://doc.powerdns.com/md/authoritative/dnssec/#supported-algorithms
+ *  "algorithm" : "key generation algorithm name as default"<string> https://doc.powerdns.com/md/authoritative/dnssec/#supported-algorithms
  *  "bits" : number of bits <int>
  *  }
  *
@@ -954,7 +956,7 @@ static void apiZoneCryptokeysDELETE(DNSName zonename, int inquireKeyId, HttpRequ
  *    The server returns 422 Unprocessable Entity {"error" : "Invalid keytype 'keytype'"}
  *  Case 2: 'bits' must be a positive integer value.
  *    The server returns 422 Unprocessable Entity {"error" : "'bits' must be a positive integer value."}
- *  Case 3: The "algo" isn't supported
+ *  Case 3: The "algorithm" isn't supported
  *    The server returns 422 Unprocessable Entity {"error" : "Unknown algorithm: 'algo'"}
  *  Case 4: Algorithm <= 10 and no bits were passed
  *    The server returns 422 Unprocessable Entity {"error" : "Creating an algorithm algo key requires the size (in bits) to be passed"}
@@ -976,7 +978,13 @@ static void apiZoneCryptokeysDELETE(DNSName zonename, int inquireKeyId, HttpRequ
 
 static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpResponse *resp, DNSSECKeeper *dk) {
   auto document = req->json();
-  auto content = document["content"];
+  string privatekey_fieldname = "privatekey";
+  auto privatekey = document["privatekey"];
+  if (privatekey.is_null()) {
+    // Fallback to the old "content" behaviour
+    privatekey = document["content"];
+    privatekey_fieldname = "content";
+  }
   bool active = boolFromJson(document, "active", false);
   bool keyOrZone;
 
@@ -990,7 +998,7 @@ static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpRespon
 
   int64_t insertedId = -1;
 
-  if (content.is_null()) {
+  if (privatekey.is_null()) {
     int bits = keyOrZone ? ::arg().asNum("default-ksk-size") : ::arg().asNum("default-zsk-size");
     auto docbits = document["bits"];
     if (!docbits.is_null()) {
@@ -1001,7 +1009,7 @@ static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpRespon
       }
     }
     int algorithm = DNSSECKeeper::shorthand2algorithm(keyOrZone ? ::arg()["default-ksk-algorithm"] : ::arg()["default-zsk-algorithm"]);
-    auto providedAlgo = document["algo"];
+    auto providedAlgo = document["algorithm"];
     if (providedAlgo.is_string()) {
       algorithm = DNSSECKeeper::shorthand2algorithm(providedAlgo.string_value());
       if (algorithm == -1)
@@ -1021,13 +1029,14 @@ static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpRespon
     }
     if (insertedId < 0)
       throw ApiException("Adding key failed, perhaps DNSSEC not enabled in configuration?");
-  } else if (document["bits"].is_null() && document["algo"].is_null()) {
-    auto keyData = stringFromJson(document, "content");
+  } else if (document["bits"].is_null() && document["algorithm"].is_null()) {
+    auto keyData = stringFromJson(document, privatekey_fieldname);
     DNSKEYRecordContent dkrc;
     DNSSECPrivateKey dpk;
     try {
       shared_ptr<DNSCryptoKeyEngine> dke(DNSCryptoKeyEngine::makeFromISCString(dkrc, keyData));
       dpk.d_algorithm = dkrc.d_algorithm;
+      // TODO remove in 4.2.0
       if(dpk.d_algorithm == 7)
         dpk.d_algorithm = 5;
 
@@ -1050,7 +1059,7 @@ static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpRespon
     if (insertedId < 0)
       throw ApiException("Adding key failed, perhaps DNSSEC not enabled in configuration?");
   } else {
-    throw ApiException("Either you submit just the 'content' field or you leave 'content' empty and submit the other fields.");
+    throw ApiException("Either you submit just the 'privatekey' field or you leave 'privatekey' empty and submit the other fields.");
   }
   apiZoneCryptokeysGET(zonename, insertedId, resp, dk);
   resp->status = 201;
index b261b98106662553f64b8470e5467857b7f9cd00..02896cd0c852afc6936a2277a826027005ec76ef 100644 (file)
@@ -1653,6 +1653,8 @@ class AuthZoneKeys(ApiTestCase, AuthZonesHelperMixin):
         del key0['dnskey']
         del key0['ds']
         expected = {
+            u'algorithm': u'ECDSAP256SHA256',
+            u'bits': 256,
             u'active': True,
             u'type': u'Cryptokey',
             u'keytype': u'csk',
index cf249beaddff7287cceb6523d9f33ec9ac14f108..cccb3c783f92240ffddac8bcfd65e8be08fd9c83 100644 (file)
@@ -72,7 +72,7 @@ class Cryptokeys(ApiTestCase):
             payload = {
                 'keytype': type,
                 'active' : active,
-                'algo' : algo
+                'algorithm' : algo
             }
         if bits > 0:
             payload['bits'] = bits
@@ -264,4 +264,4 @@ class Cryptokeys(ApiTestCase):
             out = subprocess.check_output(["../pdns/pdnsutil", "--config-dir=.", "show-zone", self.zone])
             self.assertIn("Active", out)
         except subprocess.CalledProcessError as e:
-            self.fail("pdnsutil show-zone failed: " + e.output)
\ No newline at end of file
+            self.fail("pdnsutil show-zone failed: " + e.output)