poly1305.mjs raw
1 // TinyJS Runtime — Poly1305 MAC (RFC 8439 §2.5)
2 // Uses JS BigInt for 130-bit modular arithmetic.
3
4 import { Slice } from './builtin.mjs';
5
6 const P = (1n << 130n) - 5n; // 2^130 - 5
7
8 function sliceToU8(s) {
9 if (s instanceof Uint8Array) return s;
10 if (typeof s === 'string') return new TextEncoder().encode(s);
11 const u = new Uint8Array(s.$length);
12 for (let i = 0; i < s.$length; i++) u[i] = s.$array[s.$offset + i];
13 return u;
14 }
15
16 function u8ToSlice(u8) {
17 const arr = new Array(u8.length);
18 for (let i = 0; i < u8.length; i++) arr[i] = u8[i];
19 return new Slice(arr, 0, u8.length, u8.length);
20 }
21
22 function le128(buf, off, len) {
23 let n = 0n;
24 for (let i = 0; i < len; i++) {
25 n |= BigInt(buf[off + i]) << BigInt(i * 8);
26 }
27 return n;
28 }
29
30 function clamp(r) {
31 // RFC 8439 §2.5.2: clamp r
32 r &= 0x0ffffffc0ffffffc0ffffffc0fffffffn;
33 return r;
34 }
35
36 function poly1305(key, data) {
37 // r = clamp(key[0:16]), s = key[16:32]
38 const r = clamp(le128(key, 0, 16));
39 const s = le128(key, 16, 16);
40
41 let acc = 0n;
42 let off = 0;
43 while (off < data.length) {
44 const blockLen = Math.min(16, data.length - off);
45 let n = le128(data, off, blockLen);
46 // Add high bit: 2^(8*blockLen)
47 n |= 1n << BigInt(blockLen * 8);
48 acc = ((acc + n) * r) % P;
49 off += blockLen;
50 }
51
52 // tag = (acc + s) mod 2^128
53 const tag = (acc + s) & ((1n << 128n) - 1n);
54
55 // Encode as 16-byte little-endian
56 const out = new Uint8Array(16);
57 let v = tag;
58 for (let i = 0; i < 16; i++) {
59 out[i] = Number(v & 0xffn);
60 v >>= 8n;
61 }
62 return out;
63 }
64
65 export function MAC(key, data) {
66 const k = sliceToU8(key);
67 const d = sliceToU8(data);
68 return u8ToSlice(poly1305(k, d));
69 }
70
71 export function Verify(key, data, tag) {
72 const k = sliceToU8(key);
73 const d = sliceToU8(data);
74 const t = sliceToU8(tag);
75 const computed = poly1305(k, d);
76 if (computed.length !== t.length) return false;
77 // Constant-time comparison
78 let diff = 0;
79 for (let i = 0; i < 16; i++) diff |= computed[i] ^ t[i];
80 return diff === 0;
81 }
82