Commit 74e6966d authored by Jelte Jansen's avatar Jelte Jansen
Browse files

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

parent 0334c179
......@@ -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);
}
......
......@@ -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
///
......
......@@ -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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment