--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <decaf.hxx>
+#include <decaf/eddsa.hxx>
+#include <decaf/spongerng.hxx>
+
+#include "dnssecinfra.hh"
+
+using namespace decaf;
+
+class DecafED25519DNSCryptoKeyEngine : public DNSCryptoKeyEngine
+{
+public:
+ explicit DecafED25519DNSCryptoKeyEngine(unsigned int algo) : DNSCryptoKeyEngine(algo)
+ {
+
+ }
+ string getName() const override { return "Decaf ED25519"; }
+ void create(unsigned int bits) override;
+ storvector_t convertToISCVector() const override;
+ std::string getPubKeyHash() const override;
+ std::string sign(const std::string& msg) const override;
+ bool verify(const std::string& msg, const std::string& signature) const override;
+ std::string getPublicKeyString() const override;
+ int getBits() const override;
+ void fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap) override;
+ void fromPublicKeyString(const std::string& content) override;
+ void fromPEMString(DNSKEYRecordContent& drc, const std::string& raw) override
+ {}
+
+ static std::shared_ptr<DNSCryptoKeyEngine> maker(unsigned int algorithm)
+ {
+ return std::make_shared<DecafED25519DNSCryptoKeyEngine>(algorithm);
+ }
+
+private:
+ unsigned char d_pubkey[DECAF_EDDSA_25519_PUBLIC_BYTES];
+ unsigned char d_seckey[DECAF_EDDSA_25519_PRIVATE_BYTES];
+};
+
+void DecafED25519DNSCryptoKeyEngine::create(unsigned int bits)
+{
+ if(bits != (unsigned int)getBits()) {
+ throw runtime_error("Unsupported key length of "+std::to_string(bits)+" bits requested, DecafED25519 class");
+ }
+
+ SpongeRng rng("/dev/urandom");
+
+ typename EdDSA<IsoEd25519>::PrivateKey priv(rng);
+ typename EdDSA<IsoEd25519>::PublicKey pub(priv);
+
+ priv.serialize_into(d_seckey);
+ pub.serialize_into(d_pubkey);
+}
+
+int DecafED25519DNSCryptoKeyEngine::getBits() const
+{
+ return DECAF_EDDSA_25519_PRIVATE_BYTES << 3;
+}
+
+DNSCryptoKeyEngine::storvector_t DecafED25519DNSCryptoKeyEngine::convertToISCVector() const
+{
+ /*
+ Private-key-format: v1.2
+ Algorithm: 15 (ED25519)
+ PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=
+ */
+
+ storvector_t storvector;
+
+ storvector.push_back(make_pair("Algorithm", "15 (ED25519)"));
+ storvector.push_back(make_pair("PrivateKey", string((char*)d_seckey, DECAF_EDDSA_25519_PRIVATE_BYTES)));
+
+ return storvector;
+}
+
+void DecafED25519DNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap )
+{
+ /*
+ Private-key-format: v1.2
+ Algorithm: 15 (ED25519)
+ PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=
+ */
+
+ drc.d_algorithm = pdns_stou(stormap["algorithm"]);
+ string privateKey = stormap["privatekey"];
+
+ if (privateKey.length() != DECAF_EDDSA_25519_PRIVATE_BYTES)
+ throw runtime_error("Private key size mismatch in ISCMap, DecafED25519 class");
+
+ typename EdDSA<IsoEd25519>::PrivateKey priv(Block((const unsigned char*)privateKey.c_str(), DECAF_EDDSA_25519_PRIVATE_BYTES));
+ typename EdDSA<IsoEd25519>::PublicKey pub(priv);
+
+ priv.serialize_into(d_seckey);
+ pub.serialize_into(d_pubkey);
+}
+
+std::string DecafED25519DNSCryptoKeyEngine::getPubKeyHash() const
+{
+ return this->getPublicKeyString();
+}
+
+std::string DecafED25519DNSCryptoKeyEngine::getPublicKeyString() const
+{
+ return string((char*)d_pubkey, DECAF_EDDSA_25519_PUBLIC_BYTES);
+}
+
+void DecafED25519DNSCryptoKeyEngine::fromPublicKeyString(const std::string& input)
+{
+ if (input.length() != DECAF_EDDSA_25519_PUBLIC_BYTES)
+ throw runtime_error("Public key size mismatch, DecafED25519 class");
+
+ memcpy(d_pubkey, input.c_str(), DECAF_EDDSA_25519_PUBLIC_BYTES);
+}
+
+std::string DecafED25519DNSCryptoKeyEngine::sign(const std::string& msg) const
+{
+ typename EdDSA<IsoEd25519>::PrivateKey priv(Block(d_seckey, DECAF_EDDSA_25519_PRIVATE_BYTES));
+
+ SecureBuffer message(msg.begin(), msg.end());
+
+ SecureBuffer sig = priv.sign(message);
+
+ return string(sig.begin(), sig.end());
+}
+
+bool DecafED25519DNSCryptoKeyEngine::verify(const std::string& msg, const std::string& signature) const
+{
+ if (signature.length() != DECAF_EDDSA_25519_SIGNATURE_BYTES)
+ return false;
+
+ typename EdDSA<IsoEd25519>::PublicKey pub(Block(d_pubkey, DECAF_EDDSA_25519_PUBLIC_BYTES));
+
+ SecureBuffer sig(signature.begin(), signature.end());
+ SecureBuffer message(msg.begin(), msg.end());
+
+ try {
+ pub.verify(sig, message);
+ } catch(CryptoException) {
+ return false;
+ }
+
+ return true;
+}
+
+
+class DecafED448DNSCryptoKeyEngine : public DNSCryptoKeyEngine
+{
+public:
+ explicit DecafED448DNSCryptoKeyEngine(unsigned int algo) : DNSCryptoKeyEngine(algo)
+ {
+
+ }
+ string getName() const override { return "Decaf ED448"; }
+ void create(unsigned int bits) override;
+ storvector_t convertToISCVector() const override;
+ std::string getPubKeyHash() const override;
+ std::string sign(const std::string& msg) const override;
+ bool verify(const std::string& msg, const std::string& signature) const override;
+ std::string getPublicKeyString() const override;
+ int getBits() const override;
+ void fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap) override;
+ void fromPublicKeyString(const std::string& content) override;
+ void fromPEMString(DNSKEYRecordContent& drc, const std::string& raw) override
+ {}
+
+ static std::shared_ptr<DNSCryptoKeyEngine> maker(unsigned int algorithm)
+ {
+ return std::make_shared<DecafED448DNSCryptoKeyEngine>(algorithm);
+ }
+
+private:
+ unsigned char d_pubkey[DECAF_EDDSA_448_PUBLIC_BYTES];
+ unsigned char d_seckey[DECAF_EDDSA_448_PRIVATE_BYTES];
+};
+
+void DecafED448DNSCryptoKeyEngine::create(unsigned int bits)
+{
+ if(bits != (unsigned int)getBits()) {
+ throw runtime_error("Unsupported key length of "+std::to_string(bits)+" bits requested, DecafED448 class");
+ }
+
+ SpongeRng rng("/dev/urandom");
+
+ typename EdDSA<Ed448Goldilocks>::PrivateKey priv(rng);
+ typename EdDSA<Ed448Goldilocks>::PublicKey pub(priv);
+
+ priv.serialize_into(d_seckey);
+ pub.serialize_into(d_pubkey);
+}
+
+int DecafED448DNSCryptoKeyEngine::getBits() const
+{
+ return DECAF_EDDSA_448_PRIVATE_BYTES << 3;
+}
+
+DNSCryptoKeyEngine::storvector_t DecafED448DNSCryptoKeyEngine::convertToISCVector() const
+{
+ /*
+ Private-key-format: v1.2
+ Algorithm: 16 (ED448)
+ PrivateKey: xZ+5Cgm463xugtkY5B0Jx6erFTXp13rYegst0qRtNsOYnaVpMx0Z/c5EiA9x8wWbDDct/U3FhYWA
+ */
+
+ storvector_t storvector;
+
+ storvector.push_back(make_pair("Algorithm", "16 (ED448)"));
+ storvector.push_back(make_pair("PrivateKey", string((char*)d_seckey, DECAF_EDDSA_448_PRIVATE_BYTES)));
+
+ return storvector;
+}
+
+void DecafED448DNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap )
+{
+ /*
+ Private-key-format: v1.2
+ Algorithm: 16 (ED448)
+ PrivateKey: xZ+5Cgm463xugtkY5B0Jx6erFTXp13rYegst0qRtNsOYnaVpMx0Z/c5EiA9x8wWbDDct/U3FhYWA
+ */
+
+ drc.d_algorithm = pdns_stou(stormap["algorithm"]);
+ string privateKey = stormap["privatekey"];
+
+ if (privateKey.length() != DECAF_EDDSA_448_PRIVATE_BYTES)
+ throw runtime_error("Private key size mismatch in ISCMap, DecafED448 class");
+
+ typename EdDSA<Ed448Goldilocks>::PrivateKey priv(Block((const unsigned char*)privateKey.c_str(), DECAF_EDDSA_448_PRIVATE_BYTES));
+ typename EdDSA<Ed448Goldilocks>::PublicKey pub(priv);
+
+ priv.serialize_into(d_seckey);
+ pub.serialize_into(d_pubkey);
+}
+
+std::string DecafED448DNSCryptoKeyEngine::getPubKeyHash() const
+{
+ return this->getPublicKeyString();
+}
+
+std::string DecafED448DNSCryptoKeyEngine::getPublicKeyString() const
+{
+ return string((char*)d_pubkey, DECAF_EDDSA_448_PUBLIC_BYTES);
+}
+
+void DecafED448DNSCryptoKeyEngine::fromPublicKeyString(const std::string& input)
+{
+ if (input.length() != DECAF_EDDSA_448_PUBLIC_BYTES)
+ throw runtime_error("Public key size mismatch, DecafED448 class");
+
+ memcpy(d_pubkey, input.c_str(), DECAF_EDDSA_448_PUBLIC_BYTES);
+}
+
+std::string DecafED448DNSCryptoKeyEngine::sign(const std::string& msg) const
+{
+ typename EdDSA<Ed448Goldilocks>::PrivateKey priv(Block(d_seckey, DECAF_EDDSA_448_PRIVATE_BYTES));
+
+ SecureBuffer message(msg.begin(), msg.end());
+
+ SecureBuffer sig = priv.sign(message);
+
+ return string(sig.begin(), sig.end());
+}
+
+bool DecafED448DNSCryptoKeyEngine::verify(const std::string& msg, const std::string& signature) const
+{
+ if (signature.length() != DECAF_EDDSA_448_SIGNATURE_BYTES)
+ return false;
+
+ typename EdDSA<Ed448Goldilocks>::PublicKey pub(Block(d_pubkey, DECAF_EDDSA_448_PUBLIC_BYTES));
+
+ SecureBuffer sig(signature.begin(), signature.end());
+ SecureBuffer message(msg.begin(), msg.end());
+
+ try {
+ pub.verify(sig, message);
+ } catch(CryptoException) {
+ return false;
+ }
+
+ return true;
+}
+
+
+namespace {
+struct LoaderDecafStruct
+{
+ LoaderDecafStruct()
+ {
+ DNSCryptoKeyEngine::report(15, &DecafED25519DNSCryptoKeyEngine::maker);
+ DNSCryptoKeyEngine::report(16, &DecafED448DNSCryptoKeyEngine::maker);
+ }
+} loaderdecaf;
+}
--- /dev/null
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_NO_MAIN
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <boost/test/unit_test.hpp>
+#include <boost/assign/list_of.hpp>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include "base64.hh"
+#include "dnsseckeeper.hh"
+#include "dnssecinfra.hh"
+#include "misc.hh"
+
+BOOST_AUTO_TEST_SUITE(test_signers)
+
+#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF)
+BOOST_AUTO_TEST_CASE(test_ed25519_signer) {
+ vector<std::shared_ptr<DNSRecordContent> > rrs;
+ DNSName qname("example.com.");
+ DNSKEYRecordContent drc;
+
+ // TODO: make this a collection of inputs and resulting sigs for various algos
+ shared_ptr<DNSCryptoKeyEngine> engine = DNSCryptoKeyEngine::makeFromISCString(drc,
+"Private-key-format: v1.2\n"
+"Algorithm: 15 (ED25519)\n"
+"PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=");
+
+ DNSSECPrivateKey dpk;
+ dpk.setKey(engine);
+
+ reportBasicTypes();
+
+ rrs.push_back(DNSRecordContent::makeunique(QType::MX, 1, "10 mail.example.com."));
+
+ RRSIGRecordContent rrc;
+ rrc.d_originalttl = 3600;
+ rrc.d_sigexpire = 1440021600;
+ rrc.d_siginception = 1438207200;
+ rrc.d_signer = qname;
+ rrc.d_type = QType::MX;
+ rrc.d_labels = 2;
+ // TODO: derive the next two from the key
+ rrc.d_tag = 3613;
+ rrc.d_algorithm = 15;
+
+ string msg = getMessageForRRSET(qname, rrc, rrs, false);
+
+ // vector extracted from https://gitlab.labs.nic.cz/labs/ietf/blob/master/dnskey.py (rev 476d6ded) by printing signature_data
+ BOOST_CHECK_EQUAL(makeHexDump(msg), "00 0f 0f 02 00 00 0e 10 55 d4 fc 60 55 b9 4c e0 0e 1d 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 0f 00 01 00 00 0e 10 00 14 00 0a 04 6d 61 69 6c 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 ");
+
+ string signature = engine->sign(msg);
+ string b64 = Base64Encode(signature);
+
+ // vector verified from dnskey.py as above, and confirmed with https://www.rfc-editor.org/errata_search.php?rfc=8080&eid=4935
+ BOOST_CHECK_EQUAL(b64, "oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeRAvTdszaPD+QLs3fx8A4M3e23mRZ9VrbpMngwcrqNAg==");
+}
+#endif
+
+#ifdef HAVE_LIBDECAF
+BOOST_AUTO_TEST_CASE(test_ed448_signer) {
+ vector<std::shared_ptr<DNSRecordContent> > rrs;
+ DNSName qname("example.com.");
+ DNSKEYRecordContent drc;
+
+ // TODO: make this a collection of inputs and resulting sigs for various algos
+ shared_ptr<DNSCryptoKeyEngine> engine = DNSCryptoKeyEngine::makeFromISCString(drc,
+"Private-key-format: v1.2\n"
+"Algorithm: 16 (ED448)\n"
+"PrivateKey: xZ+5Cgm463xugtkY5B0Jx6erFTXp13rYegst0qRtNsOYnaVpMx0Z/c5EiA9x8wWbDDct/U3FhYWA\n");
+
+ DNSSECPrivateKey dpk;
+ dpk.setKey(engine);
+
+ reportBasicTypes();
+
+ rrs.push_back(DNSRecordContent::makeunique(QType::MX, 1, "10 mail.example.com."));
+
+ RRSIGRecordContent rrc;
+ rrc.d_originalttl = 3600;
+ rrc.d_sigexpire = 1440021600;
+ rrc.d_siginception = 1438207200;
+ rrc.d_signer = qname;
+ rrc.d_type = QType::MX;
+ rrc.d_labels = 2;
+ // TODO: derive the next two from the key
+ rrc.d_tag = 9713;
+ rrc.d_algorithm = 16;
+
+ string msg = getMessageForRRSET(qname, rrc, rrs, false);
+
+ // vector extracted from https://gitlab.labs.nic.cz/labs/ietf/blob/master/dnskey.py (rev 476d6ded) by printing signature_data
+ BOOST_CHECK_EQUAL(makeHexDump(msg), "00 0f 10 02 00 00 0e 10 55 d4 fc 60 55 b9 4c e0 25 f1 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 0f 00 01 00 00 0e 10 00 14 00 0a 04 6d 61 69 6c 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 ");
+
+ string signature = engine->sign(msg);
+ string b64 = Base64Encode(signature);
+
+ // vector verified from dnskey.py as above, and confirmed with https://www.rfc-editor.org/errata_search.php?rfc=8080&eid=4935
+ BOOST_CHECK_EQUAL(b64, "3cPAHkmlnxcDHMyg7vFC34l0blBhuG1qpwLmjInI8w1CMB29FkEAIJUA0amxWndkmnBZ6SKiwZSAxGILn/NBtOXft0+Gj7FSvOKxE/07+4RQvE581N3Aj/JtIyaiYVdnYtyMWbSNyGEY2213WKsJlwEA");
+}
+#endif
+
+BOOST_AUTO_TEST_SUITE_END()