aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lib/base/buf_comp.h19
-rw-r--r--src/lib/pbkdf/argon2/argon2.cpp411
-rw-r--r--src/lib/pbkdf/argon2/argon2.h36
-rw-r--r--src/lib/pbkdf/argon2/info.txt8
-rw-r--r--src/tests/data/argon2.vec359
-rw-r--r--src/tests/test_pbkdf.cpp54
6 files changed, 882 insertions, 5 deletions
diff --git a/src/lib/base/buf_comp.h b/src/lib/base/buf_comp.h
index a6cc84ba3..e1971c458 100644
--- a/src/lib/base/buf_comp.h
+++ b/src/lib/base/buf_comp.h
@@ -57,11 +57,20 @@ class BOTAN_PUBLIC_API(2,0) Buffered_Computation
*/
template<typename T> void update_be(const T in)
{
- for(size_t i = 0; i != sizeof(T); ++i)
- {
- uint8_t b = get_byte(i, in);
- add_data(&b, 1);
- }
+ uint8_t inb[sizeof(T)];
+ store_be(in, inb);
+ add_data(inb, sizeof(inb));
+ }
+
+ /**
+ * Add an integer in little-endian order
+ * @param in the value
+ */
+ template<typename T> void update_le(const T in)
+ {
+ uint8_t inb[sizeof(T)];
+ store_le(in, inb);
+ add_data(inb, sizeof(inb));
}
/**
diff --git a/src/lib/pbkdf/argon2/argon2.cpp b/src/lib/pbkdf/argon2/argon2.cpp
new file mode 100644
index 000000000..e446e1513
--- /dev/null
+++ b/src/lib/pbkdf/argon2/argon2.cpp
@@ -0,0 +1,411 @@
+/**
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/argon2.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<uint32_t>(p);
+ blake2b.update_le<uint32_t>(output_len);
+ blake2b.update_le<uint32_t>(M);
+ blake2b.update_le<uint32_t>(t);
+ blake2b.update_le<uint32_t>(v);
+ blake2b.update_le<uint32_t>(y);
+
+ blake2b.update_le<uint32_t>(password_len);
+ blake2b.update(cast_char_ptr_to_uint8(password), password_len);
+
+ blake2b.update_le<uint32_t>(salt_len);
+ blake2b.update(salt, salt_len);
+
+ blake2b.update_le<uint32_t>(key_len);
+ blake2b.update(key, key_len);
+
+ blake2b.update_le<uint32_t>(ad_len);
+ blake2b.update(ad, ad_len);
+
+ return blake2b.final();
+ }
+
+void Htick(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);
+
+ secure_vector<uint8_t> B(blake2b.output_length());
+
+ blake2b.update_le<uint32_t>(output_len);
+ blake2b.update(H0);
+ blake2b.update_le<uint32_t>(p0);
+ blake2b.update_le<uint32_t>(p1);
+
+ blake2b.final(&B[0]);
+
+ while(output_len > 64)
+ {
+ copy_mem(output, &B[0], 32);
+ output_len -= 32;
+ output += 32;
+
+ blake2b.update(B);
+ blake2b.final(&B[0]);
+ }
+
+ if(output_len > 0)
+ copy_mem(output, &B[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
+ {
+ throw Not_Implemented("todo");
+ }
+ }
+
+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);
+
+ 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(&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(&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 + 2*(A & 0xFFFFFFFF)*(B & 0xFFFFFFFF);
+ D = rotr<32>(A ^ D);
+
+ C += D + 2*(C & 0xFFFFFFFF) * (D & 0xFFFFFFFF);
+ B = rotr<24>(B ^ C);
+
+ A += B + 2*(A & 0xFFFFFFFF) * (B & 0xFFFFFFFF);
+ D = rotr<16>(A ^ D);
+
+ C += D + 2*(C & 0xFFFFFFFF) * (D & 0xFFFFFFFF);
+ 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>& B,
+ size_t offset,
+ size_t prev,
+ size_t new_offset)
+ {
+ secure_vector<uint64_t> T(128);
+
+ 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>& 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);
+
+ 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)
+ {
+ secure_vector<uint64_t> T = B;
+
+ 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 ref_lane*lanes + (s + m - (p+1)) % lanes;
+ }
+
+void process_block_argon2d(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(B, offset, prev, new_offset);
+
+ index += 1;
+ }
+ }
+
+void process_block_argon2i(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(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(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(B, offset, prev, new_offset);
+
+ index += 1;
+ }
+ }
+
+void process_blocks(secure_vector<uint64_t>& B,
+ size_t t,
+ size_t memory,
+ size_t threads,
+ size_t mode)
+ {
+ const size_t lanes = memory / threads;
+ const size_t segments = lanes / SYNC_POINTS;
+
+ 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(B, n, slice, lane, lanes, segments, threads, mode, memory, t);
+ else
+ process_block_argon2d(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,
+ size_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/src/lib/pbkdf/argon2/argon2.h b/src/lib/pbkdf/argon2/argon2.h
new file mode 100644
index 000000000..27a6a3220
--- /dev/null
+++ b/src/lib/pbkdf/argon2/argon2.h
@@ -0,0 +1,36 @@
+/**
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ARGON2_H_
+#define BOTAN_ARGON2_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* Argon2 key derivation function
+*
+* @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 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,
+ size_t y, size_t p, size_t M, size_t t);
+
+}
+
+#endif
diff --git a/src/lib/pbkdf/argon2/info.txt b/src/lib/pbkdf/argon2/info.txt
new file mode 100644
index 000000000..0c7adfeea
--- /dev/null
+++ b/src/lib/pbkdf/argon2/info.txt
@@ -0,0 +1,8 @@
+<defines>
+ARGON2 -> 20190527
+</defines>
+
+<requires>
+blake2
+</requires>
+
diff --git a/src/tests/data/argon2.vec b/src/tests/data/argon2.vec
new file mode 100644
index 000000000..b81d14c0e
--- /dev/null
+++ b/src/tests/data/argon2.vec
@@ -0,0 +1,359 @@
+
+# First three are the official test vectors
+
+[Argon2d]
+
+M = 32
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 512b391b6f1162975371d30919734294f868e3be3984f3c1a13a4db9fabe4acb
+
+[Argon2i]
+M = 32
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+Secret = 0303030303030303
+AD = 040404040404040404040404
+Output = c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016dd388d29952a4c4672b6ce8
+
+[Argon2id]
+
+M = 32
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659
+
+
+# Remainder generated by Golang x/crypto
+
+[Argon2d]
+
+M = 64
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = ab75c7556cd63bbaa818e02dbdfe8c69e80375d64b31d6a7b2bf41da7f7c9951
+
+M = 128
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 5fc18a6a56b67cadf60287babc490ca0e866f0880a2b51e56a0ab0a640179d13
+
+M = 128
+T = 10
+P = 16
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = ff62eb9ab9e3f258def9fc50ce2e79ef15affb0f3a4d68768592c4934dc8e942b1b36c404e62d0878d5886bfbed5ef3f9de33559ebcdc4dedc80af5d7c5337cd
+
+M = 256
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = e0626056972c990e1de37b1da8542ee5dafa2538c304576bb22f403ea815507c
+
+M = 256
+T = 10
+P = 16
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = d30dab40b07d39f728b2acb2e8c30a4c94606d1acee7834c48d89d8425daa2ec270e76159f0364e6cf139c55e91d57581b128de54ffd2dfaeb881cdad6da8546
+
+M = 256
+T = 16
+P = 32
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 30a011a4b36ab8cf855998346b3c73bc15cc79b39a736959aac9f0fc798b62647985b517214ea28b8f841633012f9057241d2f9ea38e2d15c7d8397910fe896e
+
+M = 512
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 61176735ba6480f0640edb93536f182a1966bf4d2174ce549a755c8947be774b
+
+M = 512
+T = 10
+P = 16
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 50ac3a40ebb256aacac62c477c9748d931b37de951f3f1a39810b51b6381f7a2799bd169729bb7de2cdbb7315482ec3a6a13817d2b42543b5f8445963d330651
+
+M = 512
+T = 16
+P = 32
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 31103f14f51f0b2f474706086daec764a75384296ca80cac52147839fa02d9edc23cbdb1b478d9912c11c58fe07ec7597e5ea8a5f173b9ef5700d77a779da9e9
+
+M = 1024
+T = 32
+P = 16
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 24fdf8ec8416e8fa5a235747530984098a9c925298eb32924a7c3a2b8bddd520fc96e22501d45bd74ed9b4384fcdc5b62d034052cff68558b8c85dac23fee8cb
+
+M = 1024
+T = 32
+P = 64
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 9344d8ec27e27ca85b233fd1b7d0ff25a28856266ffce47d35a85352cb7c1cae052ba852da31508e312802586cfd795c9efc6e280c2bf7065884f30a14736779
+
+[Argon2i]
+
+M = 64
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 0f639e5eb9ae1d4d582ccb6033b95551f916a2bdf48ae23d2b8ba4414eb6a182
+
+M = 128
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 88031ec2094b24a9c4399e7f3fdaa5701dc3bae89917c6ba582e924a547a623d
+
+M = 128
+T = 10
+P = 16
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 74d7ce52764e26f3288e3904d0a38a5e91d15ee9227853b19406c383f6495d813e1eec283eb6bde34657b70b821bde646f4d89012b55be281cd5e68812fb4d73
+
+M = 256
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = bba02f115715a78c61a11aa3457169f781c86566d01841872dad7a2eeefce06a
+
+M = 256
+T = 10
+P = 16
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = b61c0c8f5fabb9fcf186e0786c8df566ea2ea715a096d7d02ff30417569076f1a98af9ea0e511775a09c866ae8692ba56986a4b1580c5455531f0ed8924498fa
+
+M = 256
+T = 16
+P = 32
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = ab86056dfbe69955d2c0985d388a4a45424c33c4ce8d15d30035da14d117640229e136974d07ac980a3d41ebbf566f61f8e1b42575eeb16d4757fdf358b30daa
+
+M = 512
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = c999a66d8811e28ba8b48334021845290c7e8cd4f010035bf212f24bacb49a96
+
+M = 512
+T = 10
+P = 16
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = f38964a89962f22ccbbbde4452298f4d33e12361c4ace50b4aacdac88f5f13b18935dc9a29424f283808904e77e3f6690fe5f83834cc7205a7e03791389b7b77
+
+M = 512
+T = 16
+P = 32
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 431e57fbf29ff740d24f1922acc89f5cc28984f0b7d4ef3fb9a4eee5bc50fdcdafbc386dd9232447093fe6afe5d3a0f0c3732d363fb66f1c0dc696af70180546
+
+M = 1024
+T = 32
+P = 16
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 66be17b18ad5626e2a872672379da0072178fb8844414cb340333a21d6670c9c1c7440b1a7dd3e6f7a110f79d86f568952733bbdc48f00355a843d55f0c0d129
+
+M = 1024
+T = 32
+P = 64
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 06835f6c5acd842f29dc5dc9a92b163082ebde4784b55684dcc9ece1537be68057dbc686a2fdf525e51b3af60b25ea4065e93b99640f0728362c8f0a7d5e6f1e
+
+M = 4096
+T = 32
+P = 64
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 2a5f4b6db72e56a65e3ae159d1761dce1f5e139d19655bd981c97977b6b5ee713944279681e4b9c73402a2c4e2aceee5d67fe84a3868b7fcf2ce4c817005d1ed
+
+[Argon2id]
+
+M = 64
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 4275ee5ad887fe3270e82f01e97db8af3cf63fc7f2102bfea84b305f416a4544
+
+M = 128
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 8ec72f253bd35d55c3e49c587c77665c9c7fcff26cb3cabe179039b7c4281a48
+
+M = 128
+T = 10
+P = 16
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 5b52005e900e0435ecdf7167e51ff629eb6ee6f475904878592180af8653bdd6ac6a891a6b386feea7db9c1addafce08a72e30822227307d4dfe4d171c1dd53a
+
+M = 256
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 42dd95b7d33d4162b307eb5520b90c2b718726cb40076092f92a450b1498fde0
+
+M = 256
+T = 10
+P = 16
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = f73b80dd42a7669e98aa98c58007b022055a0c0024d6b9064119b9d3ecba2476e4dcf4e444ba59762960a16660fff039ea80448a1f1e9b35814a05e311f52426
+
+M = 256
+T = 16
+P = 32
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 4e984d66d607d29a027b9a5a5844b3156c691772132bbba877ca317445f242d50a438a79a5efa8c2d54049d54aaf91e47576c7f331dd4d34bc710259d49b7b06
+
+M = 512
+T = 3
+P = 4
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 7c0d17d7212b31faf30b60db73e7c561be0b477d34fa1d58fb6896c5a0b8e697
+
+M = 512
+T = 10
+P = 16
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = 768bef077ad20536af34bd81ff054eb81f100d3e718768cc87f21948645594b9b452932f3ec9bd4ccf55786898942d3acc0ddd8efbeca7cc7d7ebab3df6c1758
+
+M = 512
+T = 16
+P = 32
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = a4f25b3979795e79979fde49be72cb59a4a50567a306d0a380f33103ee6ebc9fcb7a4bd9decc5ef84def1e3c24a60e43988e874c2689b1303c68701c58f099d6
+
+M = 1024
+T = 32
+P = 16
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = ab56ff1ec4b64f32083b6edad23cd880c2ade2f8474882f55ca034ab850adcdb55f565c05062f5e13ae3ac6829678518cba652b539d503902d36922773f55bdf
+
+M = 1024
+T = 32
+P = 64
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = a1187c4a4406093d68c6844731b9d9315ea9d03ff8031564dbe7f437a35debe327ea852d5a5b66a564838a5d2b5c48cc7b4ecde663f6713f4cb4e967295eafbf
+
+M = 4096
+T = 32
+P = 64
+Passphrase = 0101010101010101010101010101010101010101010101010101010101010101
+Salt = 02020202020202020202020202020202
+AD = 040404040404040404040404
+Secret = 0303030303030303
+Output = f76f7ac4e23bae5c3d1797f5d8a7b40222f770f0b6d339d8b5d4c168a2dfb512838b2bd5f110397e1c15267f782f0067d8ef567a7556470cd13af4dedf1d585d
+
diff --git a/src/tests/test_pbkdf.cpp b/src/tests/test_pbkdf.cpp
index 34ed64c3f..9ddb1e171 100644
--- a/src/tests/test_pbkdf.cpp
+++ b/src/tests/test_pbkdf.cpp
@@ -20,6 +20,10 @@
#include <botan/scrypt.h>
#endif
+#if defined(BOTAN_HAS_ARGON2)
+ #include <botan/argon2.h>
+#endif
+
namespace Botan_Tests {
namespace {
@@ -192,6 +196,56 @@ BOTAN_REGISTER_TEST("scrypt", Scrypt_KAT_Tests);
#endif
+#if defined(BOTAN_HAS_ARGON2)
+
+class Argon2_KAT_Tests final : public Text_Based_Test
+ {
+ public:
+ Argon2_KAT_Tests() : Text_Based_Test("argon2.vec", "Passphrase,Salt,P,M,T,Output", "Secret,AD") {}
+
+ Test::Result run_one_test(const std::string& mode, const VarMap& vars) override
+ {
+ const size_t P = vars.get_req_sz("P");
+ const size_t M = vars.get_req_sz("M");
+ const size_t T = vars.get_req_sz("T");
+ const std::vector<uint8_t> key = vars.get_opt_bin("Secret");
+ const std::vector<uint8_t> ad = vars.get_opt_bin("AD");
+ const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
+ const std::vector<uint8_t> passphrase = vars.get_req_bin("Passphrase");
+ const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+
+ uint8_t family;
+ if(mode == "Argon2d")
+ family = 0;
+ else if(mode == "Argon2i")
+ family = 1;
+ else if(mode == "Argon2id")
+ family = 2;
+ else
+ throw Test_Error("Unknown Argon2 mode");
+
+ Test::Result result(mode);
+
+ std::vector<uint8_t> output(expected.size());
+ Botan::argon2(output.data(), output.size(),
+ reinterpret_cast<const char*>(passphrase.data()),
+ passphrase.size(),
+ salt.data(), salt.size(),
+ key.data(), key.size(),
+ ad.data(), ad.size(),
+ family, P, M, T);
+
+ result.test_eq("derived key", output, expected);
+
+ return result;
+ }
+
+ };
+
+BOTAN_REGISTER_TEST("argon2", Argon2_KAT_Tests);
+
+#endif
+
#if defined(BOTAN_HAS_PGP_S2K)
class PGP_S2K_Iter_Test final : public Test