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:
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 \