diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/third_party/botan/src/lib/pbkdf | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/third_party/botan/src/lib/pbkdf')
25 files changed, 3031 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/pbkdf/argon2/argon2.cpp b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2.cpp new file mode 100644 index 0000000000..0d767e04e7 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2.cpp @@ -0,0 +1,443 @@ +/** +* (C) 2018,2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/argon2.h> +#include <botan/loadstor.h> +#include <botan/hash.h> +#include <botan/mem_ops.h> +#include <botan/rotate.h> +#include <botan/exceptn.h> + +namespace Botan { + +namespace { + +static const size_t SYNC_POINTS = 4; + +secure_vector<uint8_t> argon2_H0(HashFunction& blake2b, + size_t output_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len, + const uint8_t key[], size_t key_len, + const uint8_t ad[], size_t ad_len, + size_t y, size_t p, size_t M, size_t t) + { + const uint8_t v = 19; // Argon2 version code + + blake2b.update_le(static_cast<uint32_t>(p)); + blake2b.update_le(static_cast<uint32_t>(output_len)); + blake2b.update_le(static_cast<uint32_t>(M)); + blake2b.update_le(static_cast<uint32_t>(t)); + blake2b.update_le(static_cast<uint32_t>(v)); + blake2b.update_le(static_cast<uint32_t>(y)); + + blake2b.update_le(static_cast<uint32_t>(password_len)); + blake2b.update(cast_char_ptr_to_uint8(password), password_len); + + blake2b.update_le(static_cast<uint32_t>(salt_len)); + blake2b.update(salt, salt_len); + + blake2b.update_le(static_cast<uint32_t>(key_len)); + blake2b.update(key, key_len); + + blake2b.update_le(static_cast<uint32_t>(ad_len)); + blake2b.update(ad, ad_len); + + return blake2b.final(); + } + +void Htick(secure_vector<uint8_t>& T, + uint8_t output[], + size_t output_len, + HashFunction& blake2b, + const secure_vector<uint8_t>& H0, + size_t p0, size_t p1) + { + BOTAN_ASSERT_NOMSG(output_len % 64 == 0); + + blake2b.update_le(static_cast<uint32_t>(output_len)); + blake2b.update(H0); + blake2b.update_le(static_cast<uint32_t>(p0)); + blake2b.update_le(static_cast<uint32_t>(p1)); + + blake2b.final(&T[0]); + + while(output_len > 64) + { + copy_mem(output, &T[0], 32); + output_len -= 32; + output += 32; + + blake2b.update(T); + blake2b.final(&T[0]); + } + + if(output_len > 0) + copy_mem(output, &T[0], output_len); + } + +void extract_key(uint8_t output[], size_t output_len, + const secure_vector<uint64_t>& B, + size_t memory, size_t threads) + { + const size_t lanes = memory / threads; + + secure_vector<uint64_t> sum(128); + + for(size_t lane = 0; lane != threads; ++lane) + { + size_t start = 128*(lane * lanes + lanes - 1); + size_t end = 128*(lane * lanes + lanes); + + for(size_t j = start; j != end; ++j) + { + sum[j % 128] ^= B[j]; + } + } + + secure_vector<uint8_t> sum8(1024); + copy_out_le(sum8.data(), 1024, sum.data()); + + if(output_len <= 64) + { + std::unique_ptr<HashFunction> blake2b = HashFunction::create_or_throw("BLAKE2b(" + std::to_string(output_len*8) + ")"); + blake2b->update_le(static_cast<uint32_t>(output_len)); + blake2b->update(sum8.data(), sum8.size()); + blake2b->final(output); + } + else + { + secure_vector<uint8_t> T(64); + + std::unique_ptr<HashFunction> blake2b = HashFunction::create_or_throw("BLAKE2b(512)"); + blake2b->update_le(static_cast<uint32_t>(output_len)); + blake2b->update(sum8.data(), sum8.size()); + blake2b->final(&T[0]); + + while(output_len > 64) + { + copy_mem(output, &T[0], 32); + output_len -= 32; + output += 32; + + if(output_len > 64) + { + blake2b->update(T); + blake2b->final(&T[0]); + } + } + + if(output_len == 64) + { + blake2b->update(T); + blake2b->final(output); + } + else + { + std::unique_ptr<HashFunction> blake2b_f = HashFunction::create_or_throw("BLAKE2b(" + std::to_string(output_len*8) + ")"); + blake2b_f->update(T); + blake2b_f->final(output); + } + } + } + +void init_blocks(secure_vector<uint64_t>& B, + HashFunction& blake2b, + const secure_vector<uint8_t>& H0, + size_t memory, + size_t threads) + { + BOTAN_ASSERT_NOMSG(B.size() >= threads*256); + + secure_vector<uint8_t> H(1024); + secure_vector<uint8_t> T(blake2b.output_length()); + + for(size_t i = 0; i != threads; ++i) + { + const size_t B_off = i * (memory / threads); + + BOTAN_ASSERT_NOMSG(B.size() >= 128*(B_off+2)); + + Htick(T, &H[0], H.size(), blake2b, H0, 0, i); + + for(size_t j = 0; j != 128; ++j) + { + B[128*B_off+j] = load_le<uint64_t>(H.data(), j); + } + + Htick(T, &H[0], H.size(), blake2b, H0, 1, i); + + for(size_t j = 0; j != 128; ++j) + { + B[128*(B_off+1)+j] = load_le<uint64_t>(H.data(), j); + } + } + } + +inline void blamka_G(uint64_t& A, uint64_t& B, uint64_t& C, uint64_t& D) + { + A += B + (static_cast<uint64_t>(2) * static_cast<uint32_t>(A)) * static_cast<uint32_t>(B); + D = rotr<32>(A ^ D); + + C += D + (static_cast<uint64_t>(2) * static_cast<uint32_t>(C)) * static_cast<uint32_t>(D); + B = rotr<24>(B ^ C); + + A += B + (static_cast<uint64_t>(2) * static_cast<uint32_t>(A)) * static_cast<uint32_t>(B); + D = rotr<16>(A ^ D); + + C += D + (static_cast<uint64_t>(2) * static_cast<uint32_t>(C)) * static_cast<uint32_t>(D); + B = rotr<63>(B ^ C); + } + +inline void blamka(uint64_t& V0, uint64_t& V1, uint64_t& V2, uint64_t& V3, + uint64_t& V4, uint64_t& V5, uint64_t& V6, uint64_t& V7, + uint64_t& V8, uint64_t& V9, uint64_t& VA, uint64_t& VB, + uint64_t& VC, uint64_t& VD, uint64_t& VE, uint64_t& VF) + { + blamka_G(V0, V4, V8, VC); + blamka_G(V1, V5, V9, VD); + blamka_G(V2, V6, VA, VE); + blamka_G(V3, V7, VB, VF); + + blamka_G(V0, V5, VA, VF); + blamka_G(V1, V6, VB, VC); + blamka_G(V2, V7, V8, VD); + blamka_G(V3, V4, V9, VE); + } + +void process_block_xor(secure_vector<uint64_t>& T, + secure_vector<uint64_t>& B, + size_t offset, + size_t prev, + size_t new_offset) + { + for(size_t i = 0; i != 128; ++i) + T[i] = B[128*prev+i] ^ B[128*new_offset+i]; + + for(size_t i = 0; i != 128; i += 16) + { + blamka(T[i+ 0], T[i+ 1], T[i+ 2], T[i+ 3], + T[i+ 4], T[i+ 5], T[i+ 6], T[i+ 7], + T[i+ 8], T[i+ 9], T[i+10], T[i+11], + T[i+12], T[i+13], T[i+14], T[i+15]); + } + + for(size_t i = 0; i != 128 / 8; i += 2) + { + blamka(T[ i], T[ i+1], T[ 16+i], T[ 16+i+1], + T[ 32+i], T[ 32+i+1], T[ 48+i], T[ 48+i+1], + T[ 64+i], T[ 64+i+1], T[ 80+i], T[ 80+i+1], + T[ 96+i], T[ 96+i+1], T[112+i], T[112+i+1]); + } + + for(size_t i = 0; i != 128; ++i) + B[128*offset + i] ^= T[i] ^ B[128*prev+i] ^ B[128*new_offset+i]; + } + +void gen_2i_addresses(secure_vector<uint64_t>& T, secure_vector<uint64_t>& B, + size_t n, size_t lane, size_t slice, size_t memory, + size_t time, size_t mode, size_t cnt) + { + BOTAN_ASSERT_NOMSG(B.size() == 128); + BOTAN_ASSERT_NOMSG(T.size() == 128); + + clear_mem(B.data(), B.size()); + B[0] = n; + B[1] = lane; + B[2] = slice; + B[3] = memory; + B[4] = time; + B[5] = mode; + B[6] = cnt; + + for(size_t r = 0; r != 2; ++r) + { + copy_mem(T.data(), B.data(), B.size()); + + for(size_t i = 0; i != 128; i += 16) + { + blamka(T[i+ 0], T[i+ 1], T[i+ 2], T[i+ 3], + T[i+ 4], T[i+ 5], T[i+ 6], T[i+ 7], + T[i+ 8], T[i+ 9], T[i+10], T[i+11], + T[i+12], T[i+13], T[i+14], T[i+15]); + } + for(size_t i = 0; i != 128 / 8; i += 2) + { + blamka(T[ i], T[ i+1], T[ 16+i], T[ 16+i+1], + T[ 32+i], T[ 32+i+1], T[ 48+i], T[ 48+i+1], + T[ 64+i], T[ 64+i+1], T[ 80+i], T[ 80+i+1], + T[ 96+i], T[ 96+i+1], T[112+i], T[112+i+1]); + } + + for(size_t i = 0; i != 128; ++i) + B[i] ^= T[i]; + } + } + +uint32_t index_alpha(uint64_t random, + size_t lanes, + size_t segments, + size_t threads, + size_t n, + size_t slice, + size_t lane, + size_t index) + { + size_t ref_lane = static_cast<uint32_t>(random >> 32) % threads; + + if(n == 0 && slice == 0) + ref_lane = lane; + + size_t m = 3*segments; + size_t s = ((slice+1) % 4)*segments; + + if(lane == ref_lane) + m += index; + + if(n == 0) { + m = slice*segments; + s = 0; + if(slice == 0 || lane == ref_lane) + m += index; + } + + if(index == 0 || lane == ref_lane) + m -= 1; + + uint64_t p = static_cast<uint32_t>(random); + p = (p * p) >> 32; + p = (p * m) >> 32; + + return static_cast<uint32_t>(ref_lane*lanes + (s + m - (p+1)) % lanes); + } + +void process_block_argon2d(secure_vector<uint64_t>& T, + secure_vector<uint64_t>& B, + size_t n, size_t slice, size_t lane, + size_t lanes, size_t segments, size_t threads) + { + size_t index = 0; + if(n == 0 && slice == 0) + index = 2; + + while(index < segments) + { + const size_t offset = lane*lanes + slice*segments + index; + + size_t prev = offset - 1; + if(index == 0 && slice == 0) + prev += lanes; + + const uint64_t random = B.at(128*prev); + const size_t new_offset = index_alpha(random, lanes, segments, threads, n, slice, lane, index); + + process_block_xor(T, B, offset, prev, new_offset); + + index += 1; + } + } + +void process_block_argon2i(secure_vector<uint64_t>& T, + secure_vector<uint64_t>& B, + size_t n, size_t slice, size_t lane, + size_t lanes, size_t segments, size_t threads, uint8_t mode, + size_t memory, size_t time) + { + size_t index = 0; + if(n == 0 && slice == 0) + index = 2; + + secure_vector<uint64_t> addresses(128); + size_t address_counter = 1; + + gen_2i_addresses(T, addresses, n, lane, slice, memory, time, mode, address_counter); + + while(index < segments) + { + const size_t offset = lane*lanes + slice*segments + index; + + size_t prev = offset - 1; + if(index == 0 && slice == 0) + prev += lanes; + + if(index > 0 && index % 128 == 0) + { + address_counter += 1; + gen_2i_addresses(T, addresses, n, lane, slice, memory, time, mode, address_counter); + } + + const uint64_t random = addresses[index % 128]; + const size_t new_offset = index_alpha(random, lanes, segments, threads, n, slice, lane, index); + + process_block_xor(T, B, offset, prev, new_offset); + + index += 1; + } + } + +void process_blocks(secure_vector<uint64_t>& B, + size_t t, + size_t memory, + size_t threads, + uint8_t mode) + { + const size_t lanes = memory / threads; + const size_t segments = lanes / SYNC_POINTS; + + secure_vector<uint64_t> T(128); + for(size_t n = 0; n != t; ++n) + { + for(size_t slice = 0; slice != SYNC_POINTS; ++slice) + { + // TODO can run this in Thread_Pool + for(size_t lane = 0; lane != threads; ++lane) + { + if(mode == 1 || (mode == 2 && n == 0 && slice < SYNC_POINTS/2)) + process_block_argon2i(T, B, n, slice, lane, lanes, segments, threads, mode, memory, t); + else + process_block_argon2d(T, B, n, slice, lane, lanes, segments, threads); + } + } + } + + } + +} + +void argon2(uint8_t output[], size_t output_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len, + const uint8_t key[], size_t key_len, + const uint8_t ad[], size_t ad_len, + uint8_t mode, size_t threads, size_t M, size_t t) + { + BOTAN_ARG_CHECK(mode == 0 || mode == 1 || mode == 2, "Unknown Argon2 mode parameter"); + BOTAN_ARG_CHECK(output_len >= 4, "Invalid Argon2 output length"); + BOTAN_ARG_CHECK(threads >= 1 && threads <= 128, "Invalid Argon2 threads parameter"); + BOTAN_ARG_CHECK(M >= 8*threads && M <= 8192*1024, "Invalid Argon2 M parameter"); + BOTAN_ARG_CHECK(t >= 1, "Invalid Argon2 t parameter"); + + std::unique_ptr<HashFunction> blake2 = HashFunction::create_or_throw("BLAKE2b"); + + const auto H0 = argon2_H0(*blake2, output_len, + password, password_len, + salt, salt_len, + key, key_len, + ad, ad_len, + mode, threads, M, t); + + const size_t memory = (M / (SYNC_POINTS*threads)) * (SYNC_POINTS*threads); + + secure_vector<uint64_t> B(memory * 1024/8); + + init_blocks(B, *blake2, H0, memory, threads); + process_blocks(B, t, memory, threads, mode); + + clear_mem(output, output_len); + extract_key(output, output_len, B, memory, threads); + } + +} diff --git a/comm/third_party/botan/src/lib/pbkdf/argon2/argon2.h b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2.h new file mode 100644 index 0000000000..3a1b859f0c --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2.h @@ -0,0 +1,118 @@ +/** +* (C) 2018,2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_ARGON2_H_ +#define BOTAN_ARGON2_H_ + +#include <botan/pwdhash.h> + +//BOTAN_FUTURE_INTERNAL_HEADER(argon2.h) + +namespace Botan { + +class RandomNumberGenerator; + +/** +* Argon2 key derivation function +*/ +class BOTAN_PUBLIC_API(2,11) Argon2 final : public PasswordHash + { + public: + Argon2(uint8_t family, size_t M, size_t t, size_t p); + + Argon2(const Argon2& other) = default; + Argon2& operator=(const Argon2&) = default; + + /** + * Derive a new key under the current Argon2 parameter set + */ + void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const override; + + std::string to_string() const override; + + size_t M() const { return m_M; } + size_t t() const { return m_t; } + size_t p() const { return m_p; } + + size_t iterations() const override { return t(); } + + size_t parallelism() const override { return p(); } + + size_t memory_param() const override { return M(); } + + size_t total_memory_usage() const override { return M() * 1024; } + + private: + uint8_t m_family; + size_t m_M, m_t, m_p; + }; + +class BOTAN_PUBLIC_API(2,11) Argon2_Family final : public PasswordHashFamily + { + public: + Argon2_Family(uint8_t family); + + std::string name() const override; + + std::unique_ptr<PasswordHash> tune(size_t output_length, + std::chrono::milliseconds msec, + size_t max_memory) const override; + + std::unique_ptr<PasswordHash> default_params() const override; + + std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override; + + std::unique_ptr<PasswordHash> from_params( + size_t M, size_t t, size_t p) const override; + private: + const uint8_t m_family; + }; + +/** +* Argon2 key derivation function +* +* @param output the output will be placed here +* @param output_len length of output +* @param password the user password +* @param password_len the length of password +* @param salt the salt +* @param salt_len length of salt +* @param key an optional secret key +* @param key_len the length of key +* @param ad an optional additional input +* @param ad_len the length of ad +* @param y the Argon2 variant (0 = Argon2d, 1 = Argon2i, 2 = Argon2id) +* @param p the parallelization parameter +* @param M the amount of memory to use in Kb +* @param t the number of iterations to use +*/ +void BOTAN_PUBLIC_API(2,11) argon2(uint8_t output[], size_t output_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len, + const uint8_t key[], size_t key_len, + const uint8_t ad[], size_t ad_len, + uint8_t y, size_t p, size_t M, size_t t); + +std::string BOTAN_PUBLIC_API(2,11) + argon2_generate_pwhash(const char* password, size_t password_len, + RandomNumberGenerator& rng, + size_t p, size_t M, size_t t, + uint8_t y = 2, size_t salt_len = 16, size_t output_len = 32); + +/** +* Check a previously created password hash +* @param password the password to check against +* @param password_len the length of password +* @param hash the stored hash to check against +*/ +bool BOTAN_PUBLIC_API(2,11) argon2_check_pwhash(const char* password, size_t password_len, + const std::string& hash); + +} + +#endif diff --git a/comm/third_party/botan/src/lib/pbkdf/argon2/argon2fmt.cpp b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2fmt.cpp new file mode 100644 index 0000000000..480be5fa36 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2fmt.cpp @@ -0,0 +1,125 @@ +/** +* (C) 2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/argon2.h> +#include <botan/rng.h> +#include <botan/base64.h> +#include <botan/parsing.h> +#include <sstream> + +namespace Botan { + +namespace { + +std::string strip_padding(std::string s) + { + while(s.size() > 0 && s[s.size()-1] == '=') + s.resize(s.size() - 1); + return s; + } + +} + +std::string argon2_generate_pwhash(const char* password, size_t password_len, + RandomNumberGenerator& rng, + size_t p, size_t M, size_t t, + uint8_t y, size_t salt_len, size_t output_len) + { + std::vector<uint8_t> salt(salt_len); + rng.randomize(salt.data(), salt.size()); + + std::vector<uint8_t> output(output_len); + argon2(output.data(), output.size(), + password, password_len, + salt.data(), salt.size(), + nullptr, 0, + nullptr, 0, + y, p, M, t); + + std::ostringstream oss; + + if(y == 0) + oss << "$argon2d$"; + else if(y == 1) + oss << "$argon2i$"; + else + oss << "$argon2id$"; + + oss << "v=19$m=" << M << ",t=" << t << ",p=" << p << "$"; + oss << strip_padding(base64_encode(salt)) << "$" << strip_padding(base64_encode(output)); + + return oss.str(); + } + +bool argon2_check_pwhash(const char* password, size_t password_len, + const std::string& input_hash) + { + const std::vector<std::string> parts = split_on(input_hash, '$'); + + if(parts.size() != 5) + return false; + + uint8_t family = 0; + + if(parts[0] == "argon2d") + family = 0; + else if(parts[0] == "argon2i") + family = 1; + else if(parts[0] == "argon2id") + family = 2; + else + return false; + + if(parts[1] != "v=19") + return false; + + const std::vector<std::string> params = split_on(parts[2], ','); + + if(params.size() != 3) + return false; + + size_t M = 0, t = 0, p = 0; + + for(auto param_str : params) + { + const std::vector<std::string> param = split_on(param_str, '='); + + if(param.size() != 2) + return false; + + const std::string key = param[0]; + const size_t val = to_u32bit(param[1]); + if(key == "m") + M = val; + else if(key == "t") + t = val; + else if(key == "p") + p = val; + else + return false; + } + + std::vector<uint8_t> salt(base64_decode_max_output(parts[3].size())); + salt.resize(base64_decode(salt.data(), parts[3], false)); + + std::vector<uint8_t> hash(base64_decode_max_output(parts[4].size())); + hash.resize(base64_decode(hash.data(), parts[4], false)); + + if(hash.size() < 4) + return false; + + std::vector<uint8_t> generated(hash.size()); + argon2(generated.data(), generated.size(), + password, password_len, + salt.data(), salt.size(), + nullptr, 0, + nullptr, 0, + family, p, M, t); + + return constant_time_compare(generated.data(), hash.data(), generated.size()); + } + +} diff --git a/comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp new file mode 100644 index 0000000000..96f7f74fee --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp @@ -0,0 +1,154 @@ +/** +* (C) 2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/argon2.h> +#include <botan/exceptn.h> +#include <botan/internal/timer.h> +#include <algorithm> + +namespace Botan { + +Argon2::Argon2(uint8_t family, size_t M, size_t t, size_t p) : + m_family(family), + m_M(M), + m_t(t), + m_p(p) + {} + +void Argon2::derive_key(uint8_t output[], size_t output_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const + { + argon2(output, output_len, + password, password_len, + salt, salt_len, + nullptr, 0, + nullptr, 0, + m_family, m_p, m_M, m_t); + } + +namespace { + +std::string argon2_family_name(uint8_t f) + { + switch(f) + { + case 0: + return "Argon2d"; + case 1: + return "Argon2i"; + case 2: + return "Argon2id"; + default: + throw Invalid_Argument("Unknown Argon2 parameter"); + } + } + +} + +std::string Argon2::to_string() const + { + return argon2_family_name(m_family) + "(" + + std::to_string(m_M) + "," + + std::to_string(m_t) + "," + + std::to_string(m_p) + ")"; + } + +Argon2_Family::Argon2_Family(uint8_t family) : m_family(family) + { + if(m_family != 0 && m_family != 1 && m_family != 2) + throw Invalid_Argument("Unknown Argon2 family identifier"); + } + +std::string Argon2_Family::name() const + { + return argon2_family_name(m_family); + } + +std::unique_ptr<PasswordHash> Argon2_Family::tune(size_t /*output_length*/, + std::chrono::milliseconds msec, + size_t max_memory) const + { + const size_t max_kib = (max_memory == 0) ? 256*1024 : max_memory*1024; + + // Tune with a large memory otherwise we measure cache vs RAM speeds and underestimate + // costs for larger params. Default is 36 MiB, or use 128 for long times. + const size_t tune_M = (msec >= std::chrono::milliseconds(500) ? 128 : 36) * 1024; + const size_t p = 1; + size_t t = 1; + + Timer timer("Argon2"); + const auto tune_time = BOTAN_PBKDF_TUNING_TIME; + + timer.run_until_elapsed(tune_time, [&]() { + uint8_t output[64] = { 0 }; + argon2(output, sizeof(output), "test", 4, nullptr, 0, nullptr, 0, nullptr, 0, m_family, p, tune_M, t); + }); + + if(timer.events() == 0 || timer.value() == 0) + return default_params(); + + size_t M = 4*1024; + + const uint64_t measured_time = timer.value() / (timer.events() * (tune_M / M)); + + const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000); + + /* + * Argon2 scaling rules: + * k*M, k*t, k*p all increase cost by about k + * + * Since we don't even take advantage of p > 1, we prefer increasing + * t or M instead. + * + * If possible to increase M, prefer that. + */ + + uint64_t est_nsec = measured_time; + + if(est_nsec < target_nsec && M < max_kib) + { + const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec; + const uint64_t mem_headroom = max_kib / M; + + const uint64_t M_mult = std::min(desired_cost_increase, mem_headroom); + M *= static_cast<size_t>(M_mult); + est_nsec *= M_mult; + } + + if(est_nsec < target_nsec) + { + const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec; + t *= static_cast<size_t>(desired_cost_increase); + } + + return this->from_params(M, t, p); + } + +std::unique_ptr<PasswordHash> Argon2_Family::default_params() const + { + return this->from_params(128*1024, 1, 1); + } + +std::unique_ptr<PasswordHash> Argon2_Family::from_iterations(size_t iter) const + { + /* + These choices are arbitrary, but should not change in future + releases since they will break applications expecting deterministic + mapping from iteration count to params + */ + const size_t M = iter; + const size_t t = 1; + const size_t p = 1; + return this->from_params(M, t, p); + } + +std::unique_ptr<PasswordHash> Argon2_Family::from_params(size_t M, size_t t, size_t p) const + { + return std::unique_ptr<PasswordHash>(new Argon2(m_family, M, t, p)); + } + +} diff --git a/comm/third_party/botan/src/lib/pbkdf/argon2/info.txt b/comm/third_party/botan/src/lib/pbkdf/argon2/info.txt new file mode 100644 index 0000000000..91e85d0c47 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/argon2/info.txt @@ -0,0 +1,9 @@ +<defines> +ARGON2 -> 20190824 +</defines> + +<requires> +blake2 +base64 +</requires> + diff --git a/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp b/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp new file mode 100644 index 0000000000..2f2c770256 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp @@ -0,0 +1,183 @@ +/* +* (C) 2018,2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/bcrypt_pbkdf.h> +#include <botan/loadstor.h> +#include <botan/blowfish.h> +#include <botan/hash.h> +#include <botan/internal/timer.h> + +namespace Botan { + +void Bcrypt_PBKDF::derive_key(uint8_t output[], size_t output_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const + { + bcrypt_pbkdf(output, output_len, + password, password_len, + salt, salt_len, + m_iterations); + } + +std::string Bcrypt_PBKDF::to_string() const + { + return "Bcrypt-PBKDF(" + std::to_string(m_iterations) + ")"; + } + +std::string Bcrypt_PBKDF_Family::name() const + { + return "Bcrypt-PBKDF"; + } + +std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::tune(size_t output_length, + std::chrono::milliseconds msec, + size_t /*max_memory*/) const + { + Timer timer("Bcrypt_PBKDF"); + const auto tune_time = BOTAN_PBKDF_TUNING_TIME; + + const size_t blocks = (output_length + 32 - 1) / 32; + + if(blocks == 0) + return default_params(); + + const size_t starting_iter = 2; + + timer.run_until_elapsed(tune_time, [&]() { + uint8_t output[32] = { 0 }; + bcrypt_pbkdf(output, sizeof(output), "test", 4, nullptr, 0, starting_iter); + }); + + if(timer.events() < blocks || timer.value() == 0) + return default_params(); + + const uint64_t measured_time = timer.value() / (timer.events() / blocks); + + const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000); + + const uint64_t desired_increase = target_nsec / measured_time; + + if(desired_increase == 0) + return this->from_iterations(starting_iter); + + return this->from_iterations(static_cast<size_t>(desired_increase * starting_iter)); + } + +std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::default_params() const + { + return this->from_iterations(32); // About 100 ms on fast machine + } + +std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::from_iterations(size_t iter) const + { + return std::unique_ptr<PasswordHash>(new Bcrypt_PBKDF(iter)); + } + +std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::from_params(size_t iter, size_t /*t*/, size_t /*p*/) const + { + return this->from_iterations(iter); + } + +namespace { + +void bcrypt_round(Blowfish& blowfish, + const secure_vector<uint8_t>& pass_hash, + const secure_vector<uint8_t>& salt_hash, + secure_vector<uint8_t>& out, + secure_vector<uint8_t>& tmp) + { + const size_t BCRYPT_PBKDF_OUTPUT = 32; + + // "OxychromaticBlowfishSwatDynamite" + static const uint8_t BCRYPT_PBKDF_MAGIC[BCRYPT_PBKDF_OUTPUT] = { + 0x4F, 0x78, 0x79, 0x63, 0x68, 0x72, 0x6F, 0x6D, + 0x61, 0x74, 0x69, 0x63, 0x42, 0x6C, 0x6F, 0x77, + 0x66, 0x69, 0x73, 0x68, 0x53, 0x77, 0x61, 0x74, + 0x44, 0x79, 0x6E, 0x61, 0x6D, 0x69, 0x74, 0x65 + }; + + const size_t BCRYPT_PBKDF_WORKFACTOR = 6; + const size_t BCRYPT_PBKDF_ROUNDS = 64; + + blowfish.salted_set_key(pass_hash.data(), pass_hash.size(), + salt_hash.data(), salt_hash.size(), + BCRYPT_PBKDF_WORKFACTOR, true); + + copy_mem(tmp.data(), BCRYPT_PBKDF_MAGIC, BCRYPT_PBKDF_OUTPUT); + for(size_t i = 0; i != BCRYPT_PBKDF_ROUNDS; ++i) + blowfish.encrypt(tmp); + + /* + Bcrypt PBKDF loads the Blowfish output as big endian for no reason + in particular. We can't just swap everything once at the end + because the (big-endian) values are fed into SHA-512 to generate + the salt for the next round + */ + for(size_t i = 0; i != 32/4; ++i) + { + const uint32_t w = load_le<uint32_t>(tmp.data(), i); + store_be(w, &tmp[sizeof(uint32_t)*i]); + } + + xor_buf(out.data(), tmp.data(), BCRYPT_PBKDF_OUTPUT); + } + +} + +void bcrypt_pbkdf(uint8_t output[], size_t output_len, + const char* pass, size_t pass_len, + const uint8_t salt[], size_t salt_len, + size_t rounds) + { + BOTAN_ARG_CHECK(rounds >= 1, "Invalid rounds for Bcrypt PBKDF"); + + // No output desired, so we are all done already... + if(output_len == 0) + return; + + BOTAN_ARG_CHECK(output_len <= 10*1024*1024, "Too much output for Bcrypt PBKDF"); + + const size_t BCRYPT_BLOCK_SIZE = 32; + const size_t blocks = (output_len + BCRYPT_BLOCK_SIZE - 1) / BCRYPT_BLOCK_SIZE; + + std::unique_ptr<HashFunction> sha512 = HashFunction::create_or_throw("SHA-512"); + const secure_vector<uint8_t> pass_hash = sha512->process(reinterpret_cast<const uint8_t*>(pass), pass_len); + + secure_vector<uint8_t> salt_hash(sha512->output_length()); + + Blowfish blowfish; + secure_vector<uint8_t> out(BCRYPT_BLOCK_SIZE); + secure_vector<uint8_t> tmp(BCRYPT_BLOCK_SIZE); + + for(size_t block = 0; block != blocks; ++block) + { + clear_mem(out.data(), out.size()); + + sha512->update(salt, salt_len); + sha512->update_be(static_cast<uint32_t>(block + 1)); + sha512->final(salt_hash.data()); + + bcrypt_round(blowfish, pass_hash, salt_hash, out, tmp); + + for(size_t r = 1; r != rounds; ++r) + { + // Next salt is H(prev_output) + sha512->update(tmp); + sha512->final(salt_hash.data()); + + bcrypt_round(blowfish, pass_hash, salt_hash, out, tmp); + } + + for(size_t i = 0; i != BCRYPT_BLOCK_SIZE; ++i) + { + const size_t dest = i * blocks + block; + if(dest < output_len) + output[dest] = out[i]; + } + } + } + +} diff --git a/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h b/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h new file mode 100644 index 0000000000..0d459e8dbc --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h @@ -0,0 +1,77 @@ +/* +* (C) 2018,2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PBKDF_BCRYPT_H_ +#define BOTAN_PBKDF_BCRYPT_H_ + +#include <botan/pwdhash.h> + +BOTAN_FUTURE_INTERNAL_HEADER(bcrypt_pbkdf.h) + +namespace Botan { + +/** +* Bcrypt-PBKDF key derivation function +*/ +class BOTAN_PUBLIC_API(2,11) Bcrypt_PBKDF final : public PasswordHash + { + public: + Bcrypt_PBKDF(size_t iterations) : m_iterations(iterations) {} + + Bcrypt_PBKDF(const Bcrypt_PBKDF& other) = default; + Bcrypt_PBKDF& operator=(const Bcrypt_PBKDF&) = default; + + /** + * Derive a new key under the current Bcrypt-PBKDF parameter set + */ + void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const override; + + std::string to_string() const override; + + size_t iterations() const override { return m_iterations; } + + size_t parallelism() const override { return 0; } + + size_t memory_param() const override { return 0; } + + size_t total_memory_usage() const override { return 4096; } + + private: + size_t m_iterations; + }; + +class BOTAN_PUBLIC_API(2,11) Bcrypt_PBKDF_Family final : public PasswordHashFamily + { + public: + Bcrypt_PBKDF_Family() {} + + std::string name() const override; + + std::unique_ptr<PasswordHash> tune(size_t output_length, + std::chrono::milliseconds msec, + size_t max_memory) const override; + + std::unique_ptr<PasswordHash> default_params() const override; + + std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override; + + std::unique_ptr<PasswordHash> from_params( + size_t i, size_t, size_t) const override; + }; + +/** +* Bcrypt PBKDF compatible with OpenBSD bcrypt_pbkdf +*/ +void BOTAN_UNSTABLE_API bcrypt_pbkdf(uint8_t output[], size_t output_len, + const char* pass, size_t pass_len, + const uint8_t salt[], size_t salt_len, + size_t rounds); + +} + +#endif diff --git a/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/info.txt b/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/info.txt new file mode 100644 index 0000000000..0e2b10178e --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/info.txt @@ -0,0 +1,8 @@ +<defines> +PBKDF_BCRYPT -> 20190531 +</defines> + +<requires> +blowfish +sha2_64 +</requires> diff --git a/comm/third_party/botan/src/lib/pbkdf/info.txt b/comm/third_party/botan/src/lib/pbkdf/info.txt new file mode 100644 index 0000000000..650414f41b --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/info.txt @@ -0,0 +1,13 @@ +<defines> +PBKDF -> 20180902 +</defines> + +<requires> +mac +hash +</requires> + +<header:public> +pwdhash.h +pbkdf.h +</header:public> diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf.cpp b/comm/third_party/botan/src/lib/pbkdf/pbkdf.cpp new file mode 100644 index 0000000000..73b482725c --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf.cpp @@ -0,0 +1,133 @@ +/* +* PBKDF +* (C) 2012 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/pbkdf.h> +#include <botan/exceptn.h> +#include <botan/scan_name.h> + +#if defined(BOTAN_HAS_PBKDF1) +#include <botan/pbkdf1.h> +#endif + +#if defined(BOTAN_HAS_PBKDF2) +#include <botan/pbkdf2.h> +#endif + +#if defined(BOTAN_HAS_PGP_S2K) +#include <botan/pgp_s2k.h> +#endif + +namespace Botan { + +std::unique_ptr<PBKDF> PBKDF::create(const std::string& algo_spec, + const std::string& provider) + { + const SCAN_Name req(algo_spec); + +#if defined(BOTAN_HAS_PBKDF2) + if(req.algo_name() == "PBKDF2") + { + // TODO OpenSSL + + if(provider.empty() || provider == "base") + { + if(auto mac = MessageAuthenticationCode::create(req.arg(0))) + return std::unique_ptr<PBKDF>(new PKCS5_PBKDF2(mac.release())); + + if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")")) + return std::unique_ptr<PBKDF>(new PKCS5_PBKDF2(mac.release())); + } + + return nullptr; + } +#endif + +#if defined(BOTAN_HAS_PBKDF1) + if(req.algo_name() == "PBKDF1" && req.arg_count() == 1) + { + if(auto hash = HashFunction::create(req.arg(0))) + return std::unique_ptr<PBKDF>(new PKCS5_PBKDF1(hash.release())); + + } +#endif + +#if defined(BOTAN_HAS_PGP_S2K) + if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1) + { + if(auto hash = HashFunction::create(req.arg(0))) + return std::unique_ptr<PBKDF>(new OpenPGP_S2K(hash.release())); + } +#endif + + BOTAN_UNUSED(req); + BOTAN_UNUSED(provider); + + return nullptr; + } + +//static +std::unique_ptr<PBKDF> +PBKDF::create_or_throw(const std::string& algo, + const std::string& provider) + { + if(auto pbkdf = PBKDF::create(algo, provider)) + { + return pbkdf; + } + throw Lookup_Error("PBKDF", algo, provider); + } + +std::vector<std::string> PBKDF::providers(const std::string& algo_spec) + { + return probe_providers_of<PBKDF>(algo_spec, { "base", "openssl" }); + } + +void PBKDF::pbkdf_timed(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const + { + iterations = pbkdf(out, out_len, passphrase, salt, salt_len, 0, msec); + } + +void PBKDF::pbkdf_iterations(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const + { + if(iterations == 0) + throw Invalid_Argument(name() + ": Invalid iteration count"); + + const size_t iterations_run = pbkdf(out, out_len, passphrase, + salt, salt_len, iterations, + std::chrono::milliseconds(0)); + BOTAN_ASSERT_EQUAL(iterations, iterations_run, "Expected PBKDF iterations"); + } + +secure_vector<uint8_t> PBKDF::pbkdf_iterations(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const + { + secure_vector<uint8_t> out(out_len); + pbkdf_iterations(out.data(), out_len, passphrase, salt, salt_len, iterations); + return out; + } + +secure_vector<uint8_t> PBKDF::pbkdf_timed(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const + { + secure_vector<uint8_t> out(out_len); + pbkdf_timed(out.data(), out_len, passphrase, salt, salt_len, msec, iterations); + return out; + } + +} diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf.h b/comm/third_party/botan/src/lib/pbkdf/pbkdf.h new file mode 100644 index 0000000000..e7f0a84eec --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf.h @@ -0,0 +1,246 @@ +/* +* PBKDF +* (C) 1999-2007,2012,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PBKDF_H_ +#define BOTAN_PBKDF_H_ + +#include <botan/symkey.h> +#include <chrono> + +namespace Botan { + +/** +* Base class for PBKDF (password based key derivation function) +* implementations. Converts a password into a key using a salt +* and iterated hashing to make brute force attacks harder. +* +* Starting in 2.8 this functionality is also offered by PasswordHash. +* The PBKDF interface may be removed in a future release. +*/ +class BOTAN_PUBLIC_API(2,0) PBKDF + { + public: + /** + * Create an instance based on a name + * If provider is empty then best available is chosen. + * @param algo_spec algorithm name + * @param provider provider implementation to choose + * @return a null pointer if the algo/provider combination cannot be found + */ + static std::unique_ptr<PBKDF> create(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * Create an instance based on a name, or throw if the + * algo/provider combination cannot be found. If provider is + * empty then best available is chosen. + */ + static std::unique_ptr<PBKDF> + create_or_throw(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @return list of available providers for this algorithm, empty if not available + */ + static std::vector<std::string> providers(const std::string& algo_spec); + + /** + * @return new instance of this same algorithm + */ + virtual PBKDF* clone() const = 0; + + /** + * @return name of this PBKDF + */ + virtual std::string name() const = 0; + + virtual ~PBKDF() = default; + + /** + * Derive a key from a passphrase for a number of iterations + * specified by either iterations or if iterations == 0 then + * running until msec time has elapsed. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + * @param msec if iterations is zero, then instead the PBKDF is + * run until msec milliseconds has passed. + * @return the number of iterations performed + */ + virtual size_t pbkdf(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const = 0; + + /** + * Derive a key from a passphrase for a number of iterations. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + */ + void pbkdf_iterations(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const; + + /** + * Derive a key from a passphrase, running until msec time has elapsed. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec if iterations is zero, then instead the PBKDF is + * run until msec milliseconds has passed. + * @param iterations set to the number iterations executed + */ + void pbkdf_timed(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const; + + /** + * Derive a key from a passphrase for a number of iterations. + * + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + * @return the derived key + */ + secure_vector<uint8_t> pbkdf_iterations(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const; + + /** + * Derive a key from a passphrase, running until msec time has elapsed. + * + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec if iterations is zero, then instead the PBKDF is + * run until msec milliseconds has passed. + * @param iterations set to the number iterations executed + * @return the derived key + */ + secure_vector<uint8_t> pbkdf_timed(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const; + + // Following kept for compat with 1.10: + + /** + * Derive a key from a passphrase + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + */ + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const + { + return pbkdf_iterations(out_len, passphrase, salt, salt_len, iterations); + } + + /** + * Derive a key from a passphrase + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param iterations the number of iterations to use (use 10K or more) + */ + template<typename Alloc> + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const std::vector<uint8_t, Alloc>& salt, + size_t iterations) const + { + return pbkdf_iterations(out_len, passphrase, salt.data(), salt.size(), iterations); + } + + /** + * Derive a key from a passphrase + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec is how long to run the PBKDF + * @param iterations is set to the number of iterations used + */ + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const + { + return pbkdf_timed(out_len, passphrase, salt, salt_len, msec, iterations); + } + + /** + * Derive a key from a passphrase using a certain amount of time + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param msec is how long to run the PBKDF + * @param iterations is set to the number of iterations used + */ + template<typename Alloc> + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const std::vector<uint8_t, Alloc>& salt, + std::chrono::milliseconds msec, + size_t& iterations) const + { + return pbkdf_timed(out_len, passphrase, salt.data(), salt.size(), msec, iterations); + } + }; + +/* +* Compatibility typedef +*/ +typedef PBKDF S2K; + +/** +* Password based key derivation function factory method +* @param algo_spec the name of the desired PBKDF algorithm +* @param provider the provider to use +* @return pointer to newly allocated object of that type +*/ +inline PBKDF* get_pbkdf(const std::string& algo_spec, + const std::string& provider = "") + { + return PBKDF::create_or_throw(algo_spec, provider).release(); + } + +inline PBKDF* get_s2k(const std::string& algo_spec) + { + return get_pbkdf(algo_spec); + } + + +} + +#endif diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf1/info.txt b/comm/third_party/botan/src/lib/pbkdf/pbkdf1/info.txt new file mode 100644 index 0000000000..e4b972e4b6 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf1/info.txt @@ -0,0 +1,7 @@ +<defines> +PBKDF1 -> 20131128 +</defines> + +<requires> +hash +</requires> diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.cpp b/comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.cpp new file mode 100644 index 0000000000..ad922ce9c6 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.cpp @@ -0,0 +1,54 @@ +/* +* PBKDF1 +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/pbkdf1.h> +#include <botan/exceptn.h> + +namespace Botan { + +size_t PKCS5_PBKDF1::pbkdf(uint8_t output_buf[], size_t output_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const + { + if(output_len > m_hash->output_length()) + throw Invalid_Argument("PKCS5_PBKDF1: Requested output length too long"); + + m_hash->update(passphrase); + m_hash->update(salt, salt_len); + secure_vector<uint8_t> key = m_hash->final(); + + const auto start = std::chrono::high_resolution_clock::now(); + size_t iterations_performed = 1; + + while(true) + { + if(iterations == 0) + { + if(iterations_performed % 10000 == 0) + { + auto time_taken = std::chrono::high_resolution_clock::now() - start; + auto msec_taken = std::chrono::duration_cast<std::chrono::milliseconds>(time_taken); + if(msec_taken > msec) + break; + } + } + else if(iterations_performed == iterations) + break; + + m_hash->update(key); + m_hash->final(key.data()); + + ++iterations_performed; + } + + copy_mem(output_buf, key.data(), output_len); + return iterations_performed; + } + +} diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.h b/comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.h new file mode 100644 index 0000000000..f5e95b8368 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.h @@ -0,0 +1,53 @@ +/* +* PBKDF1 +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PBKDF1_H_ +#define BOTAN_PBKDF1_H_ + +#include <botan/pbkdf.h> +#include <botan/hash.h> + +BOTAN_FUTURE_INTERNAL_HEADER(pbkdf1.h) + +namespace Botan { + +/** +* PKCS #5 v1 PBKDF, aka PBKDF1 +* Can only generate a key up to the size of the hash output. +* Unless needed for backwards compatibility, use PKCS5_PBKDF2 +*/ +class BOTAN_PUBLIC_API(2,0) PKCS5_PBKDF1 final : public PBKDF + { + public: + /** + * Create a PKCS #5 instance using the specified hash function. + * @param hash pointer to a hash function object to use + */ + explicit PKCS5_PBKDF1(HashFunction* hash) : m_hash(hash) {} + + std::string name() const override + { + return "PBKDF1(" + m_hash->name() + ")"; + } + + PBKDF* clone() const override + { + return new PKCS5_PBKDF1(m_hash->clone()); + } + + size_t pbkdf(uint8_t output_buf[], size_t output_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const override; + private: + std::unique_ptr<HashFunction> m_hash; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf2/info.txt b/comm/third_party/botan/src/lib/pbkdf/pbkdf2/info.txt new file mode 100644 index 0000000000..b6c6fb4e65 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf2/info.txt @@ -0,0 +1,7 @@ +<defines> +PBKDF2 -> 20180902 +</defines> + +<requires> +hmac +</requires> diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.cpp b/comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.cpp new file mode 100644 index 0000000000..122d0fae3e --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.cpp @@ -0,0 +1,228 @@ +/* +* PBKDF2 +* (C) 1999-2007 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/pbkdf2.h> +#include <botan/exceptn.h> +#include <botan/internal/rounding.h> +#include <botan/internal/timer.h> + +namespace Botan { + +namespace { + +void pbkdf2_set_key(MessageAuthenticationCode& prf, + const char* password, + size_t password_len) + { + try + { + prf.set_key(cast_char_ptr_to_uint8(password), password_len); + } + catch(Invalid_Key_Length&) + { + throw Invalid_Argument("PBKDF2 cannot accept passphrase of the given size"); + } + } + +} + +size_t +pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], + size_t out_len, + const std::string& password, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) + { + if(iterations == 0) + { + iterations = PBKDF2(prf, out_len, msec).iterations(); + } + + PBKDF2 pbkdf2(prf, iterations); + + pbkdf2.derive_key(out, out_len, + password.c_str(), password.size(), + salt, salt_len); + + return iterations; + } + +namespace { + +size_t tune_pbkdf2(MessageAuthenticationCode& prf, + size_t output_length, + uint32_t msec) + { + if(output_length == 0) + output_length = 1; + + const size_t prf_sz = prf.output_length(); + BOTAN_ASSERT_NOMSG(prf_sz > 0); + secure_vector<uint8_t> U(prf_sz); + + const size_t trial_iterations = 2000; + + // Short output ensures we only need a single PBKDF2 block + + Timer timer("PBKDF2"); + + const auto tune_time = BOTAN_PBKDF_TUNING_TIME; + + prf.set_key(nullptr, 0); + + timer.run_until_elapsed(tune_time, [&]() { + uint8_t out[12] = { 0 }; + uint8_t salt[12] = { 0 }; + pbkdf2(prf, out, sizeof(out), salt, sizeof(salt), trial_iterations); + }); + + if(timer.events() == 0) + return trial_iterations; + + const uint64_t duration_nsec = timer.value() / timer.events(); + + const uint64_t desired_nsec = static_cast<uint64_t>(msec) * 1000000; + + if(duration_nsec > desired_nsec) + return trial_iterations; + + const size_t blocks_needed = (output_length + prf_sz - 1) / prf_sz; + + const size_t multiplier = static_cast<size_t>(desired_nsec / duration_nsec / blocks_needed); + + if(multiplier == 0) + return trial_iterations; + else + return trial_iterations * multiplier; + } + +} + +void pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], + size_t out_len, + const uint8_t salt[], + size_t salt_len, + size_t iterations) + { + if(iterations == 0) + throw Invalid_Argument("PBKDF2: Invalid iteration count"); + + clear_mem(out, out_len); + + if(out_len == 0) + return; + + const size_t prf_sz = prf.output_length(); + BOTAN_ASSERT_NOMSG(prf_sz > 0); + + secure_vector<uint8_t> U(prf_sz); + + uint32_t counter = 1; + while(out_len) + { + const size_t prf_output = std::min<size_t>(prf_sz, out_len); + + prf.update(salt, salt_len); + prf.update_be(counter++); + prf.final(U.data()); + + xor_buf(out, U.data(), prf_output); + + for(size_t i = 1; i != iterations; ++i) + { + prf.update(U); + prf.final(U.data()); + xor_buf(out, U.data(), prf_output); + } + + out_len -= prf_output; + out += prf_output; + } + } + +// PBKDF interface +size_t +PKCS5_PBKDF2::pbkdf(uint8_t key[], size_t key_len, + const std::string& password, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const + { + if(iterations == 0) + { + iterations = PBKDF2(*m_mac, key_len, msec).iterations(); + } + + PBKDF2 pbkdf2(*m_mac, iterations); + + pbkdf2.derive_key(key, key_len, + password.c_str(), password.size(), + salt, salt_len); + + return iterations; + } + +std::string PKCS5_PBKDF2::name() const + { + return "PBKDF2(" + m_mac->name() + ")"; + } + +PBKDF* PKCS5_PBKDF2::clone() const + { + return new PKCS5_PBKDF2(m_mac->clone()); + } + +// PasswordHash interface + +PBKDF2::PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec) : + m_prf(prf.clone()), + m_iterations(tune_pbkdf2(*m_prf, olen, static_cast<uint32_t>(msec.count()))) + {} + +std::string PBKDF2::to_string() const + { + return "PBKDF2(" + m_prf->name() + "," + std::to_string(m_iterations) + ")"; + } + +void PBKDF2::derive_key(uint8_t out[], size_t out_len, + const char* password, const size_t password_len, + const uint8_t salt[], size_t salt_len) const + { + pbkdf2_set_key(*m_prf, password, password_len); + pbkdf2(*m_prf, out, out_len, salt, salt_len, m_iterations); + } + +std::string PBKDF2_Family::name() const + { + return "PBKDF2(" + m_prf->name() + ")"; + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t) const + { + return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, output_len, msec)); + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::default_params() const + { + return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, 150000)); + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::from_params(size_t iter, size_t, size_t) const + { + return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, iter)); + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::from_iterations(size_t iter) const + { + return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, iter)); + } + +} diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.h b/comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.h new file mode 100644 index 0000000000..9f90799c40 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.h @@ -0,0 +1,117 @@ +/* +* PBKDF2 +* (C) 1999-2007,2012 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PBKDF2_H_ +#define BOTAN_PBKDF2_H_ + +#include <botan/pbkdf.h> +#include <botan/pwdhash.h> +#include <botan/mac.h> + +BOTAN_FUTURE_INTERNAL_HEADER(pbkdf2.h) + +namespace Botan { + +BOTAN_PUBLIC_API(2,0) size_t pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], + size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec); + +/** +* Perform PBKDF2. The prf is assumed to be keyed already. +*/ +BOTAN_PUBLIC_API(2,8) void pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], size_t out_len, + const uint8_t salt[], size_t salt_len, + size_t iterations); + +/** +* PBKDF2 +*/ +class BOTAN_PUBLIC_API(2,8) PBKDF2 final : public PasswordHash + { + public: + PBKDF2(const MessageAuthenticationCode& prf, size_t iter) : + m_prf(prf.clone()), + m_iterations(iter) + {} + + PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec); + + size_t iterations() const override { return m_iterations; } + + std::string to_string() const override; + + void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const override; + private: + std::unique_ptr<MessageAuthenticationCode> m_prf; + size_t m_iterations; + }; + +/** +* Family of PKCS #5 PBKDF2 operations +*/ +class BOTAN_PUBLIC_API(2,8) PBKDF2_Family final : public PasswordHashFamily + { + public: + PBKDF2_Family(MessageAuthenticationCode* prf) : m_prf(prf) {} + + std::string name() const override; + + std::unique_ptr<PasswordHash> tune(size_t output_len, + std::chrono::milliseconds msec, + size_t max_memory) const override; + + /** + * Return some default parameter set for this PBKDF that should be good + * enough for most users. The value returned may change over time as + * processing power and attacks improve. + */ + std::unique_ptr<PasswordHash> default_params() const override; + + std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override; + + std::unique_ptr<PasswordHash> from_params( + size_t iter, size_t, size_t) const override; + private: + std::unique_ptr<MessageAuthenticationCode> m_prf; + }; + +/** +* PKCS #5 PBKDF2 (old interface) +*/ +class BOTAN_PUBLIC_API(2,0) PKCS5_PBKDF2 final : public PBKDF + { + public: + std::string name() const override; + + PBKDF* clone() const override; + + size_t pbkdf(uint8_t output_buf[], size_t output_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const override; + + /** + * Create a PKCS #5 instance using the specified message auth code + * @param mac_fn the MAC object to use as PRF + */ + explicit PKCS5_PBKDF2(MessageAuthenticationCode* mac_fn) : m_mac(mac_fn) {} + private: + std::unique_ptr<MessageAuthenticationCode> m_mac; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/info.txt b/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/info.txt new file mode 100644 index 0000000000..5aabc09195 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/info.txt @@ -0,0 +1,7 @@ +<defines> +PGP_S2K -> 20170527 +</defines> + +<requires> +hash +</requires> diff --git a/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp b/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp new file mode 100644 index 0000000000..8bcf9239fc --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp @@ -0,0 +1,219 @@ +/* +* OpenPGP S2K +* (C) 1999-2007,2017 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/pgp_s2k.h> +#include <botan/exceptn.h> +#include <botan/internal/timer.h> +#include <algorithm> + +namespace Botan { + +/* +PGP stores the iteration count as a single byte +Thus it can only actually take on one of 256 values, based on the +formula in RFC 4880 section 3.6.1.3 +*/ +static const uint32_t OPENPGP_S2K_ITERS[256] = { + 1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, + 1664, 1728, 1792, 1856, 1920, 1984, 2048, 2176, 2304, 2432, + 2560, 2688, 2816, 2944, 3072, 3200, 3328, 3456, 3584, 3712, + 3840, 3968, 4096, 4352, 4608, 4864, 5120, 5376, 5632, 5888, + 6144, 6400, 6656, 6912, 7168, 7424, 7680, 7936, 8192, 8704, + 9216, 9728, 10240, 10752, 11264, 11776, 12288, 12800, 13312, + 13824, 14336, 14848, 15360, 15872, 16384, 17408, 18432, 19456, + 20480, 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672, + 29696, 30720, 31744, 32768, 34816, 36864, 38912, 40960, 43008, + 45056, 47104, 49152, 51200, 53248, 55296, 57344, 59392, 61440, + 63488, 65536, 69632, 73728, 77824, 81920, 86016, 90112, 94208, + 98304, 102400, 106496, 110592, 114688, 118784, 122880, 126976, + 131072, 139264, 147456, 155648, 163840, 172032, 180224, 188416, + 196608, 204800, 212992, 221184, 229376, 237568, 245760, 253952, + 262144, 278528, 294912, 311296, 327680, 344064, 360448, 376832, + 393216, 409600, 425984, 442368, 458752, 475136, 491520, 507904, + 524288, 557056, 589824, 622592, 655360, 688128, 720896, 753664, + 786432, 819200, 851968, 884736, 917504, 950272, 983040, 1015808, + 1048576, 1114112, 1179648, 1245184, 1310720, 1376256, 1441792, + 1507328, 1572864, 1638400, 1703936, 1769472, 1835008, 1900544, + 1966080, 2031616, 2097152, 2228224, 2359296, 2490368, 2621440, + 2752512, 2883584, 3014656, 3145728, 3276800, 3407872, 3538944, + 3670016, 3801088, 3932160, 4063232, 4194304, 4456448, 4718592, + 4980736, 5242880, 5505024, 5767168, 6029312, 6291456, 6553600, + 6815744, 7077888, 7340032, 7602176, 7864320, 8126464, 8388608, + 8912896, 9437184, 9961472, 10485760, 11010048, 11534336, + 12058624, 12582912, 13107200, 13631488, 14155776, 14680064, + 15204352, 15728640, 16252928, 16777216, 17825792, 18874368, + 19922944, 20971520, 22020096, 23068672, 24117248, 25165824, + 26214400, 27262976, 28311552, 29360128, 30408704, 31457280, + 32505856, 33554432, 35651584, 37748736, 39845888, 41943040, + 44040192, 46137344, 48234496, 50331648, 52428800, 54525952, + 56623104, 58720256, 60817408, 62914560, 65011712 }; + +uint8_t RFC4880_encode_count(size_t desired_iterations) + { + if(desired_iterations <= OPENPGP_S2K_ITERS[0]) + return 0; + + if(desired_iterations >= OPENPGP_S2K_ITERS[255]) + return 255; + + auto i = std::lower_bound(OPENPGP_S2K_ITERS, OPENPGP_S2K_ITERS + 256, desired_iterations); + + return static_cast<uint8_t>(i - OPENPGP_S2K_ITERS); + } + +size_t RFC4880_decode_count(uint8_t iter) + { + return OPENPGP_S2K_ITERS[iter]; + } + +namespace { + +void pgp_s2k(HashFunction& hash, + uint8_t output_buf[], size_t output_len, + const char* password, const size_t password_size, + const uint8_t salt[], size_t salt_len, + size_t iterations) + { + if(iterations > 1 && salt_len == 0) + throw Invalid_Argument("OpenPGP S2K requires a salt in iterated mode"); + + secure_vector<uint8_t> input_buf(salt_len + password_size); + if(salt_len > 0) + { + copy_mem(&input_buf[0], salt, salt_len); + } + if(password_size > 0) + { + copy_mem(&input_buf[salt_len], + cast_char_ptr_to_uint8(password), + password_size); + } + + secure_vector<uint8_t> hash_buf(hash.output_length()); + + size_t pass = 0; + size_t generated = 0; + + while(generated != output_len) + { + const size_t output_this_pass = + std::min(hash_buf.size(), output_len - generated); + + // Preload some number of zero bytes (empty first iteration) + std::vector<uint8_t> zero_padding(pass); + hash.update(zero_padding); + + // The input is always fully processed even if iterations is very small + if(input_buf.empty() == false) + { + size_t left = std::max(iterations, input_buf.size()); + while(left > 0) + { + const size_t input_to_take = std::min(left, input_buf.size()); + hash.update(input_buf.data(), input_to_take); + left -= input_to_take; + } + } + + hash.final(hash_buf.data()); + copy_mem(output_buf + generated, hash_buf.data(), output_this_pass); + generated += output_this_pass; + ++pass; + } + } + +} + +size_t OpenPGP_S2K::pbkdf(uint8_t output_buf[], size_t output_len, + const std::string& password, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const + { + std::unique_ptr<PasswordHash> pwdhash; + + if(iterations == 0) + { + RFC4880_S2K_Family s2k_params(m_hash->clone()); + iterations = s2k_params.tune(output_len, msec, 0)->iterations(); + } + + pgp_s2k(*m_hash, output_buf, output_len, + password.c_str(), password.size(), + salt, salt_len, + iterations); + + return iterations; + } + +std::string RFC4880_S2K_Family::name() const + { + return "OpenPGP-S2K(" + m_hash->name() + ")"; + } + +std::unique_ptr<PasswordHash> RFC4880_S2K_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t) const + { + const auto tune_time = BOTAN_PBKDF_TUNING_TIME; + + const size_t buf_size = 1024; + std::vector<uint8_t> buffer(buf_size); + + Timer timer("RFC4880_S2K", buf_size); + timer.run_until_elapsed(tune_time, [&]() { + m_hash->update(buffer); + }); + + const double hash_bytes_per_second = timer.bytes_per_second(); + const uint64_t desired_nsec = msec.count() * 1000000; + + const size_t hash_size = m_hash->output_length(); + const size_t blocks_required = (output_len <= hash_size ? 1 : (output_len + hash_size - 1) / hash_size); + + const double bytes_to_be_hashed = (hash_bytes_per_second * (desired_nsec / 1000000000.0)) / blocks_required; + const size_t iterations = RFC4880_round_iterations(static_cast<size_t>(bytes_to_be_hashed)); + + return std::unique_ptr<PasswordHash>(new RFC4880_S2K(m_hash->clone(), iterations)); + } + +std::unique_ptr<PasswordHash> RFC4880_S2K_Family::from_params(size_t iter, size_t, size_t) const + { + return std::unique_ptr<PasswordHash>(new RFC4880_S2K(m_hash->clone(), iter)); + } + +std::unique_ptr<PasswordHash> RFC4880_S2K_Family::default_params() const + { + return std::unique_ptr<PasswordHash>(new RFC4880_S2K(m_hash->clone(), 50331648)); + } + +std::unique_ptr<PasswordHash> RFC4880_S2K_Family::from_iterations(size_t iter) const + { + return std::unique_ptr<PasswordHash>(new RFC4880_S2K(m_hash->clone(), iter)); + } + +RFC4880_S2K::RFC4880_S2K(HashFunction* hash, size_t iterations) : + m_hash(hash), + m_iterations(iterations) + { + } + +std::string RFC4880_S2K::to_string() const + { + return "OpenPGP-S2K(" + m_hash->name() + "," + std::to_string(m_iterations) + ")"; + } + +void RFC4880_S2K::derive_key(uint8_t out[], size_t out_len, + const char* password, const size_t password_len, + const uint8_t salt[], size_t salt_len) const + { + pgp_s2k(*m_hash, out, out_len, + password, password_len, + salt, salt_len, + m_iterations); + } + +} diff --git a/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.h b/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.h new file mode 100644 index 0000000000..7fda724c2a --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.h @@ -0,0 +1,164 @@ +/* +* OpenPGP PBKDF +* (C) 1999-2007,2017 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_OPENPGP_S2K_H_ +#define BOTAN_OPENPGP_S2K_H_ + +#include <botan/pbkdf.h> +#include <botan/pwdhash.h> +#include <botan/hash.h> + +/* +This header will not be fully internal - the RFC4880 count +encoding functions will remain here. But the definition of +OpenPGP_S2K will be made internal +*/ + +//BOTAN_FUTURE_INTERNAL_HEADER(pgp_s2k.h) + +namespace Botan { + +/** +* RFC 4880 encodes the iteration count to a single-byte value +*/ +uint8_t BOTAN_PUBLIC_API(2,8) RFC4880_encode_count(size_t iterations); + +/** +* Decode the iteration count from RFC 4880 encoding +*/ +size_t BOTAN_PUBLIC_API(2,8) RFC4880_decode_count(uint8_t encoded_iter); + +/** +* Round an arbitrary iteration count to next largest iteration count +* supported by RFC4880 encoding. +*/ +inline size_t RFC4880_round_iterations(size_t iterations) + { + return RFC4880_decode_count(RFC4880_encode_count(iterations)); + } + +/** +* OpenPGP's S2K +* +* See RFC 4880 sections 3.7.1.1, 3.7.1.2, and 3.7.1.3 +* If the salt is empty and iterations == 1, "simple" S2K is used +* If the salt is non-empty and iterations == 1, "salted" S2K is used +* If the salt is non-empty and iterations > 1, "iterated" S2K is used +* +* Due to complexities of the PGP S2K algorithm, time-based derivation +* is not supported. So if iterations == 0 and msec.count() > 0, an +* exception is thrown. In the future this may be supported, in which +* case "iterated" S2K will be used and the number of iterations +* performed is returned. +* +* Note that unlike PBKDF2, OpenPGP S2K's "iterations" are defined as +* the number of bytes hashed. +*/ +class BOTAN_PUBLIC_API(2,2) OpenPGP_S2K final : public PBKDF + { + public: + /** + * @param hash the hash function to use + */ + explicit OpenPGP_S2K(HashFunction* hash) : m_hash(hash) {} + + std::string name() const override + { + return "OpenPGP-S2K(" + m_hash->name() + ")"; + } + + PBKDF* clone() const override + { + return new OpenPGP_S2K(m_hash->clone()); + } + + size_t pbkdf(uint8_t output_buf[], size_t output_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const override; + + /** + * RFC 4880 encodes the iteration count to a single-byte value + */ + static uint8_t encode_count(size_t iterations) + { + return RFC4880_encode_count(iterations); + } + + static size_t decode_count(uint8_t encoded_iter) + { + return RFC4880_decode_count(encoded_iter); + } + + private: + std::unique_ptr<HashFunction> m_hash; + }; + +/** +* OpenPGP's S2K +* +* See RFC 4880 sections 3.7.1.1, 3.7.1.2, and 3.7.1.3 +* If the salt is empty and iterations == 1, "simple" S2K is used +* If the salt is non-empty and iterations == 1, "salted" S2K is used +* If the salt is non-empty and iterations > 1, "iterated" S2K is used +* +* Note that unlike PBKDF2, OpenPGP S2K's "iterations" are defined as +* the number of bytes hashed. +*/ +class BOTAN_PUBLIC_API(2,8) RFC4880_S2K final : public PasswordHash + { + public: + /** + * @param hash the hash function to use + * @param iterations is rounded due to PGP formatting + */ + RFC4880_S2K(HashFunction* hash, size_t iterations); + + std::string to_string() const override; + + size_t iterations() const override { return m_iterations; } + + void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const override; + + private: + std::unique_ptr<HashFunction> m_hash; + size_t m_iterations; + }; + +class BOTAN_PUBLIC_API(2,8) RFC4880_S2K_Family final : public PasswordHashFamily + { + public: + RFC4880_S2K_Family(HashFunction* hash) : m_hash(hash) {} + + std::string name() const override; + + std::unique_ptr<PasswordHash> tune(size_t output_len, + std::chrono::milliseconds msec, + size_t max_mem) const override; + + /** + * Return some default parameter set for this PBKDF that should be good + * enough for most users. The value returned may change over time as + * processing power and attacks improve. + */ + std::unique_ptr<PasswordHash> default_params() const override; + + std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override; + + std::unique_ptr<PasswordHash> from_params( + size_t iter, size_t, size_t) const override; + private: + std::unique_ptr<HashFunction> m_hash; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/pbkdf/pwdhash.cpp b/comm/third_party/botan/src/lib/pbkdf/pwdhash.cpp new file mode 100644 index 0000000000..718024a227 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pwdhash.cpp @@ -0,0 +1,118 @@ +/* +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/pwdhash.h> +#include <botan/exceptn.h> +#include <botan/scan_name.h> + +#if defined(BOTAN_HAS_PBKDF2) + #include <botan/pbkdf2.h> +#endif + +#if defined(BOTAN_HAS_PGP_S2K) + #include <botan/pgp_s2k.h> +#endif + +#if defined(BOTAN_HAS_SCRYPT) + #include <botan/scrypt.h> +#endif + +#if defined(BOTAN_HAS_ARGON2) + #include <botan/argon2.h> +#endif + +#if defined(BOTAN_HAS_PBKDF_BCRYPT) + #include <botan/bcrypt_pbkdf.h> +#endif + +namespace Botan { + +std::unique_ptr<PasswordHashFamily> PasswordHashFamily::create(const std::string& algo_spec, + const std::string& provider) + { + const SCAN_Name req(algo_spec); + +#if defined(BOTAN_HAS_PBKDF2) + if(req.algo_name() == "PBKDF2") + { + // TODO OpenSSL + + if(provider.empty() || provider == "base") + { + if(auto mac = MessageAuthenticationCode::create(req.arg(0))) + return std::unique_ptr<PasswordHashFamily>(new PBKDF2_Family(mac.release())); + + if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")")) + return std::unique_ptr<PasswordHashFamily>(new PBKDF2_Family(mac.release())); + } + + return nullptr; + } +#endif + +#if defined(BOTAN_HAS_SCRYPT) + if(req.algo_name() == "Scrypt") + { + return std::unique_ptr<PasswordHashFamily>(new Scrypt_Family); + } +#endif + +#if defined(BOTAN_HAS_ARGON2) + if(req.algo_name() == "Argon2d") + { + return std::unique_ptr<PasswordHashFamily>(new Argon2_Family(0)); + } + else if(req.algo_name() == "Argon2i") + { + return std::unique_ptr<PasswordHashFamily>(new Argon2_Family(1)); + } + else if(req.algo_name() == "Argon2id") + { + return std::unique_ptr<PasswordHashFamily>(new Argon2_Family(2)); + } +#endif + +#if defined(BOTAN_HAS_PBKDF_BCRYPT) + if(req.algo_name() == "Bcrypt-PBKDF") + { + return std::unique_ptr<PasswordHashFamily>(new Bcrypt_PBKDF_Family); + } +#endif + +#if defined(BOTAN_HAS_PGP_S2K) + if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1) + { + if(auto hash = HashFunction::create(req.arg(0))) + { + return std::unique_ptr<PasswordHashFamily>(new RFC4880_S2K_Family(hash.release())); + } + } +#endif + + BOTAN_UNUSED(req); + BOTAN_UNUSED(provider); + + return nullptr; + } + +//static +std::unique_ptr<PasswordHashFamily> +PasswordHashFamily::create_or_throw(const std::string& algo, + const std::string& provider) + { + if(auto pbkdf = PasswordHashFamily::create(algo, provider)) + { + return pbkdf; + } + throw Lookup_Error("PasswordHashFamily", algo, provider); + } + +std::vector<std::string> PasswordHashFamily::providers(const std::string& algo_spec) + { + return probe_providers_of<PasswordHashFamily>(algo_spec, { "base", "openssl" }); + } + +} diff --git a/comm/third_party/botan/src/lib/pbkdf/pwdhash.h b/comm/third_party/botan/src/lib/pbkdf/pwdhash.h new file mode 100644 index 0000000000..ba64a73fd5 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/pwdhash.h @@ -0,0 +1,162 @@ +/* +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PWDHASH_H_ +#define BOTAN_PWDHASH_H_ + +#include <botan/types.h> +#include <string> +#include <memory> +#include <vector> +#include <chrono> + +namespace Botan { + +/** +* Base class for password based key derivation functions. +* +* Converts a password into a key using a salt and iterated hashing to +* make brute force attacks harder. +*/ +class BOTAN_PUBLIC_API(2,8) PasswordHash + { + public: + virtual ~PasswordHash() = default; + + virtual std::string to_string() const = 0; + + /** + * Most password hashes have some notion of iterations. + */ + virtual size_t iterations() const = 0; + + /** + * Some password hashing algorithms have a parameter which controls how + * much memory is used. If not supported by some algorithm, returns 0. + */ + virtual size_t memory_param() const { return 0; } + + /** + * Some password hashing algorithms have a parallelism parameter. + * If the algorithm does not support this notion, then the + * function returns zero. This allows distinguishing between a + * password hash which just does not support parallel operation, + * vs one that does support parallel operation but which has been + * configured to use a single lane. + */ + virtual size_t parallelism() const { return 0; } + + /** + * Returns an estimate of the total memory usage required to perform this + * key derivation. + * + * If this algorithm uses a small and constant amount of memory, with no + * effort made towards being memory hard, this function returns 0. + */ + virtual size_t total_memory_usage() const { return 0; } + + /** + * Derive a key from a password + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param password the password to derive the key from + * @param password_len the length of password in bytes + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * + * This function is const, but is not thread safe. Different threads should + * either use unique objects, or serialize all access. + */ + virtual void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const = 0; + }; + +class BOTAN_PUBLIC_API(2,8) PasswordHashFamily + { + public: + /** + * Create an instance based on a name + * If provider is empty then best available is chosen. + * @param algo_spec algorithm name + * @param provider provider implementation to choose + * @return a null pointer if the algo/provider combination cannot be found + */ + static std::unique_ptr<PasswordHashFamily> create(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * Create an instance based on a name, or throw if the + * algo/provider combination cannot be found. If provider is + * empty then best available is chosen. + */ + static std::unique_ptr<PasswordHashFamily> + create_or_throw(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @return list of available providers for this algorithm, empty if not available + */ + static std::vector<std::string> providers(const std::string& algo_spec); + + virtual ~PasswordHashFamily() = default; + + /** + * @return name of this PasswordHash + */ + virtual std::string name() const = 0; + + /** + * Return a new parameter set tuned for this machine + * @param output_length how long the output length will be + * @param msec the desired execution time in milliseconds + * + * @param max_memory_usage_mb some password hash functions can use a tunable + * amount of memory, in this case max_memory_usage limits the amount of RAM + * the returned parameters will require, in mebibytes (2**20 bytes). It may + * require some small amount above the request. Set to zero to place no + * limit at all. + */ + virtual std::unique_ptr<PasswordHash> tune(size_t output_length, + std::chrono::milliseconds msec, + size_t max_memory_usage_mb = 0) const = 0; + + /** + * Return some default parameter set for this PBKDF that should be good + * enough for most users. The value returned may change over time as + * processing power and attacks improve. + */ + virtual std::unique_ptr<PasswordHash> default_params() const = 0; + + /** + * Return a parameter chosen based on a rough approximation with the + * specified iteration count. The exact value this returns for a particular + * algorithm may change from over time. Think of it as an alternative to + * tune, where time is expressed in terms of PBKDF2 iterations rather than + * milliseconds. + */ + virtual std::unique_ptr<PasswordHash> from_iterations(size_t iterations) const = 0; + + /** + * Create a password hash using some scheme specific format. + * Eg PBKDF2 and PGP-S2K set iterations in i1 + * Scrypt uses N,r,p in i{1-3} + * Bcrypt-PBKDF just has iterations + * Argon2{i,d,id} would use iterations, memory, parallelism for i{1-3}, + * and Argon2 type is part of the family. + * + * Values not needed should be set to 0 + */ + virtual std::unique_ptr<PasswordHash> from_params( + size_t i1, + size_t i2 = 0, + size_t i3 = 0) const = 0; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/pbkdf/scrypt/info.txt b/comm/third_party/botan/src/lib/pbkdf/scrypt/info.txt new file mode 100644 index 0000000000..3ba48fd726 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/scrypt/info.txt @@ -0,0 +1,10 @@ +<defines> +SCRYPT -> 20180902 +</defines> + +<requires> +salsa20 +pbkdf2 +hmac +sha2_32 +</requires> diff --git a/comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.cpp b/comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.cpp new file mode 100644 index 0000000000..8275ac9567 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.cpp @@ -0,0 +1,249 @@ +/** +* (C) 2018 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/scrypt.h> +#include <botan/pbkdf2.h> +#include <botan/salsa20.h> +#include <botan/loadstor.h> +#include <botan/exceptn.h> +#include <botan/internal/bit_ops.h> +#include <botan/internal/timer.h> +#include <sstream> + +namespace Botan { + +std::string Scrypt_Family::name() const + { + return "Scrypt"; + } + +std::unique_ptr<PasswordHash> Scrypt_Family::default_params() const + { + return std::unique_ptr<PasswordHash>(new Scrypt(32768, 8, 1)); + } + +std::unique_ptr<PasswordHash> Scrypt_Family::tune(size_t output_length, + std::chrono::milliseconds msec, + size_t max_memory_usage_mb) const + { + BOTAN_UNUSED(output_length); + + /* + * Some rough relations between scrypt parameters and runtime. + * Denote here by stime(N,r,p) the msec it takes to run scrypt. + * + * Emperically for smaller sizes: + * stime(N,8*r,p) / stime(N,r,p) is ~ 6-7 + * stime(N,r,8*p) / stime(N,r,8*p) is ~ 7 + * stime(2*N,r,p) / stime(N,r,p) is ~ 2 + * + * Compute stime(8192,1,1) as baseline and extrapolate + */ + + const size_t max_memory_usage = max_memory_usage_mb * 1024 * 1024; + // Starting parameters + size_t N = 8192; + size_t r = 1; + size_t p = 1; + + Timer timer("Scrypt"); + const auto tune_time = BOTAN_PBKDF_TUNING_TIME; + + timer.run_until_elapsed(tune_time, [&]() { + uint8_t output[32] = { 0 }; + scrypt(output, sizeof(output), "test", 4, nullptr, 0, N, r, p); + }); + + // No timer events seems strange, perhaps something is wrong - give + // up on this and just return default params + if(timer.events() == 0) + return default_params(); + + // nsec per eval of scrypt with initial params + const uint64_t measured_time = timer.value() / timer.events(); + + const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000); + + uint64_t est_nsec = measured_time; + + // First move increase r by 8x if possible + + if(max_memory_usage == 0 || scrypt_memory_usage(N, r, p)*8 < max_memory_usage) + { + if(target_nsec / est_nsec >= 5) + { + r *= 8; + est_nsec *= 5; + } + } + + // Now double N as many times as we can + + while(max_memory_usage == 0 || scrypt_memory_usage(N, r, p)*2 < max_memory_usage) + { + if(target_nsec / est_nsec >= 2) + { + N *= 2; + est_nsec *= 2; + } + else + break; + } + + // If we have extra runtime budget, increment p + + if(target_nsec / est_nsec > 2) + p *= std::min<size_t>(1024, static_cast<size_t>(target_nsec / est_nsec)); + + return std::unique_ptr<PasswordHash>(new Scrypt(N, r, p)); + } + +std::unique_ptr<PasswordHash> Scrypt_Family::from_params(size_t N, size_t r, size_t p) const + { + return std::unique_ptr<PasswordHash>(new Scrypt(N, r, p)); + } + +std::unique_ptr<PasswordHash> Scrypt_Family::from_iterations(size_t iter) const + { + const size_t r = 8; + const size_t p = 1; + + size_t N = 8192; + + if(iter > 50000) + N = 16384; + if(iter > 100000) + N = 32768; + if(iter > 150000) + N = 65536; + + return std::unique_ptr<PasswordHash>(new Scrypt(N, r, p)); + } + +Scrypt::Scrypt(size_t N, size_t r, size_t p) : + m_N(N), m_r(r), m_p(p) + { + if(!is_power_of_2(N)) + throw Invalid_Argument("Scrypt N parameter must be a power of 2"); + + if(p == 0 || p > 1024) + throw Invalid_Argument("Invalid or unsupported scrypt p"); + if(r == 0 || r > 256) + throw Invalid_Argument("Invalid or unsupported scrypt r"); + if(N < 1 || N > 4194304) + throw Invalid_Argument("Invalid or unsupported scrypt N"); + } + +std::string Scrypt::to_string() const + { + std::ostringstream oss; + oss << "Scrypt(" << m_N << "," << m_r << "," << m_p << ")"; + return oss.str(); + } + +size_t Scrypt::total_memory_usage() const + { + return scrypt_memory_usage(m_N, m_r, m_p); + } + +void Scrypt::derive_key(uint8_t output[], size_t output_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const + { + scrypt(output, output_len, + password, password_len, + salt, salt_len, + N(), r(), p()); + } + +namespace { + +void scryptBlockMix(size_t r, uint8_t* B, uint8_t* Y) + { + uint32_t B32[16]; + secure_vector<uint8_t> X(64); + copy_mem(X.data(), &B[(2*r-1)*64], 64); + + for(size_t i = 0; i != 2*r; i++) + { + xor_buf(X.data(), &B[64*i], 64); + load_le<uint32_t>(B32, X.data(), 16); + Salsa20::salsa_core(X.data(), B32, 8); + copy_mem(&Y[64*i], X.data(), 64); + } + + for(size_t i = 0; i < r; ++i) + { + copy_mem(&B[i*64], &Y[(i * 2) * 64], 64); + } + + for(size_t i = 0; i < r; ++i) + { + copy_mem(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64); + } + } + +void scryptROMmix(size_t r, size_t N, uint8_t* B, secure_vector<uint8_t>& V) + { + const size_t S = 128 * r; + + for(size_t i = 0; i != N; ++i) + { + copy_mem(&V[S*i], B, S); + scryptBlockMix(r, B, &V[N*S]); + } + + for(size_t i = 0; i != N; ++i) + { + // compiler doesn't know here that N is power of 2 + const size_t j = load_le<uint32_t>(&B[(2*r-1)*64], 0) & (N - 1); + xor_buf(B, &V[j*S], S); + scryptBlockMix(r, B, &V[N*S]); + } + } + +} + +void scrypt(uint8_t output[], size_t output_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len, + size_t N, size_t r, size_t p) + { + const size_t S = 128 * r; + secure_vector<uint8_t> B(p * S); + // temp space + secure_vector<uint8_t> V((N+1) * S); + + auto hmac_sha256 = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)"); + + try + { + hmac_sha256->set_key(cast_char_ptr_to_uint8(password), password_len); + } + catch(Invalid_Key_Length&) + { + throw Invalid_Argument("Scrypt cannot accept passphrases of the provided length"); + } + + pbkdf2(*hmac_sha256.get(), + B.data(), B.size(), + salt, salt_len, + 1); + + // these can be parallel + for(size_t i = 0; i != p; ++i) + { + scryptROMmix(r, N, &B[128*r*i], V); + } + + pbkdf2(*hmac_sha256.get(), + output, output_len, + B.data(), B.size(), + 1); + } + +} diff --git a/comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.h b/comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.h new file mode 100644 index 0000000000..60cdbd0460 --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.h @@ -0,0 +1,127 @@ +/** +* (C) 2018 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SCRYPT_H_ +#define BOTAN_SCRYPT_H_ + +#include <botan/pwdhash.h> + +//BOTAN_FUTURE_INTERNAL_HEADER(scrypt.h) + +namespace Botan { + +/** +* Scrypt key derivation function (RFC 7914) +*/ +class BOTAN_PUBLIC_API(2,8) Scrypt final : public PasswordHash + { + public: + Scrypt(size_t N, size_t r, size_t p); + + Scrypt(const Scrypt& other) = default; + Scrypt& operator=(const Scrypt&) = default; + + /** + * Derive a new key under the current Scrypt parameter set + */ + void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const override; + + std::string to_string() const override; + + size_t N() const { return m_N; } + size_t r() const { return m_r; } + size_t p() const { return m_p; } + + size_t iterations() const override { return r(); } + + size_t parallelism() const override { return p(); } + + size_t memory_param() const override { return N(); } + + size_t total_memory_usage() const override; + + private: + size_t m_N, m_r, m_p; + }; + +class BOTAN_PUBLIC_API(2,8) Scrypt_Family final : public PasswordHashFamily + { + public: + std::string name() const override; + + std::unique_ptr<PasswordHash> tune(size_t output_length, + std::chrono::milliseconds msec, + size_t max_memory) const override; + + std::unique_ptr<PasswordHash> default_params() const override; + + std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override; + + std::unique_ptr<PasswordHash> from_params( + size_t N, size_t r, size_t p) const override; + }; + +/** +* Scrypt key derivation function (RFC 7914) +* +* @param output the output will be placed here +* @param output_len length of output +* @param password the user password +* @param password_len length of password +* @param salt the salt +* @param salt_len length of salt +* @param N the CPU/Memory cost parameter, must be power of 2 +* @param r the block size parameter +* @param p the parallelization parameter +* +* Suitable parameters for most uses would be N = 32768, r = 8, p = 1 +* +* Scrypt uses approximately (p + N + 1) * 128 * r bytes of memory +*/ +void BOTAN_PUBLIC_API(2,8) scrypt(uint8_t output[], size_t output_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len, + size_t N, size_t r, size_t p); + +/** +* Scrypt key derivation function (RFC 7914) +* Before 2.8 this function was the primary interface for scrypt +* +* @param output the output will be placed here +* @param output_len length of output +* @param password the user password +* @param salt the salt +* @param salt_len length of salt +* @param N the CPU/Memory cost parameter, must be power of 2 +* @param r the block size parameter +* @param p the parallelization parameter +* +* Suitable parameters for most uses would be N = 32768, r = 8, p = 1 +* +* Scrypt uses approximately (p + N + 1) * 128 * r bytes of memory +*/ +inline void scrypt(uint8_t output[], size_t output_len, + const std::string& password, + const uint8_t salt[], size_t salt_len, + size_t N, size_t r, size_t p) + { + return scrypt(output, output_len, + password.c_str(), password.size(), + salt, salt_len, + N, r, p); + } + +inline size_t scrypt_memory_usage(size_t N, size_t r, size_t p) + { + return 128 * r * (N + p); + } + +} + +#endif |