summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan/src/lib/pbkdf
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /comm/third_party/botan/src/lib/pbkdf
parentInitial commit. (diff)
downloadthunderbird-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')
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/argon2/argon2.cpp443
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/argon2/argon2.h118
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/argon2/argon2fmt.cpp125
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp154
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/argon2/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp183
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h77
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/info.txt13
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf.cpp133
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf.h246
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf1/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.cpp54
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.h53
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf2/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.cpp228
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.h117
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pgp_s2k/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp219
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.h164
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pwdhash.cpp118
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pwdhash.h162
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/scrypt/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.cpp249
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.h127
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