BIND 10 trac781, updated. 74e6966d7f8f8216dad871f1648cab977fb9fea2 [trac781] add other sign() parameter options, and possibilty for truncated sigs

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Apr 18 11:56:14 UTC 2011


The branch, trac781 has been updated
       via  74e6966d7f8f8216dad871f1648cab977fb9fea2 (commit)
       via  0334c1798ea43061494a530d8bc274283f1f1fc7 (commit)
      from  40f4efbbd9f5685147cd1abf19d12c336bc1b8d9 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 74e6966d7f8f8216dad871f1648cab977fb9fea2
Author: Jelte Jansen <jelte at isc.org>
Date:   Mon Apr 18 13:55:42 2011 +0200

    [trac781] add other sign() parameter options, and possibilty for truncated sigs

commit 0334c1798ea43061494a530d8bc274283f1f1fc7
Author: Jelte Jansen <jelte at isc.org>
Date:   Mon Apr 18 09:39:52 2011 +0200

    [trac781] add a Crypto class for initialization (instead of static)

-----------------------------------------------------------------------

Summary of changes:
 src/lib/crypto/crypto.cc                 |  108 ++++++++++++++++++++++++------
 src/lib/crypto/crypto.h                  |   68 ++++++++++++++++++-
 src/lib/crypto/tests/crypto_unittests.cc |   75 +++++++++++++++++++--
 src/lib/crypto/tests/run_unittests.cc    |    4 +-
 4 files changed, 222 insertions(+), 33 deletions(-)

-----------------------------------------------------------------------
diff --git a/src/lib/crypto/crypto.cc b/src/lib/crypto/crypto.cc
index 022d3c5..8aba543 100644
--- a/src/lib/crypto/crypto.cc
+++ b/src/lib/crypto/crypto.cc
@@ -33,33 +33,51 @@ const char*
 getBotanHashAlgorithmName(isc::crypto::HMAC::HashAlgorithm algorithm) {
     switch (algorithm) {
     case isc::crypto::HMAC::MD5:
-        return "MD5";
+        return ("MD5");
         break;
     case isc::crypto::HMAC::SHA1:
-        return "SHA-1";
+        return ("SHA-1");
         break;
     case isc::crypto::HMAC::SHA256:
-        return "SHA-256";
+        return ("SHA-256");
         break;
     case isc::crypto::HMAC::UNKNOWN:
-        return "Unknown";
+        return ("Unknown");
         break;
     }
     // compiler should have prevented us to reach this, since we have
     // no default. But we need a return value anyway
-    return "Unknown";
+    return ("Unknown");
 }
 
