hare

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

poly1305.ha (4087B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 // This implementation was ported from Loup Vaillant's monocypher project.
      5 
      6 use bytes;
      7 use crypto::mac;
      8 use endian;
      9 use io;
     10 
     11 // Internal block size in bytes.
     12 export def BLOCKSZ: size = 16;
     13 
     14 // Length of the resulting MAC in bytes.
     15 export def SZ: size = 16;
     16 
     17 // Poly1305 key.
     18 export type key = [32]u8;
     19 
     20 export type state = struct {
     21 	mac::mac,
     22 	r: [4]u32,
     23 	h: [5]u32,
     24 	c: [16]u8,
     25 	pad: [4]u32,
     26 	cidx: size,
     27 };
     28 
     29 const poly1305_vtable: io::vtable = io::vtable {
     30 	writer = &write,
     31 	...
     32 };
     33 
     34 // Creates a [[crypto::mac::mac]] that computes the poly1305 MAC. It needs to
     35 // be initialised using [[init]]. Like the other MACs it needs to be finished
     36 // using [[crypto::mac::finish]] after use.
     37 export fn poly1305() state = {
     38 	return state {
     39 		stream = &poly1305_vtable,
     40 		sum = &sum,
     41 		finish = &finish,
     42 		sz = SZ,
     43 		bsz = BLOCKSZ,
     44 		...
     45 	};
     46 };
     47 
     48 // Initialises the MAC with given one time key.
     49 export fn init(p: *state, key: *key) void = {
     50 	leputu32slice(p.r, key);
     51 	leputu32slice(p.pad, key[16..]);
     52 
     53 	p.r[0] &= 0x0fffffff;
     54 	for (let i = 1z; i < 4; i += 1) {
     55 		p.r[i] &= 0x0ffffffc;
     56 	};
     57 
     58 	p.cidx = 0;
     59 };
     60 
     61 fn leputu32slice(dest: []u32, src: []u8) void = {
     62 	for (let i = 0z; i < len(dest); i += 1) {
     63 		dest[i] = endian::legetu32(src[i * 4..(i + 1) * 4]);
     64 	};
     65 };
     66 
     67 fn write(st: *io::stream, bbuf: const []u8) (size | io::error) = {
     68 	let p = st: *state;
     69 	let buf: []u8 = bbuf;
     70 	const written = len(buf);
     71 
     72 	for (len(buf) > 0) {
     73 		const n = if (len(buf) <= BLOCKSZ - p.cidx) {
     74 			yield len(buf);
     75 		} else {
     76 			yield BLOCKSZ - p.cidx;
     77 		};
     78 
     79 		p.c[p.cidx..p.cidx + n] = buf[..n];
     80 		p.cidx += n;
     81 		buf = buf[n..];
     82 
     83 		if (p.cidx >= BLOCKSZ) {
     84 			block(p, p.c, 1);
     85 			p.cidx = 0;
     86 		};
     87 	};
     88 
     89 	return written;
     90 };
     91 
     92 fn block(p: *state, buf: []u8, end: u32) void = {
     93 	let s: [4]u32 = [0...];
     94 	leputu32slice(s, buf);
     95 
     96 	// s = h + c, without carry propagation
     97 	const s0: u64 = p.h[0]: u64 + s[0];
     98 	const s1: u64 = p.h[1]: u64 + s[1];
     99 	const s2: u64 = p.h[2]: u64 + s[2];
    100 	const s3: u64 = p.h[3]: u64 + s[3];
    101 	const s4: u32 = p.h[4] + end;
    102 
    103 	// Local all the things!
    104 	const r0: u32 = p.r[0];
    105 	const r1: u32 = p.r[1];
    106 	const r2: u32 = p.r[2];
    107 	const r3: u32 = p.r[3];
    108 	const rr0: u32 = (r0 >> 2) * 5;
    109 	const rr1: u32 = (r1 >> 2) + r1;
    110 	const rr2: u32 = (r2 >> 2) + r2;
    111 	const rr3: u32 = (r3 >> 2) + r3;
    112 
    113 	// (h + c) * r, without carry propagation
    114 	const x0: u64 = s0 * r0 + s1 * rr3 + s2 * rr2 + s3 * rr1 + s4 * rr0;
    115 	const x1: u64 = s0 * r1 + s1 * r0 + s2 * rr3 + s3 * rr2 + s4 * rr1;
    116 	const x2: u64 = s0 * r2 + s1 * r1 + s2 * r0 + s3 * rr3 + s4 * rr2;
    117 	const x3: u64 = s0 * r3 + s1 * r2 + s2 * r1 + s3 * r0 + s4 * rr3;
    118 	const x4: u32 = s4 * (r0 & 3);
    119 
    120 	// partial reduction modulo 2^130 - 5
    121 	const u5: u32 = x4 + (x3 >> 32): u32;
    122 	const u0: u64 = (u5 >>  2) * 5 + (x0 & 0xffffffff);
    123 	const u1: u64 = (u0 >> 32) + (x1 & 0xffffffff) + (x0 >> 32);
    124 	const u2: u64 = (u1 >> 32) + (x2 & 0xffffffff) + (x1 >> 32);
    125 	const u3: u64 = (u2 >> 32) + (x3 & 0xffffffff) + (x2 >> 32);
    126 	const u4: u64 = (u3 >> 32) + (u5 & 3);
    127 
    128 	// Update the hash
    129 	p.h[0] = u0: u32;
    130 	p.h[1] = u1: u32;
    131 	p.h[2] = u2: u32;
    132 	p.h[3] = u3: u32;
    133 	p.h[4] = u4: u32;
    134 };
    135 
    136 fn sum(m: *mac::mac, buf: []u8) void = {
    137 	let p = m: *state;
    138 	if (p.cidx > 0) {
    139 		// update last block
    140 		p.c[p.cidx] = 1;
    141 		bytes::zero(p.c[p.cidx + 1..]);
    142 		block(p, p.c, 0);
    143 	};
    144 
    145 	// check if we should subtract 2^130-5 by performing the
    146 	// corresponding carry propagation.
    147 	let c: u64 = 5;
    148 	for (let i = 0z; i < 4; i += 1) {
    149 		c  += p.h[i];
    150 		c >>= 32;
    151 	};
    152 	c += p.h[4];
    153 	c = (c >> 2) * 5; // shift the carry back to the beginning
    154 	// c now indicates how many times we should subtract 2^130-5 (0 or 1)
    155 
    156 	for (let i = 0z; i < 4; i += 1) {
    157 		c += p.h[i]: u64 + p.pad[i]: u64;
    158 		endian::leputu32(buf[i * 4..(i + 1) * 4], c: u32);
    159 		c = c >> 32;
    160 	};
    161 };
    162 
    163 fn finish(m: *mac::mac) void = {
    164 	let p = m: *state;
    165 	bytes::zero((p.r[..]: *[*]u8)[..len(p.r) * size(u32)]);
    166 	bytes::zero((p.h[..]: *[*]u8)[..len(p.h) * size(u32)]);
    167 	bytes::zero(p.c[..]);
    168 	bytes::zero((p.pad[..]: *[*]u8)[..len(p.pad) * size(u32)]);
    169 };