]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[sedhcpv6] Wrote ECDSA code for OpenSSL, still to debug and do Botan
authorFrancis Dupont <fdupont@isc.org>
Sun, 7 Jun 2015 04:40:19 +0000 (06:40 +0200)
committerFrancis Dupont <fdupont@isc.org>
Sun, 7 Jun 2015 04:40:19 +0000 (06:40 +0200)
src/lib/cryptolink/openssl_ecdsa.cc [new file with mode: 0644]
src/lib/cryptolink/openssl_ecdsa.h [new file with mode: 0644]
src/lib/cryptolink/tests/ecdsa_unittests.cc [new file with mode: 0644]
src/lib/cryptolink/tests/rsa_unittests.cc [new file with mode: 0644]
src/lib/cryptolink/tests/testdata/eccert.pem [new file with mode: 0644]
src/lib/cryptolink/tests/testdata/ecpkcs8.pem [new file with mode: 0644]
src/lib/cryptolink/tests/testdata/ecpkcs8ne.pem [new file with mode: 0644]
src/lib/cryptolink/tests/testdata/ecpub.pem [new file with mode: 0644]

diff --git a/src/lib/cryptolink/openssl_ecdsa.cc b/src/lib/cryptolink/openssl_ecdsa.cc
new file mode 100644 (file)
index 0000000..2e43feb
--- /dev/null
@@ -0,0 +1,1160 @@
+// Copyright (C) 2015  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cryptolink.h>
+#include <cryptolink/crypto_asym.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <openssl/pem.h>
+#include <openssl/objects.h>
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+#include <openssl/x509.h>
+
+#include <util/encode/base64.h>
+#include <cryptolink/openssl_common.h>
+#include <cryptolink/openssl_ecdsa.h>
+
+#include <cstdio>
+#include <cstring>
+
+#ifndef NID_X9_62_prime256v1
+#error "P-256 group is not known (NID_X9_62_prime256v1)"
+#endif
+#ifndef NID_secp384r1
+#error "P-384 group is not known (NID_secp384r1)"
+#endif
+
+namespace isc {
+namespace cryptolink {
+
+/// @brief Constructor from a key, asym and hash algorithm,
+///        key kind and key binary format
+EcDsaAsymImpl::EcDsaAsymImpl(const void* key, size_t key_len,
+                             const HashAlgorithm hash_algorithm,
+                             const AsymKeyKind key_kind,
+                             const AsymFormat key_format) {
+    algo_ = ECDSA_;
+    hash_ = hash_algorithm;
+    kind_ = key_kind;
+    eckey_ = NULL;
+    x509_ = NULL;
+    int curve_nid = 0;
+    switch (hash_) {
+    case SHA256:
+        curve_nid = NID_X9_62_prime256v1;
+        break;
+    case SHA384:
+        curve_nid = NID_secp384r1;
+        break;
+    default:
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown hash algorithm: " << static_cast<int>(hash_));
+    }
+    if (key_len == 0) {
+        isc_throw(BadKey, "Bad ECDSA " <<
+                  (kind_ != CERT ? "key" : "cert") << " length: 0");
+    }
+
+    if ((kind_ == PRIVATE) && (key_format == BASIC)) {
+        // The private key is just a number
+        const unsigned char* p = reinterpret_cast<const unsigned char*>(key);
+        BIGNUM* privkey = BN_bin2bn(p, static_cast<int>(key_len), NULL);
+        if (!privkey) {
+            throw std::bad_alloc();
+        }
+        eckey_ = EC_KEY_new_by_curve_name(curve_nid);
+        if (!eckey_) {
+            BN_clear_free(privkey);
+            isc_throw(LibraryError, "EC_KEY_new_by_curve_name");
+        }
+        EC_KEY_set_asn1_flag(eckey_, OPENSSL_EC_NAMED_CURVE);
+        if (!EC_KEY_set_private_key(eckey_, privkey)) {
+            BN_clear_free(privkey);
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(LibraryError, "EC_KEY_set_private_key");
+        }
+        // Compute the public key
+        const EC_GROUP* grp = EC_KEY_get0_group(eckey_);
+        EC_POINT* pubkey = EC_POINT_new(grp);
+        if (!pubkey) {
+            BN_clear_free(privkey);
+            throw std::bad_alloc();
+        }
+        if (!EC_POINT_mul(grp, pubkey, privkey, NULL, NULL, NULL)) {
+            EC_POINT_free(pubkey);
+            BN_clear_free(privkey);
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(LibraryError, "EC_POINT_mul");
+        }
+        if (!EC_KEY_set_public_key(eckey_, pubkey)) {
+            EC_POINT_free(pubkey);
+            BN_clear_free(privkey);
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(BadKey, "EC_KEY_set_public_key");
+        }
+        EC_POINT_free(pubkey);
+        BN_clear_free(privkey);
+    } else if (kind_ == PRIVATE) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown ECDSA Private Key format: " <<
+                  static_cast<int>(key_format));
+    } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
+        // SubjectPublicKeyInfo
+        const unsigned char* p = reinterpret_cast<const unsigned char*>(key);
+        eckey_ = d2i_EC_PUBKEY(NULL, &p, static_cast<long>(key_len));
+        if (!eckey_) {
+            isc_throw(BadKey, "d2i_EC_PUBKEY");
+        }
+        if (EC_KEY_check_key(eckey_) != 1) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(BadKey, "EC_KEY_check_key");
+        }
+        EC_GROUP* wanted = EC_GROUP_new_by_curve_name(curve_nid);
+        if (!wanted) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(LibraryError, "EC_GROUP_new_by_curve_name");
+        }
+        const int status =
+            EC_GROUP_cmp(EC_KEY_get0_group(eckey_), wanted, NULL);
+        EC_GROUP_free(wanted);
+        if (status < 0) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(LibraryError, "EC_GROUP_cmp");
+        } else if (status != 0) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(BadKey, "EC_GROUP_cmp");
+        }
+    } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
+        // RFC 6605 DNS wire format
+        // key_len == 0 was already checked
+        size_t len;
+        std::string algo_name;
+        if (hash_ == SHA256) {
+            len = 64;
+            algo_name = "ECDSA P256";
+        } else {
+            len = 96;
+            algo_name ="ECDSA P384";
+        }
+        if (key_len != len) {
+            isc_throw(BadKey, "Bad " << algo_name <<
+                      "Public Key length: " <<
+                      key_len << ", expected " << len);
+        }
+        std::vector<uint8_t> pubbin(len + 1);
+        pubbin[0] = POINT_CONVERSION_UNCOMPRESSED;
+        std::memcpy(&pubbin[1], key, len);
+        const uint8_t* p = &pubbin[0];
+        eckey_ = EC_KEY_new_by_curve_name(curve_nid);
+        if (!eckey_) {
+            isc_throw(LibraryError, "EC_KEY_new_by_curve_name");
+        }
+        EC_KEY_set_asn1_flag(eckey_, OPENSSL_EC_NAMED_CURVE);
+        if (o2i_ECPublicKey(&eckey_, &p,
+                            static_cast<long>(len + 1)) == NULL) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(BadKey, "o2i_ECPublicKey");
+        }
+        if (!EC_KEY_check_key(eckey_)) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(BadKey, "EC_KEY_check_key");
+        }
+    } else if (kind_ == PUBLIC) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown ECDSA Public Key format: " <<
+                  static_cast<int>(key_format));
+    } else if ((kind_ == CERT) && (key_format == ASN1)) {
+        // X.509 Public Key Certificate
+        const unsigned char* p = reinterpret_cast<const unsigned char*>(key);
+        x509_ = d2i_X509(NULL, &p, static_cast<long>(key_len));
+        if (!x509_) {
+            isc_throw(BadKey, "d2i_X509");
+        }
+        int sig_nid = OBJ_obj2nid(x509_->sig_alg->algorithm);
+        if (hash_ == SHA256) {
+            if (sig_nid != OBJ_txt2nid("1.2.840.10045.4.3.2")) {
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(BadKey, "Require an ECDSA P256 certificate");
+            }
+        } else {
+            if (sig_nid != OBJ_txt2nid("1.2.840.10045.4.3.3")) {
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(BadKey, "Require an ECDSA P256 certificate");
+            }
+        }
+        EVP_PKEY* pkey = X509_get_pubkey(x509_);
+        if (!pkey) {
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(BadKey, "X509_get_pubkey");
+        }
+        eckey_ = EVP_PKEY_get1_EC_KEY(pkey);
+        EVP_PKEY_free(pkey);
+        if (!eckey_) {
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(BadKey, "EVP_PKEY_get1_EC_KEY");
+        }
+    } else if (kind_ == CERT) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown Certificate format: " <<
+                  static_cast<int>(key_format));
+    } else {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown ECDSA Key kind: " << static_cast<int>(kind_));
+    }
+}
+
+/// @brief Constructor from a key file with password,
+///        asym and hash algorithm, key kind and key binary format
+EcDsaAsymImpl::EcDsaAsymImpl(const std::string& filename,
+                         const std::string& password,
+                         const HashAlgorithm hash_algorithm,
+                         const AsymKeyKind key_kind,
+                         const AsymFormat key_format) {
+    algo_ = ECDSA_;
+    hash_ = hash_algorithm;
+    kind_ = key_kind;
+    eckey_ = NULL;
+    x509_ = NULL;
+    int curve_nid = 0;
+    switch (hash_) {
+    case SHA256:
+        curve_nid = NID_X9_62_prime256v1;
+        break;
+    case SHA384:
+        curve_nid = NID_secp384r1;
+        break;
+    default:
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown hash algorithm: " << static_cast<int>(hash_));
+    }
+
+    if ((kind_ == PRIVATE) && (key_format == ASN1)) {
+        // PKCS#8 Private Key PEM file
+        FILE* fp = fopen(filename.c_str(), "r");
+        if (!fp) {
+            isc_throw(BadKey, "Can't open file: " << filename);
+        }
+        char* pwd = NULL;
+        if (!password.empty()) {
+            pwd = const_cast<char*>(password.c_str());
+        }
+        eckey_ = PEM_read_ECPrivateKey(fp, NULL, 0, pwd);
+        fclose(fp);
+        if (!eckey_) {
+            isc_throw(BadKey, "PEM_read_ECPrivateKey");
+        }
+        if (!EC_KEY_get0_public_key(eckey_)) {
+            // Compute the public key as a side effect
+            const EC_GROUP* grp = EC_KEY_get0_group(eckey_);
+            const BIGNUM* privkey = EC_KEY_get0_private_key(eckey_);
+            EC_POINT* pubkey = EC_POINT_new(grp);
+            if (!pubkey) {
+                throw std::bad_alloc();
+            }
+            if (!EC_POINT_mul(grp, pubkey, privkey, NULL, NULL, NULL)) {
+                EC_POINT_free(pubkey);
+                EC_KEY_free(eckey_);
+                eckey_ = NULL;
+                isc_throw(LibraryError, "EC_POINT_mul");
+            }
+            if (!EC_KEY_set_public_key(eckey_, pubkey)) {
+                EC_POINT_free(pubkey);
+                EC_KEY_free(eckey_);
+                eckey_ = NULL;
+                isc_throw(BadKey, "EC_KEY_set_public_key");
+            }
+            EC_POINT_free(pubkey);
+        }
+        if (EC_KEY_check_key(eckey_) != 1) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(BadKey, "EC_KEY_check_key");
+        }
+        EC_GROUP* wanted = EC_GROUP_new_by_curve_name(curve_nid);
+        if (!wanted) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(LibraryError, "EC_GROUP_new_by_curve_name");
+        }
+        const int status =
+            EC_GROUP_cmp(EC_KEY_get0_group(eckey_), wanted, NULL);
+        EC_GROUP_free(wanted);
+        if (status < 0) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(LibraryError, "EC_GROUP_cmp");
+        } else if (status != 0) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(BadKey, "EC_GROUP_cmp");
+        }
+    } else if ((kind_ == PRIVATE) && (key_format == DNS)) {
+        // bind9 .private file
+        // warn when password not empty
+        FILE* fp = fopen(filename.c_str(), "r");
+        if (!fp) {
+            isc_throw(BadKey, "Can't open file: " << filename);
+        }
+        bool got_algorithm = false;
+        bool got_privatekey = false;
+        BIGNUM* privkey = NULL;
+        char line[4096];
+        while (fgets(line, sizeof(line), fp)) {
+            if (strncmp(line, "Algorithm:", strlen("Algorithm:")) == 0) {
+                if (got_algorithm) {
+                    fclose(fp);
+                    isc_throw(BadKey, "Two Algorithm entries");
+                }
+                got_algorithm = true;
+                std::string value(line + strlen("Algorithm:") + 1);
+                int alg = std::atoi(value.c_str());
+                if (alg == 13) {
+                    // ECDSAP256SHA256
+                    if (hash_ != SHA256) {
+                        fclose(fp);
+                        isc_throw(BadKey, "Require an ECDSA P256 key");
+                    }
+                } else if (alg == 14) {
+                    // ECDSAP384SHA384
+                    if (hash_ != SHA384) {
+                        fclose(fp);
+                        isc_throw(BadKey, "Require an ECDSA SHA384 key");
+                    }
+                } else {
+                    fclose(fp);
+                    isc_throw(BadKey, "Bad Algorithm: " << alg);
+                }
+            } else if (strncmp(line, "PrivateKey:",
+                               strlen("PrivateKey:")) == 0) {
+                if (got_privatekey) {
+                    fclose(fp);
+                    isc_throw(BadKey, "Two PrivateKey entries");
+                }
+                got_privatekey = true;
+                std::string value(line + strlen("PrivateKey:") + 1);
+                std::vector<uint8_t> bin;
+                try {
+                    isc::util::encode::decodeBase64(value, bin);
+                } catch (const BadValue& exc) {
+                    fclose(fp);
+                    isc_throw(BadKey, "PrivateKey: " << exc.what());
+                }
+                int len = static_cast<int>(bin.size());
+                privkey = BN_bin2bn(&bin[0], len, NULL);
+            }
+        }
+        fclose(fp);
+        // Check first the existence of the PrivateKey
+        if (!got_privatekey) {
+            isc_throw(BadKey, "Missing PrivateExponent entry");
+        }
+        if (!got_algorithm) {
+            BN_clear_free(privkey);
+            isc_throw(BadKey, "Missing Algorithm entry");
+        }
+        eckey_ = EC_KEY_new_by_curve_name(curve_nid);
+        if (!eckey_) {
+            BN_clear_free(privkey);
+            isc_throw(LibraryError, "EC_KEY_new_by_curve_name");
+        }
+        EC_KEY_set_asn1_flag(eckey_, OPENSSL_EC_NAMED_CURVE);
+        if (!EC_KEY_set_private_key(eckey_, privkey)) {
+            BN_clear_free(privkey);
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(LibraryError, "EC_KEY_set_private_key");
+        }
+        BN_clear_free(privkey);
+        // Compute the public key as a side effect
+        if (!EC_KEY_generate_key(eckey_)) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(BadKey, "EC_KEY_generate_key");
+        }
+    } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
+        // SubjectPublicKeyInfo PEM file
+        // warn when password not empty
+        FILE* fp = fopen(filename.c_str(), "r");
+        if (!fp) {
+            isc_throw(BadKey, "Can't open file: " << filename);
+        }
+        char* pwd = NULL;
+        if (!password.empty()) {
+            pwd = const_cast<char*>(password.c_str());
+        }
+        eckey_ = PEM_read_EC_PUBKEY(fp, NULL, 0, pwd);
+        fclose(fp);
+        if (!eckey_) {
+            isc_throw(BadKey, "PEM_read_EC_PUBKEY");
+        }
+        if (EC_KEY_check_key(eckey_) != 1) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(BadKey, "EC_KEY_check_key");
+        }
+        EC_GROUP* wanted = EC_GROUP_new_by_curve_name(curve_nid);
+        if (!wanted) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(LibraryError, "EC_GROUP_new_by_curve_name");
+        }
+        const int status =
+            EC_GROUP_cmp(EC_KEY_get0_group(eckey_), wanted, NULL);
+        EC_GROUP_free(wanted);
+        if (status < 0) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(LibraryError, "EC_GROUP_cmp");
+        } else if (status != 0) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(BadKey, "EC_GROUP_cmp");
+        }
+    } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
+        // bind9 .key file (RDATA)
+        // warn when password not empty
+        FILE* fp = fopen(filename.c_str(), "r");
+        if (!fp) {
+            isc_throw(BadKey, "Can't open file: " << filename);
+        }
+        char line[4096];
+        bool found = false;
+        while (fgets(line, sizeof(line), fp)) {
+            if ((line[0] == '\0') || (line[0] == ';')) {
+                continue;
+            }
+            if (strstr(line, "DNSKEY") == NULL) {
+                continue;
+            }
+            found = true;
+            if (line[strlen(line) - 1] == '\n') {
+                line[strlen(line) - 1] = 0;
+            }
+            break;
+        }
+        fclose(fp);
+        if (!found) {
+            isc_throw(BadKey, "Can't find a DNSKEY");
+        }
+        const char b64[] =
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
+            "ghijklmnopqrstuvwxyz0123456789+/=";
+        size_t last = strlen(line) - 1;
+        while (strchr(b64, static_cast<int>(line[last])) != NULL) {
+            --last;
+        }
+        const std::string value(line + last + 1);
+        std::vector<uint8_t> bin;
+        try {
+            util::encode::decodeBase64(value, bin);
+        } catch (const BadValue& exc) {
+           isc_throw(BadKey, "Can't decode base64: " << exc.what());
+        }
+        size_t len;
+        std::string algo_name;
+        if (hash_ == SHA256) {
+            len = 64;
+            algo_name = "ECDSA P256";
+        } else {
+            len = 96;
+            algo_name ="ECDSA P384";
+        }
+        if (bin.size() != len) {
+            isc_throw(BadKey, "Bad " << algo_name <<
+                      "Public Key length: " <<
+                      bin.size() << ", expected " << len);
+        }
+        bin.insert(bin.begin(), POINT_CONVERSION_UNCOMPRESSED);
+        const uint8_t* p = &bin[0];
+        eckey_ = EC_KEY_new_by_curve_name(curve_nid);
+        if (!eckey_) {
+            isc_throw(LibraryError, "EC_KEY_new_by_curve_name");
+        }
+        EC_KEY_set_asn1_flag(eckey_, OPENSSL_EC_NAMED_CURVE);
+        if (o2i_ECPublicKey(&eckey_, &p,
+                            static_cast<long>(len + 1)) == NULL) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(BadKey, "o2i_ECPublicKey");
+        }
+        if (!EC_KEY_check_key(eckey_)) {
+            EC_KEY_free(eckey_);
+            eckey_ = NULL;
+            isc_throw(BadKey, "EC_KEY_check_key");
+        }
+    } else if (kind_ == PUBLIC) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown ECDSA Public Key format: " <<
+                  static_cast<int>(key_format));
+    } else if ((kind_ == CERT) && (key_format == ASN1)) {
+        // Public Key Certificate PEM file
+        // warn when password not empty
+        FILE* fp = fopen(filename.c_str(), "r");
+        if (!fp) {
+            isc_throw(BadKey, "Can't open file: " << filename);
+        }
+        x509_ = PEM_read_X509(fp, NULL, 0, NULL);
+        fclose(fp);
+        if (!x509_) {
+            isc_throw(BadKey, "PEM_read_X509");
+        }
+        int sig_nid = OBJ_obj2nid(x509_->sig_alg->algorithm);
+        if (hash_ == SHA256) {
+            if (sig_nid != OBJ_txt2nid("1.2.840.10045.4.3.2")) {
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(BadKey, "Require an ECDSA P256 certificate");
+            }
+        } else {
+            if (sig_nid != OBJ_txt2nid("1.2.840.10045.4.3.3")) {
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(BadKey, "Require an ECDSA P256 certificate");
+            }
+        }
+        EVP_PKEY* pkey = X509_get_pubkey(x509_);
+        if (!pkey) {
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(BadKey, "X509_get_pubkey");
+        }
+        eckey_ = EVP_PKEY_get1_EC_KEY(pkey);
+        EVP_PKEY_free(pkey);
+        if (!eckey_) {
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(BadKey, "EVP_PKEY_get1_EC_KEY");
+        }
+    } else if (kind_ == CERT) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown Public Key Certificate format: " <<
+                  static_cast<int>(key_format));
+    } else {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown ECDSA Key kind: " << static_cast<int>(kind_));
+    }
+}
+
+/// @brief Destructor
+EcDsaAsymImpl::~EcDsaAsymImpl() {
+    tbs_.clear();
+    if (eckey_) {
+        EC_KEY_free(eckey_);
+        eckey_ = NULL;
+    }
+    if (x509_) {
+        X509_free(x509_);
+        x509_ = NULL;
+    }
+}
+
+/// @brief Returns the AsymAlgorithm of the object
+AsymAlgorithm EcDsaAsymImpl::getAsymAlgorithm() const {
+    return (algo_);
+}
+
+/// @brief Returns the HashAlgorithm of the object
+HashAlgorithm EcDsaAsymImpl::getHashAlgorithm() const {
+    return (hash_);
+}
+
+/// @brief Returns the AsymKeyKind of the object
+AsymKeyKind EcDsaAsymImpl::getAsymKeyKind() const {
+    return (kind_);
+}
+
+/// @brief Returns the key size in bits
+size_t EcDsaAsymImpl::getKeySize() const {
+    return (hash_ == SHA256 ? 256 : 384);
+}
+
+/// @brief Returns the output size of the signature
+size_t EcDsaAsymImpl::getSignatureLength(const AsymFormat sig_format) const {
+    switch (sig_format) {
+    case BASIC:
+    case DNS:
+        // In both cases a pair of big integers
+        return (hash_ == SHA256 ? 64 : 96);
+    case ASN1:
+        // Compute the maximum size of the ASN.1 structure
+        return (hash_ == SHA256 ? 64 + 8 : 96 + 8);
+    default:
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown ECDSA Signature format: " <<
+                  static_cast<int>(sig_format));
+    }
+}           
+
+/// @brief Add data to digest
+void EcDsaAsymImpl::update(const void* data, const size_t len) {
+    const size_t old = tbs_.size();
+    tbs_.resize(old + len);
+    std::memcpy(&tbs_[old], data, len);
+}
+
+/// @brief Calculate the final signature
+void EcDsaAsymImpl::sign(isc::util::OutputBuffer& result, size_t len,
+                         const AsymFormat sig_format) {
+    if ((sig_format != BASIC) &&
+        (sig_format != ASN1) &&
+        (sig_format != DNS)) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown Signature format: " <<
+                  static_cast<int>(sig_format));
+    }
+    size_t size = getSignatureLength(sig_format);
+    ossl::SecBuf<uint8_t> buf(size);
+    ECDSA_SIG* sig = ECDSA_do_sign(&tbs_[0],
+                                   static_cast<int>(tbs_.size()),
+                                   eckey_);
+    if (!sig) {
+        isc_throw(LibraryError, "ECDSA_do_sign");
+    }
+    if ((sig_format == BASIC) || (sig_format == DNS)) {
+        // Store the 2 integers with padding
+        BN_bn2bin(sig->r, &buf[(size / 2) - BN_num_bytes(sig->r)]);
+        BN_bn2bin(sig->s, &buf[size - BN_num_bytes(sig->s)]);
+        ECDSA_SIG_free(sig);
+        if (len > size) {
+            len = size;
+        }
+    } else {
+        // Store the ECDSA_SIG structure in DER
+        int sig_len = i2d_ECDSA_SIG(sig, NULL);
+        if (sig_len < 0) {
+            ECDSA_SIG_free(sig);
+            isc_throw(LibraryError, "i2d_ECDSA_SIG 0");
+        }
+        buf.resize(sig_len);
+        uint8_t* p = &buf[0];
+        sig_len = i2d_ECDSA_SIG(sig, &p);
+        ECDSA_SIG_free(sig);
+        if (sig_len != static_cast<int>(buf.size())) {
+            isc_throw(LibraryError, "i2d_ECDSA_SIG");
+        }
+        size_t sig_size = static_cast<size_t>(sig_len);
+        if (len > sig_size) {
+            len = sig_size;
+        }
+    }
+    result.writeData(&buf[0], len);
+}
+
+/// @brief Calculate the final signature
+void EcDsaAsymImpl::sign(void* result, size_t len,
+                         const AsymFormat sig_format) {
+    if ((sig_format != BASIC) &&
+        (sig_format != ASN1) &&
+        (sig_format != DNS)) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown Signature format: " <<
+                  static_cast<int>(sig_format));
+    }
+    size_t size = getSignatureLength(sig_format);
+    ossl::SecBuf<uint8_t> buf(size);
+    ECDSA_SIG* sig = ECDSA_do_sign(&tbs_[0],
+                                   static_cast<int>(tbs_.size()),
+                                   eckey_);
+    if (!sig) {
+        isc_throw(LibraryError, "ECDSA_do_sign");
+    }
+    if ((sig_format == BASIC) || (sig_format == DNS)) {
+        // Store the 2 integers with padding
+        BN_bn2bin(sig->r, &buf[(size / 2) - BN_num_bytes(sig->r)]);
+        BN_bn2bin(sig->s, &buf[size - BN_num_bytes(sig->s)]);
+        ECDSA_SIG_free(sig);
+        if (len > size) {
+            len = size;
+        }
+    } else {
+        // Store the ECDSA_SIG structure in DER
+        int sig_len = i2d_ECDSA_SIG(sig, NULL);
+        if (sig_len < 0) {
+            ECDSA_SIG_free(sig);
+            isc_throw(LibraryError, "i2d_ECDSA_SIG 0");
+        }
+        buf.resize(sig_len);
+        uint8_t* p = &buf[0];
+        sig_len = i2d_ECDSA_SIG(sig, &p);
+        ECDSA_SIG_free(sig);
+        if (sig_len != static_cast<int>(buf.size())) {
+            isc_throw(LibraryError, "i2d_ECDSA_SIG");
+        }
+        size_t sig_size = static_cast<size_t>(sig_len);
+        if (len > sig_size) {
+            len = sig_size;
+        }
+    }
+    std::memcpy(result, &buf[0], len);
+}
+
+/// @brief Calculate the final signature
+std::vector<uint8_t> EcDsaAsymImpl::sign(size_t len,
+                                         const AsymFormat sig_format) {
+    if ((sig_format != BASIC) &&
+        (sig_format != ASN1) &&
+        (sig_format != DNS)) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown Signature format: " <<
+                  static_cast<int>(sig_format));
+    }
+    size_t size = getSignatureLength(sig_format);
+    ossl::SecBuf<uint8_t> buf(size);
+    ECDSA_SIG* sig = ECDSA_do_sign(&tbs_[0],
+                                   static_cast<int>(tbs_.size()),
+                                   eckey_);
+    if (!sig) {
+        isc_throw(LibraryError, "ECDSA_do_sign");
+    }
+    if ((sig_format == BASIC) || (sig_format == DNS)) {
+        // Store the 2 integers with padding
+        BN_bn2bin(sig->r, &buf[(size / 2) - BN_num_bytes(sig->r)]);
+        BN_bn2bin(sig->s, &buf[size - BN_num_bytes(sig->s)]);
+        ECDSA_SIG_free(sig);
+        // resize to min(len, size)
+        buf.resize(len < size ? len : size);
+    } else {
+        // Store the ECDSA_SIG structure in DER
+        int sig_len = i2d_ECDSA_SIG(sig, NULL);
+        if (sig_len < 0) {
+            ECDSA_SIG_free(sig);
+            isc_throw(LibraryError, "i2d_ECDSA_SIG 0");
+        }
+        buf.resize(sig_len);
+        uint8_t* p = &buf[0];
+        sig_len = i2d_ECDSA_SIG(sig, &p);
+        ECDSA_SIG_free(sig);
+        if (sig_len != static_cast<int>(buf.size())) {
+            isc_throw(LibraryError, "i2d_ECDSA_SIG");
+        }
+        // resize to min(len, sig_len)
+        size_t sig_size = static_cast<size_t>(sig_len);
+        buf.resize(len < sig_size ? len : sig_size);
+    }
+    return (std::vector<uint8_t>(buf.begin(), buf.end()));
+}  
+
+/// @brief Verify an existing signature
+bool EcDsaAsymImpl::verify(const void* sig, size_t len,
+                           const AsymFormat sig_format) {
+    if ((sig_format != BASIC) &&
+        (sig_format != ASN1) &&
+        (sig_format != DNS)) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown Signature format: " <<
+                  static_cast<int>(sig_format));
+    }
+    ECDSA_SIG* asn_sig;
+    const uint8_t* sigbuf =
+        reinterpret_cast<const uint8_t*>(const_cast<void*>(sig));
+    if ((sig_format == BASIC) || (sig_format == DNS)) {
+        size_t size = getSignatureLength(sig_format);
+        if (len != size) {
+            return false;
+        }
+        asn_sig = ECDSA_SIG_new();
+        if (!asn_sig) {
+            throw std::bad_alloc();
+        }
+        if (asn_sig->r) {
+            BN_clear_free(asn_sig->r);
+        }
+        asn_sig->r = BN_bin2bn(sigbuf, size / 2, NULL);
+        if (!asn_sig->r) {
+            ECDSA_SIG_free(asn_sig);
+            throw std::bad_alloc();
+        }
+        if (asn_sig->s) {
+            BN_clear_free(asn_sig->s);
+        }
+        asn_sig->s = BN_bin2bn(sigbuf + size / 2, size / 2, NULL);
+        if (!asn_sig->s) {
+            ECDSA_SIG_free(asn_sig);
+            throw std::bad_alloc();
+        }
+    } else {
+        asn_sig = d2i_ECDSA_SIG(NULL, &sigbuf, static_cast<long>(len));
+        if (!asn_sig) {
+            // Don't throw: just return false
+            return false;
+        }
+    }
+    int status = ECDSA_do_verify(&tbs_[0], static_cast<int>(tbs_.size()),
+                                 asn_sig, eckey_);
+    ECDSA_SIG_free(asn_sig);
+    switch (status) {
+    case 1:
+        return true;
+    case 0:
+        return false;
+    case -1:
+    default:
+        isc_throw(LibraryError, "ECDSA_do_verify");
+    }
+}
+
+/// @brief Clear the crypto state and go back to the initial state
+void EcDsaAsymImpl::clear() {
+    tbs_.clear();
+}
+
+/// @brief Export the key value (binary)
+std::vector<uint8_t>
+EcDsaAsymImpl::exportkey(const AsymKeyKind key_kind,
+                       const AsymFormat key_format) const {
+    if ((key_kind == PRIVATE) && (key_format == BASIC)) {
+        // The private key is as just a number
+        if (kind_ != PRIVATE) {
+            isc_throw(UnsupportedAlgorithm, "Have no ECDSA Private Key");
+        }
+        const BIGNUM* privkey = EC_KEY_get0_private_key(eckey_);
+        if (!privkey) {
+            isc_throw(LibraryError, "EC_KEY_get0_private_key");
+        }
+        int len = BN_num_bytes(privkey);
+        std::vector<uint8_t> bin(static_cast<size_t>(len));
+        len = BN_bn2bin(privkey, &bin[0]);
+        if (len != static_cast<int>(bin.size())) {
+            isc_throw(LibraryError, "BN_bn2bin");
+        }
+        return bin;
+    } else if (key_kind == PRIVATE) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown ECDSA Private Key format: " <<
+                  static_cast<int>(key_format));
+    } else if ((key_kind == PUBLIC) && (key_format == ASN1)) {
+        // SubjectPublicKeyInfo
+        int len = i2d_EC_PUBKEY(eckey_, NULL);
+        if (len < 0) {
+            isc_throw(LibraryError, "i2d_EC_PUBKEY 0");
+        }
+        std::vector<uint8_t> der(static_cast<size_t>(len));
+        unsigned char* p = &der[0];
+        len = i2d_EC_PUBKEY(eckey_, &p);
+        if (len != static_cast<int>(der.size())) {
+            isc_throw(LibraryError, "i2d_EC_PUBKEY");
+        }
+        return der;
+    } else if ((key_kind == PUBLIC) && (key_format == DNS)) {
+        // RFC 6605 DNS wire format
+        int len = i2o_ECPublicKey(eckey_, NULL);
+        if (len < 1) {
+            isc_throw(LibraryError, "i2o_ECPublicKey 0");
+        }
+        std::vector<uint8_t> pubkey(static_cast<size_t>(len));
+        uint8_t* p = &pubkey[0];
+        len = i2o_ECPublicKey(eckey_, &p);
+        if (len != static_cast<int>(pubkey.size())) {
+            isc_throw(LibraryError, "i2o_ECPublicKey");
+        }
+        return std::vector<uint8_t>(pubkey.begin() + 1, pubkey.end());
+    } else if (key_kind == PUBLIC) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown ECDSA Public Key format: " <<
+                  static_cast<int>(key_format));
+    } else if ((key_kind == CERT) && (key_format == ASN1)) {
+        // X.509 Public Key Certificate
+        if (kind_ != CERT) {
+            isc_throw(UnsupportedAlgorithm, "Have no Certificate");
+        }
+        int len = i2d_X509(x509_, NULL);
+        if (len < 0) {
+            isc_throw(LibraryError, "i2d_X509 0");
+        }
+        std::vector<uint8_t> ber(static_cast<size_t>(len));
+        unsigned char* p = &ber[0];
+        len = i2d_X509(x509_, &p);
+        if (len != static_cast<int>(ber.size())) {
+            isc_throw(LibraryError, "i2d_X509");
+        }
+        return ber;
+    } else if (key_kind == CERT) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown Certificate format: " <<
+                  static_cast<int>(key_format));
+    } else {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown ECDSA Key kind: " <<
+                  static_cast<int>(key_kind));
+    }
+}
+
+/// @brief Export the key value (file)
+void EcDsaAsymImpl::exportkey(const std::string& filename,
+                            const std::string& password,
+                            const AsymKeyKind key_kind,
+                            const AsymFormat key_format) const {
+    if ((key_kind == PRIVATE) && (key_format == ASN1)) {
+        // PKCS#8 Private Key PEM file
+        if (kind_ != PRIVATE) {
+            isc_throw(UnsupportedAlgorithm, "Have no ECDSA Private Key");
+        }
+        char* pwd = NULL;
+        const EVP_CIPHER* enc = NULL;
+        if (!password.empty()) {
+            pwd = const_cast<char*>(password.c_str());
+            enc = EVP_des_ede3_cbc();
+            if (!enc) {
+                isc_throw(LibraryError, "EVP_des_ede3_cbc");
+            }
+        }
+        FILE* fp = fopen(filename.c_str(), "w");
+        if (!fp) {
+            isc_throw(BadKey, "Can't open file: " << filename);
+        }
+        EVP_PKEY* pkey = EVP_PKEY_new();
+        if (!pkey) {
+            fclose(fp);
+            throw std::bad_alloc();
+        }
+        if (!EVP_PKEY_set1_EC_KEY(pkey, eckey_)) {
+            EVP_PKEY_free(pkey);
+            fclose(fp);
+            isc_throw(LibraryError, "EVP_PKEY_set1_EC_KEY");
+        }
+        if (!PEM_write_PKCS8PrivateKey(fp, pkey, enc, pwd,
+                                       static_cast<int>(password.size()),
+                                       0, NULL)) {
+            EVP_PKEY_free(pkey);
+            fclose(fp);
+            isc_throw(LibraryError, "PEM_write_PKCS8PrivateKey");
+        }
+        EVP_PKEY_free(pkey);
+        fclose(fp);
+    } else if ((key_kind == PRIVATE) && (key_format == DNS)) {
+        //  bind9 .private file
+        if (kind_ != PRIVATE) {
+            isc_throw(UnsupportedAlgorithm, "Have no ECDSA Private Key");
+        }
+        FILE* fp = fopen(filename.c_str(), "w");
+        if (!fp) {
+            isc_throw(BadKey, "Can't open file: " << filename);
+        }
+        const BIGNUM* privkey = EC_KEY_get0_private_key(eckey_);
+        if (!privkey) {
+            fclose(fp);
+            isc_throw(LibraryError, "EC_KEY_get0_private_key");
+        }
+        fprintf(fp, "Private-key-format: v1.2\n");
+        if (hash_ == SHA256) {
+            fprintf(fp, "Algorithm: 13 (ECDSAP256SHA256)\n");
+        } else if (hash_ == SHA256) {
+            fprintf(fp, "Algorithm: 1 (ECDSAP384SHA384)\n");
+        }
+        std::vector<uint8_t> bin;
+        bin.resize(BN_num_bytes(privkey));
+        BN_bn2bin(privkey, &bin[0]);
+        fprintf(fp, "PrivateKey: %s\n",
+                util::encode::encodeBase64(bin).c_str());
+        fclose(fp);
+    } else if (key_kind == PRIVATE) {
+        if (kind_ != PRIVATE) {
+            isc_throw(UnsupportedAlgorithm, "Have no ECDSA Private Key");
+        }
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown ECDSA Private Key format: " <<
+                  static_cast<int>(key_format));
+    } else if ((key_kind == PUBLIC) && (key_format == ASN1)) {
+        // SubjectPublicKeyInfo PEM file
+        // warn when password not empty
+        FILE* fp = fopen(filename.c_str(), "w");
+        if (!fp) {
+            isc_throw(BadKey, "Can't open file: " << filename);
+        }
+        if (!PEM_write_EC_PUBKEY(fp, eckey_)) {
+            fclose(fp);
+            isc_throw(LibraryError, "PEM_write_EC_PUBKEY");
+        }
+        fclose(fp);
+    }  else if ((key_kind == PUBLIC) && (key_format == DNS)) {
+        // bind9 .key file (RDATA)
+        // warn when password not empty
+        std::vector<uint8_t> bin = exportkey(key_kind, key_format);
+        FILE* fp = fopen(filename.c_str(), "w");
+        if (!fp) {
+            isc_throw(BadKey, "Can't open file: " << filename);
+        }
+        fprintf(fp, "; DNSKEY RDATA: %s\n",
+                util::encode::encodeBase64(bin).c_str());
+        fclose(fp);
+    } else if (key_kind == PUBLIC) {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown ECDSA Public Key format: " <<
+                  static_cast<int>(key_format));
+    } else if ((key_kind == CERT) && (key_format == ASN1)) {
+        // Public Key Certificate PEM file
+        // warn when password not empty
+        if (!x509_) {
+            isc_throw(UnsupportedAlgorithm, "Have no Certificate");
+        }
+        FILE* fp = fopen(filename.c_str(), "w");
+        if (!fp) {
+            isc_throw(BadKey, "Can't open file: " << filename);
+        }
+        if (!PEM_write_X509(fp, x509_)) {
+            fclose(fp);
+            isc_throw(LibraryError, "PEM_write_X509");
+        }
+        fclose(fp);
+    } else if (key_kind == CERT) {
+        if (!x509_) {
+            isc_throw(UnsupportedAlgorithm, "Have no Certificate");
+        }
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown Certificate format: " <<
+                  static_cast<int>(key_format));
+    } else {
+        isc_throw(UnsupportedAlgorithm,
+                  "Unknown ECDSA Key kind: " <<
+                  static_cast<int>(key_kind));
+    }
+}
+
+/// @brief Check the validity
+bool EcDsaAsymImpl::validate() const {
+    X509_STORE* store;
+    X509_STORE_CTX* ctx;
+    int status;
+    switch (kind_) {
+    case PUBLIC:
+    case PRIVATE:
+        return (EC_KEY_check_key(eckey_) == 1);
+    case CERT:
+        store = X509_STORE_new();
+        if (!store) {
+            return false;
+        }
+        if (!X509_STORE_add_cert(store, x509_)) {
+            X509_STORE_free(store);
+            store = NULL;
+            return false;
+        }
+        ctx = X509_STORE_CTX_new();
+        if (!ctx) {
+            X509_STORE_free(store);
+            store = NULL;
+            return false;
+        }
+        if (!X509_STORE_CTX_init(ctx, store, x509_, NULL)) {
+            X509_STORE_CTX_free(ctx);
+            ctx = NULL;
+            X509_STORE_free(store);
+            store = NULL;
+            return false;
+        }
+        // By default OpenSSL skips self-signatures
+        X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_CHECK_SS_SIGNATURE);
+        status = X509_verify_cert(ctx);
+        // text version of status available by
+        // X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx))
+        X509_STORE_CTX_free(ctx);
+        ctx = NULL;
+        X509_STORE_free(store);
+        store = NULL;
+        if (status == 1) {
+            return true;
+        }
+        return false;
+    default:
+        return false;
+    }
+}
+
+/// @brief Compare two keys
+bool EcDsaAsymImpl::compare(const EcDsaAsymImpl* other,
+                            const AsymKeyKind key_kind) const {
+    if (!other || (other->algo_ != ECDSA_)) {
+        return false;
+    }
+    const EC_GROUP* grp = EC_KEY_get0_group(eckey_);
+    const EC_GROUP* ogrp = EC_KEY_get0_group(other->eckey_);
+    const EC_POINT* pub = EC_KEY_get0_public_key(eckey_);
+    const EC_POINT* opub = EC_KEY_get0_public_key(other->eckey_);
+    int status;
+    switch (key_kind) {
+    case CERT:
+        // Special case for cert - cert
+        if ((kind_ == CERT) && (other->kind_ == CERT)) {
+            return (X509_cmp(x509_, other->x509_) == 0);
+        }
+        // At least one should be a cert
+        if ((kind_ != CERT) && (other->kind_ != CERT)) {
+            return false;
+        }
+        goto cmppub;
+    case PRIVATE:
+        if ((kind_ != PRIVATE) || (other->kind_ != PRIVATE)) {
+            return false;
+        }
+        // If public keys match so private too
+        goto cmppub;
+    case PUBLIC:
+        // Compare curves and public keys
+        if ((kind_ != PUBLIC) &&
+            (kind_ != PRIVATE) &&
+            (kind_ != CERT)) {
+            return false;
+        }
+        if ((other->kind_ != PUBLIC) &&
+            (other->kind_ != PRIVATE) &&
+            (other->kind_ != CERT)) {
+            return false;
+        }
+    cmppub:
+        status = EC_GROUP_cmp(grp, ogrp, NULL);
+        switch (status) {
+        case 0:
+            // match but not finished
+            break;
+        case 1:
+            // don't match
+            return false;
+        default:
+            // errors
+            return false;
+        }
+        status = EC_POINT_cmp(grp, pub, opub, NULL);
+        switch (status) {
+        case 0:
+            // match
+            return true;
+        case 1:
+            // don't match
+            return false;
+        default:
+            // errors
+            return false;
+        }
+    default:
+        return false;
+    }
+}
+
+} // namespace cryptolink
+} // namespace isc
diff --git a/src/lib/cryptolink/openssl_ecdsa.h b/src/lib/cryptolink/openssl_ecdsa.h
new file mode 100644 (file)
index 0000000..2779532
--- /dev/null
@@ -0,0 +1,149 @@
+// Copyright (C) 2015  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+namespace isc {
+namespace cryptolink {
+
+/// @brief OpenSSL implementation of asymmetrical cryptography (Asym).
+// Each method is the counterpart of the Asym corresponding method.
+// ECDSA
+class EcDsaAsymImpl : public AsymImpl {
+public:
+    /// @brief Constructor from a key, asym and hash algorithm,
+    ///        key kind and key binary format
+    ///
+    /// See constructor of the @ref isc::cryptolink::Asym class for details.
+    ///
+    /// @param key            The key to sign/verify with
+    /// @param len            The length of the key
+    /// @param hash_algorithm The hash algorithm
+    /// @param key_kind       The key kind
+    /// @param key_format     The key binary format
+    EcDsaAsymImpl(const void* key, size_t key_len,
+                  const HashAlgorithm hash_algorithm,
+                  const AsymKeyKind key_kind,
+                  const AsymFormat key_format);
+
+    /// @brief Constructor from a key file with password,
+    ///        asym and hash algorithm, key kind and key binary format
+    ///
+    /// See constructor of the @ref isc::cryptolink::Asym class for details.
+    ///
+    /// @param filename       The key file name/path
+    /// @param password       The PKCS#8 password
+    /// @param hash_algorithm The hash algorithm
+    /// @param key_kind       The key kind
+    /// @param key_format     The key binary format
+    EcDsaAsymImpl(const std::string& filename,
+                  const std::string& password,
+                  const HashAlgorithm hash_algorithm,
+                  const AsymKeyKind key_kind,
+                  const AsymFormat key_format);
+
+    /// @brief Destructor
+    virtual ~EcDsaAsymImpl();
+
+    /// @brief Returns the AsymAlgorithm of the object
+    AsymAlgorithm getAsymAlgorithm() const;
+
+    /// @brief Returns the HashAlgorithm of the object
+    HashAlgorithm getHashAlgorithm() const;
+
+    /// @brief Returns the AsymKeyKind of the object
+    AsymKeyKind getAsymKeyKind() const;
+
+    /// @brief Returns the key size in bits
+    ///
+    size_t getKeySize() const;
+
+    /// @brief Returns the output size of the signature
+    ///
+    /// \param sig_format The signature binary format
+    size_t getSignatureLength(const AsymFormat sig_format) const;
+
+    /// @brief Add data to digest
+    ///
+    /// See @ref isc::cryptolink::AsymBase::update() for details.
+    void update(const void* data, const size_t len);
+
+    /// @brief Calculate the final signature
+    ///
+    /// See @ref isc::cryptolink::AsymBase::sign() for details.
+    void sign(isc::util::OutputBuffer& result, size_t len,
+              const AsymFormat sig_format);
+
+    /// @brief Calculate the final signature
+    ///
+    /// See @ref isc::cryptolink::AsymBase::sign() for details.
+    void sign(void* result, size_t len, const AsymFormat sig_format);
+
+    /// @brief Calculate the final signature
+    ///
+    /// See @ref isc::cryptolink::AsymBase::sign() for details.
+    std::vector<uint8_t> sign(size_t len, const AsymFormat sig_format);
+
+    /// @brief Verify an existing signature
+    ///
+    /// See @ref isc::cryptolink::AsymBase::verify() for details.
+    bool verify(const void* sig, size_t len, const AsymFormat sig_format);
+
+    /// @brief Clear the crypto state and go back to the initial state
+    /// (must be called before reusing an Asym object)
+    void clear();
+
+    /// @brief Export the key value (binary)
+    ///
+    /// See @ref isc::cryptolink::AsymBase::exportkey() for details
+    std::vector<uint8_t> exportkey(const AsymKeyKind key_kind,
+                                   const AsymFormat key_format) const;
+
+    /// @brief Export the key value (file)
+    ///
+    /// See @ref isc::cryptolink::AsymBase::exportkey() for details
+    void exportkey(const std::string& filename,
+                   const std::string& password,
+                   const AsymKeyKind key_kind,
+                   const AsymFormat key_format) const;
+
+    /// @brief Check the validity
+    ///
+    /// See @ref isc::cryptolink::AsymBase::validate() for details
+    bool validate() const;
+
+    /// @brief Compare two keys
+    ///
+    /// See @ref isc::cryptolink::Asym::compare() for details
+    bool compare(const EcDsaAsymImpl* other, const AsymKeyKind key_kind) const;
+
+private:
+    /// @brief The asymmetrical cryptography algorithm
+    AsymAlgorithm algo_;
+    /// @brief The hash algorithm
+    HashAlgorithm hash_;
+    /// @brief The key kind
+    AsymKeyKind kind_;
+    /// @brief The to be signed cache
+    ossl::SecBuf<uint8_t> tbs_;
+    /// @brief The raw pointer to the OpenSSL EC_KEY structure
+    /// There is no EC_PKEY_init() or EC_PKEY_cleanup() so
+    /// a smart pointer cannot be used.
+    EC_KEY* eckey_;
+    /// @brief The raw pointer to the OpenSSL X509 structure
+    /// There is no X509_init() or X509_cleanup() so
+    /// a smart pointer cannot be used.
+    X509* x509_;
+};
+
+} // namespace cryptolink
+} // namespace isc
diff --git a/src/lib/cryptolink/tests/ecdsa_unittests.cc b/src/lib/cryptolink/tests/ecdsa_unittests.cc
new file mode 100644 (file)
index 0000000..b63b140
--- /dev/null
@@ -0,0 +1,507 @@
+// Copyright (C) 2015  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+#include <stdlib.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <gtest/gtest.h>
+
+#include <util/encode/base64.h>
+
+#include <hooks/hooks_manager.h>
+#include <hooks/callout_handle.h>
+
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_asym.h>
+
+#include <util/buffer.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+using boost::lexical_cast;
+using namespace isc::util;
+using namespace isc::util::encode;
+using namespace isc::cryptolink;
+
+namespace {
+
+#define P256_OBJ_ID \
+0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, \
+0x01, 0x07
+
+#define P384_OBJ_ID \
+0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22
+
+#define PRIVATE_KEY \
+0xdc, 0x51, 0xd3, 0x86, 0x6a, 0x15, 0xba, 0xcd, \
+0xe3, 0x3d, 0x96, 0xf9, 0x92, 0xfc, 0xa9, 0x9d, \
+0xa7, 0xe6, 0xef, 0x09, 0x34, 0xe7, 0x09, 0x75, \
+0x59, 0xc2, 0x7f, 0x16, 0x14, 0xc8, 0x8a, 0x7f
+
+#define PUBLIC_KEY \
+0x24, 0x42, 0xa5, 0xcc, 0x0e, 0xcd, 0x01, 0x5f, \
+0xa3, 0xca, 0x31, 0xdc, 0x8e, 0x2b, 0xbc, 0x70, \
+0xbf, 0x42, 0xd6, 0x0c, 0xbc, 0xa2, 0x00, 0x85, \
+0xe0, 0x82, 0x2c, 0xb0, 0x42, 0x35, 0xe9, 0x70, \
+0x6f, 0xc9, 0x8b, 0xd7, 0xe5, 0x02, 0x11, 0xa4, \
+0xa2, 0x71, 0x02, 0xfa, 0x35, 0x49, 0xdf, 0x79, \
+0xeb, 0xcb, 0x4b, 0xf2, 0x46, 0xb8, 0x09, 0x45, \
+0xcd, 0xdf, 0xe7, 0xd5, 0x09, 0xbb, 0xfd, 0x7d
+
+#define TBS_DATA \
+0x61, 0x62, 0x63
+
+#define SIGNATURE_R \
+0xcb, 0x28, 0xe0, 0x99, 0x9b, 0x9c, 0x77, 0x15, \
+0xfd, 0x0a, 0x80, 0xd8, 0xe4, 0x7a, 0x77, 0x07, \
+0x97, 0x16, 0xcb, 0xbf, 0x91, 0x7d, 0xd7, 0x2e, \
+0x97, 0x56, 0x6e, 0xa1, 0xc0, 0x66, 0x95, 0x7c
+
+#define SIGNATURE_S \
+0x86, 0xfa, 0x3b, 0xb4, 0xe2, 0x6c, 0xad, 0x5b, \
+0xf9, 0x0b, 0x7f, 0x81, 0x89, 0x92, 0x56, 0xce, \
+0x75, 0x94, 0xbb, 0x1e, 0xa0, 0xc8, 0x92, 0x12, \
+0x74, 0x8b, 0xff, 0x3b, 0x3d, 0x5b, 0x03, 0x15
+
+    /// @brief Public Key in SubjectPublicKeyInfo format
+    const uint8_t pubspki[] = {
+        0x30, // tag=SEQUENCE (SubjectPublicKeyInfo)
+        0x59, // length=89
+         0x30, // tag=SEQUENCE (algorithm : AlgorithmIdentifier)
+          0x13, // length=19
+          0x06, // tag=OBJECTID (algorithm)
+          0x07, // length=7
+           0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // id-ecPublicKey
+          0x06, // tag=OBJECTID (namedCurve)
+          0x08, // length=8
+           0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, // X9_62_prime256v1
+         0x03, // tag=BIT STRING
+        0x42, // length=66
+         0x00, 0x04, PUBLIC_KEY
+    };
+    size_t pubspkilen = 91;
+
+    /// @brief Public Key in DNSSEC format (length 132)
+    const uint8_t pubdns[] = {
+        PUBLIC_KEY
+    };
+    size_t pubdnslen = 64;
+
+    /// @brief ASN.1 ECDSA Signature layout
+    const uint8_t asn1sig[] = {
+        0x30, // tag=SEQUENCE
+        0x45, // length=69
+         0x02, // tag=INTEGER
+         0x21, // length=33
+          0x00, SIGNATURE_R,
+         0x02, // tag=INTEGER
+         0x21, // length=33
+        0x00, SIGNATURE_S
+    };
+    size_t asn1siglen = 72;
+
+    /// @brief Public Key file (SubjectPublicKeyInfo in PEM)
+    const std::string pubfile = TEST_DATA_SRCDIR "/ecpub.pem";
+
+    /// @brief Private Key file (PKCS#8 in PEM, password '1234')
+    const std::string privfile = TEST_DATA_SRCDIR "/ecpkcs8.pem";
+
+    /// @brief Certificate file (X.509 Public Key Certificate in PEM)
+    const std::string certfile = TEST_DATA_SRCDIR "/ecx509.pem";
+
+    /// @brief Compare data with expected value
+    /// @param data Value to compare
+    /// @param expected Expected value
+    /// @param len Length of the expected value
+    void checkData(const uint8_t* data, const uint8_t* expected,
+                   size_t len) {
+        for (size_t i = 0; i < len; ++i) {
+            ASSERT_EQ(expected[i], data[i]);
+        }
+    }
+
+    /// @brief Compare OutputBuffer with expected value
+    /// encapsulated checkData()
+    /// @param buf buffer to compare
+    /// @param expected Expected value
+    /// @param len Length of the expected value
+    void checkBuffer(const OutputBuffer& buf, const uint8_t* expected,
+                     size_t len)
+    {
+        ASSERT_EQ(len, buf.getLength());
+        checkData(static_cast<const uint8_t*>(buf.getData()), expected,
+                  len);
+    }
+
+    /// @brief Sign and verify with an instantiation of an asymmetrical
+    /// cryptography object
+    /// See @ref doEcDsaTest for parameters
+    void doEcDsaTestDirect(const std::string& data,
+                           const std::string& privfilename,
+                           const std::string& password,
+                           const std::string& pubfilename,
+                           const AsymAlgorithm asym_algorithm,
+                           const HashAlgorithm hash_algorithm,
+                           const AsymFormat sig_format,
+                           const uint8_t* expected_sig,
+                           size_t sig_len) {
+        OutputBuffer data_buf(data.size());
+        data_buf.writeData(data.c_str(), data.size());
+        OutputBuffer sig(1);
+        CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+        // Sign it
+        boost::shared_ptr<Asym> ecdsa_sign(crypto.createAsym(privfilename,
+                                                             password,
+                                                             asym_algorithm,
+                                                             hash_algorithm,
+                                                             PRIVATE,
+                                                             ASN1),
+                                           deleteAsym);
+        ecdsa_sign->update(data_buf.getData(), data_buf.getLength());
+        ecdsa_sign->sign(sig, sig_len, sig_format);
+
+        // Check whether we can verify it ourselves
+        boost::shared_ptr<Asym> ecdsa_verify(crypto.createAsym(pubfilename,
+                                                               "",
+                                                               asym_algorithm,
+                                                               hash_algorithm,
+                                                               PUBLIC,
+                                                               ASN1),
+                                             deleteAsym);
+        ecdsa_verify->update(data_buf.getData(), data_buf.getLength());
+        EXPECT_TRUE(ecdsa_verify->verify(sig.getData(),
+                                         sig.getLength(),
+                                         sig_format));
+
+        // Check if the signature we expect can verify
+        // (beware we can't compare signature as they are random)
+        EXPECT_TRUE(ecdsa_verify->verify(expected_sig,
+                                         sig_len,
+                                         sig_format));
+
+        // Change the length and check whether verification fails then
+        // Relies on the fact the length is checked first
+        EXPECT_FALSE(ecdsa_verify->verify(sig.getData(),
+                                          sig.getLength() - 1,
+                                          sig_format));
+        EXPECT_FALSE(ecdsa_verify->verify(sig.getData(),
+                                          sig.getLength() + 1,
+                                          sig_format));
+
+        // Change the sig by flipping the first octet, and check
+        // whether verification fails then
+        sig.writeUint8At(~sig[0], 0);
+        EXPECT_FALSE(ecdsa_verify->verify(sig.getData(),
+                                          sig.getLength(),
+                                          sig_format));
+    }
+
+    /// @brief Sign and verify with vector representation of signature
+    /// cryptography object
+    /// See @ref doEcDsaTest for parameters
+    void doEcDsaTestVector(const std::string& data,
+                           const std::string& privfilename,
+                           const std::string& password,
+                           const std::string& pubfilename,
+                           const AsymAlgorithm asym_algorithm,
+                           const HashAlgorithm hash_algorithm,
+                           const AsymFormat sig_format,
+                           const uint8_t* expected_sig,
+                           size_t sig_len) {
+        CryptoLink& crypto = CryptoLink::getCryptoLink();
+        boost::shared_ptr<Asym> ecdsa_sign(crypto.createAsym(privfilename,
+                                                             password,
+                                                             asym_algorithm,
+                                                             hash_algorithm,
+                                                             PRIVATE,
+                                                             ASN1),
+                                           deleteAsym);
+        ecdsa_sign->update(data.c_str(), data.size());
+        std::vector<uint8_t> sig = ecdsa_sign->sign(sig_len, sig_format);
+        ASSERT_EQ(sig_len, sig.size());
+
+        boost::shared_ptr<Asym> ecdsa_verify(crypto.createAsym(pubfilename,
+                                                               "",
+                                                               asym_algorithm,
+                                                               hash_algorithm,
+                                                               PUBLIC,
+                                                               ASN1),
+                                             deleteAsym);
+        ecdsa_verify->update(data.c_str(), data.size());
+        EXPECT_TRUE(ecdsa_verify->verify(&sig[0], sig.size(), sig_format));
+
+        EXPECT_TRUE(ecdsa_verify->verify(expected_sig,
+                                         sig_len,
+                                         sig_format));
+
+        EXPECT_FALSE(ecdsa_verify->verify(&sig[0],
+                                          sig.size() - 1,
+                                          sig_format));
+        EXPECT_FALSE(ecdsa_verify->verify(&sig[0],
+                                          sig.size() + 1,
+                                          sig_format));
+
+        sig[0] = ~sig[0];
+        EXPECT_FALSE(ecdsa_verify->verify(&sig[0], sig.size(), sig_format));
+    }
+
+    /// @brief Sign and verify with array representation of signature
+    /// cryptography object
+    /// See @ref doEcDsaTest for parameters
+    void doEcDsaTestArray(const std::string& data,
+                          const std::string& privfilename,
+                          const std::string& password,
+                          const std::string& pubfilename,
+                          const AsymAlgorithm asym_algorithm,
+                          const HashAlgorithm hash_algorithm,
+                          const AsymFormat sig_format,
+                          const uint8_t* expected_sig,
+                          size_t sig_len) {
+        CryptoLink& crypto = CryptoLink::getCryptoLink();
+        boost::shared_ptr<Asym> ecdsa_sign(crypto.createAsym(privfilename,
+                                                             password,
+                                                             asym_algorithm,
+                                                             hash_algorithm,
+                                                             PRIVATE,
+                                                             ASN1),
+                                           deleteAsym);
+        ecdsa_sign->update(data.c_str(), data.size());
+
+        // note: this is not exception-safe, and can leak, but
+        // if there is an unexpected exception in the code below we
+        // have more important things to fix.
+        uint8_t* sig = new uint8_t[sig_len];
+
+        ecdsa_sign->sign(sig, sig_len, sig_format);
+
+        boost::shared_ptr<Asym> ecdsa_verify(crypto.createAsym(pubfilename,
+                                                               "",
+                                                               asym_algorithm,
+                                                               hash_algorithm,
+                                                               PUBLIC,
+                                                               ASN1),
+                                             deleteAsym);
+        ecdsa_verify->update(data.c_str(), data.size());
+        EXPECT_TRUE(ecdsa_verify->verify(sig, sig_len, sig_format));
+
+        EXPECT_TRUE(ecdsa_verify->verify(expected_sig, sig_len, sig_format));
+
+        EXPECT_FALSE(ecdsa_verify->verify(sig, sig_len - 1, sig_format));
+        EXPECT_FALSE(ecdsa_verify->verify(sig, sig_len + 1, sig_format));
+
+        sig[0] = ~sig[0];
+        EXPECT_FALSE(ecdsa_verify->verify(sig, sig_len, sig_format));
+
+        delete[] sig;
+    }
+
+    /// @brief Sign and verify using all variants
+    /// @param data Input value
+    /// @param privfilename Private key file name
+    /// @param password Private key password
+    /// @param pubfilename Public key file name
+    /// @param asym_algorithm Asym algorithm enum
+    /// @param hash_algorithm Hash algorithm enum
+    /// @param sig_format Signature format enum
+    /// @param expected_sig Expected value
+    /// @param sig_len Expected value length
+    void doEcDsaTest(const std::string& data,
+                     const std::string& privfilename,
+                     const std::string& password,
+                     const std::string& pubfilename,
+                     const AsymAlgorithm asym_algorithm,
+                     const HashAlgorithm hash_algorithm,
+                     const AsymFormat sig_format,
+                     const uint8_t* expected_sig,
+                     size_t sig_len) {
+        doEcDsaTestDirect(data, privfilename, password, pubfilename,
+                          asym_algorithm, hash_algorithm, sig_format,
+                          expected_sig, sig_len);
+        doEcDsaTestVector(data, privfilename, password, pubfilename,
+                          asym_algorithm, hash_algorithm, sig_format,
+                          expected_sig, sig_len);
+        doEcDsaTestArray(data, privfilename, password, pubfilename,
+                         asym_algorithm, hash_algorithm, sig_format,
+                         expected_sig, sig_len);
+    }
+}
+
+//
+// Test values
+//
+TEST(EcDSATest, SHA256) {
+    uint8_t privkey[] = {
+        PRIVATE_KEY
+    };
+    size_t privkeylen = 32;
+    uint8_t pubkey[] = {
+        PUBLIC_KEY
+    };
+    size_t pubkeylen = 64;
+    uint8_t data[] = {
+        TBS_DATA
+    };
+    size_t datalen = 3;
+    uint8_t sig[] = {
+        SIGNATURE_R,
+        SIGNATURE_S
+    };
+    size_t siglen = 64;
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> ecdsa_sign(crypto.createAsym(privkey,
+                                                         privkeylen,
+                                                         ECDSA_,
+                                                         SHA256,
+                                                         PRIVATE,
+                                                         BASIC),
+                                       deleteAsym);
+    ASSERT_TRUE(ecdsa_sign);
+    EXPECT_EQ(ECDSA_, ecdsa_sign->getAsymAlgorithm());
+    EXPECT_EQ(SHA256, ecdsa_sign->getHashAlgorithm());
+    EXPECT_EQ(PRIVATE, ecdsa_sign->getAsymKeyKind());
+    EXPECT_EQ(256, ecdsa_sign->getKeySize());
+    EXPECT_EQ(64, ecdsa_sign->getSignatureLength(BASIC));
+    EXPECT_EQ(72, ecdsa_sign->getSignatureLength(ASN1));
+    EXPECT_EQ(64, ecdsa_sign->getSignatureLength(DNS));
+
+    std::vector<uint8_t> basicprivkey = ecdsa_sign->exportkey(PRIVATE, BASIC);
+    ASSERT_EQ(32, basicprivkey.size());
+    EXPECT_TRUE(std::memcmp(privkey, &basicprivkey[0], privkeylen) == 0);
+    std::vector<uint8_t> ans1pubkey = ecdsa_sign->exportkey(PUBLIC, ASN1);
+    ASSERT_EQ(pubspkilen, ans1pubkey.size());
+    EXPECT_TRUE(std::memcmp(pubspki, &ans1pubkey[0], pubspkilen) == 0);
+    std::vector<uint8_t> dnspubkey = ecdsa_sign->exportkey(PUBLIC, DNS);
+    ASSERT_EQ(64, dnspubkey.size());
+    EXPECT_TRUE(std::memcmp(pubkey, &dnspubkey[0], pubkeylen) == 0);
+
+    ecdsa_sign->update(data, datalen);
+    OutputBuffer nsig(1);
+    size_t nsig_len = ecdsa_sign->getSignatureLength(BASIC);
+    ecdsa_sign->sign(nsig, nsig_len, BASIC);
+
+    boost::shared_ptr<Asym> ecdsa_verify(crypto.createAsym(pubfile,
+                                                           "",
+                                                           ECDSA_,
+                                                           SHA256,
+                                                           PUBLIC,
+                                                           ASN1),
+                                         deleteAsym);
+    ASSERT_TRUE(ecdsa_verify);
+
+    ecdsa_verify->update(data, datalen);
+    EXPECT_TRUE(ecdsa_verify->verify(nsig.getData(), nsig.getLength(), BASIC));
+
+    EXPECT_TRUE(ecdsa_verify->verify(sig, siglen, DNS));
+
+    EXPECT_TRUE(ecdsa_verify->verify(asn1sig, asn1siglen, ASN1));
+}
+
+//
+// Multiple signatures
+//
+TEST(EcDsaTest, doubleSign) {
+    std::string data = "Kea provides DHCPv4 and DHCPv6 servers";
+    OutputBuffer data_buf(data.size());
+    data_buf.writeData(data.c_str(), data.size());
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+    // Sign it
+    boost::shared_ptr<Asym> ecdsa_sign(crypto.createAsym(privfile, "1234",
+                                                         ECDSA_, SHA256,
+                                                         PRIVATE, ASN1),
+                                       deleteAsym);
+    ASSERT_TRUE(ecdsa_sign);
+
+    OutputBuffer sig1(1);
+    size_t sig1_len = ecdsa_sign->getSignatureLength(BASIC);
+    EXPECT_EQ(64, sig1_len);
+    ecdsa_sign->update(data_buf.getData(), data_buf.getLength());
+    ecdsa_sign->sign(sig1, sig1_len, BASIC);
+    ASSERT_EQ(sig1_len, sig1.getLength());
+
+    // Clear state
+    ecdsa_sign->clear();
+
+    // Sign it again
+    OutputBuffer sig2(1);
+    size_t sig2_len = ecdsa_sign->getSignatureLength(BASIC);
+    EXPECT_EQ(64, sig2_len);
+    ecdsa_sign->update(data_buf.getData(), data_buf.getLength());
+    ecdsa_sign->sign(sig2, sig2_len, BASIC);
+    EXPECT_EQ(sig2_len, sig2.getLength());
+
+    // Compare
+    ASSERT_EQ(sig1_len, sig2_len);
+
+    // Verify
+    boost::shared_ptr<Asym> ecdsa_verify(crypto.createAsym(pubfile, "",
+                                                           ECDSA_, SHA256,
+                                                           PUBLIC, ASN1),
+                                         deleteAsym);
+    ASSERT_TRUE(ecdsa_verify);
+
+    ecdsa_verify->update(data_buf.getData(), data_buf.getLength());
+    EXPECT_TRUE(ecdsa_verify->verify(sig1.getData(), sig1.getLength(), BASIC));
+    EXPECT_TRUE(ecdsa_verify->verify(sig2.getData(), sig2.getLength(), BASIC));
+
+}
+
+//
+// Multiple verifies
+//
+TEST(EcdsaTest, doubleVerify) {
+    std::string data = "Kea provides DHCPv4 and DHCPv6 servers";
+    OutputBuffer data_buf(data.size());
+    data_buf.writeData(data.c_str(), data.size());
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+    // Sign it
+    boost::shared_ptr<Asym> ecdsa_sign(crypto.createAsym(privfile, "1234",
+                                                         ECDSA_, SHA256,
+                                                         PRIVATE, ASN1),
+                                       deleteAsym);
+    ASSERT_TRUE(ecdsa_sign);
+
+    OutputBuffer sig(1);
+    size_t sig_len = ecdsa_sign->getSignatureLength(BASIC);
+    EXPECT_EQ(64, sig_len);
+    ecdsa_sign->update(data_buf.getData(), data_buf.getLength());
+    ecdsa_sign->sign(sig, sig_len, BASIC);
+    EXPECT_EQ(sig_len, sig.getLength());
+
+    // Verify
+    boost::shared_ptr<Asym> ecdsa_verify(crypto.createAsym(pubfile, "",
+                                                           ECDSA_, SHA256,
+                                                           PUBLIC, ASN1),
+                                         deleteAsym);
+    ASSERT_TRUE(ecdsa_verify);
+
+    ecdsa_verify->update(data_buf.getData(), data_buf.getLength());
+    EXPECT_TRUE(ecdsa_verify->verify(sig.getData(), sig.getLength(), BASIC));
+
+    // Clear state
+    ecdsa_verify->clear();
+
+    // Verify again
+    ecdsa_verify->update(data_buf.getData(), data_buf.getLength());
+    EXPECT_TRUE(ecdsa_verify->verify(sig.getData(), sig.getLength(), BASIC));
+}
diff --git a/src/lib/cryptolink/tests/rsa_unittests.cc b/src/lib/cryptolink/tests/rsa_unittests.cc
new file mode 100644 (file)
index 0000000..f5b0c54
--- /dev/null
@@ -0,0 +1,877 @@
+// Copyright (C) 2014, 2015  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+#include <stdlib.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <gtest/gtest.h>
+
+#include <util/encode/base64.h>
+
+#include <hooks/hooks_manager.h>
+#include <hooks/callout_handle.h>
+
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_asym.h>
+
+#include <util/buffer.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+using boost::lexical_cast;
+using namespace isc::util;
+using namespace isc::util::encode;
+using namespace isc::cryptolink;
+
+namespace {
+
+#define MODULUS \
+0xb3, 0x25, 0xc2, 0x01, 0xee, 0x8c, 0x91, 0x5d, \
+0xa3, 0xea, 0x55, 0x90, 0xa2, 0x2c, 0xb3, 0xb1, \
+0x35, 0x18, 0x7b, 0xa1, 0x00, 0x80, 0x4f, 0x21, \
+0x32, 0x94, 0xd1, 0xa2, 0xc3, 0x1a, 0xbc, 0x0a, \
+0xee, 0xcd, 0x7f, 0x23, 0x12, 0x5d, 0xf8, 0x92, \
+0xcb, 0x87, 0x69, 0xfd, 0x85, 0x0f, 0x25, 0x57, \
+0x88, 0x69, 0x4b, 0x5e, 0x93, 0x12, 0x3a, 0x93, \
+0xa9, 0x23, 0x64, 0x0f, 0x7c, 0xaa, 0x20, 0xca, \
+0x56, 0x93, 0x81, 0xe9, 0x64, 0xb2, 0x20, 0xad, \
+0x55, 0x25, 0x87, 0xb1, 0x85, 0xc8, 0xd6, 0xfe, \
+0x5f, 0xa9, 0xcd, 0x53, 0x2e, 0xaf, 0x9e, 0x53, \
+0xa4, 0xfb, 0xba, 0xef, 0x30, 0x53, 0x9d, 0x01, \
+0xee, 0xb7, 0xff, 0xaa, 0xde, 0x7a, 0xfa, 0x57, \
+0xfb, 0xdf, 0x63, 0xf3, 0x9c, 0x77, 0x2e, 0xa4, \
+0x97, 0x10, 0x30, 0xc1, 0x15, 0x48, 0x6b, 0x01, \
+0x1e, 0x57, 0x8d, 0x5d, 0xeb, 0xfc, 0x42, 0xe1
+
+#define PUBLIC_EXPONENT 0x01, 0x00, 0x01
+
+#define PUBLIC_KEY \
+0x30, 0x81, 0x89, \
+0x02, 0x81, 0x81, 0x00, MODULUS, \
+0x02, 0x03, PUBLIC_EXPONENT
+
+#define PRIVATE_EXPONENT \
+0xa8, 0xc9, 0x93, 0x5f, 0xe4, 0x94, 0xf6, 0x45, \
+0x26, 0xb2, 0x1b, 0x8a, 0x18, 0xf2, 0x4b, 0x1f, \
+0x54, 0x2a, 0x4c, 0x18, 0xe6, 0x72, 0xfd, 0x9b, \
+0x06, 0xa0, 0x26, 0x5f, 0xd6, 0xb9, 0x32, 0xa0, \
+0x8e, 0x5c, 0x79, 0x43, 0xdf, 0x03, 0x40, 0xb7, \
+0x76, 0x21, 0x90, 0xa0, 0x37, 0x24, 0x8d, 0x07, \
+0x4a, 0xd4, 0x02, 0x1a, 0x0a, 0x31, 0x6b, 0x95, \
+0x42, 0x0f, 0xc1, 0x2f, 0xc2, 0x42, 0x3c, 0x7f, \
+0x33, 0xb3, 0x54, 0x2f, 0x83, 0xf8, 0x5d, 0x7f, \
+0x86, 0xa5, 0xe8, 0xbe, 0xae, 0x40, 0x41, 0x20, \
+0x5d, 0xa2, 0x66, 0x9a, 0x82, 0x66, 0x03, 0xfc, \
+0xf0, 0x62, 0x24, 0x08, 0x25, 0x99, 0xc6, 0x2b, \
+0xef, 0x9d, 0x79, 0x54, 0x21, 0x91, 0x62, 0x76, \
+0xe5, 0x0b, 0x30, 0xe7, 0x96, 0x9c, 0xfa, 0x45, \
+0xf1, 0x6f, 0xa7, 0x6b, 0x94, 0xa2, 0x70, 0x3d, \
+0xdd, 0x74, 0x6d, 0x4c, 0x8e, 0x8e, 0xe7, 0x01
+
+#define PRIME1 \
+0xda, 0x36, 0xde, 0xd3, 0x0a, 0x15, 0x20, 0xac, \
+0x79, 0xa7, 0xe0, 0xa6, 0x55, 0x69, 0xea, 0xcd, \
+0xf7, 0xe7, 0xf3, 0xda, 0xf6, 0xeb, 0xdc, 0xdb, \
+0x7c, 0xdd, 0x13, 0x6c, 0xaa, 0xfd, 0x6e, 0x98, \
+0x7b, 0x54, 0x25, 0xc0, 0x3b, 0x7d, 0xf9, 0xb5, \
+0x5b, 0x97, 0x2f, 0xe6, 0xc1, 0x4d, 0x7d, 0x5e, \
+0x3b, 0x51, 0x9c, 0xc6, 0xb3, 0xd4, 0x23, 0x43, \
+0xbf, 0x42, 0x98, 0x50, 0x03, 0xfb, 0x0b, 0x51
+
+#define PRIME2 \
+0xd2, 0x2b, 0x1c, 0x29, 0x83, 0x01, 0xfd, 0x41, \
+0x49, 0xfb, 0x95, 0x89, 0x7c, 0xaf, 0x81, 0x81, \
+0xce, 0xd1, 0x77, 0x5f, 0x65, 0x61, 0x61, 0x07, \
+0xd5, 0x45, 0x58, 0xc5, 0xdd, 0x73, 0x6b, 0x63, \
+0x3a, 0x82, 0x92, 0xad, 0xa6, 0x71, 0xa1, 0xcf, \
+0xe3, 0xb5, 0x10, 0x52, 0x42, 0xe5, 0x76, 0x8a, \
+0x1b, 0xca, 0x6a, 0x52, 0x9d, 0x54, 0x78, 0x07, \
+0xcf, 0x1e, 0xed, 0xe4, 0xf2, 0xf2, 0xba, 0x91
+
+#define EXPONENT1 \
+0x6e, 0xf6, 0xa4, 0x0c, 0x90, 0xfd, 0xf9, 0x65, \
+0x7b, 0x5f, 0xa0, 0xdf, 0x34, 0x63, 0xed, 0xe0, \
+0xdb, 0x05, 0x7a, 0x7d, 0x88, 0x3e, 0x9c, 0x4a, \
+0x88, 0x8e, 0x2b, 0x08, 0x81, 0x52, 0xea, 0x60, \
+0x63, 0xa6, 0x80, 0xa3, 0xe6, 0x1c, 0xc3, 0x54, \
+0x33, 0xc5, 0x07, 0xb8, 0xc1, 0xe7, 0x53, 0xaf, \
+0x0d, 0x5f, 0x0c, 0xe8, 0x06, 0x1e, 0x03, 0xe8, \
+0xb9, 0x63, 0x75, 0xec, 0x8a, 0x79, 0xa8, 0x61
+
+#define EXPONENT2 \
+0x7c, 0x6d, 0x29, 0x6f, 0x2a, 0x30, 0xb3, 0x4f, \
+0x44, 0x0d, 0xbe, 0xaa, 0x77, 0x37, 0x30, 0xe5, \
+0x39, 0x1c, 0xaa, 0x1f, 0xc0, 0x55, 0xb6, 0xac, \
+0x7c, 0x87, 0x61, 0xd7, 0x43, 0x14, 0x62, 0x2f, \
+0x8d, 0x24, 0x60, 0xd2, 0x8e, 0x08, 0x18, 0x54, \
+0x8b, 0xeb, 0x56, 0x8e, 0x5d, 0x2c, 0x9c, 0xd1, \
+0x87, 0x42, 0x7f, 0x50, 0x09, 0xf5, 0x48, 0x5a, \
+0xd7, 0x34, 0xe8, 0x82, 0xf3, 0x92, 0xe1, 0x01
+
+#define COEFFICIENT \
+0x13, 0x5f, 0x97, 0x80, 0x10, 0x66, 0x54, 0x05, \
+0x55, 0x05, 0x03, 0x37, 0x96, 0xe0, 0xc0, 0xa7, \
+0x25, 0x7b, 0x70, 0x4a, 0xd2, 0x0e, 0xce, 0x82, \
+0x42, 0x3e, 0xb9, 0x5a, 0x2d, 0x87, 0xca, 0xfa, \
+0xaa, 0x82, 0xe4, 0xed, 0xdb, 0xbf, 0xbf, 0x26, \
+0x9b, 0x50, 0x84, 0x9e, 0xb0, 0xd0, 0x10, 0xb4, \
+0x79, 0x56, 0x1c, 0xd1, 0x49, 0xa1, 0x4f, 0xf8, \
+0x52, 0x6e, 0xb4, 0x1b, 0x1a, 0x5f, 0x48, 0x0c
+
+#define PRIVATE_KEY \
+0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, 0x00, \
+0x02, 0x81, 0x81, 0x00, MODULUS, \
+0x02, 0x03, PUBLIC_EXPONENT, \
+0x02, 0x81, 0x81, 0x00, PRIVATE_EXPONENT, \
+0x02, 0x41, 0x00, PRIME1, \
+0x02, 0x41, 0x00, PRIME2, \
+0x02, 0x40, EXPONENT1, \
+0x02, 0x40, EXPONENT2, \
+0x02, 0x40, COEFFICIENT
+
+    /// @brief Public Key in PKCS#1 format
+    const uint8_t pubpkcs1[] = {
+        0x30, // tag=SEQUENCE (RSAPublicKey)
+        0x81, 0x89, // length=137
+         0x02, // tag=INTEGER (modulus -- n)
+         0x81, 0x81, // length=129
+          0x00, MODULUS,
+         0x02, // tag=INTEGER (publicExponent -- e)
+         0x03, // length=3
+          PUBLIC_EXPONENT
+    };
+    size_t pubpkcs1len = 140;
+
+    /// @brief Public Key in SubjectPublicKeyInfo format
+    const uint8_t pubspki[] = {
+        0x30, // tag=SEQUENCE (SubjectPublicKeyInfo)
+        0x81, 0x9f, // length=159
+         0x30, // tag=SEQUENCE (algorithm : AlgorithmIdentifier)
+         0x0d, // length=13
+          0x06, // tag=OBJECTID (algorithm)
+          0x09, // length=9
+           0x2a, 0x86, 0x48, 0x86,
+           0xf7, 0x0d, 0x01, 0x01,
+           0x01, // value=rsaEncryption
+          0x05, // tag=NULL (parameter)
+          0x00, // length=00
+         0x03, // tag=BIT STRING (subjectPublicKey)
+        0x81, 0x8d, // length=141
+         0x00, PUBLIC_KEY
+    };
+    size_t pubspkilen = 162;
+
+    /// @brief Public Key in DNSSEC format (length 132)
+    const uint8_t pubdns[] = {
+        0x03, // length=3
+        PUBLIC_EXPONENT,
+        MODULUS
+    };
+    size_t pubdnslen = 132;
+
+    /// @brief Private Key in PKCS#1 format
+    const uint8_t privpkcs1[] = {
+        0x30, // tag=SEQUENCE (RSAPrivateKey)
+        0x82, 0x02, 0x5c, // length=604
+         0x02, // tag=INTEGER (version : Version)
+         0x01, // length=1
+          0x00, // value=0
+         0x02, // tag=INTEGER (modulus -- n)
+         0x81, 0x81, // length=129
+          0x00, MODULUS,
+         0x02, // tag=INTEGER (publicExponent -- e)
+         0x03, // length=3
+          PUBLIC_EXPONENT,
+         0x02, // tag=INTEGER (privateExponent -- d)
+         0x81, 0x81, // length=129
+          0x00, PRIVATE_EXPONENT,
+         0x02, // tag=INTEGER (prime1 -- p)
+         0x41, // length=65
+          0x00, PRIME1,
+         0x02, // tag=INTEGER (prime2 -- q)
+         0x41, // length=65
+          0x00, PRIME2,
+         0x02, // tag=INTEGER (exponent1 -- d mod (p-1))
+         0x40, // length=64
+          EXPONENT1,
+         0x02, // tag=INTEGER (exponent2 -- d mod (q-1))
+         0x40, // length=64
+          EXPONENT2,
+         0x02, // tag=INTEGER (coefficient -- 1/q mod p)
+         0x40, // length=64
+          COEFFICIENT
+    };
+    size_t privpkcs1len = 608;
+
+    /// @brief Public Key file (SubjectPublicKeyInfo in PEM)
+    const std::string pubfile = TEST_DATA_SRCDIR "/pub.pem";
+
+    /// @brief Private Key file (PKCS#8 in PEM, password '1234')
+    const std::string privfile = TEST_DATA_SRCDIR "/pkcs8.pem";
+
+    /// @brief Certificate file (X.509 Public Key Certificate in PEM)
+    const std::string certfile = TEST_DATA_SRCDIR "/x509.pem";
+
+    /// @brief Compare data with expected value
+    /// @param data Value to compare
+    /// @param expected Expected value
+    /// @param len Length of the expected value
+    void checkData(const uint8_t* data, const uint8_t* expected,
+                   size_t len) {
+        for (size_t i = 0; i < len; ++i) {
+            ASSERT_EQ(expected[i], data[i]);
+        }
+    }
+
+    /// @brief Compare OutputBuffer with expected value
+    /// encapsulated checkData()
+    /// @param buf buffer to compare
+    /// @param expected Expected value
+    /// @param len Length of the expected value
+    void checkBuffer(const OutputBuffer& buf, const uint8_t* expected,
+                     size_t len)
+    {
+        ASSERT_EQ(len, buf.getLength());
+        checkData(static_cast<const uint8_t*>(buf.getData()), expected,
+                  len);
+    }
+
+    /// @brief Sign and verify with an instantiation of an asymmetrical
+    /// cryptography object
+    /// See @ref doRsaTest for parameters
+    void doRsaTestDirect(const std::string& data,
+                         const std::string& privfilename,
+                         const std::string& password,
+                         const std::string& pubfilename,
+                         const AsymAlgorithm asym_algorithm,
+                         const HashAlgorithm hash_algorithm,
+                         const AsymFormat sig_format,
+                         const uint8_t* expected_sig,
+                         size_t sig_len) {
+        OutputBuffer data_buf(data.size());
+        data_buf.writeData(data.c_str(), data.size());
+        OutputBuffer sig(1);
+        CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+        // Sign it
+        boost::shared_ptr<Asym> rsa_sign(crypto.createAsym(privfilename,
+                                                           password,
+                                                           asym_algorithm,
+                                                           hash_algorithm,
+                                                           PRIVATE,
+                                                           ASN1),
+                                         deleteAsym);
+        rsa_sign->update(data_buf.getData(), data_buf.getLength());
+        rsa_sign->sign(sig, sig_len, sig_format);
+
+        // Check if the signature is what we expect
+        checkBuffer(sig, expected_sig, sig_len);
+
+        // Check whether we can verify it ourselves
+        boost::shared_ptr<Asym> rsa_verify(crypto.createAsym(pubfilename,
+                                                             "",
+                                                             asym_algorithm,
+                                                             hash_algorithm,
+                                                             PUBLIC,
+                                                             ASN1),
+                                           deleteAsym);
+        rsa_verify->update(data_buf.getData(), data_buf.getLength());
+        EXPECT_TRUE(rsa_verify->verify(sig.getData(),
+                                       sig.getLength(),
+                                       sig_format));
+
+        // Change the length and check whether verification fails then
+        // Relies on the fact the length is checked first
+        EXPECT_FALSE(rsa_verify->verify(sig.getData(),
+                                        sig.getLength() - 1,
+                                        sig_format));
+        EXPECT_FALSE(rsa_verify->verify(sig.getData(),
+                                        sig.getLength() + 1,
+                                        sig_format));
+
+        // Change the sig by flipping the first octet, and check
+        // whether verification fails then
+        sig.writeUint8At(~sig[0], 0);
+        EXPECT_FALSE(rsa_verify->verify(sig.getData(),
+                                        sig.getLength(),
+                                        sig_format));
+    }
+
+    /// @brief Sign and verify with vector representation of signature
+    /// cryptography object
+    /// See @ref doRsaTest for parameters
+    void doRsaTestVector(const std::string& data,
+                         const std::string& privfilename,
+                         const std::string& password,
+                         const std::string& pubfilename,
+                         const AsymAlgorithm asym_algorithm,
+                         const HashAlgorithm hash_algorithm,
+                         const AsymFormat sig_format,
+                         const uint8_t* expected_sig,
+                         size_t sig_len) {
+        CryptoLink& crypto = CryptoLink::getCryptoLink();
+        boost::shared_ptr<Asym> rsa_sign(crypto.createAsym(privfilename,
+                                                           password,
+                                                           asym_algorithm,
+                                                           hash_algorithm,
+                                                           PRIVATE,
+                                                           ASN1),
+                                         deleteAsym);
+        rsa_sign->update(data.c_str(), data.size());
+        std::vector<uint8_t> sig = rsa_sign->sign(sig_len, sig_format);
+        ASSERT_EQ(sig_len, sig.size());
+        checkData(&sig[0], expected_sig, sig_len);
+
+        boost::shared_ptr<Asym> rsa_verify(crypto.createAsym(pubfilename,
+                                                             "",
+                                                             asym_algorithm,
+                                                             hash_algorithm,
+                                                             PUBLIC,
+                                                             ASN1),
+                                           deleteAsym);
+        rsa_verify->update(data.c_str(), data.size());
+        EXPECT_TRUE(rsa_verify->verify(&sig[0], sig.size(), sig_format));
+
+        EXPECT_FALSE(rsa_verify->verify(&sig[0], sig.size() - 1, sig_format));
+        EXPECT_FALSE(rsa_verify->verify(&sig[0], sig.size() + 1, sig_format));
+
+        sig[0] = ~sig[0];
+        EXPECT_FALSE(rsa_verify->verify(&sig[0], sig.size(), sig_format));
+    }
+
+    /// @brief Sign and verify with array representation of signature
+    /// cryptography object
+    /// See @ref doRsaTest for parameters
+    void doRsaTestArray(const std::string& data,
+                        const std::string& privfilename,
+                        const std::string& password,
+                        const std::string& pubfilename,
+                        const AsymAlgorithm asym_algorithm,
+                        const HashAlgorithm hash_algorithm,
+                        const AsymFormat sig_format,
+                        const uint8_t* expected_sig,
+                        size_t sig_len) {
+        CryptoLink& crypto = CryptoLink::getCryptoLink();
+        boost::shared_ptr<Asym> rsa_sign(crypto.createAsym(privfilename,
+                                                           password,
+                                                           asym_algorithm,
+                                                           hash_algorithm,
+                                                           PRIVATE,
+                                                           ASN1),
+                                         deleteAsym);
+        rsa_sign->update(data.c_str(), data.size());
+
+        // note: this is not exception-safe, and can leak, but
+        // if there is an unexpected exception in the code below we
+        // have more important things to fix.
+        uint8_t* sig = new uint8_t[sig_len];
+
+        rsa_sign->sign(sig, sig_len, sig_format);
+        checkData(sig, expected_sig, sig_len);
+
+        boost::shared_ptr<Asym> rsa_verify(crypto.createAsym(pubfilename,
+                                                             "",
+                                                             asym_algorithm,
+                                                             hash_algorithm,
+                                                             PUBLIC,
+                                                             ASN1),
+                                           deleteAsym);
+        rsa_verify->update(data.c_str(), data.size());
+        EXPECT_TRUE(rsa_verify->verify(sig, sig_len, sig_format));
+
+        EXPECT_FALSE(rsa_verify->verify(sig, sig_len - 1, sig_format));
+        EXPECT_FALSE(rsa_verify->verify(sig, sig_len + 1, sig_format));
+
+        sig[0] = ~sig[0];
+        EXPECT_FALSE(rsa_verify->verify(sig, sig_len, sig_format));
+
+        delete[] sig;
+    }
+
+    /// @brief Sign and verify using all variants
+    /// @param data Input value
+    /// @param privfilename Private key file name
+    /// @param password Private key password
+    /// @param pubfilename Public key file name
+    /// @param asym_algorithm Asym algorithm enum
+    /// @param hash_algorithm Hash algorithm enum
+    /// @param sig_format Signature format enum
+    /// @param expected_sig Expected value
+    /// @param sig_len Expected value length
+    void doRsaTest(const std::string& data,
+                   const std::string& privfilename,
+                   const std::string& password,
+                   const std::string& pubfilename,
+                   const AsymAlgorithm asym_algorithm,
+                   const HashAlgorithm hash_algorithm,
+                   const AsymFormat sig_format,
+                   const uint8_t* expected_sig,
+                   size_t sig_len) {
+        doRsaTestDirect(data, privfilename, password, pubfilename,
+                        asym_algorithm, hash_algorithm, sig_format,
+                        expected_sig, sig_len);
+        doRsaTestVector(data, privfilename, password, pubfilename,
+                        asym_algorithm, hash_algorithm, sig_format,
+                        expected_sig, sig_len);
+        doRsaTestArray(data, privfilename, password, pubfilename,
+                       asym_algorithm, hash_algorithm, sig_format,
+                       expected_sig, sig_len);
+    }
+}
+
+//
+// Test values
+//
+TEST(RsaTest, RSA_SHA1) {
+    const uint8_t sig_expected[] = {
+        0x16, 0xaa, 0xd3, 0x27, 0x5b, 0x22, 0xff, 0x2b,
+        0x74, 0x77, 0x7a, 0x20, 0x6c, 0xdc, 0xa6, 0xb1,
+        0x88, 0x1a, 0xb3, 0xc6, 0x5a, 0xae, 0x35, 0x3a,
+        0x04, 0x8d, 0x7f, 0x81, 0x5b, 0xef, 0xd6, 0xe2,
+        0x07, 0xee, 0xec, 0x1e, 0xf4, 0x89, 0x82, 0x6c,
+        0x2c, 0x0c, 0x25, 0x8a, 0xf0, 0x8a, 0xde, 0x6c,
+        0xf7, 0x66, 0x9d, 0xa6, 0xd5, 0x69, 0x1e, 0x47,
+        0x76, 0xf3, 0xe7, 0x47, 0x12, 0xd5, 0x92, 0x45,
+        0xb5, 0xc6, 0x50, 0x32, 0xe1, 0x25, 0xd9, 0xa1,
+        0x23, 0xd1, 0x12, 0x6d, 0x1f, 0xa8, 0x9e, 0xc3,
+        0xdb, 0x41, 0xb3, 0x13, 0x0b, 0x7a, 0xea, 0x72,
+        0xa7, 0x60, 0xc8, 0xfd, 0x89, 0xee, 0x36, 0xe4,
+        0x5c, 0xa8, 0xfa, 0x86, 0x4b, 0xcc, 0x15, 0x7d,
+        0xcb, 0x79, 0x90, 0x51, 0xa2, 0x62, 0x43, 0xc7,
+        0xe1, 0x04, 0x08, 0x13, 0x68, 0x6d, 0x7b, 0x4d,
+        0x45, 0xca, 0x3e, 0x6c, 0xba, 0x62, 0x90, 0x0a,
+        // pad
+        0x00 
+    };
+    const size_t sig_len = 128;
+    doRsaTest("Permission to use, copy, modify, and/or "
+              "distribute this software\n",
+              privfile, "1234", pubfile, RSA_, SHA1, BASIC,
+              sig_expected, sig_len);
+}
+
+TEST(RsaTest, RSA_PUB_PKCS1) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> pub_key(crypto.createAsym(pubpkcs1, pubpkcs1len,
+                                                      RSA_, SHA1,
+                                                      PUBLIC, BASIC),
+                                    deleteAsym);
+    ASSERT_TRUE(pub_key->validate());
+    EXPECT_EQ(RSA_, pub_key->getAsymAlgorithm());
+    EXPECT_EQ(SHA1, pub_key->getHashAlgorithm());
+    EXPECT_EQ(PUBLIC, pub_key->getAsymKeyKind());
+    EXPECT_EQ(1024, pub_key->getKeySize());
+    EXPECT_EQ(128, pub_key->getSignatureLength(BASIC));
+    EXPECT_EQ(128, pub_key->getSignatureLength(ASN1));
+    EXPECT_EQ(128, pub_key->getSignatureLength(DNS));
+
+    EXPECT_THROW(crypto.createAsym(pubpkcs1, pubpkcs1len - 1,
+                                   RSA_, SHA1, PUBLIC, BASIC),
+                 BadKey);
+
+    boost::shared_ptr<Asym> ref_key(crypto.createAsym(pubfile, "",
+                                                      RSA_, SHA1,
+                                                      PUBLIC, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(pub_key->compare(ref_key.get(), PUBLIC));
+    EXPECT_TRUE(ref_key->compare(pub_key.get(), PUBLIC));
+    EXPECT_FALSE(pub_key->compare(ref_key.get(), PRIVATE));
+    EXPECT_FALSE(pub_key->compare(ref_key.get(), CERT));
+
+    const std::vector<uint8_t> pubbin = ref_key->exportkey(PUBLIC, BASIC);
+    checkData(&pubbin[0], pubpkcs1, pubpkcs1len);
+}
+
+TEST(RsaTest, RSA_PUB_SPKI) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> pub_key(crypto.createAsym(pubspki, pubspkilen,
+                                                      RSA_, SHA1,
+                                                      PUBLIC, ASN1),
+                                    deleteAsym);
+    ASSERT_TRUE(pub_key->validate());
+    EXPECT_EQ(RSA_, pub_key->getAsymAlgorithm());
+    EXPECT_EQ(SHA1, pub_key->getHashAlgorithm());
+    EXPECT_EQ(PUBLIC, pub_key->getAsymKeyKind());
+    EXPECT_EQ(1024, pub_key->getKeySize());
+
+    EXPECT_THROW(crypto.createAsym(pubspki, pubspkilen - 1,
+                                   RSA_, SHA1, PUBLIC, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(pubspki, pubspkilen,
+                                   RSA_, SHA1, PUBLIC, BASIC),
+                 BadKey);
+
+    boost::shared_ptr<Asym> ref_key(crypto.createAsym(pubfile, "",
+                                                      RSA_, SHA1,
+                                                      PUBLIC, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(pub_key->compare(ref_key.get(), PUBLIC));
+    EXPECT_TRUE(ref_key->compare(pub_key.get(), PUBLIC));
+    EXPECT_FALSE(pub_key->compare(ref_key.get(), PRIVATE));
+    EXPECT_FALSE(pub_key->compare(ref_key.get(), CERT));
+
+    const std::vector<uint8_t> pubbin = ref_key->exportkey(PUBLIC, ASN1);
+    checkData(&pubbin[0], pubspki, pubspkilen);
+}
+
+TEST(RsaTest, RSA_PUB_DNS) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> pub_key(crypto.createAsym(pubdns, pubdnslen,
+                                                      RSA_, SHA1,
+                                                      PUBLIC, DNS),
+                                    deleteAsym);
+    ASSERT_TRUE(pub_key->validate());
+    EXPECT_EQ(RSA_, pub_key->getAsymAlgorithm());
+    EXPECT_EQ(SHA1, pub_key->getHashAlgorithm());
+    EXPECT_EQ(PUBLIC, pub_key->getAsymKeyKind());
+    EXPECT_EQ(1024, pub_key->getKeySize());
+
+    boost::shared_ptr<Asym> ref_key(crypto.createAsym(pubfile, "",
+                                                      RSA_, SHA1,
+                                                      PUBLIC, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(pub_key->compare(ref_key.get(), PUBLIC));
+    EXPECT_TRUE(ref_key->compare(pub_key.get(), PUBLIC));
+    EXPECT_FALSE(pub_key->compare(ref_key.get(), PRIVATE));
+    EXPECT_FALSE(pub_key->compare(ref_key.get(), CERT));
+
+    const std::vector<uint8_t> pubbin = ref_key->exportkey(PUBLIC, DNS);
+    EXPECT_EQ(pubbin.size(), pubdnslen);
+    checkData(&pubbin[0], pubdns, pubdnslen);
+
+    const std::string keystr =
+       "AwEAAbMlwgHujJFdo+pVkKIss7E1GHuhAIBPITKU0aLDGrwK7s1/IxJd+JLLh2n"
+       "9hQ8lV4hpS16TEjqTqSNkD3yqIMpWk4HpZLIgrVUlh7GFyNb+X6nNUy6vnlOk+7r"
+       "vMFOdAe63/6reevpX+99j85x3LqSXEDDBFUhrAR5XjV3r/ELh";
+    std::vector<uint8_t> keybin;
+    decodeBase64(keystr, keybin);
+    EXPECT_EQ(keybin.size(), pubdnslen);
+    checkData(&keybin[0], pubdns, pubdnslen);
+
+    const std::string dnsfile = TEST_DATA_SRCDIR "/Kexample.+005+18330.key";
+    boost::shared_ptr<Asym> dns_key(crypto.createAsym(dnsfile, "",
+                                                      RSA_, SHA1,
+                                                      PUBLIC, DNS),
+                                    deleteAsym);
+    EXPECT_TRUE(dns_key->compare(ref_key.get(), PUBLIC));
+    EXPECT_TRUE(ref_key->compare(dns_key.get(), PUBLIC));
+}
+
+TEST(RsaTest, RSA_PRIV_PKCS1) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> priv_key(crypto.createAsym(privpkcs1, privpkcs1len,
+                                                      RSA_, SHA1,
+                                                      PRIVATE, BASIC),
+                                    deleteAsym);
+    ASSERT_TRUE(priv_key->validate());
+    EXPECT_EQ(RSA_, priv_key->getAsymAlgorithm());
+    EXPECT_EQ(SHA1, priv_key->getHashAlgorithm());
+    EXPECT_EQ(PRIVATE, priv_key->getAsymKeyKind());
+    EXPECT_EQ(1024, priv_key->getKeySize());
+    EXPECT_EQ(128, priv_key->getSignatureLength(BASIC));
+    EXPECT_EQ(128, priv_key->getSignatureLength(ASN1));
+    EXPECT_EQ(128, priv_key->getSignatureLength(DNS));
+
+    EXPECT_THROW(crypto.createAsym(privpkcs1, privpkcs1len - 1,
+                                   RSA_, SHA1, PRIVATE, BASIC),
+                 BadKey);
+
+    boost::shared_ptr<Asym> ref_key(crypto.createAsym(privfile, "1234",
+                                                      RSA_, SHA1,
+                                                      PRIVATE, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(priv_key->compare(ref_key.get(), PRIVATE));
+    EXPECT_TRUE(ref_key->compare(priv_key.get(), PRIVATE));
+    EXPECT_TRUE(priv_key->compare(ref_key.get(), PUBLIC));
+    EXPECT_FALSE(priv_key->compare(ref_key.get(), CERT));
+
+    const std::vector<uint8_t> privbin = ref_key->exportkey(PRIVATE, BASIC);
+    checkData(&privbin[0], privpkcs1, privpkcs1len);
+
+    boost::shared_ptr<Asym> pub_key(crypto.createAsym(pubfile, "",
+                                                      RSA_, SHA1,
+                                                      PUBLIC, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(priv_key->compare(pub_key.get(), PUBLIC));
+    EXPECT_TRUE(pub_key->compare(priv_key.get(), PUBLIC));
+}
+
+TEST(RsaTest, RSA_PRIV_PKCS) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> ref_key(crypto.createAsym(privfile, "1234",
+                                                      RSA_, SHA1,
+                                                      PRIVATE, ASN1),
+                                    deleteAsym);
+#ifndef WITH_BOTAN
+    const std::string pkcs1file = TEST_DATA_SRCDIR "/priv.pem";
+    boost::shared_ptr<Asym> pkcs1_key(crypto.createAsym(pkcs1file, "",
+                                                        RSA_, SHA1,
+                                                        PRIVATE, BASIC),
+                                    deleteAsym);
+    EXPECT_TRUE(pkcs1_key->validate());
+    EXPECT_TRUE(pkcs1_key->compare(ref_key.get(), PRIVATE));
+    EXPECT_TRUE(ref_key->compare(pkcs1_key.get(), PRIVATE));
+#endif
+
+    // PKCS#8 without encryption
+    const std::string nefile = TEST_DATA_SRCDIR "/pkcs8ne.pem";
+    boost::shared_ptr<Asym> ne_key(crypto.createAsym(nefile, "",
+                                                     RSA_, SHA1,
+                                                     PRIVATE, ASN1),
+                                   deleteAsym);
+    EXPECT_TRUE(ne_key->validate());
+    EXPECT_TRUE(ne_key->compare(ref_key.get(), PRIVATE));
+    EXPECT_TRUE(ref_key->compare(ne_key.get(), PRIVATE));
+}
+
+TEST(RsaTest, RSA_PRIV_DNS) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    const std::string privdnsfile =
+        TEST_DATA_SRCDIR "/Kexample.+005+18330.private";
+    boost::shared_ptr<Asym> dns_key(crypto.createAsym(privdnsfile, "",
+                                                      RSA_, SHA1,
+                                                      PRIVATE, DNS),
+                                    deleteAsym);
+    EXPECT_TRUE(dns_key->validate());
+    EXPECT_EQ(RSA_, dns_key->getAsymAlgorithm());
+    EXPECT_EQ(SHA1, dns_key->getHashAlgorithm());
+    EXPECT_EQ(PRIVATE, dns_key->getAsymKeyKind());
+    EXPECT_EQ(1024, dns_key->getKeySize());
+
+#ifndef WITH_BOTAN
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, SHA1, PRIVATE, BASIC),
+                 BadKey);
+#endif
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, SHA1, PRIVATE, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, MD5, PRIVATE, DNS),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, SHA256, PRIVATE, DNS),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, SHA512, PRIVATE, DNS),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, SHA224, PRIVATE, DNS),
+                 UnsupportedAlgorithm);
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, SHA384, PRIVATE, DNS),
+                 UnsupportedAlgorithm);
+
+    boost::shared_ptr<Asym> ref_key(crypto.createAsym(privfile, "1234",
+                                                      RSA_, SHA1,
+                                                      PRIVATE, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(dns_key->compare(ref_key.get(), PRIVATE));
+    EXPECT_TRUE(ref_key->compare(dns_key.get(), PRIVATE));
+    EXPECT_TRUE(dns_key->compare(ref_key.get(), PUBLIC));
+    EXPECT_FALSE(dns_key->compare(ref_key.get(), CERT));
+
+    char tempname[] = "/tmp/privateXXXXXX";
+    const std::string testfile = mktemp(tempname);
+    ref_key->exportkey(testfile, "", PRIVATE, DNS);
+    FILE* fp;
+    fp = fopen(testfile.c_str(), "r");
+    ASSERT_TRUE(fp != NULL);
+    std::vector<char> testbuf(1024);
+    size_t tcc = fread(&testbuf[0], 1024, 1, fp);
+    testbuf.resize(tcc);
+    fclose(fp);
+    fp = fopen(privdnsfile.c_str(), "r");
+    ASSERT_TRUE(fp != NULL);
+    std::vector<char> refbuf(1024);
+    int rcc = fread(&refbuf[0], 1024, 1, fp);
+    refbuf.resize(rcc);
+    fclose(fp);
+    EXPECT_EQ(rcc, tcc);
+    EXPECT_TRUE(testbuf == refbuf);
+    unlink(testfile.c_str());
+}
+
+TEST(RsaTest, CERTIFICATE) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> from_file(crypto.createAsym(certfile, "",
+                                                        RSA_, SHA1,
+                                                        CERT, ASN1),
+                                      deleteAsym);
+    EXPECT_TRUE(from_file->validate());
+    EXPECT_EQ(RSA_, from_file->getAsymAlgorithm());
+    EXPECT_EQ(SHA1, from_file->getHashAlgorithm());
+    EXPECT_EQ(CERT, from_file->getAsymKeyKind());
+    EXPECT_EQ(1024, from_file->getKeySize());
+
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA1, PUBLIC, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA1, PRIVATE, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA1, CERT, BASIC),
+                 UnsupportedAlgorithm);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA1, CERT, DNS),
+                 UnsupportedAlgorithm);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, MD5, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA224, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA256, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA384, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA512, CERT, ASN1),
+                 BadKey);
+
+    boost::shared_ptr<Asym> pub_key(crypto.createAsym(pubfile, "",
+                                                      RSA_, SHA1,
+                                                      PUBLIC, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(from_file->compare(pub_key.get(), PUBLIC));
+    EXPECT_TRUE(pub_key->compare(from_file.get(), PUBLIC));
+    EXPECT_FALSE(from_file->compare(pub_key.get(), PRIVATE));
+    EXPECT_FALSE(pub_key->compare(from_file.get(), PRIVATE));
+
+    std::vector<uint8_t> certbin = from_file->exportkey(CERT, ASN1);
+    boost::shared_ptr<Asym> from_bin(crypto.createAsym(&certbin[0],
+                                                       certbin.size(),
+                                                       RSA_, SHA1,
+                                                       CERT, ASN1),
+                                     deleteAsym);
+    EXPECT_TRUE(from_bin->validate());
+    EXPECT_TRUE(from_file->compare(from_bin.get(), PUBLIC));
+    EXPECT_TRUE(from_bin->compare(from_file.get(), PUBLIC));
+    EXPECT_TRUE(from_file->compare(from_bin.get(), CERT));
+    EXPECT_TRUE(from_bin->compare(from_file.get(), CERT));
+
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size() - 1,
+                                   RSA_, SHA1, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, SHA1, CERT, BASIC),
+                 UnsupportedAlgorithm);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, SHA1, CERT, DNS),
+                 UnsupportedAlgorithm);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, MD5, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, SHA224, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, SHA256, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, SHA384, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, SHA512, CERT, ASN1),
+                 BadKey);
+
+    certbin[certbin.size() - 1]  = ~certbin[certbin.size() - 1];
+    boost::shared_ptr<Asym> bad_bin(crypto.createAsym(&certbin[0],
+                                                      certbin.size(),
+                                                      RSA_, SHA1,
+                                                      CERT, ASN1),
+                                    deleteAsym);
+    EXPECT_FALSE(bad_bin->validate());
+}
+
+//
+// Multiple signatures
+//
+TEST(RsaTest, doubleSign) {
+    std::string data = "Kea provides DHCPv4 and DHCPv6 servers";
+    OutputBuffer data_buf(data.size());
+    data_buf.writeData(data.c_str(), data.size());
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+    // Sign it
+    boost::shared_ptr<Asym> rsa_sign(crypto.createAsym(privfile, "1234",
+                                                       RSA_, SHA1,
+                                                       PRIVATE, ASN1),
+                                     deleteAsym);
+    ASSERT_TRUE(rsa_sign);
+
+    OutputBuffer sig1(1);
+    size_t sig1_len = rsa_sign->getSignatureLength(BASIC);
+    EXPECT_EQ(128, sig1_len);
+    rsa_sign->update(data_buf.getData(), data_buf.getLength());
+    rsa_sign->sign(sig1, sig1_len, BASIC);
+    ASSERT_EQ(sig1_len, sig1.getLength());
+
+    // Clear state
+    rsa_sign->clear();
+
+    // Sign it again
+    OutputBuffer sig2(1);
+    size_t sig2_len = rsa_sign->getSignatureLength(BASIC);
+    EXPECT_EQ(128, sig2_len);
+    rsa_sign->update(data_buf.getData(), data_buf.getLength());
+    rsa_sign->sign(sig2, sig2_len, BASIC);
+    EXPECT_EQ(sig2_len, sig2.getLength());
+
+    // Compare
+    ASSERT_EQ(sig1_len, sig2_len);
+    EXPECT_TRUE(std::memcmp(sig1.getData(), sig2.getData(), sig1_len) == 0);
+}
+
+//
+// Multiple verifies
+//
+TEST(RsaTest, doubleVerify) {
+    std::string data = "Kea provides DHCPv4 and DHCPv6 servers";
+    OutputBuffer data_buf(data.size());
+    data_buf.writeData(data.c_str(), data.size());
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+    // Sign it
+    boost::shared_ptr<Asym> rsa_sign(crypto.createAsym(privfile, "1234",
+                                                       RSA_, SHA1,
+                                                       PRIVATE, ASN1),
+                                     deleteAsym);
+    ASSERT_TRUE(rsa_sign);
+
+    OutputBuffer sig(1);
+    size_t sig_len = rsa_sign->getSignatureLength(BASIC);
+    EXPECT_EQ(128, sig_len);
+    rsa_sign->update(data_buf.getData(), data_buf.getLength());
+    rsa_sign->sign(sig, sig_len, BASIC);
+    EXPECT_EQ(sig_len, sig.getLength());
+
+    // Verify
+    boost::shared_ptr<Asym> rsa_verify(crypto.createAsym(pubfile, "",
+                                                         RSA_, SHA1,
+                                                         PUBLIC, ASN1),
+                                       deleteAsym);
+    ASSERT_TRUE(rsa_verify);
+
+    rsa_verify->update(data_buf.getData(), data_buf.getLength());
+    EXPECT_TRUE(rsa_verify->verify(sig.getData(), sig.getLength(), BASIC));
+
+    // Clear state
+    rsa_verify->clear();
+
+    // Verify again
+    rsa_verify->update(data_buf.getData(), data_buf.getLength());
+    EXPECT_TRUE(rsa_verify->verify(sig.getData(), sig.getLength(), BASIC));
+}
diff --git a/src/lib/cryptolink/tests/testdata/eccert.pem b/src/lib/cryptolink/tests/testdata/eccert.pem
new file mode 100644 (file)
index 0000000..8916ff3
--- /dev/null
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB2jCCAX+gAwIBAgIJALJtRCtzuGsCMAoGCCqGSM49BAMCMEkxJDAiBgNVBAoM
+G0ludGVybmV0IFN5c3RlbXMgQ29uc29ydGl1bTEhMB8GA1UEAwwYc2VjdXJlIERI
+Q1B2NiBLZWEgc2VydmVyMB4XDTE1MDYwNzAzNTU0OVoXDTE2MDYwNjAzNTU0OVow
+STEkMCIGA1UECgwbSW50ZXJuZXQgU3lzdGVtcyBDb25zb3J0aXVtMSEwHwYDVQQD
+DBhzZWN1cmUgREhDUHY2IEtlYSBzZXJ2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMB
+BwNCAASgAH2PNkhG5DQwiS9St+qtX5sfmav7ETctzPXraS+erMVvZVKj02IIGkEY
+5SC9qKKy/VT0ixfDq9Uzr6Y6bUUlo1AwTjAdBgNVHQ4EFgQUPRv03Ug/4eBh0Eya
+pXlRvSkGQ+wwHwYDVR0jBBgwFoAUPRv03Ug/4eBh0EyapXlRvSkGQ+wwDAYDVR0T
+BAUwAwEB/zAKBggqhkjOPQQDAgNJADBGAiEAtzJGSIZWaqTzwGoSk7FDJ+w2ODuU
+nftHstS0k1GLJl0CIQD5/5tRjAIm57R2nEjBEAvWy01jpNF97/1RXGbPt/deoA==
+-----END CERTIFICATE-----
diff --git a/src/lib/cryptolink/tests/testdata/ecpkcs8.pem b/src/lib/cryptolink/tests/testdata/ecpkcs8.pem
new file mode 100644 (file)
index 0000000..d17b0e5
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIHVMEAGCSqGSIb3DQEFDTAzMBsGCSqGSIb3DQEFDDAOBAj+ar59eXmOVgICCAAw
+FAYIKoZIhvcNAwcECHzRG/4+8LHeBIGQQc0G8p+hrVJgby9CM3F9JUnsdbw75zao
+eFPDnXYzqWqZM2p8/OWetFfK2bhSnmzhNuheVFZ8a4ODnk7CvmQMzCFE14TIx5q7
+szaazLlqTkuQNrwvGjhpZMMVtaFIvRyapVECXO1PzbXkgFPOuUpKY407T22fj4cD
+OfXsRIkLqRIHrUlpMub8unF8mcjsQmKb
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/src/lib/cryptolink/tests/testdata/ecpkcs8ne.pem b/src/lib/cryptolink/tests/testdata/ecpkcs8ne.pem
new file mode 100644 (file)
index 0000000..12e5823
--- /dev/null
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgi4bkYMdLmFN5+PYA
+K550QeLhyOtHooherZNmjHUBQK+hRANCAASgAH2PNkhG5DQwiS9St+qtX5sfmav7
+ETctzPXraS+erMVvZVKj02IIGkEY5SC9qKKy/VT0ixfDq9Uzr6Y6bUUl
+-----END PRIVATE KEY-----
diff --git a/src/lib/cryptolink/tests/testdata/ecpub.pem b/src/lib/cryptolink/tests/testdata/ecpub.pem
new file mode 100644 (file)
index 0000000..b9f897e
--- /dev/null
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoAB9jzZIRuQ0MIkvUrfqrV+bH5mr
++xE3Lcz162kvnqzFb2VSo9NiCBpBGOUgvaiisv1U9IsXw6vVM6+mOm1FJQ==
+-----END PUBLIC KEY-----