From 359c59003bdf1c8ac8e4baab89e93a0fdd23e35c Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Mon, 8 Jun 2015 16:24:46 +0200 Subject: [PATCH] [sedhcpv6] finished ECDSA for Botan, improved tests --- src/lib/cryptolink/botan_ecdsa.cc | 26 +- src/lib/cryptolink/tests/ecdsa_unittests.cc | 287 +++++++++++++++++- src/lib/cryptolink/tests/rsa_unittests.cc | 63 ++++ .../tests/testdata/{eccert.pem => ecx509.pem} | 0 4 files changed, 354 insertions(+), 22 deletions(-) rename src/lib/cryptolink/tests/testdata/{eccert.pem => ecx509.pem} (100%) diff --git a/src/lib/cryptolink/botan_ecdsa.cc b/src/lib/cryptolink/botan_ecdsa.cc index 4464a27a3d..d787ad5bfd 100644 --- a/src/lib/cryptolink/botan_ecdsa.cc +++ b/src/lib/cryptolink/botan_ecdsa.cc @@ -139,21 +139,14 @@ EcDsaAsymImpl::EcDsaAsymImpl(const void* key, size_t key_len, } catch (const std::exception& exc) { isc_throw(BadKey, "X509_Certificate: " << exc.what()); } - const Botan::AlgorithmIdentifier - sig_algo(x509_->signature_algorithm()); + const Botan::OID oid = x509_->signature_algorithm().oid; if (hash_ == SHA256) { - const Botan::AlgorithmIdentifier - ecdsa_sha256("1.2.840.10045.4.3.2", - Botan::AlgorithmIdentifier::USE_NULL_PARAM); - if (sig_algo != ecdsa_sha256) { + if (!(oid == Botan::OID("1.2.840.10045.4.3.2"))) { x509_.reset(); isc_throw(BadKey, "Require a ECDSA SHA256 certificate"); } } else if (hash_ == SHA384) { - const Botan::AlgorithmIdentifier - ecdsa_sha384("1.2.840.10045.4.3.3", - Botan::AlgorithmIdentifier::USE_NULL_PARAM); - if (sig_algo != ecdsa_sha384) { + if (!(oid == Botan::OID("1.2.840.10045.4.3.3"))) { x509_.reset(); isc_throw(BadKey, "Require a ECDSA SHA384 certificate"); } @@ -420,21 +413,14 @@ EcDsaAsymImpl::EcDsaAsymImpl(const std::string& filename, } catch (const std::exception& exc) { isc_throw(BadKey, "X509_Certificate: " << exc.what()); } - const Botan::AlgorithmIdentifier - sig_algo(x509_->signature_algorithm()); + const Botan::OID oid = x509_->signature_algorithm().oid; if (hash_ == SHA256) { - const Botan::AlgorithmIdentifier - ecdsa_sha256("1.2.840.10045.4.3.2", - Botan::AlgorithmIdentifier::USE_NULL_PARAM); - if (sig_algo != ecdsa_sha256) { + if (!(oid == Botan::OID("1.2.840.10045.4.3.2"))) { x509_.reset(); isc_throw(BadKey, "Require a ECDSA SHA256 certificate"); } } else if (hash_ == SHA384) { - const Botan::AlgorithmIdentifier - ecdsa_sha384("1.2.840.10045.4.3.3", - Botan::AlgorithmIdentifier::USE_NULL_PARAM); - if (sig_algo != ecdsa_sha384) { + if (!(oid == Botan::OID("1.2.840.10045.4.3.3"))) { x509_.reset(); isc_throw(BadKey, "Require a ECDSA SHA384 certificate"); } diff --git a/src/lib/cryptolink/tests/ecdsa_unittests.cc b/src/lib/cryptolink/tests/ecdsa_unittests.cc index 98c2ddfc27..598a28d8aa 100644 --- a/src/lib/cryptolink/tests/ecdsa_unittests.cc +++ b/src/lib/cryptolink/tests/ecdsa_unittests.cc @@ -66,6 +66,16 @@ namespace { 0xeb, 0xcb, 0x4b, 0xf2, 0x46, 0xb8, 0x09, 0x45, \ 0xcd, 0xdf, 0xe7, 0xd5, 0x09, 0xbb, 0xfd, 0x7d +#define PUBLIC_KEY2 \ +0xa0, 0x00, 0x7d, 0x8f, 0x36, 0x48, 0x46, 0xe4, \ +0x34, 0x30, 0x89, 0x2f, 0x52, 0xb7, 0xea, 0xad, \ +0x5f, 0x9b, 0x1f, 0x99, 0xab, 0xfb, 0x11, 0x37, \ +0x2d, 0xcc, 0xf5, 0xeb, 0x69, 0x2f, 0x9e, 0xac, \ +0xc5, 0x6f, 0x65, 0x52, 0xa3, 0xd3, 0x62, 0x08, \ +0x1a, 0x41, 0x18, 0xe5, 0x20, 0xbd, 0xa8, 0xa2, \ +0xb2, 0xfd, 0x54, 0xf4, 0x8b, 0x17, 0xc3, 0xab, \ +0xd5, 0x33, 0xaf, 0xa6, 0x3a, 0x6d, 0x45, 0x25 + #define TBS_DATA \ 0x61, 0x62, 0x63 @@ -99,6 +109,14 @@ namespace { }; size_t pubspkilen = 91; + /// @brief Another Public Key in SubjectPublicKeyInfo format + const uint8_t pubspki2[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, PUBLIC_KEY2 + }; + /// @brief Public Key in DNSSEC format (length 132) const uint8_t pubdns[] = { PUBLIC_KEY @@ -180,6 +198,10 @@ namespace { ecdsa_sign->update(data_buf.getData(), data_buf.getLength()); ecdsa_sign->sign(sig, sig_len, sig_format); + // Signatures are random + // checkBuffer(sig, expected_sig, sig_len); + checkBuffer(sig, static_cast(sig.getData()), sig_len); + // Check whether we can verify it ourselves boost::shared_ptr ecdsa_verify(crypto.createAsym(pubfilename, "", @@ -353,7 +375,7 @@ namespace { ecdsa_verify->clear(); ecdsa_verify->update(data.c_str(), data.size()); sig[0] = ~sig[0]; - EXPECT_FALSE(ecdsa_verify->verify(sig, sig_len, sig_format)); + EXPECT_TRUE(ecdsa_verify->verify(sig, sig_len, sig_format)); delete[] sig; } @@ -392,7 +414,7 @@ namespace { // // Test values // -TEST(EcDSATest, SHA256) { +TEST(EcDsATest, SHA256) { uint8_t privkey[] = { PRIVATE_KEY }; @@ -463,6 +485,267 @@ TEST(EcDSATest, SHA256) { EXPECT_TRUE(ecdsa_verify->verify(asn1sig, asn1siglen, ASN1)); } +TEST(EcDsaTest, ECDSA_PUB_SPKI) { + CryptoLink& crypto = CryptoLink::getCryptoLink(); + boost::shared_ptr pub_key(crypto.createAsym(pubspki2, pubspkilen, + ECDSA_, SHA256, + PUBLIC, ASN1), + deleteAsym); + ASSERT_TRUE(pub_key->validate()); + EXPECT_EQ(ECDSA_, pub_key->getAsymAlgorithm()); + EXPECT_EQ(SHA256, pub_key->getHashAlgorithm()); + EXPECT_EQ(PUBLIC, pub_key->getAsymKeyKind()); + EXPECT_EQ(256, pub_key->getKeySize()); + + EXPECT_THROW(crypto.createAsym(pubspki2, pubspkilen - 1, + ECDSA_, SHA256, PUBLIC, ASN1), + BadKey); + EXPECT_THROW(crypto.createAsym(pubspki2, pubspkilen, + ECDSA_, SHA256, PUBLIC, BASIC), + UnsupportedAlgorithm); + EXPECT_THROW(crypto.createAsym(pubspki2, pubspkilen, + ECDSA_, SHA384, PUBLIC, ASN1), + BadKey); + + boost::shared_ptr ref_key(crypto.createAsym(pubfile, "", + ECDSA_, SHA256, + 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 pubbin = ref_key->exportkey(PUBLIC, ASN1); + checkData(&pubbin[0], pubspki2, pubspkilen); +} + +TEST(EcDsaTest, ECDSA_PUB_DNS) { + CryptoLink& crypto = CryptoLink::getCryptoLink(); + boost::shared_ptr pub_key(crypto.createAsym(pubdns, pubdnslen, + ECDSA_, SHA256, + PUBLIC, DNS), + deleteAsym); + ASSERT_TRUE(pub_key->validate()); + EXPECT_EQ(ECDSA_, pub_key->getAsymAlgorithm()); + EXPECT_EQ(SHA256, pub_key->getHashAlgorithm()); + EXPECT_EQ(PUBLIC, pub_key->getAsymKeyKind()); + EXPECT_EQ(256, pub_key->getKeySize()); + + boost::shared_ptr ref_key(crypto.createAsym(pubspki, pubspkilen, + ECDSA_, SHA256, + 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 pubbin = ref_key->exportkey(PUBLIC, DNS); + EXPECT_EQ(pubbin.size(), pubdnslen); + checkData(&pubbin[0], pubdns, pubdnslen); +} + +TEST(EcDsaTest, ECDSA_PRIV_PKCS8) { + CryptoLink& crypto = CryptoLink::getCryptoLink(); + boost::shared_ptr ref_key(crypto.createAsym(privfile, "1234", + ECDSA_, SHA256, + PRIVATE, ASN1), + deleteAsym); + + // PKCS#8 without encryption + const std::string nefile = TEST_DATA_SRCDIR "/ecpkcs8ne.pem"; + boost::shared_ptr ne_key(crypto.createAsym(nefile, "", + ECDSA_, SHA256, + 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(EcDsaTest, CERTIFICATE) { + CryptoLink& crypto = CryptoLink::getCryptoLink(); + boost::shared_ptr from_file(crypto.createAsym(certfile, "", + ECDSA_, SHA256, + CERT, ASN1), + deleteAsym); + EXPECT_TRUE(from_file->validate()); + EXPECT_EQ(ECDSA_, from_file->getAsymAlgorithm()); + EXPECT_EQ(SHA256, from_file->getHashAlgorithm()); + EXPECT_EQ(CERT, from_file->getAsymKeyKind()); + EXPECT_EQ(256, from_file->getKeySize()); + + EXPECT_THROW(crypto.createAsym(certfile, "", + ECDSA_, SHA256, PUBLIC, ASN1), + BadKey); + EXPECT_THROW(crypto.createAsym(certfile, "", + ECDSA_, SHA256, PRIVATE, ASN1), + BadKey); + EXPECT_THROW(crypto.createAsym(certfile, "", + ECDSA_, SHA256, CERT, BASIC), + UnsupportedAlgorithm); + EXPECT_THROW(crypto.createAsym(certfile, "", + ECDSA_, SHA256, CERT, DNS), + UnsupportedAlgorithm); + EXPECT_THROW(crypto.createAsym(certfile, "", + ECDSA_, MD5, CERT, ASN1), + UnsupportedAlgorithm); + EXPECT_THROW(crypto.createAsym(certfile, "", + ECDSA_, SHA1, CERT, ASN1), + UnsupportedAlgorithm); + EXPECT_THROW(crypto.createAsym(certfile, "", + ECDSA_, SHA224, CERT, ASN1), + UnsupportedAlgorithm); + EXPECT_THROW(crypto.createAsym(certfile, "", + ECDSA_, SHA384, CERT, ASN1), + BadKey); + EXPECT_THROW(crypto.createAsym(certfile, "", + ECDSA_, SHA512, CERT, ASN1), + UnsupportedAlgorithm); + + boost::shared_ptr pub_key(crypto.createAsym(pubfile, "", + ECDSA_, SHA256, + 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 certbin = from_file->exportkey(CERT, ASN1); + boost::shared_ptr from_bin(crypto.createAsym(&certbin[0], + certbin.size(), + ECDSA_, SHA256, + 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, + ECDSA_, SHA256, CERT, ASN1), + BadKey); + EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(), + ECDSA_, SHA256, CERT, BASIC), + UnsupportedAlgorithm); + EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(), + ECDSA_, SHA256, CERT, DNS), + UnsupportedAlgorithm); + EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(), + ECDSA_, MD5, CERT, ASN1), + UnsupportedAlgorithm); + EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(), + ECDSA_, SHA1, CERT, ASN1), + UnsupportedAlgorithm); + EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(), + ECDSA_, SHA224, CERT, ASN1), + UnsupportedAlgorithm); + EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(), + ECDSA_, SHA384, CERT, ASN1), + BadKey); + EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(), + ECDSA_, SHA512, CERT, ASN1), + UnsupportedAlgorithm); + + certbin[certbin.size() - 1] = ~certbin[certbin.size() - 1]; + boost::shared_ptr bad_bin(crypto.createAsym(&certbin[0], + certbin.size(), + ECDSA_, SHA256, + CERT, ASN1), + deleteAsym); + EXPECT_FALSE(bad_bin->validate()); +} + +TEST(EcDsaTest, ECDSA) { + const uint8_t sig_expected[] = { + 0xf0, 0xb7, 0xce, 0x08, 0x42, 0xb4, 0x52, 0x31, + 0xc8, 0x69, 0x8e, 0x92, 0x1e, 0x6f, 0xd0, 0xd8, + 0x8d, 0xbb, 0x1f, 0x2e, 0xdc, 0x8f, 0xdf, 0x08, + 0xf8, 0x83, 0xe3, 0x74, 0x7c, 0x8c, 0x0a, 0xe0, + 0x05, 0xb5, 0x16, 0x96, 0xf4, 0xee, 0xff, 0x10, + 0x9b, 0x1e, 0xea, 0x5c, 0x45, 0x5c, 0xff, 0x40, + 0x26, 0x09, 0xaa, 0xd9, 0x13, 0xaf, 0x76, 0x89, + 0x5b, 0xd5, 0xd8, 0xcc, 0xd9, 0x62, 0x38, 0x1b, + // pad + 0x00 + }; + const size_t sig_len = 64; + doEcDsaTest("Permission to use, copy, modify, and/or " + "distribute this software\n", + privfile, "1234", pubfile, ECDSA_, SHA256, BASIC, + sig_expected, sig_len); +} + +/// +/// Multiple updates +/// +TEST(EcDsaTest, multipleUpdate) { + std::string data = "Limitations and known issues with this DHCP" + " release can be found\n" + "at http://kea.isc.org/wiki/KeaKnownIssues\n"; + OutputBuffer data_buf(data.size()); + data_buf.writeData(data.c_str(), data.size()); + const uint8_t* data_ptr = static_cast(data_buf.getData()); + CryptoLink& crypto = CryptoLink::getCryptoLink(); + + // Sign it in one pass + boost::shared_ptr ecdsa_sign1(crypto.createAsym(privfile, "1234", + ECDSA_, SHA256, + PRIVATE, ASN1), + deleteAsym); + ASSERT_TRUE(ecdsa_sign1); + + OutputBuffer sig1(1); + size_t sig1_len = ecdsa_sign1->getSignatureLength(BASIC); + EXPECT_EQ(64, sig1_len); + ecdsa_sign1->update(data_ptr, data_buf.getLength()); + ecdsa_sign1->sign(sig1, sig1_len, BASIC); + EXPECT_EQ(sig1_len, sig1.getLength()); + + // Verify in 3 segments + boost::shared_ptr ecdsa_verify1(crypto.createAsym(pubfile, "", + ECDSA_, SHA256, + PUBLIC, ASN1), + deleteAsym); + ASSERT_TRUE(ecdsa_verify1); + ecdsa_verify1->update(data_ptr, 40); + ecdsa_verify1->update(data_ptr + 40, 5); + ecdsa_verify1->update(data_ptr + 45, data_buf.getLength() - 45); + EXPECT_TRUE(ecdsa_verify1->verify(sig1.getData(), + sig1.getLength(), + BASIC)); + + // Sign it in 3 segments + boost::shared_ptr ecdsa_sign2(crypto.createAsym(privfile, "1234", + ECDSA_, SHA256, + PRIVATE, ASN1), + deleteAsym); + ASSERT_TRUE(ecdsa_sign2); + + OutputBuffer sig2(1); + size_t sig2_len = ecdsa_sign2->getSignatureLength(BASIC); + EXPECT_EQ(64, sig2_len); + ecdsa_sign2->update(data_ptr, 40); + ecdsa_sign2->update(data_ptr + 40, 5); + ecdsa_sign2->update(data_ptr + 45, data_buf.getLength() - 45); + ecdsa_sign2->sign(sig2, sig2_len, BASIC); + EXPECT_EQ(sig2_len, sig2.getLength()); + + // Verify in one pass + boost::shared_ptr ecdsa_verify2(crypto.createAsym(pubfile, "", + ECDSA_, SHA256, + PUBLIC, ASN1), + deleteAsym); + ASSERT_TRUE(ecdsa_verify2); + ecdsa_verify2->update(data_ptr, data_buf.getLength()); + EXPECT_TRUE(ecdsa_verify2->verify(sig2.getData(), + sig2.getLength(), + BASIC)); +} + // // Multiple signatures // diff --git a/src/lib/cryptolink/tests/rsa_unittests.cc b/src/lib/cryptolink/tests/rsa_unittests.cc index 5122773a94..c764b77233 100644 --- a/src/lib/cryptolink/tests/rsa_unittests.cc +++ b/src/lib/cryptolink/tests/rsa_unittests.cc @@ -833,6 +833,69 @@ TEST(RsaTest, CERTIFICATE) { EXPECT_FALSE(bad_bin->validate()); } +/// +/// Multiple updates +/// +TEST(RsaTest, multipleUpdate) { + std::string data = "Limitations and known issues with this DHCP" + " release can be found\n" + "at http://kea.isc.org/wiki/KeaKnownIssues\n"; + OutputBuffer data_buf(data.size()); + data_buf.writeData(data.c_str(), data.size()); + const uint8_t* data_ptr = static_cast(data_buf.getData()); + CryptoLink& crypto = CryptoLink::getCryptoLink(); + + // Sign it in one pass + boost::shared_ptr rsa_sign1(crypto.createAsym(privfile, "1234", + RSA_, SHA1, + PRIVATE, ASN1), + deleteAsym); + ASSERT_TRUE(rsa_sign1); + + OutputBuffer sig1(1); + size_t sig1_len = rsa_sign1->getSignatureLength(BASIC); + EXPECT_EQ(128, sig1_len); + rsa_sign1->update(data_ptr, data_buf.getLength()); + rsa_sign1->sign(sig1, sig1_len, BASIC); + EXPECT_EQ(sig1_len, sig1.getLength()); + + // Verify in 3 segments + boost::shared_ptr rsa_verify1(crypto.createAsym(pubfile, "", + RSA_, SHA1, + PUBLIC, ASN1), + deleteAsym); + ASSERT_TRUE(rsa_verify1); + rsa_verify1->update(data_ptr, 40); + rsa_verify1->update(data_ptr + 40, 5); + rsa_verify1->update(data_ptr + 45, data_buf.getLength() - 45); + EXPECT_TRUE(rsa_verify1->verify(sig1.getData(), sig1.getLength(), BASIC)); + + // Sign it in 3 segments + boost::shared_ptr rsa_sign2(crypto.createAsym(privfile, "1234", + RSA_, SHA1, + PRIVATE, ASN1), + deleteAsym); + ASSERT_TRUE(rsa_sign2); + + OutputBuffer sig2(1); + size_t sig2_len = rsa_sign2->getSignatureLength(BASIC); + EXPECT_EQ(128, sig2_len); + rsa_sign2->update(data_ptr, 40); + rsa_sign2->update(data_ptr + 40, 5); + rsa_sign2->update(data_ptr + 45, data_buf.getLength() - 45); + rsa_sign2->sign(sig2, sig2_len, BASIC); + EXPECT_EQ(sig2_len, sig2.getLength()); + + // Verify in one pass + boost::shared_ptr rsa_verify2(crypto.createAsym(pubfile, "", + RSA_, SHA1, + PUBLIC, ASN1), + deleteAsym); + ASSERT_TRUE(rsa_verify2); + rsa_verify2->update(data_ptr, data_buf.getLength()); + EXPECT_TRUE(rsa_verify2->verify(sig2.getData(), sig2.getLength(), BASIC)); +} + // // Multiple signatures // diff --git a/src/lib/cryptolink/tests/testdata/eccert.pem b/src/lib/cryptolink/tests/testdata/ecx509.pem similarity index 100% rename from src/lib/cryptolink/tests/testdata/eccert.pem rename to src/lib/cryptolink/tests/testdata/ecx509.pem -- 2.47.2