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 };