hare

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

commit 2a6211aa53b1371cdf0a3b9a8078e2b93ec9624c
parent c3a38c276427ba1c9633a415dd448927a160c89b
Author: Armin Preiml <apreiml@strohwolke.at>
Date:   Mon, 10 Jan 2022 13:20:25 +0100

implement the crypto::poly1305 mac

Signed-off-by: Armin Preiml <apreiml@strohwolke.at>

Diffstat:
Acrypto/poly1305/+test.ha | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrypto/poly1305/README | 7+++++++
Acrypto/poly1305/poly1305.ha | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 14++++++++++++++
Mstdlib.mk | 33+++++++++++++++++++++++++++++++++
5 files changed, 337 insertions(+), 0 deletions(-)

diff --git a/crypto/poly1305/+test.ha b/crypto/poly1305/+test.ha @@ -0,0 +1,122 @@ +use bytes; +use crypto::mac; +use encoding::hex; + +// example taken from the Poly1305-AES paper from D. J. Bernstein +@test fn example1() void = { + const message: [_]u8 = [0xf3, 0xf6]; + + const key: [32]u8 = [ + 0x85, 0x1f, 0xc4, 0x0c, 0x34, 0x67, 0xac, 0x0b, + 0xe0, 0x5c, 0xc2, 0x04, 0x04, 0xf3, 0xf7, 0x00, + 0x58, 0x0b, 0x3b, 0x0f, 0x94, 0x47, 0xbb, 0x1e, + 0x69, 0xd0, 0x95, 0xb5, 0x92, 0x8b, 0x6d, 0xbc, + ]; + + const expected: [_]u8 = [ + 0xf4, 0xc6, 0x33, 0xc3, 0x04, 0x4f, 0xc1, 0x45, + 0xf8, 0x4f, 0x33, 0x5c, 0xb8, 0x19, 0x53, 0xde, + ]; + + let result: [16]u8 = [0...]; + + let p = poly1305(); + init(&p, key); + mac::write(&p, message); + mac::sum(&p, result); + mac::finish(&p); + + assert(bytes::equal(expected, result)); +}; + +// example taken from the Poly1305-AES paper from D. J. Bernstein +@test fn example2() void = { + const key: [_]u8 = [ + 0xa0, 0xf3, 0x08, 0x00, 0x00, 0xf4, 0x64, 0x00, + 0xd0, 0xc7, 0xe9, 0x07, 0x6c, 0x83, 0x44, 0x03, + 0xdd, 0x3f, 0xab, 0x22, 0x51, 0xf1, 0x1a, 0xc7, + 0x59, 0xf0, 0x88, 0x71, 0x29, 0xcc, 0x2e, 0xe7, + ]; + + const expected: [_]u8 = [ + 0xdd, 0x3f, 0xab, 0x22, 0x51, 0xf1, 0x1a, 0xc7, + 0x59, 0xf0, 0x88, 0x71, 0x29, 0xcc, 0x2e, 0xe7, + ]; + + let result: [16]u8 = [0...]; + + let p = poly1305(); + init(&p, key); + mac::sum(&p, result); + mac::finish(&p); + + assert(bytes::equal(expected, result)); +}; + +// example taken from the Poly1305-AES paper from D. J. Bernstein +@test fn example3() void = { + const message: [_]u8 = [ + 0x66, 0x3c, 0xea, 0x19, 0x0f, 0xfb, 0x83, 0xd8, + 0x95, 0x93, 0xf3, 0xf4, 0x76, 0xb6, 0xbc, 0x24, + 0xd7, 0xe6, 0x79, 0x10, 0x7e, 0xa2, 0x6a, 0xdb, + 0x8c, 0xaf, 0x66, 0x52, 0xd0, 0x65, 0x61, 0x36, + ]; + + const expected: [_]u8 = [ + 0x0e, 0xe1, 0xc1, 0x6b, 0xb7, 0x3f, 0x0f, 0x4f, + 0xd1, 0x98, 0x81, 0x75, 0x3c, 0x01, 0xcd, 0xbe, + ]; + + const key: [32]u8 = [ + 0x48, 0x44, 0x3d, 0x0b, 0xb0, 0xd2, 0x11, 0x09, + 0xc8, 0x9a, 0x10, 0x0b, 0x5c, 0xe2, 0xc2, 0x08, + 0x83, 0x14, 0x9c, 0x69, 0xb5, 0x61, 0xdd, 0x88, + 0x29, 0x8a, 0x17, 0x98, 0xb1, 0x07, 0x16, 0xef, + ]; + + let result: [16]u8 = [0...]; + + let p = poly1305(); + init(&p, key); + mac::write(&p, message); + mac::sum(&p, result); + mac::finish(&p); + + assert(bytes::equal(expected, result)); +}; + +// example taken from the Poly1305-AES paper from D. J. Bernstein +@test fn example4() void = { + const message: [_]u8 = [ + 0xab, 0x08, 0x12, 0x72, 0x4a, 0x7f, 0x1e, 0x34, 0x27, 0x42, + 0xcb, 0xed, 0x37, 0x4d, 0x94, 0xd1, 0x36, 0xc6, 0xb8, 0x79, + 0x5d, 0x45, 0xb3, 0x81, 0x98, 0x30, 0xf2, 0xc0, 0x44, 0x91, + 0xfa, 0xf0, 0x99, 0x0c, 0x62, 0xe4, 0x8b, 0x80, 0x18, 0xb2, + 0xc3, 0xe4, 0xa0, 0xfa, 0x31, 0x34, 0xcb, 0x67, 0xfa, 0x83, + 0xe1, 0x58, 0xc9, 0x94, 0xd9, 0x61, 0xc4, 0xcb, 0x21, 0x09, + 0x5c, 0x1b, 0xf9, + ]; + + const expected: [_]u8 = [ + 0x51, 0x54, 0xad, 0x0d, 0x2c, 0xb2, 0x6e, 0x01, + 0x27, 0x4f, 0xc5, 0x11, 0x48, 0x49, 0x1f, 0x1b, + ]; + + const key: [32]u8 = [ + 0x12, 0x97, 0x6a, 0x08, 0xc4, 0x42, 0x6d, 0x0c, + 0xe8, 0xa8, 0x24, 0x07, 0xc4, 0xf4, 0x82, 0x07, + 0x80, 0xf8, 0xc2, 0x0a, 0xa7, 0x12, 0x02, 0xd1, + 0xe2, 0x91, 0x79, 0xcb, 0xcb, 0x55, 0x5a, 0x57, + ]; + + let result: [16]u8 = [0...]; + + let p = poly1305(); + init(&p, key); + mac::write(&p, message); + mac::sum(&p, result); + mac::finish(&p); + + assert(bytes::equal(expected, result)); +}; + diff --git a/crypto/poly1305/README b/crypto/poly1305/README @@ -0,0 +1,7 @@ +This package provides the poly1305 MAC as defined in RFC 8439. + +This is a low-level module which implements cryptographic primitives. Direct use +of cryptographic primitives is not recommended for non-experts, as incorrect use +of these primitives can easily lead to the introduction of security +vulnerabilities. Non-experts are advised to use the high-level operations +available in the top-level [[crypto]] module. diff --git a/crypto/poly1305/poly1305.ha b/crypto/poly1305/poly1305.ha @@ -0,0 +1,161 @@ +// This implementation was ported from Loup Vaillants monocypher project. + +use bytes; +use crypto::mac; +use endian; +use encoding::hex; +use io; + +// Internal block size in bytes. +export def BLOCKSIZE: size = 16; + +// Length of the resulting MAC in bytes. +export def SIZE: size = 16; + +export type key = [32]u8; + +export type state = struct { + mac::mac, + r: [4]u32, + h: [5]u32, + c: [16]u8, + pad: [4]u32, + cidx: size, +}; + +// Creates a [[crypto::mac::mac]] that computes the poly1305 MAC. It needs to +// be initialised using [[init]]. Like the other MACs it needs to be finished +// using [[crypto::mac::finish]] after use. +export fn poly1305() state = { + return state { + writer = &write, + sum = &sum, + finish = &finish, + sz = SIZE, + bsz = BLOCKSIZE, + ... + }; +}; + +// Initialises the MAC with given one time key. +export fn init(p: *state, key: key) void = { + leputu32slice(p.r, key); + leputu32slice(p.pad, key[16..]); + + p.r[0] &= 0x0fffffff; + for (let i = 1z; i < 4; i += 1) { + p.r[i] &= 0x0ffffffc; + }; + + p.cidx = 0; +}; + +fn leputu32slice(dest: []u32, src: []u8) void = { + for (let i = 0z; i < len(dest); i += 1) { + dest[i] = endian::legetu32(src[i * 4..(i + 1) * 4]); + }; +}; + +fn write(st: *io::stream, bbuf: const []u8) (size | io::error) = { + let p = st: *state; + let buf: []u8 = bbuf; + const written = len(buf); + + for (len(buf) > 0) { + const n = if (len(buf) - p.cidx < BLOCKSIZE) { + yield len(buf) - p.cidx; + } else { + yield BLOCKSIZE; + }; + + p.c[p.cidx..] = buf[..n]; + p.cidx += n; + buf = buf[n..]; + + if (p.cidx >= BLOCKSIZE) { + block(p, p.c, 1); + p.cidx = 0; + }; + }; + + return written; +}; + +fn block(p: *state, buf: []u8, end: u32) void = { + let s: [4]u32 = [0...]; + leputu32slice(s, buf); + + // s = h + c, without carry propagation + const s0: u64 = p.h[0]: u64 + s[0]; + const s1: u64 = p.h[1]: u64 + s[1]; + const s2: u64 = p.h[2]: u64 + s[2]; + const s3: u64 = p.h[3]: u64 + s[3]; + const s4: u32 = p.h[4] + end; + + // Local all the things! + const r0: u32 = p.r[0]; + const r1: u32 = p.r[1]; + const r2: u32 = p.r[2]; + const r3: u32 = p.r[3]; + const rr0: u32 = (r0 >> 2) * 5; + const rr1: u32 = (r1 >> 2) + r1; + const rr2: u32 = (r2 >> 2) + r2; + const rr3: u32 = (r3 >> 2) + r3; + + // (h + c) * r, without carry propagation + const x0: u64 = s0 * r0 + s1 * rr3 + s2 * rr2 + s3 * rr1 + s4 * rr0; + const x1: u64 = s0 * r1 + s1 * r0 + s2 * rr3 + s3 * rr2 + s4 * rr1; + const x2: u64 = s0 * r2 + s1 * r1 + s2 * r0 + s3 * rr3 + s4 * rr2; + const x3: u64 = s0 * r3 + s1 * r2 + s2 * r1 + s3 * r0 + s4 * rr3; + const x4: u32 = s4 * (r0 & 3); + + // partial reduction modulo 2^130 - 5 + const u5: u32 = x4 + (x3 >> 32): u32; + const u0: u64 = (u5 >> 2) * 5 + (x0 & 0xffffffff); + const u1: u64 = (u0 >> 32) + (x1 & 0xffffffff) + (x0 >> 32); + const u2: u64 = (u1 >> 32) + (x2 & 0xffffffff) + (x1 >> 32); + const u3: u64 = (u2 >> 32) + (x3 & 0xffffffff) + (x2 >> 32); + const u4: u64 = (u3 >> 32) + (u5 & 3); + + // Update the hash + p.h[0] = u0: u32; + p.h[1] = u1: u32; + p.h[2] = u2: u32; + p.h[3] = u3: u32; + p.h[4] = u4: u32; +}; + +fn sum(m: *mac::mac, buf: []u8) void = { + let p = m: *state; + if (p.cidx > 0) { + // update last block + p.c[p.cidx] = 1; + bytes::zero(p.c[p.cidx + 1..]); + block(p, p.c, 0); + }; + + // check if we should subtract 2^130-5 by performing the + // corresponding carry propagation. + let c: u64 = 5; + for (let i = 0z; i < 4; i += 1) { + c += p.h[i]; + c >>= 32; + }; + c += p.h[4]; + c = (c >> 2) * 5; // shift the carry back to the beginning + // c now indicates how many times we should subtract 2^130-5 (0 or 1) + + for (let i = 0z; i < 4; i += 1) { + c += p.h[i]: u64 + p.pad[i]: u64; + endian::leputu32(buf[i * 4..(i + 1) * 4], c: u32); + c = c >> 32; + }; +}; + +fn finish(m: *mac::mac) void = { + let p = m: *state; + bytes::zero((p.r[..]: *[*]u8)[..len(p.r) * size(u32)]); + bytes::zero((p.h[..]: *[*]u8)[..len(p.h) * size(u32)]); + bytes::zero(p.c[..]); + bytes::zero((p.pad[..]: *[*]u8)[..len(p.pad) * size(u32)]); +}; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -277,6 +277,19 @@ crypto_math() { gen_ssa crypto::math } +crypto_poly1305() { + if [ $testing -eq 0 ] + then + gen_srcs crypto::poly1305 \ + poly1305.ha + else + gen_srcs crypto::poly1305 \ + poly1305.ha \ + +test.ha + fi + gen_ssa crypto::poly1305 bytes crypto::mac endian encoding::hex io +} + crypto_random() { gen_srcs -plinux crypto::random \ +linux.ha \ @@ -1117,6 +1130,7 @@ crypto::hmac crypto::mac crypto::math crypto::random linux freebsd +crypto::poly1305 crypto::salsa crypto::sha1 crypto::sha256 diff --git a/stdlib.mk b/stdlib.mk @@ -200,6 +200,12 @@ stdlib_deps_linux+=$(stdlib_crypto_random_linux) stdlib_crypto_random_freebsd=$(HARECACHE)/crypto/random/crypto_random-freebsd.o stdlib_deps_freebsd+=$(stdlib_crypto_random_freebsd) +# gen_lib crypto::poly1305 (any) +stdlib_crypto_poly1305_any=$(HARECACHE)/crypto/poly1305/crypto_poly1305-any.o +stdlib_deps_any+=$(stdlib_crypto_poly1305_any) +stdlib_crypto_poly1305_linux=$(stdlib_crypto_poly1305_any) +stdlib_crypto_poly1305_freebsd=$(stdlib_crypto_poly1305_any) + # gen_lib crypto::salsa (any) stdlib_crypto_salsa_any=$(HARECACHE)/crypto/salsa/crypto_salsa-any.o stdlib_deps_any+=$(stdlib_crypto_salsa_any) @@ -772,6 +778,16 @@ $(HARECACHE)/crypto/random/crypto_random-freebsd.ssa: $(stdlib_crypto_random_fre @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::random \ -t$(HARECACHE)/crypto/random/crypto_random.td $(stdlib_crypto_random_freebsd_srcs) +# crypto::poly1305 (+any) +stdlib_crypto_poly1305_any_srcs= \ + $(STDLIB)/crypto/poly1305/poly1305.ha + +$(HARECACHE)/crypto/poly1305/crypto_poly1305-any.ssa: $(stdlib_crypto_poly1305_any_srcs) $(stdlib_rt) $(stdlib_bytes_$(PLATFORM)) $(stdlib_crypto_mac_$(PLATFORM)) $(stdlib_endian_$(PLATFORM)) $(stdlib_encoding_hex_$(PLATFORM)) $(stdlib_io_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(HARECACHE)/crypto/poly1305 + @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::poly1305 \ + -t$(HARECACHE)/crypto/poly1305/crypto_poly1305.td $(stdlib_crypto_poly1305_any_srcs) + # crypto::salsa (+any) stdlib_crypto_salsa_any_srcs= \ $(STDLIB)/crypto/salsa/salsa20.ha @@ -1951,6 +1967,12 @@ testlib_deps_linux+=$(testlib_crypto_random_linux) testlib_crypto_random_freebsd=$(TESTCACHE)/crypto/random/crypto_random-freebsd.o testlib_deps_freebsd+=$(testlib_crypto_random_freebsd) +# gen_lib crypto::poly1305 (any) +testlib_crypto_poly1305_any=$(TESTCACHE)/crypto/poly1305/crypto_poly1305-any.o +testlib_deps_any+=$(testlib_crypto_poly1305_any) +testlib_crypto_poly1305_linux=$(testlib_crypto_poly1305_any) +testlib_crypto_poly1305_freebsd=$(testlib_crypto_poly1305_any) + # gen_lib crypto::salsa (any) testlib_crypto_salsa_any=$(TESTCACHE)/crypto/salsa/crypto_salsa-any.o testlib_deps_any+=$(testlib_crypto_salsa_any) @@ -2531,6 +2553,17 @@ $(TESTCACHE)/crypto/random/crypto_random-freebsd.ssa: $(testlib_crypto_random_fr @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::random \ -t$(TESTCACHE)/crypto/random/crypto_random.td $(testlib_crypto_random_freebsd_srcs) +# crypto::poly1305 (+any) +testlib_crypto_poly1305_any_srcs= \ + $(STDLIB)/crypto/poly1305/poly1305.ha \ + $(STDLIB)/crypto/poly1305/+test.ha + +$(TESTCACHE)/crypto/poly1305/crypto_poly1305-any.ssa: $(testlib_crypto_poly1305_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_mac_$(PLATFORM)) $(testlib_endian_$(PLATFORM)) $(testlib_encoding_hex_$(PLATFORM)) $(testlib_io_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(TESTCACHE)/crypto/poly1305 + @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::poly1305 \ + -t$(TESTCACHE)/crypto/poly1305/crypto_poly1305.td $(testlib_crypto_poly1305_any_srcs) + # crypto::salsa (+any) testlib_crypto_salsa_any_srcs= \ $(STDLIB)/crypto/salsa/salsa20.ha \