commit 134428cc317cfceb3820967a9245f639b7bace9a
parent 8bfee5cdda2c957d35e730a2b3e744b390c62070
Author: Eyal Sawady <ecs@d2evs.net>
Date: Mon, 26 Apr 2021 12:54:55 -0400
crypto: add blake2b
Diffstat:
5 files changed, 287 insertions(+), 0 deletions(-)
diff --git a/crypto/blake2b/+test.ha b/crypto/blake2b/+test.ha
@@ -0,0 +1,64 @@
+use encoding::hex;
+use fmt;
+use hash;
+use io;
+use strings;
+use strio;
+
+@test fn blake2b() void = {
+ for (let i = 0z; i < len(vectors); i += 1) {
+ let key = hex::decode(vectors[i].key)!;
+ defer free(key);
+ let out = hex::decode(vectors[i].out)!;
+ defer free(out);
+ let in = hex::decode(vectors[i].in)!;
+ defer free(in);
+ let blake = blake2b(key, len(out));
+ hash::write(blake, in);
+ let sum: []u8 = alloc([], len(out));
+ defer free(sum);
+ for (let i = 0z; i < len(out); i += 1) {
+ append(sum, 0);
+ };
+ hash::finish(blake, sum);
+ let out = strio::dynamic();
+ hex::encode(out, sum)!;
+ assert(strio::string(out) == vectors[i].out);
+ };
+
+ let blake = blake2b([], 64);
+ defer hash::close(blake);
+ const vectors = [
+ ("", "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"),
+ ("abc", "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"),
+ ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "7285ff3e8bd768d69be62b3bf18765a325917fa9744ac2f582a20850bc2b1141ed1b3e4528595acc90772bdf2d37dc8a47130b44f33a02e8730e5ad8e166e888"),
+ ("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", "ce741ac5930fe346811175c5227bb7bfcd47f42612fae46c0809514f9e0e3a11ee1773287147cdeaeedff50709aa716341fe65240f4ad6777d6bfaf9726e5e52"),
+ ("'UNIX was not designed to stop its users from doing stupid things, as that would also stop them from doing clever things' - Doug Gwyn",
+ "ecd6fbbe1c86782edf2a00d008787f8ef3afb5fd6e9f93a1c9ec121feb3aca3935c64f57b75e73e2b3754c10d4cc5638e32a3dfc55cf259a7e57ad3222ff70f3"),
+ ("'Life is too short to run proprietary software' - Bdale Garbee", "62d6301236854494d2303c4cf35e56a26b00eedeb603cc975bbcb8208cfb8ca5b13ffe5ff7d38beffe2a75aad5386eac1b3f3896fe4ba4bee70abbc4523f1808"),
+ ("'The central enemy of reliability is complexity.' - Geer et al", "855016890590a1e470d01154fcd4acd23ba4a64699a1ef0375c2b6227c6a928768589788316e8eb6008811027ffde1f6ce16bd6ad7f002888fbf45461a2e1a12"),
+ ];
+
+ for (let i = 0z; i < len(vectors); i += 1) {
+ const vector = vectors[i];
+ hash::reset(blake);
+ hash::write(blake, strings::toutf8(vector.0));
+
+ static let sum: [64]u8 = [0...];
+ assert(len(sum) >= hash::sz(blake));
+ hash::sum(blake, sum);
+
+ let hex = strio::dynamic();
+ defer io::close(hex);
+
+ for (let j = 0z; j < len(sum); j += 1) {
+ fmt::fprintf(hex, "{:02x}", sum[j])!;
+ };
+
+ if (strio::string(hex) != vector.1) {
+ fmt::errorfln("Vector {}: {} != {}",
+ i, strio::string(hex), vector.1)!;
+ abort();
+ };
+ };
+};
diff --git a/crypto/blake2b/blake2b.ha b/crypto/blake2b/blake2b.ha
@@ -0,0 +1,176 @@
+use crypto::math;
+use endian;
+use hash;
+use io;
+
+def R1: int = 32;
+def R2: int = 24;
+def R3: int = 16;
+def R4: int = 63;
+def r: u64 = 12;
+def BSIZE: size = 128;
+
+const iv: [8]u64 = [
+ 0x6A09E667F3BCC908, 0xBB67AE8584CAA73B,
+ 0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1,
+ 0x510E527FADE682D1, 0x9B05688C2B3E6C1F,
+ 0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179,
+];
+
+const sigma: [12][16]u8 = [
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
+ [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
+ [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
+ [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
+ [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
+ [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
+ [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
+ [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
+ [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
+ [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
+ [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
+];
+
+type digest = struct {
+ hash: hash::hash,
+ h: [8]u64,
+ tlow: u64,
+ thigh: u64,
+ block: [BSIZE]u8,
+ blocklen: size,
+ key: [BSIZE]u8,
+ keylen: size,
+};
+
+// Creates a [[hash::hash]] which computes a BLAKE2b hash with a given key and a
+// given hash size. The size must be between 1 and 64, inclusive.
+export fn blake2b(key: []u8, sz: size) *hash::hash = {
+ assert(1 <= sz);
+ assert(sz <= 64);
+ assert(len(key) <= 64);
+ let h = iv;
+ h[0] ^= 0x01010000 ^ (len(key) << 8) ^ sz;
+ let keyblock: [BSIZE]u8 = [0...];
+ keyblock[..len(key)] = key;
+ return alloc(digest {
+ hash = hash::hash {
+ stream = io::stream {
+ writer = &write,
+ closer = &close,
+ },
+ sum = &sum,
+ reset = &reset,
+ sz = sz,
+ ...
+ },
+ h = h,
+ tlow = 0,
+ thigh = 0,
+ block = keyblock,
+ blocklen = if (len(key) > 0) BSIZE else 0,
+ key = keyblock,
+ keylen = len(key),
+ }): *hash::hash;
+};
+
+fn write(st: *io::stream, buf: const []u8) (size | io::error) = {
+ if (len(buf) == 0) return 0;
+ let h = st: *digest;
+ let length = len(buf);
+ let buf = buf[..];
+ if (h.blocklen == BSIZE) {
+ assert(h.tlow == 0 && h.thigh == 0 && h.keylen != 0);
+ h.tlow += BSIZE;
+ };
+ for (h.blocklen + len(buf) > BSIZE) {
+ const n = BSIZE - h.blocklen;
+ h.block[h.blocklen..] = buf[..n];
+ buf = buf[n..];
+
+ h.tlow += n;
+ if (h.tlow < n: u64) h.thigh += 1;
+
+ compress(&h.h, &h.block, h.tlow, h.thigh, false);
+ h.blocklen = 0;
+ };
+ h.block[h.blocklen..h.blocklen + len(buf)] = buf;
+ h.blocklen += len(buf);
+ return length;
+};
+
+fn close(st: *io::stream) void = {
+ free(st);
+};
+
+fn sum(h: *hash::hash, buf: []u8) void = {
+ let h = h: *digest;
+ let copy = *h;
+ let h = ©
+
+ h.tlow += h.blocklen;
+ if (h.tlow < h.blocklen: u64) h.thigh += 1;
+
+ // Padding
+ let tmp: [BSIZE]u8 = [0...];
+ h.block[h.blocklen..BSIZE] = tmp[..BSIZE - h.blocklen];
+ h.blocklen = BSIZE;
+
+ compress(&h.h, &h.block, h.tlow, h.thigh, true);
+
+ for (let i = 0z; i < h.hash.sz / 8; i += 1) {
+ endian::leputu64(buf[i * 8..i * 8 + 8], h.h[i]);
+ };
+};
+
+fn reset(h: *hash::hash) void = {
+ let h = h: *digest;
+ h.h = iv;
+ h.h[0] ^= 0x01010000 ^ (h.keylen << 8) ^ h.hash.sz;
+ h.tlow = 0;
+ h.thigh = 0;
+ h.block = h.key;
+ h.blocklen = if (h.keylen > 0) BSIZE else 0;
+};
+
+// Compression function F
+fn compress(h: *[8]u64, b: *[BSIZE]u8, tlow: u64, thigh: u64, f: bool) void = {
+ let v: [16]u64 = [0...];
+ v[..8] = h[..];
+ v[8..] = iv;
+ v[12] ^= tlow;
+ v[13] ^= thigh;
+ if (f) v[14] = ~v[14];
+ let m: [16]u64 = [0...];
+ for (let i = 0z; i < len(m); i += 1) {
+ m[i] = endian::legetu64(b[i * 8..i * 8 + 8]);
+ };
+ for (let i = 0u64; i < r; i += 1) {
+ const s = &sigma[i];
+ mix(&v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
+ mix(&v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
+ mix(&v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
+ mix(&v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
+
+ mix(&v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
+ mix(&v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
+ mix(&v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
+ mix(&v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
+ };
+
+ for (let i = 0; i < 8; i += 1) {
+ h[i] ^= v[i] ^ v[i + 8];
+ };
+};
+
+// Mixing function G
+fn mix(v: *[16]u64, a: size, b: size, c: size, d: size, x: u64, y: u64) void = {
+ v[a] = v[a] + v[b] + x;
+ v[d] = math::rotr64(v[d] ^ v[a], R1);
+ v[c] = v[c] + v[d];
+ v[b] = math::rotr64(v[b] ^ v[c], R2);
+ v[a] = v[a] + v[b] + y;
+ v[d] = math::rotr64(v[d] ^ v[a], R3);
+ v[c] = v[c] + v[d];
+ v[b] = math::rotr64(v[b] ^ v[c], R4);
+};
diff --git a/crypto/blake2b/vectors+test.ha b/crypto/blake2b/vectors+test.ha
@@ -0,0 +1,7 @@
+type vector = struct {
+ in: str,
+ key: str,
+ out: str,
+};
+
+const vectors: []vector = [];
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -152,6 +152,17 @@ compress_zlib() {
hash::adler32 io
}
+crypto_blake2b() {
+ if [ $testing -eq 0 ]
+ then
+ gen_srcs crypto::blake2b blake2b.ha
+ gen_ssa crypto::blake2b hash io endian crypto::math $*
+ else
+ gen_srcs crypto::blake2b blake2b.ha +test.ha vectors+test.ha
+ gen_ssa crypto::blake2b hash io endian crypto::math strings strio encoding::hex $*
+ fi
+}
+
crypto_math() {
gen_srcs crypto::math \
bits.ha
@@ -686,6 +697,7 @@ bufio
bytes
compress_flate
compress_zlib
+crypto_blake2b
crypto_math
crypto_random
crypto_md5
diff --git a/stdlib.mk b/stdlib.mk
@@ -91,6 +91,9 @@ hare_stdlib_deps+=$(stdlib_compress_flate)
stdlib_compress_zlib=$(HARECACHE)/compress/zlib/compress_zlib.o
hare_stdlib_deps+=$(stdlib_compress_zlib)
+stdlib_crypto_blake2b=$(HARECACHE)/crypto/blake2b/crypto_blake2b.o
+hare_stdlib_deps+=$(stdlib_crypto_blake2b)
+
stdlib_crypto_math=$(HARECACHE)/crypto/math/crypto_math.o
hare_stdlib_deps+=$(stdlib_crypto_math)
@@ -300,6 +303,16 @@ $(HARECACHE)/compress/zlib/compress_zlib.ssa: $(stdlib_compress_zlib_srcs) $(std
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncompress::zlib \
-t$(HARECACHE)/compress/zlib/compress_zlib.td $(stdlib_compress_zlib_srcs)
+# crypto::blake2b
+stdlib_crypto_blake2b_srcs= \
+ $(STDLIB)/crypto/blake2b/blake2b.ha
+
+$(HARECACHE)/crypto/blake2b/crypto_blake2b.ssa: $(stdlib_crypto_blake2b_srcs) $(stdlib_rt) $(stdlib_hash) $(stdlib_io) $(stdlib_endian) $(stdlib_crypto_math)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(HARECACHE)/crypto/blake2b
+ @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::blake2b \
+ -t$(HARECACHE)/crypto/blake2b/crypto_blake2b.td $(stdlib_crypto_blake2b_srcs)
+
# crypto::math
stdlib_crypto_math_srcs= \
$(STDLIB)/crypto/math/bits.ha
@@ -991,6 +1004,9 @@ hare_testlib_deps+=$(testlib_compress_flate)
testlib_compress_zlib=$(TESTCACHE)/compress/zlib/compress_zlib.o
hare_testlib_deps+=$(testlib_compress_zlib)
+testlib_crypto_blake2b=$(TESTCACHE)/crypto/blake2b/crypto_blake2b.o
+hare_testlib_deps+=$(testlib_crypto_blake2b)
+
testlib_crypto_math=$(TESTCACHE)/crypto/math/crypto_math.o
hare_testlib_deps+=$(testlib_crypto_math)
@@ -1201,6 +1217,18 @@ $(TESTCACHE)/compress/zlib/compress_zlib.ssa: $(testlib_compress_zlib_srcs) $(te
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncompress::zlib \
-t$(TESTCACHE)/compress/zlib/compress_zlib.td $(testlib_compress_zlib_srcs)
+# crypto::blake2b
+testlib_crypto_blake2b_srcs= \
+ $(STDLIB)/crypto/blake2b/blake2b.ha \
+ $(STDLIB)/crypto/blake2b/+test.ha \
+ $(STDLIB)/crypto/blake2b/vectors+test.ha
+
+$(TESTCACHE)/crypto/blake2b/crypto_blake2b.ssa: $(testlib_crypto_blake2b_srcs) $(testlib_rt) $(testlib_hash) $(testlib_io) $(testlib_endian) $(testlib_crypto_math) $(testlib_strings) $(testlib_strio) $(testlib_bufio)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(TESTCACHE)/crypto/blake2b
+ @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::blake2b \
+ -t$(TESTCACHE)/crypto/blake2b/crypto_blake2b.td $(testlib_crypto_blake2b_srcs)
+
# crypto::math
testlib_crypto_math_srcs= \
$(STDLIB)/crypto/math/bits.ha