commit bdcabe65573c3fd5f63c01a963afb899dea2754d
parent f37b4746f4e762593d6a58997bcdff30b9f08a31
Author: Armin Preiml <apreiml@strohwolke.at>
Date: Mon, 20 Dec 2021 18:57:27 +0100
implement version 1.3 of crypto::argon2
The implemenation is based on the rfc9106. Comments and parameter
choice guidelines have been taken or derived from the rfc.
Signed-off-by: Armin Preiml <apreiml@strohwolke.at>
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
5 files changed, 701 insertions(+), 0 deletions(-)
diff --git a/crypto/argon2/+test.ha b/crypto/argon2/+test.ha
@@ -0,0 +1,128 @@
+use bytes;
+
+@test fn mode_d_one_pass() void = {
+ let pass: [32]u8 = [1...];
+ let salt: [16]u8 = [2...];
+ let secret: [8]u8 = [3...];
+ let data: [12]u8 = [4...];
+ let result: [32]u8 = [0...];
+
+ let expected: [_]u8 = [
+ 0xfa, 0x17, 0x75, 0xca, 0x80, 0x90, 0x64, 0x66, 0x18, 0xbe,
+ 0x70, 0xeb, 0x0f, 0xc9, 0xde, 0x43, 0x67, 0x58, 0xed, 0x0c,
+ 0xa5, 0x36, 0x83, 0x1a, 0xe9, 0xe1, 0x03, 0x48, 0x93, 0x81,
+ 0xc1, 0x79,
+ ];
+
+ let cfg = config {
+ secret = secret,
+ data = data,
+ passes = 1,
+ parallel = 4,
+ version = 0x13,
+ mem = 32,
+ ...
+ };
+
+ argon2d(result[..], pass, salt, cfg)!;
+
+ assert(bytes::equal(result, expected));
+};
+
+@test fn rfc_d_test_vector() void = {
+ let pass: [32]u8 = [1...];
+ let salt: [16]u8 = [2...];
+ let secret: [8]u8 = [3...];
+ let data: [12]u8 = [4...];
+ let result: [32]u8 = [0...];
+
+ let mem: *[32 * BLOCKSIZE]u64 = alloc([0...]);
+ defer free(mem);
+
+ let expected: [_]u8 = [
+ 0x51, 0x2b, 0x39, 0x1b, 0x6f, 0x11, 0x62, 0x97, 0x53, 0x71,
+ 0xd3, 0x09, 0x19, 0x73, 0x42, 0x94, 0xf8, 0x68, 0xe3, 0xbe,
+ 0x39, 0x84, 0xf3, 0xc1, 0xa1, 0x3a, 0x4d, 0xb9, 0xfa, 0xbe,
+ 0x4a, 0xcb,
+ ];
+
+ let cfg = config {
+ secret = secret,
+ data = data,
+ passes = 3,
+ parallel = 4,
+ version = 0x13,
+ mem = mem[..],
+ ...
+ };
+
+ argon2d(result[..], pass, salt, cfg)!;
+
+ assert(bytes::equal(result, expected));
+};
+
+
+@test fn rfc_i_test_vector() void = {
+ let pass: [32]u8 = [1...];
+ let salt: [16]u8 = [2...];
+ let secret: [8]u8 = [3...];
+ let data: [12]u8 = [4...];
+ let result: [32]u8 = [0...];
+
+ let mem: *[32 * BLOCKSIZE]u64 = alloc([0...]);
+ defer free(mem);
+
+ let expected: [_]u8 = [
+ 0xc8, 0x14, 0xd9, 0xd1, 0xdc, 0x7f, 0x37, 0xaa, 0x13, 0xf0,
+ 0xd7, 0x7f, 0x24, 0x94, 0xbd, 0xa1, 0xc8, 0xde, 0x6b, 0x01,
+ 0x6d, 0xd3, 0x88, 0xd2, 0x99, 0x52, 0xa4, 0xc4, 0x67, 0x2b,
+ 0x6c, 0xe8,
+ ];
+
+ let cfg = config {
+ secret = secret,
+ data = data,
+ passes = 3,
+ parallel = 4,
+ version = 0x13,
+ mem = mem[..],
+ ...
+ };
+
+ argon2i(result[..], pass, salt, cfg)!;
+
+ assert(bytes::equal(result, expected));
+};
+
+@test fn rfc_id_test_vector() void = {
+ let pass: [32]u8 = [1...];
+ let salt: [16]u8 = [2...];
+ let secret: [8]u8 = [3...];
+ let data: [12]u8 = [4...];
+ let result: [32]u8 = [0...];
+
+ let mem: *[32 * BLOCKSIZE]u64 = alloc([0...]);
+ defer free(mem);
+
+ let expected: [_]u8 = [
+ 0x0d, 0x64, 0x0d, 0xf5, 0x8d, 0x78, 0x76, 0x6c, 0x08, 0xc0,
+ 0x37, 0xa3, 0x4a, 0x8b, 0x53, 0xc9, 0xd0, 0x1e, 0xf0, 0x45,
+ 0x2d, 0x75, 0xb6, 0x5e, 0xb5, 0x25, 0x20, 0xe9, 0x6b, 0x01,
+ 0xe6, 0x59,
+ ];
+
+ let cfg = config {
+ secret = secret,
+ data = data,
+ passes = 3,
+ parallel = 4,
+ version = 0x13,
+ mem = mem[..],
+ ...
+ };
+
+ argon2id(result[..], pass, salt, cfg)!;
+
+ assert(bytes::equal(result, expected));
+};
+
diff --git a/crypto/argon2/README b/crypto/argon2/README
@@ -0,0 +1,21 @@
+This module provides an implementation of the argon2 key derivation function as
+described by RFC 9106. This is the recommended algorithm for password hashing in
+Hare programs, and for deriving keys for use with other cryptographic
+algorithms. Some thought must be given to the appropriate configuration for your
+use case. Some general advice is provided here; if in doubt, consult the RFC.
+
+The argon2 parameters are configured via the [[config]] structure. To determine
+the appropriate configuration parameters for a particular use-case, consult
+section 4 of the RFC. Otherwise, sane defaults for common scenarios are provided
+via [[default_config]] and [[low_mem_config]]; consult the docs of each
+configuration for details.
+
+Once a suitable configuration has been selected, the user must provide a salt.
+This salt should be stored alongside the hash, should be unique for each
+password, and should be random: see [[crypto::random]]. The salt and hash
+lengths are configurable, the recommended defaults are 16 and 32 bytes
+respectively.
+
+Equipped with the necessary parameters, the user may call the appropriate argon2
+variant via [[argon2d]], [[argon2i]], or [[argon2id]]. If unsure which to use,
+choose [[argon2i]].
diff --git a/crypto/argon2/argon2.ha b/crypto/argon2/argon2.ha
@@ -0,0 +1,506 @@
+use bufio;
+use bytes;
+use crypto::blake2b;
+use crypto::math;
+use endian;
+use errors::{nomem};
+use hash;
+use io;
+use rt;
+use types;
+
+// Current implemented version of argon2
+export def VERSION: u8 = 0x13;
+
+// Number of u64 elements of one block.
+export def BLOCKSIZE: u32 = 128;
+
+def SLICES: size = 4;
+
+type block64 = [BLOCKSIZE]u64;
+
+const zeroblock: block64 = [0...];
+
+type mode = enum {
+ D = 0,
+ I = 1,
+ ID = 2,
+};
+
+// This type provides configuration options for the argon2 algorithm. Most users
+// will find [[default_config]] or [[low_mem_config]] suitable for their needs
+// without providing a custom configuration. If writing a custom configuration,
+// consult the RFC for advice on selecting suitable values for your use-case.
+//
+// 'parallel' specifies the number of parallel processes. 'pass' configures the
+// number of iterations. Both values must be at least one. Note: the Hare
+// implementation of argon2 does not process hashes in parallel, though it will
+// still compute the correct hash if this value is greater than one.
+//
+// 'version' specifies the version of the argon2 function. The implementation
+// currently only supports version 1.3. Use [[argon2::VERSION]] here.
+//
+// 'secret' and 'data' are optional byte arrays that are applied to the initial
+// state. Consult the RFC for details.
+//
+// The 'mem' parameter is used to configure working memory used during the
+// computation. The argon2 algorithm requires a large amount of memory to
+// compute hashes. If 'mem' set to a u32, it is interpreted as the desired
+// number of 1024-byte blocks the implementation shall allocate for you. If the
+// caller wants to manage the allocation itself, provide a []u8 instead. The
+// length of this slice must be at least 8 times the value of 'parallel' in
+// blocks, and must be a multiple of [[BLOCKSIZE]]. To have the implementation
+// allocate 64 KiB, set 'mem' to 64. To use the same amount of caller-provided
+// memory, provide a slice of length 64 * [[BLOCKSIZE]].
+export type config = struct {
+ mem: (u32 | []u64),
+ parallel: u32,
+ passes: u32,
+ version: u8,
+ secret: []u8,
+ data: []u8
+};
+
+// The default recommended configuration for most use cases. This configuration
+// uses 2 GiB of working memory. A 16-byte 'salt' and 32-byte 'dest' parameter
+// is recommended in combination with this configuration.
+export const default_config: config = config {
+ mem = 2 * 1024 * 1024,
+ passes = 1,
+ parallel = 4,
+ version = 0x13,
+ ...
+};
+
+// The default recommended configuration for memory-constrained use cases. This
+// configuration uses 64 MiB of working memory. A 16-byte 'salt' and 32-byte
+// 'dest' parameter is recommended in combination with this configuration.
+export const low_mem_config: config = config {
+ mem = 64 * 1024,
+ passes = 3,
+ parallel = 4,
+ version = 0x13,
+ ...
+};
+
+type context = struct {
+ mode: mode,
+ cols: size,
+ rows: size,
+ sliceblocks: size,
+ mem: []u64,
+ pass: u32,
+ seedsinit: block64,
+ seedblock: block64,
+};
+
+// Computes an argon2d hash, writing the digest to 'dest'. A 'salt' length of 16
+// bytes is recommended, and 8 bytes is the minimum. A 'dest' length of 32 bytes
+// is recommended, and 4 bytes is the minimum.
+//
+// The argon2d mode uses data-dependent memory access and is suitable for
+// applications with no threats of side-channel timing attacks.
+export fn argon2d(
+ dest: []u8,
+ password: []u8,
+ salt: []u8,
+ cfg: config
+) (void | nomem) = {
+ return argon2(dest, password, salt, cfg, mode::D);
+};
+
+// Computes an argon2i hash, writing the digest to 'dest'. A 'salt' length of 16
+// bytes is recommended, and 8 bytes is the minimum. A 'dest' length of 32 bytes
+// is recommended, and 4 bytes is the minimum.
+//
+// The argon2i mode uses data-independent memory access and is suitable for
+// password hashing and key derivation. It makes more passes over memory to
+// protect from trade-off attacks.
+//
+// If you are unsure which variant to use, argon2i is recommended.
+export fn argon2i(
+ dest: []u8,
+ password: []u8,
+ salt: []u8,
+ cfg: config
+) (void | nomem) = {
+ return argon2(dest, password, salt, cfg, mode::I);
+};
+
+// Computes an argon2id hash, writing the digest to 'dest'. A 'salt' length of
+// 16 bytes is recommended, and 8 bytes is the minimum. A 'dest' length of 32
+// bytes is recommended, and 4 bytes is the minimum.
+//
+// The argon2id mode works by using argon2i for the first half of the first pass
+// and argon2d further on. It provides therefore protection from side-channel
+// attacks and brute-force cost savings due to memory trade-offs.
+export fn argon2id(
+ dest: []u8,
+ password: []u8,
+ salt: []u8,
+ cfg: config
+) (void | nomem) = {
+ return argon2(dest, password, salt, cfg, mode::ID);
+};
+
+fn argon2(
+ dest: []u8,
+ password: []u8,
+ salt: []u8,
+ cfg: config,
+ mode: mode,
+) (void | nomem) = {
+ assert(endian::host == &endian::little, "TODO big endian support");
+
+ assert(len(dest) >= 4 && len(dest) <= types::U32_MAX);
+ assert(len(password) <= types::U32_MAX);
+ assert(len(salt) >= 8 && len(salt) <= types::U32_MAX);
+ assert(cfg.parallel >= 1);
+ assert(cfg.passes >= 1);
+ assert(len(cfg.secret) <= types::U32_MAX);
+ assert(len(cfg.data) <= types::U32_MAX);
+
+ let mem: []u64 = match (cfg.mem) {
+ case let mem: []u64 =>
+ assert(len(mem) >= 8 * cfg.parallel * BLOCKSIZE
+ && len(mem) % BLOCKSIZE == 0
+ && len(mem) / BLOCKSIZE <= types::U32_MAX);
+ yield mem;
+ case let memsize: u32 =>
+ assert(memsize >= 8 * cfg.parallel
+ && memsize <= types::U32_MAX);
+
+ // TODO https://todo.sr.ht/~sircmpwn/hare/285
+ let membytes = memsize * BLOCKSIZE * 8;
+ let mem = match (rt::malloc(membytes): nullable *[*]u64) {
+ case null =>
+ return nomem;
+ case let mem: *[*]u64 =>
+ yield mem;
+ };
+ yield mem[..membytes / 8];
+ };
+ // round down memory to nearest multiple of 4 times parallel
+ mem = mem[..len(mem) - len(mem) % (4 * cfg.parallel * BLOCKSIZE)];
+ const memsize = (len(mem) / BLOCKSIZE): u32;
+
+ let h0: [64]u8 = [0...];
+ inithash(&h0, len(dest): u32, password, salt, cfg, mode, memsize);
+
+ const cols = 4 * (memsize / (4 * cfg.parallel));
+ let ctx = context {
+ rows = cfg.parallel,
+ cols = cols,
+ sliceblocks = cols / 4,
+ pass = 0,
+ mem = mem,
+ mode = mode,
+ seedsinit = [0...],
+ seedblock = [0...],
+ ...
+ };
+
+ // hash first and second blocks of each row
+ for (let i = 0z; i < ctx.rows; i += 1) {
+ let src: [72]u8 = [0...];
+ src[..64] = h0[..];
+
+ endian::leputu32(src[64..68], 0);
+ endian::leputu32(src[68..], i: u32);
+ varhash(blocku8(&ctx, i, 0), src);
+
+ endian::leputu32(src[64..68], 1);
+ endian::leputu32(src[68..], i: u32);
+ varhash(blocku8(&ctx, i, 1), src);
+ };
+
+ // process segments
+ for (ctx.pass < cfg.passes; ctx.pass += 1) {
+ for (let s = 0z; s < SLICES; s += 1) {
+ for (let i = 0z; i < ctx.rows; i += 1) {
+ segproc(&cfg, &ctx, i, s);
+ };
+ };
+ };
+
+ // final hash
+ let b = blocku8(&ctx, 0, ctx.cols - 1);
+ for (let i = 1z; i < ctx.rows; i += 1) {
+ math::xor(b, b, blocku8(&ctx, i, ctx.cols - 1));
+ };
+
+ varhash(dest, b);
+
+ bytes::zero((h0: []u64: *[*]u8)[..len(h0) * size(u64)]);
+ bytes::zero((ctx.mem: *[*]u8)[..len(ctx.mem) * size(u64)]);
+
+ if (cfg.mem is u32) {
+ // mem was allocated internally
+ free(ctx.mem);
+ };
+};
+
+fn block(ctx: *context, i: size, j: size) []u64 = {
+ let index = (ctx.cols * i + j) * BLOCKSIZE;
+ return ctx.mem[index..index + BLOCKSIZE];
+};
+
+fn blocku8(ctx: *context, i: size, j: size) []u8 = {
+ return (block(ctx, i, j): *[*]u8)[..BLOCKSIZE * size(u64)];
+};
+
+fn refblock(cfg: *config, ctx: *context, seed: u64, i: size, j: size) []u64 = {
+ const j1: u64 = seed & 0xffffffff;
+ const segstart = j - (j % ctx.sliceblocks);
+
+ const l: u64 = if (segstart == 0 && ctx.pass == 0) {
+ yield 0;
+ } else {
+ yield (seed >> 32) % cfg.parallel;
+ };
+
+ let poolstart: u64 = 0;
+ let poolsize: u64 = segstart;
+ if (ctx.pass > 0) {
+ poolstart = (segstart + ctx.sliceblocks) % ctx.cols;
+ poolsize = 3 * ctx.sliceblocks;
+ };
+
+ if (l == i: u64) {
+ poolsize += (j - segstart);
+ };
+
+ if ((j - segstart) == 0 || l == i: u64) {
+ poolsize -= 1;
+ };
+
+ const x: u64 = (j1 * j1) >> 32;
+ const y: u64 = (poolsize * x) >> 32;
+ const z: u64 = (poolstart + poolsize - (y+1)) % ctx.cols: u64;
+
+ return block(ctx, l: size, z: size);
+};
+
+fn inithash(
+ dest: *[64]u8,
+ taglen: u32,
+ password: []u8,
+ salt: []u8,
+ cfg: config,
+ mode: mode,
+ memsize: u32,
+) void = {
+ let u32buf: [4]u8 = [0...];
+ let h = blake2b::blake2b([], 64);
+
+ hash_leputu32(&h, cfg.parallel);
+ hash_leputu32(&h, taglen);
+ hash_leputu32(&h, memsize);
+ hash_leputu32(&h, cfg.passes);
+ hash_leputu32(&h, cfg.version);
+ hash_leputu32(&h, mode: u32);
+ hash_leputu32(&h, len(password): u32);
+ hash::write(&h, password);
+
+ hash_leputu32(&h, len(salt): u32);
+ hash::write(&h, salt);
+
+ hash_leputu32(&h, len(cfg.secret): u32);
+ hash::write(&h, cfg.secret);
+
+ hash_leputu32(&h, len(cfg.data): u32);
+ hash::write(&h, cfg.data);
+
+ hash::finish(&h, dest[..]);
+};
+
+fn hash_leputu32(h: *hash::hash, u: u32) void = {
+ let buf: [4]u8 = [0...];
+ endian::leputu32(buf, u);
+ hash::write(h, buf[..]);
+};
+
+// The variable hash function H'
+fn varhash(dest: []u8, block: []u8) void = {
+ let u32buf: [4]u8 = [0...];
+
+ if (len(dest) <= 64) {
+ let h = blake2b::blake2b([], len(dest));
+ hash_leputu32(&h, len(dest): u32);
+ hash::write(&h, block);
+ hash::finish(&h, dest);
+ return;
+ };
+
+ // TODO this may be replaced with a constant time divceil in future to
+ // avoid leaking the dest len.
+ const r = divceil(len(dest): u32, 32) - 2;
+ let v: [64]u8 = [0...];
+ let h = blake2b::blake2b([], 64);
+ let destbuf = bufio::fixed(dest, io::mode::WRITE);
+ defer io::close(destbuf);
+
+ hash_leputu32(&h, len(dest): u32);
+ hash::write(&h, block);
+ hash::sum(&h, v[..]);
+
+ io::write(destbuf, v[..32])!;
+
+ for (let i = 1z; i < r; i += 1) {
+ hash::reset(&h);
+ hash::write(&h, v[..]);
+ hash::sum(&h, v[..]);
+ io::write(destbuf, v[..32])!;
+ };
+
+ const remainder = len(dest) - 32 * r;
+ let hend = blake2b::blake2b([], remainder);
+ hash::write(&hend, v[..]);
+ hash::finish(&hend, v[..remainder]);
+ io::write(destbuf, v[..remainder])!;
+};
+
+fn divceil(dividend: u32, divisor: u32) u32 = {
+ let result = dividend / divisor;
+ if (dividend % divisor > 0) {
+ result += 1;
+ };
+ return result;
+};
+
+fn xorblock(dest: []u64, x: []u64, y: []u64) void = {
+ math::xor((dest: *[*]u8)[..len(dest) * size(u64)],
+ (x: *[*]u8)[..len(dest) * size(u64)],
+ (y: *[*]u8)[..len(dest) * size(u64)]);
+};
+
+fn segproc(cfg: *config, ctx: *context, i: size, slice: size) void = {
+ const init = switch (ctx.mode) {
+ case mode::I =>
+ yield true;
+ case mode::ID =>
+ yield ctx.pass == 0 && slice <= 2;
+ case mode::D =>
+ yield false;
+ };
+ if (init) {
+ ctx.seedsinit[0] = ctx.pass;
+ ctx.seedsinit[1] = i;
+ ctx.seedsinit[2] = slice;
+ ctx.seedsinit[3] = len(ctx.mem) / BLOCKSIZE;
+ ctx.seedsinit[4] = cfg.passes;
+ ctx.seedsinit[5] = ctx.mode: u64;
+ ctx.seedsinit[6] = 0;
+
+ if (ctx.pass == 0 && slice == 0) {
+ ctx.seedsinit[6] += 1;
+ compress(ctx.seedblock, ctx.seedsinit, zeroblock,
+ false);
+ compress(ctx.seedblock, ctx.seedblock, zeroblock,
+ false);
+ };
+ };
+
+ for (let b = 0z; b < ctx.sliceblocks; b += 1) {
+ const j = slice * ctx.sliceblocks + b;
+ if (ctx.pass == 0 && j < 2) {
+ continue;
+ };
+
+ const dmodeseed = switch (ctx.mode) {
+ case mode::D =>
+ yield true;
+ case mode::ID =>
+ yield ctx.pass > 0 || slice > 1;
+ case mode::I =>
+ yield false;
+ };
+ const seed: u64 = if (dmodeseed) {
+ yield block(ctx, i, (j - 1) % ctx.cols)[0];
+ } else {
+ if (b % BLOCKSIZE == 0) {
+ ctx.seedsinit[6] += 1;
+ compress(ctx.seedblock, ctx.seedsinit,
+ zeroblock, false);
+ compress(ctx.seedblock, ctx.seedblock,
+ zeroblock, false);
+ };
+ yield ctx.seedblock[b % BLOCKSIZE];
+ };
+ compress(block(ctx, i, j), block(ctx, i, (j - 1) % ctx.cols),
+ refblock(cfg, ctx, seed, i, j), ctx.pass > 0);
+ };
+};
+
+fn compress(dest: []u64, x: []u64, y: []u64, xor: bool) void = {
+ let r: block64 = [0...];
+ xorblock(r, x, y);
+
+ let z: block64 = [0...];
+ z[..] = r[..];
+
+ for (let i = 0z; i < 128; i += 16) {
+ perm(&z[i], &z[i + 1], &z[i + 2], &z[i + 3], &z[i + 4],
+ &z[i + 5], &z[i + 6], &z[i + 7], &z[i + 8], &z[i + 9],
+ &z[i + 10], &z[i + 11], &z[i + 12], &z[i + 13],
+ &z[i + 14], &z[i + 15]);
+ };
+
+ for (let i = 0z; i < 16; i += 2) {
+ perm(&z[i], &z[i + 1], &z[i + 16], &z[i + 17], &z[i + 32],
+ &z[i + 33], &z[i + 48], &z[i + 49], &z[i + 64],
+ &z[i + 65], &z[i + 80], &z[i + 81], &z[i + 96],
+ &z[i + 97], &z[i + 112], &z[i + 113]);
+ };
+
+ if (xor) {
+ xorblock(r, r, dest);
+ };
+
+ xorblock(dest, z, r);
+};
+
+fn perm(
+ x0: *u64,
+ x1: *u64,
+ x2: *u64,
+ x3: *u64,
+ x4: *u64,
+ x5: *u64,
+ x6: *u64,
+ x7: *u64,
+ x8: *u64,
+ x9: *u64,
+ x10: *u64,
+ x11: *u64,
+ x12: *u64,
+ x13: *u64,
+ x14: *u64,
+ x15: *u64,
+) void = {
+ mix(x0, x4, x8, x12);
+ mix(x1, x5, x9, x13);
+ mix(x2, x6, x10, x14);
+ mix(x3, x7, x11, x15);
+
+ mix(x0, x5, x10, x15);
+ mix(x1, x6, x11, x12);
+ mix(x2, x7, x8, x13);
+ mix(x3, x4, x9, x14);
+};
+
+fn mix(a: *u64, b: *u64, c: *u64, d: *u64) void = {
+ *a = *a + *b + 2 * trunc(*a) * trunc(*b);
+ *d = math::rotr64(*d ^ *a, 32);
+ *c = *c + *d + 2 * trunc(*c) * trunc(*d);
+ *b = math::rotr64(*b ^ *c, 24);
+
+ *a = *a + *b + 2 * trunc(*a) * trunc(*b);
+ *d = math::rotr64(*d ^ *a, 16);
+ *c = *c + *d + 2 * trunc(*c) * trunc(*d);
+ *b = math::rotr64(*b ^ *c, 63);
+};
+
+fn trunc(a: u64) u64 = {
+ return a & 0xffffffff;
+};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -202,6 +202,18 @@ crypto_aes() {
fi
}
+crypto_argon2() {
+ if [ $testing -eq 0 ]
+ then
+ gen_srcs crypto::argon2 argon2.ha
+ else
+ gen_srcs crypto::argon2 argon2.ha +test.ha
+ fi
+
+ gen_ssa crypto::argon2 bufio bytes crypto::blake2b \
+ crypto::math endian errors hash io rt types
+}
+
gensrcs_crypto_blake2b() {
gen_srcs crypto::blake2b \
blake2b.ha \
@@ -1095,6 +1107,7 @@ bytes
compress::flate
compress::zlib
crypto::aes
+crypto::argon2
crypto::blake2b
crypto::cipher
crypto::hmac
diff --git a/stdlib.mk b/stdlib.mk
@@ -150,6 +150,12 @@ stdlib_deps_any+=$(stdlib_crypto_aes_any)
stdlib_crypto_aes_linux=$(stdlib_crypto_aes_any)
stdlib_crypto_aes_freebsd=$(stdlib_crypto_aes_any)
+# gen_lib crypto::argon2 (any)
+stdlib_crypto_argon2_any=$(HARECACHE)/crypto/argon2/crypto_argon2-any.o
+stdlib_deps_any+=$(stdlib_crypto_argon2_any)
+stdlib_crypto_argon2_linux=$(stdlib_crypto_argon2_any)
+stdlib_crypto_argon2_freebsd=$(stdlib_crypto_argon2_any)
+
# gen_lib crypto::blake2b (any)
stdlib_crypto_blake2b_any=$(HARECACHE)/crypto/blake2b/crypto_blake2b-any.o
stdlib_deps_any+=$(stdlib_crypto_blake2b_any)
@@ -663,6 +669,16 @@ $(HARECACHE)/crypto/aes/crypto_aes-any.ssa: $(stdlib_crypto_aes_any_srcs) $(stdl
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::aes \
-t$(HARECACHE)/crypto/aes/crypto_aes.td $(stdlib_crypto_aes_any_srcs)
+# crypto::argon2 (+any)
+stdlib_crypto_argon2_any_srcs= \
+ $(STDLIB)/crypto/argon2/argon2.ha
+
+$(HARECACHE)/crypto/argon2/crypto_argon2-any.ssa: $(stdlib_crypto_argon2_any_srcs) $(stdlib_rt) $(stdlib_bufio_$(PLATFORM)) $(stdlib_bytes_$(PLATFORM)) $(stdlib_crypto_blake2b_$(PLATFORM)) $(stdlib_crypto_math_$(PLATFORM)) $(stdlib_endian_$(PLATFORM)) $(stdlib_errors_$(PLATFORM)) $(stdlib_hash_$(PLATFORM)) $(stdlib_io_$(PLATFORM)) $(stdlib_rt_$(PLATFORM)) $(stdlib_types_$(PLATFORM))
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(HARECACHE)/crypto/argon2
+ @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::argon2 \
+ -t$(HARECACHE)/crypto/argon2/crypto_argon2.td $(stdlib_crypto_argon2_any_srcs)
+
# crypto::blake2b (+any)
stdlib_crypto_blake2b_any_srcs= \
$(STDLIB)/crypto/blake2b/blake2b.ha
@@ -1864,6 +1880,12 @@ testlib_deps_any+=$(testlib_crypto_aes_any)
testlib_crypto_aes_linux=$(testlib_crypto_aes_any)
testlib_crypto_aes_freebsd=$(testlib_crypto_aes_any)
+# gen_lib crypto::argon2 (any)
+testlib_crypto_argon2_any=$(TESTCACHE)/crypto/argon2/crypto_argon2-any.o
+testlib_deps_any+=$(testlib_crypto_argon2_any)
+testlib_crypto_argon2_linux=$(testlib_crypto_argon2_any)
+testlib_crypto_argon2_freebsd=$(testlib_crypto_argon2_any)
+
# gen_lib crypto::blake2b (any)
testlib_crypto_blake2b_any=$(TESTCACHE)/crypto/blake2b/crypto_blake2b-any.o
testlib_deps_any+=$(testlib_crypto_blake2b_any)
@@ -2381,6 +2403,17 @@ $(TESTCACHE)/crypto/aes/crypto_aes-any.ssa: $(testlib_crypto_aes_any_srcs) $(tes
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::aes \
-t$(TESTCACHE)/crypto/aes/crypto_aes.td $(testlib_crypto_aes_any_srcs)
+# crypto::argon2 (+any)
+testlib_crypto_argon2_any_srcs= \
+ $(STDLIB)/crypto/argon2/argon2.ha \
+ $(STDLIB)/crypto/argon2/+test.ha
+
+$(TESTCACHE)/crypto/argon2/crypto_argon2-any.ssa: $(testlib_crypto_argon2_any_srcs) $(testlib_rt) $(testlib_bufio_$(PLATFORM)) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_blake2b_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_endian_$(PLATFORM)) $(testlib_errors_$(PLATFORM)) $(testlib_hash_$(PLATFORM)) $(testlib_io_$(PLATFORM)) $(testlib_rt_$(PLATFORM)) $(testlib_types_$(PLATFORM))
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(TESTCACHE)/crypto/argon2
+ @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::argon2 \
+ -t$(TESTCACHE)/crypto/argon2/crypto_argon2.td $(testlib_crypto_argon2_any_srcs)
+
# crypto::blake2b (+any)
testlib_crypto_blake2b_any_srcs= \
$(STDLIB)/crypto/blake2b/blake2b.ha \