hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 134428cc317cfceb3820967a9245f639b7bace9a
parent 8bfee5cdda2c957d35e730a2b3e744b390c62070
Author: Eyal Sawady <ecs@d2evs.net>
Date:   Mon, 26 Apr 2021 12:54:55 -0400

crypto: add blake2b

Diffstat:
Acrypto/blake2b/+test.ha | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrypto/blake2b/blake2b.ha | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrypto/blake2b/vectors+test.ha | 7+++++++
Mscripts/gen-stdlib | 12++++++++++++
Mstdlib.mk | 28++++++++++++++++++++++++++++
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 = &copy; + + 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