From 98ba096951247b7e2119afe6ba4e4ddd7d4a4485 Mon Sep 17 00:00:00 2001 From: Nils Maier Date: Sat, 19 Apr 2014 19:07:38 +0200 Subject: Add util::security (compare, HMAC, PBKDF2) See #220 --- src/util_security.cc | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 src/util_security.cc (limited to 'src/util_security.cc') diff --git a/src/util_security.cc b/src/util_security.cc new file mode 100644 index 00000000..9348486b --- /dev/null +++ b/src/util_security.cc @@ -0,0 +1,190 @@ +/* */ + +#include "util_security.h" + +#include "FatalException.h" +#include "util.h" + +namespace { +using namespace aria2; + +static inline size_t getBlockSize(const std::string& algorithm) +{ + std::string canon; + if (!MessageDigest::supports(algorithm)) { + goto err; + } + canon = MessageDigest::getCanonicalHashType(algorithm); + + // Per RFC 6234 + if (canon == "sha-1") { + return 64; + } + if (canon == "sha-224") { + return 64; + } + if (canon == "sha-256") { + return 64; + } + if (canon == "sha-384") { + return 128; + } + if (canon == "sha-512") { + // RFC 4868 + return 128; + } + +err: + throw FATAL_EXCEPTION(fmt("HMAC does not support algorithm %s", algorithm.c_str())); +} + +} // namespace + +namespace aria2 { +namespace util { +namespace security { + +bool compare(const unsigned char a, const unsigned char b) +{ + unsigned char rv = ~(a ^ b); + rv &= rv >> 4; + rv &= rv >> 2; + rv &= rv >> 1; + return rv; +} + +bool compare(const uint8_t *a, const uint8_t *b, size_t length) +{ + unsigned char rv = 0; + for (size_t i = 0; i < length; ++i) { + rv |= a[i] ^ b[i]; + } + return compare(rv, 0); +} + +HMAC::HMAC(const std::string& algorithm, const char* secret, size_t length) + : blockSize_(getBlockSize(algorithm)), md_(MessageDigest::create(algorithm)), + clean_(false) +{ + ipad_.assign(blockSize_, 0x36); + opad_.assign(blockSize_, 0x5c); + + if (length > blockSize_) { + md_->reset(); + md_->update(secret, length); + auto hash = md_->digest(); + for (size_t i = 0uL, e = hash.length(); i < e; ++i) { + ipad_.replace(i, 1, 1, hash[i] ^ 0x36); + opad_.replace(i, 1, 1, hash[i] ^ 0x5c); + } + } + else { + for (size_t i = 0uL, e = length; i < e; ++i) { + ipad_.replace(i, 1, 1, secret[i] ^ 0x36); + opad_.replace(i, 1, 1, secret[i] ^ 0x5c); + } + } + reset(); +} + +std::unique_ptr HMAC::createRandom(const std::string& algorithm) +{ + const auto len = MessageDigest::getDigestLength(algorithm); + if (len == 0) { + return nullptr; + } + auto buf = make_unique(len); + generateRandomData((unsigned char*)buf.get(), len); + return create(algorithm, buf.get(), len); +} + +bool HMAC::supports(const std::string& algorithm) { + if (!MessageDigest::supports(algorithm)) { + return false; + } + const auto canon = MessageDigest::getCanonicalHashType(algorithm); + return canon == "sha-1" || canon == "sha-224" || canon == "sha-256" || + canon == "sha-384" || canon == "sha-512"; +} + +HMACResult PBKDF2(HMAC* hmac, const char* salt, size_t salt_length, + size_t iterations, size_t key_length) +{ + if (!hmac) { + throw FATAL_EXCEPTION("hmac cannot be null"); + } + const size_t hmac_length = hmac->length(); + if (key_length == 0) { + key_length = hmac_length; + } + typedef union { + uint8_t bytes[4]; + uint32_t count; + } counter_t; + counter_t counter, swapped; + counter.count = 1; + + auto work = make_unique(hmac_length); + char* p = work.get(); + std::string rv; + + hmac->reset(); + + while (key_length) { + hmac->update(salt, salt_length); + swapped.count = htonl(counter.count++); + hmac->update((char*)swapped.bytes, sizeof(swapped.bytes)); + + auto bytes = hmac->getResult().getBytes(); + memcpy(p, bytes.data(), bytes.length()); + + for (size_t i = 1uL; i < iterations; ++i) { + hmac->update(bytes); + bytes = hmac->getResult().getBytes(); + for (size_t j = 0uL; j < hmac_length; ++j) { + p[j] ^= bytes[j]; + } + } + auto use = std::min(key_length, hmac_length); + rv.append(p, use); + key_length -= use; + } + return HMACResult(rv); +} + +} // namespace security +} // namespace util +} // namespace aria2 -- cgit