@stablelib/poly1305
Version:
Poly1305 one-time message authentication code
441 lines (396 loc) • 13.7 kB
text/typescript
// Copyright (C) 2016 Dmitry Chestnykh
// MIT License. See LICENSE file for details.
/**
* Package poly1305 implements Poly1305 one-time message authentication algorithm.
*/
import { equal as constantTimeEqual } from "@stablelib/constant-time";
import { wipe } from "@stablelib/wipe";
export const DIGEST_LENGTH = 16;
// Port of Andrew Moon's Poly1305-donna-16. Public domain.
// https://github.com/floodyberry/poly1305-donna
/**
* Poly1305 computes 16-byte authenticator of message using
* a one-time 32-byte key.
*
* Important: key should be used for only one message,
* it should never repeat.
*/
export class Poly1305 {
readonly digestLength = DIGEST_LENGTH;
private _buffer = new Uint8Array(16);
private _r = new Uint16Array(10);
private _h = new Uint16Array(10);
private _pad = new Uint16Array(8);
private _leftover = 0;
private _fin = 0;
private _finished = false;
constructor(key: Uint8Array) {
let t0 = key[0] | key[1] << 8; this._r[0] = (t0) & 0x1fff;
let t1 = key[2] | key[3] << 8; this._r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
let t2 = key[4] | key[5] << 8; this._r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;
let t3 = key[6] | key[7] << 8; this._r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
let t4 = key[8] | key[9] << 8; this._r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;
this._r[5] = ((t4 >>> 1)) & 0x1ffe;
let t5 = key[10] | key[11] << 8; this._r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
let t6 = key[12] | key[13] << 8; this._r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;
let t7 = key[14] | key[15] << 8; this._r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
this._r[9] = ((t7 >>> 5)) & 0x007f;
this._pad[0] = key[16] | key[17] << 8;
this._pad[1] = key[18] | key[19] << 8;
this._pad[2] = key[20] | key[21] << 8;
this._pad[3] = key[22] | key[23] << 8;
this._pad[4] = key[24] | key[25] << 8;
this._pad[5] = key[26] | key[27] << 8;
this._pad[6] = key[28] | key[29] << 8;
this._pad[7] = key[30] | key[31] << 8;
}
private _blocks(m: Uint8Array, mpos: number, bytes: number) {
let hibit = this._fin ? 0 : 1 << 11;
let h0 = this._h[0],
h1 = this._h[1],
h2 = this._h[2],
h3 = this._h[3],
h4 = this._h[4],
h5 = this._h[5],
h6 = this._h[6],
h7 = this._h[7],
h8 = this._h[8],
h9 = this._h[9];
let r0 = this._r[0],
r1 = this._r[1],
r2 = this._r[2],
r3 = this._r[3],
r4 = this._r[4],
r5 = this._r[5],
r6 = this._r[6],
r7 = this._r[7],
r8 = this._r[8],
r9 = this._r[9];
while (bytes >= 16) {
let t0 = m[mpos + 0] | m[mpos + 1] << 8; h0 += (t0) & 0x1fff;
let t1 = m[mpos + 2] | m[mpos + 3] << 8; h1 += ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
let t2 = m[mpos + 4] | m[mpos + 5] << 8; h2 += ((t1 >>> 10) | (t2 << 6)) & 0x1fff;
let t3 = m[mpos + 6] | m[mpos + 7] << 8; h3 += ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
let t4 = m[mpos + 8] | m[mpos + 9] << 8; h4 += ((t3 >>> 4) | (t4 << 12)) & 0x1fff;
h5 += ((t4 >>> 1)) & 0x1fff;
let t5 = m[mpos + 10] | m[mpos + 11] << 8; h6 += ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
let t6 = m[mpos + 12] | m[mpos + 13] << 8; h7 += ((t5 >>> 11) | (t6 << 5)) & 0x1fff;
let t7 = m[mpos + 14] | m[mpos + 15] << 8; h8 += ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
h9 += ((t7 >>> 5)) | hibit;
let c = 0;
let d0 = c;
d0 += h0 * r0;
d0 += h1 * (5 * r9);
d0 += h2 * (5 * r8);
d0 += h3 * (5 * r7);
d0 += h4 * (5 * r6);
c = (d0 >>> 13); d0 &= 0x1fff;
d0 += h5 * (5 * r5);
d0 += h6 * (5 * r4);
d0 += h7 * (5 * r3);
d0 += h8 * (5 * r2);
d0 += h9 * (5 * r1);
c += (d0 >>> 13); d0 &= 0x1fff;
let d1 = c;
d1 += h0 * r1;
d1 += h1 * r0;
d1 += h2 * (5 * r9);
d1 += h3 * (5 * r8);
d1 += h4 * (5 * r7);
c = (d1 >>> 13); d1 &= 0x1fff;
d1 += h5 * (5 * r6);
d1 += h6 * (5 * r5);
d1 += h7 * (5 * r4);
d1 += h8 * (5 * r3);
d1 += h9 * (5 * r2);
c += (d1 >>> 13); d1 &= 0x1fff;
let d2 = c;
d2 += h0 * r2;
d2 += h1 * r1;
d2 += h2 * r0;
d2 += h3 * (5 * r9);
d2 += h4 * (5 * r8);
c = (d2 >>> 13); d2 &= 0x1fff;
d2 += h5 * (5 * r7);
d2 += h6 * (5 * r6);
d2 += h7 * (5 * r5);
d2 += h8 * (5 * r4);
d2 += h9 * (5 * r3);
c += (d2 >>> 13); d2 &= 0x1fff;
let d3 = c;
d3 += h0 * r3;
d3 += h1 * r2;
d3 += h2 * r1;
d3 += h3 * r0;
d3 += h4 * (5 * r9);
c = (d3 >>> 13); d3 &= 0x1fff;
d3 += h5 * (5 * r8);
d3 += h6 * (5 * r7);
d3 += h7 * (5 * r6);
d3 += h8 * (5 * r5);
d3 += h9 * (5 * r4);
c += (d3 >>> 13); d3 &= 0x1fff;
let d4 = c;
d4 += h0 * r4;
d4 += h1 * r3;
d4 += h2 * r2;
d4 += h3 * r1;
d4 += h4 * r0;
c = (d4 >>> 13); d4 &= 0x1fff;
d4 += h5 * (5 * r9);
d4 += h6 * (5 * r8);
d4 += h7 * (5 * r7);
d4 += h8 * (5 * r6);
d4 += h9 * (5 * r5);
c += (d4 >>> 13); d4 &= 0x1fff;
let d5 = c;
d5 += h0 * r5;
d5 += h1 * r4;
d5 += h2 * r3;
d5 += h3 * r2;
d5 += h4 * r1;
c = (d5 >>> 13); d5 &= 0x1fff;
d5 += h5 * r0;
d5 += h6 * (5 * r9);
d5 += h7 * (5 * r8);
d5 += h8 * (5 * r7);
d5 += h9 * (5 * r6);
c += (d5 >>> 13); d5 &= 0x1fff;
let d6 = c;
d6 += h0 * r6;
d6 += h1 * r5;
d6 += h2 * r4;
d6 += h3 * r3;
d6 += h4 * r2;
c = (d6 >>> 13); d6 &= 0x1fff;
d6 += h5 * r1;
d6 += h6 * r0;
d6 += h7 * (5 * r9);
d6 += h8 * (5 * r8);
d6 += h9 * (5 * r7);
c += (d6 >>> 13); d6 &= 0x1fff;
let d7 = c;
d7 += h0 * r7;
d7 += h1 * r6;
d7 += h2 * r5;
d7 += h3 * r4;
d7 += h4 * r3;
c = (d7 >>> 13); d7 &= 0x1fff;
d7 += h5 * r2;
d7 += h6 * r1;
d7 += h7 * r0;
d7 += h8 * (5 * r9);
d7 += h9 * (5 * r8);
c += (d7 >>> 13); d7 &= 0x1fff;
let d8 = c;
d8 += h0 * r8;
d8 += h1 * r7;
d8 += h2 * r6;
d8 += h3 * r5;
d8 += h4 * r4;
c = (d8 >>> 13); d8 &= 0x1fff;
d8 += h5 * r3;
d8 += h6 * r2;
d8 += h7 * r1;
d8 += h8 * r0;
d8 += h9 * (5 * r9);
c += (d8 >>> 13); d8 &= 0x1fff;
let d9 = c;
d9 += h0 * r9;
d9 += h1 * r8;
d9 += h2 * r7;
d9 += h3 * r6;
d9 += h4 * r5;
c = (d9 >>> 13); d9 &= 0x1fff;
d9 += h5 * r4;
d9 += h6 * r3;
d9 += h7 * r2;
d9 += h8 * r1;
d9 += h9 * r0;
c += (d9 >>> 13); d9 &= 0x1fff;
c = (((c << 2) + c)) | 0;
c = (c + d0) | 0;
d0 = c & 0x1fff;
c = (c >>> 13);
d1 += c;
h0 = d0;
h1 = d1;
h2 = d2;
h3 = d3;
h4 = d4;
h5 = d5;
h6 = d6;
h7 = d7;
h8 = d8;
h9 = d9;
mpos += 16;
bytes -= 16;
}
this._h[0] = h0;
this._h[1] = h1;
this._h[2] = h2;
this._h[3] = h3;
this._h[4] = h4;
this._h[5] = h5;
this._h[6] = h6;
this._h[7] = h7;
this._h[8] = h8;
this._h[9] = h9;
}
finish(mac: Uint8Array, macpos = 0): this {
const g = new Uint16Array(10);
let c: number;
let mask: number;
let f: number;
let i: number;
if (this._leftover) {
i = this._leftover;
this._buffer[i++] = 1;
for (; i < 16; i++) {
this._buffer[i] = 0;
}
this._fin = 1;
this._blocks(this._buffer, 0, 16);
}
c = this._h[1] >>> 13;
this._h[1] &= 0x1fff;
for (i = 2; i < 10; i++) {
this._h[i] += c;
c = this._h[i] >>> 13;
this._h[i] &= 0x1fff;
}
this._h[0] += (c * 5);
c = this._h[0] >>> 13;
this._h[0] &= 0x1fff;
this._h[1] += c;
c = this._h[1] >>> 13;
this._h[1] &= 0x1fff;
this._h[2] += c;
g[0] = this._h[0] + 5;
c = g[0] >>> 13;
g[0] &= 0x1fff;
for (i = 1; i < 10; i++) {
g[i] = this._h[i] + c;
c = g[i] >>> 13;
g[i] &= 0x1fff;
}
g[9] -= (1 << 13);
mask = (c ^ 1) - 1;
for (i = 0; i < 10; i++) {
g[i] &= mask;
}
mask = ~mask;
for (i = 0; i < 10; i++) {
this._h[i] = (this._h[i] & mask) | g[i];
}
this._h[0] = ((this._h[0]) | (this._h[1] << 13)) & 0xffff;
this._h[1] = ((this._h[1] >>> 3) | (this._h[2] << 10)) & 0xffff;
this._h[2] = ((this._h[2] >>> 6) | (this._h[3] << 7)) & 0xffff;
this._h[3] = ((this._h[3] >>> 9) | (this._h[4] << 4)) & 0xffff;
this._h[4] = ((this._h[4] >>> 12) | (this._h[5] << 1) | (this._h[6] << 14)) & 0xffff;
this._h[5] = ((this._h[6] >>> 2) | (this._h[7] << 11)) & 0xffff;
this._h[6] = ((this._h[7] >>> 5) | (this._h[8] << 8)) & 0xffff;
this._h[7] = ((this._h[8] >>> 8) | (this._h[9] << 5)) & 0xffff;
f = this._h[0] + this._pad[0];
this._h[0] = f & 0xffff;
for (i = 1; i < 8; i++) {
f = (((this._h[i] + this._pad[i]) | 0) + (f >>> 16)) | 0;
this._h[i] = f & 0xffff;
}
mac[macpos + 0] = this._h[0] >>> 0;
mac[macpos + 1] = this._h[0] >>> 8;
mac[macpos + 2] = this._h[1] >>> 0;
mac[macpos + 3] = this._h[1] >>> 8;
mac[macpos + 4] = this._h[2] >>> 0;
mac[macpos + 5] = this._h[2] >>> 8;
mac[macpos + 6] = this._h[3] >>> 0;
mac[macpos + 7] = this._h[3] >>> 8;
mac[macpos + 8] = this._h[4] >>> 0;
mac[macpos + 9] = this._h[4] >>> 8;
mac[macpos + 10] = this._h[5] >>> 0;
mac[macpos + 11] = this._h[5] >>> 8;
mac[macpos + 12] = this._h[6] >>> 0;
mac[macpos + 13] = this._h[6] >>> 8;
mac[macpos + 14] = this._h[7] >>> 0;
mac[macpos + 15] = this._h[7] >>> 8;
this._finished = true;
return this;
}
update(m: Uint8Array): this {
let mpos = 0;
let bytes = m.length;
let want: number;
if (this._leftover) {
want = (16 - this._leftover);
if (want > bytes) {
want = bytes;
}
for (let i = 0; i < want; i++) {
this._buffer[this._leftover + i] = m[mpos + i];
}
bytes -= want;
mpos += want;
this._leftover += want;
if (this._leftover < 16) {
return this;
}
this._blocks(this._buffer, 0, 16);
this._leftover = 0;
}
if (bytes >= 16) {
want = bytes - (bytes % 16);
this._blocks(m, mpos, want);
mpos += want;
bytes -= want;
}
if (bytes) {
for (let i = 0; i < bytes; i++) {
this._buffer[this._leftover + i] = m[mpos + i];
}
this._leftover += bytes;
}
return this;
}
digest(): Uint8Array {
// TODO(dchest): it behaves differently than other hashes/HMAC,
// because it throws when finished — others just return saved result.
if (this._finished) {
throw new Error("Poly1305 was finished");
}
let mac = new Uint8Array(16);
this.finish(mac);
return mac;
}
clean(): this {
wipe(this._buffer);
wipe(this._r);
wipe(this._h);
wipe(this._pad);
this._leftover = 0;
this._fin = 0;
this._finished = true; // mark as finished even if not
return this;
}
}
/**
* Returns 16-byte authenticator of data using a one-time 32-byte key.
*
* Important: key should be used for only one message, it should never repeat.
*/
export function oneTimeAuth(key: Uint8Array, data: Uint8Array): Uint8Array {
const h = new Poly1305(key);
h.update(data);
const digest = h.digest();
h.clean();
return digest;
}
/**
* Returns true if two authenticators are 16-byte long and equal.
* Uses contant-time comparison to avoid leaking timing information.
*/
export function equal(a: Uint8Array, b: Uint8Array): boolean {
if (a.length !== DIGEST_LENGTH || b.length !== DIGEST_LENGTH) {
return false;
}
return constantTimeEqual(a, b);
}