Browse Source

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

Jelte Jansen 14 years ago
parent
commit
74e6966d7f
3 changed files with 185 additions and 29 deletions
  1. 70 19
      src/lib/crypto/crypto.cc
  2. 46 3
      src/lib/crypto/crypto.h
  3. 69 7
      src/lib/crypto/tests/crypto_unittests.cc

+ 70 - 19
src/lib/crypto/crypto.cc

@@ -33,21 +33,21 @@ 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");
 }
 
 } // local namespace
@@ -58,15 +58,19 @@ namespace crypto {
 // For Botan, we use the Crypto class object in RAII style
 class CryptoImpl {
 public:
-    CryptoImpl() { _botan_init.initialize(); };
-    ~CryptoImpl() { _botan_init.deinitialize(); };
+    CryptoImpl() {}
+    ~CryptoImpl() {};
         
 private:
     Botan::LibraryInitializer _botan_init;
 };
 
 Crypto::Crypto() {
-    impl_ = new CryptoImpl();
+    try {
+        impl_ = new CryptoImpl();
+    } catch (Botan::Exception ex) {
+        isc_throw(InitializationError, ex.what());
+    }
 }
 
 Crypto::~Crypto() {
@@ -109,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());
 
-        // write mac to result
-        result.writeData(b_result.begin(), b_result.size());
+        if (len == 0 || len > b_result.size()) {
+            len = b_result.size();
+        }
+        result.writeData(b_result.begin(), len);
     }
 
-    bool verify(const void* sig, const size_t len) {
-        return (hmac_->verify_mac(static_cast<const Botan::byte*>(sig), 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);
+    }
+
+    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, 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:
@@ -140,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
@@ -158,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);
 }
 
 

+ 46 - 3
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 {
@@ -103,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
@@ -114,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);
 
@@ -149,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
 ///

+ 69 - 7
src/lib/crypto/tests/crypto_unittests.cc

@@ -23,13 +23,16 @@ using namespace isc::dns;
 using namespace isc::crypto;
 
 namespace {
-    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());
+    void checkData(const uint8_t* data1, const uint8_t* data2, size_t len) {
         for (size_t i = 0; i < len; ++i) {
-            ASSERT_EQ(data[i], buf_d[i]);
+            ASSERT_EQ(data1[i], data2[i]);
         }
     }
+    
+    void checkBuffer(const OutputBuffer& buf, uint8_t *data, size_t len) {
+        ASSERT_EQ(len, buf.getLength());
+        checkData(static_cast<const uint8_t*>(buf.getData()), data, len);
+    }
 
     // Sign and verify with the convenience functions
     void doHMACTestConv(const std::string& data,
@@ -40,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);
@@ -78,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);
@@ -96,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,
@@ -106,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);
     }
 }
 
@@ -160,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) {
@@ -236,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) {
@@ -313,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);