-// Library needs to have been inited during the entire program
-// should we make this a singleton? (for hsm we'll need more
-// initialization, and dynamic loading)
-Botan::LibraryInitializer init;
-
 } // local namespace
 
 namespace isc {
 namespace crypto {
 
+// For Botan, we use the Crypto class object in RAII style
+class CryptoImpl {
+public:
+    CryptoImpl() {}
+    ~CryptoImpl() {};
+        
+private:
+    Botan::LibraryInitializer _botan_init;
+};
+
+Crypto::Crypto() {
+    try {
+        impl_ = new CryptoImpl();
+    } catch (Botan::Exception ex) {
+        isc_throw(InitializationError, ex.what());
+    }
+}
+
+Crypto::~Crypto() {
+    delete impl_;
+}
+
+
 class HMACImpl {
 public:
     explicit HMACImpl(const void* secret, size_t secret_len,
@@ -75,7 +93,6 @@ public:
 
         hmac_ = new Botan::HMAC::HMAC(hash);
 
-        // Take the 'secret' from the key
         // If the key length is larger than the block size, we hash the
         // key itself first.
         try {
@@ -96,21 +113,53 @@ public:
 
     ~HMACImpl() { delete hmac_; }
 
+    size_t getOutputLength() {
+        return (hmac_->OUTPUT_LENGTH);
+    }
+
     void update(const void* data, const size_t len) {
-        // update the data from whatever we get (probably as a buffer)
         hmac_->update(static_cast<const Botan::byte*>(data), len);
     }
 
-    void sign(isc::dns::OutputBuffer& result) {
-        // And generate the mac
+    void sign(isc::dns::OutputBuffer& result, size_t len) {
+        Botan::SecureVector<Botan::byte> b_result(hmac_->final());
+
+        if (len == 0 || len > b_result.size()) {
+            len = b_result.size();
+        }
+        result.writeData(b_result.begin(), len);
+    }
+
+    void sign(void* result, size_t len) {
         Botan::SecureVector<Botan::byte> b_result(hmac_->final());
+        size_t output_size = getOutputLength();
+        if (output_size > len) {
+            output_size = len;
+        }
+        memcpy(result, b_result.begin(), output_size);
+    }
 
-        // write mac to result
-        result.writeData(b_result.begin(), b_result.size());
+    std::vector<uint8_t> sign(size_t len) {
+        Botan::SecureVector<Botan::byte> b_result(hmac_->final());
+        if (len == 0 || len > b_result.size()) {
+            return(std::vector<uint8_t>(b_result.begin(), b_result.end()));
+        } else {
+            return(std::vector<uint8_t>(b_result.begin(), &b_result[len]));
+        }
     }
 
-    bool verify(const void* sig, const size_t len) {
-        return (hmac_->verify_mac(static_cast<const Botan::byte*>(sig), len));
+
+    bool verify(const void* sig, size_t len) {
+        // Botan's verify_mac checks if len matches the output_length,
+        // which causes it to fail for truncated signatures, so we do
+        // the check ourselves
+        Botan::SecureVector<Botan::byte> our_mac = hmac_->final();
+        if (len == 0 || len > getOutputLength()) {
+            len = getOutputLength();
+        }
+        return (Botan::same_mem(&our_mac[0],
+                                static_cast<const unsigned char*>(sig),
+                                len));
     }
 
 private:
@@ -127,14 +176,29 @@ HMAC::~HMAC() {
     delete impl_;
 }
 
+size_t
+HMAC::getOutputLength() {
+    return impl_->getOutputLength();
+}
+
 void
 HMAC::update(const void* data, const size_t len) {
     impl_->update(data, len);
 }
 
 void
-HMAC::sign(isc::dns::OutputBuffer& result) {
-    impl_->sign(result);
+HMAC::sign(isc::dns::OutputBuffer& result, size_t len) {
+    impl_->sign(result, len);
+}
+
+void
+HMAC::sign(void* result, size_t len) {
+    impl_->sign(result, len);
+}
+
+std::vector<uint8_t>
+HMAC::sign(size_t len) {
+    return impl_->sign(len);
 }
 
 bool
@@ -145,11 +209,11 @@ HMAC::verify(const void* sig, const size_t len) {
 void
 signHMAC(const void* data, size_t data_len, const void* secret,
          size_t secret_len, const HMAC::HashAlgorithm hash_algorithm,
-         isc::dns::OutputBuffer& result)
+         isc::dns::OutputBuffer& result, size_t len)
 {
     HMAC hmac(secret, secret_len, hash_algorithm);
     hmac.update(data, data_len);
-    hmac.sign(result);
+    hmac.sign(result, len);
 }
 
 
diff --git a/src/lib/crypto/crypto.h b/src/lib/crypto/crypto.h
index a323a97..5389e0c 100644
--- a/src/lib/crypto/crypto.h
+++ b/src/lib/crypto/crypto.h
@@ -32,6 +32,14 @@ public:
         isc::Exception(file, line, what) {}
 };
 
+/// This exception is thrown if there was a problem initializing the
+/// crypto library
+class InitializationError : public CryptoError {
+public:
+    InitializationError(const char* file, size_t line, const char* what) :
+        CryptoError(file, line, what) {}
+};
+
 /// This exception is thrown when a cryptographic action is requested
 /// for an algorithm that is not supported by the underlying algorithm.
 class UnsupportedAlgorithm : public CryptoError {
@@ -48,6 +56,25 @@ public:
         CryptoError(file, line, what) {}
 };
 
+class CryptoImpl;
+
+/// \brief Initializer object
+///
+/// This object represents 'global' state for the backend crypto
+/// library, and must be initialized before any cryptographic calls
+/// are made. It may not be destroyed until all cryptographic objects
+/// are.
+/// Preferably, this object is created in the program's main() function
+// Internal note: we can use this class later to initialize and manage
+// dynamic (PKCS#11) libs
+class Crypto {
+public:
+    Crypto();
+    ~Crypto();
+private:
+    CryptoImpl* impl_;
+};
+
 /// Forward declaration, pimpl style
 class HMACImpl;
 
@@ -84,6 +111,11 @@ public:
     /// \brief Destructor
     ~HMAC();
 
+    /// \brief Returns the output size of the digest
+    ///
+    /// \return output size of the digest
+    size_t getOutputLength();
+
     /// \brief Add data to digest
     ///
     /// \param data The data to add
@@ -95,12 +127,39 @@ public:
     /// The result will be appended to the given outputbuffer
     ///
     /// \param result The OutputBuffer to append the result to
-    void sign(isc::dns::OutputBuffer& result);
+    /// \param len The number of bytes from the result to copy. If this
+    ///        value is smaller than the algorithms output size, the
+    ///        result will be truncated. If this value is larger, or 0
+    ///        (the default), it will be ignored
+    void sign(isc::dns::OutputBuffer& result, size_t len = 0);
+
+    /// \brief Calculate the final signature
+    ///
+    /// len bytes of data from the result will be copied to *result
+    /// If len is larger than the output size, only output_size bytes
+    /// will be copied. If it is smaller, the output will be truncated
+    ///
+    /// At least len bytes of data must be available for writing at
+    /// result
+    void sign(void* result, size_t len);
+
+    /// \brief Calculate the final signatre
+    ///
+    /// The result will be returned as a std::vector<uint8_t>
+    ///
+    /// \param len The number of bytes from the result to copy. If this
+    ///        value is smaller than the algorithms output size, the
+    ///        result will be truncated. If this value is larger, or 0
+    ///        (the default), it will be ignored
+    /// \return a vector containing the signature
+    std::vector<uint8_t> sign(size_t len = 0);
 
     /// \brief Verify an existing signature
     ///
     /// \param sig The signature to verify
-    /// \param len The length of the sig
+    /// \param len The length of the signature. If this is non-zero,
+    ///            and smaller than the output length of the algorithm,
+    ///            only len bytes will be checked
     /// \return true if the signature is correct, false otherwise
     bool verify(const void* sig, size_t len);
 
@@ -130,12 +189,15 @@ private:
 /// \param secret_len The length of the secret
 /// \param hash_algorithm The hash algorithm
 /// \param result The signature will be appended to this buffer
+/// \param len If this is non-zero and less than the output size,
+///            the result will be truncated to len bytes
 void signHMAC(const void* data,
               const size_t data_len,
               const void* secret,
               size_t secret_len,
               const HMAC::HashAlgorithm hash_algorithm,
-              isc::dns::OutputBuffer& result);
+              isc::dns::OutputBuffer& result,
+              size_t len = 0);
 
 /// \brief Verify an HMAC signature for the given data
 ///
diff --git a/src/lib/crypto/tests/crypto_unittests.cc b/src/lib/crypto/tests/crypto_unittests.cc
index 3cd8613..2c6c7a3 100644
--- a/src/lib/crypto/tests/crypto_unittests.cc
+++ b/src/lib/crypto/tests/crypto_unittests.cc
@@ -23,13 +23,15 @@ using namespace isc::dns;
 using namespace isc::crypto;
 
 namespace {
+    void checkData(const uint8_t* data1, const uint8_t* data2, size_t len) {
+        for (size_t i = 0; i < len; ++i) {
+            ASSERT_EQ(data1[i], data2[i]);
+        }
+    }
     
     void checkBuffer(const OutputBuffer& buf, uint8_t *data, size_t len) {
         ASSERT_EQ(len, buf.getLength());
-        const uint8_t* buf_d = static_cast<const uint8_t*>(buf.getData());
-        for (size_t i = 0; i < len; ++i) {
-            ASSERT_EQ(data[i], buf_d[i]);
-        }
+        checkData(static_cast<const uint8_t*>(buf.getData()), data, len);
     }
 
     // Sign and verify with the convenience functions
@@ -41,11 +43,11 @@ namespace {
                         size_t hmac_len) {
         OutputBuffer data_buf(data.size());
         data_buf.writeData(data.c_str(), data.size());
-        OutputBuffer hmac_sig(1);
+        OutputBuffer hmac_sig(0);
 
         // Sign it
         signHMAC(data_buf.getData(), data_buf.getLength(),
-                 secret, secret_len, hash_algorithm, hmac_sig);
+                 secret, secret_len, hash_algorithm, hmac_sig, hmac_len);
 
         // Check if the signature is what we expect
         checkBuffer(hmac_sig, expected_hmac, hmac_len);
@@ -79,7 +81,7 @@ namespace {
         // Sign it
         HMAC hmac_sign(secret, secret_len, hash_algorithm);
         hmac_sign.update(data_buf.getData(), data_buf.getLength());
-        hmac_sign.sign(hmac_sig);
+        hmac_sign.sign(hmac_sig, hmac_len);
 
         // Check if the signature is what we expect
         checkBuffer(hmac_sig, expected_hmac, hmac_len);
@@ -97,6 +99,48 @@ namespace {
                                         hmac_sig.getLength()));
     }
 
+    void doHMACTestVector(const std::string& data,
+                          const void* secret,
+                          size_t secret_len,
+                          const HMAC::HashAlgorithm hash_algorithm,
+                          uint8_t* expected_hmac,
+                          size_t hmac_len) {
+        HMAC hmac_sign(secret, secret_len, hash_algorithm);
+        hmac_sign.update(data.c_str(), data.size());
+        std::vector<uint8_t> sig = hmac_sign.sign(hmac_len);
+        ASSERT_EQ(hmac_len, sig.size());
+        checkData(&sig[0], expected_hmac, hmac_len);
+
+        HMAC hmac_verify(secret, secret_len, hash_algorithm);
+        hmac_verify.update(data.c_str(), data.size());
+        EXPECT_TRUE(hmac_verify.verify(&sig[0], sig.size()));
+
+        sig[0] = ~sig[0];
+        EXPECT_FALSE(hmac_verify.verify(&sig[0], sig.size()));
+    }
+
+    void doHMACTestArray(const std::string& data,
+                         const void* secret,
+                         size_t secret_len,
+                         const HMAC::HashAlgorithm hash_algorithm,
+                         uint8_t* expected_hmac,
+                         size_t hmac_len) {
+        HMAC hmac_sign(secret, secret_len, hash_algorithm);
+        hmac_sign.update(data.c_str(), data.size());
+        
+        uint8_t sig[hmac_len];
+
+        hmac_sign.sign(sig, hmac_len);
+        checkData(sig, expected_hmac, hmac_len);
+
+        HMAC hmac_verify(secret, secret_len, hash_algorithm);
+        hmac_verify.update(data.c_str(), data.size());
+        EXPECT_TRUE(hmac_verify.verify(sig, hmac_len));
+
+        sig[0] = ~sig[0];
+        EXPECT_FALSE(hmac_verify.verify(sig, hmac_len));
+    }
+
     void doHMACTest(const std::string& data,
                     const void* secret,
                     size_t secret_len,
@@ -107,6 +151,10 @@ namespace {
                        expected_hmac, hmac_len);
         doHMACTestDirect(data, secret, secret_len, hash_algorithm,
                          expected_hmac, hmac_len);
+        doHMACTestVector(data, secret, secret_len, hash_algorithm,
+                         expected_hmac, hmac_len);
+        doHMACTestArray(data, secret, secret_len, hash_algorithm,
+                        expected_hmac, hmac_len);
     }
 }
 
@@ -161,6 +209,8 @@ TEST(CryptoTest, HMAC_MD5_RFC2202_SIGN) {
                                  0x69, 0x0e, 0xfd, 0x4c };
     doHMACTest("Test With Truncation", secret5, 16, HMAC::MD5,
                hmac_expected5, 16);
+    doHMACTest("Test With Truncation", secret5, 16, HMAC::MD5,
+               hmac_expected5, 12);
 
     std::string secret6;
     for (int i = 0; i < 80; ++i) {
@@ -237,6 +287,8 @@ TEST(CryptoTest, HMAC_SHA1_RFC2202_SIGN) {
                                  0x5a, 0x04 };
     doHMACTest("Test With Truncation", secret5, 20, HMAC::SHA1,
                hmac_expected5, 20);
+    doHMACTest("Test With Truncation", secret5, 20, HMAC::SHA1,
+               hmac_expected5, 12);
 
     std::string secret6;
     for (int i = 0; i < 80; ++i) {
@@ -314,6 +366,15 @@ TEST(CryptoTest, HMAC_SHA256_RFC2202_SIGN) {
                                  0x66, 0x5b };
     doHMACTest(data4, secret4, 25, HMAC::SHA256, hmac_expected4, 32);
 
+    uint8_t secret5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+                          0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+                          0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c };
+    uint8_t hmac_expected5[] = { 0xa3, 0xb6, 0x16, 0x74, 0x73, 0x10,
+                                 0x0e, 0xe0, 0x6e, 0x0c, 0x79, 0x6c,
+                                 0x29, 0x55, 0x55, 0x2b };
+    doHMACTest("Test With Truncation", secret5, 20, HMAC::SHA256,
+               hmac_expected5, 16);
+
     std::string secret6;
     for (int i = 0; i < 131; ++i) {
         secret6.push_back(0xaa);
diff --git a/src/lib/crypto/tests/run_unittests.cc b/src/lib/crypto/tests/run_unittests.cc
index d5e20c9..1ef3ff3 100644
--- a/src/lib/crypto/tests/run_unittests.cc
+++ b/src/lib/crypto/tests/run_unittests.cc
@@ -15,10 +15,12 @@
 #include <gtest/gtest.h>
 
 #include <dns/tests/unittest_util.h>
+#include <crypto/crypto.h>
 
 int
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
-
+    isc::crypto::Crypto crypto;
+    
     return (RUN_ALL_TESTS());
 }




More information about the bind10-changes mailing list