Skip to main content
Latest
File
import { encode, decode } from "./deps.ts";
function rotl(x: number, n: number): number { return (x << n) | (x >>> (32 - n));}
/** Byte length of a SHA1 digest. */export const BYTES: number = 20;
/** A class representation of the SHA1 algorithm. */export class SHA1 { readonly hashSize: number = BYTES;
private _buf: Uint8Array = new Uint8Array(64); private _bufIdx!: number; private _count!: Uint32Array; private _K: Uint32Array = new Uint32Array([0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6]); private _H!: Uint32Array; private _finalized!: boolean;
/** Creates a SHA1 instance. */ constructor() { this.init(); }
/** Reduces the four input numbers to a single one. */ protected static F(t: number, b: number, c: number, d: number): number { if (t <= 19) { return (b & c) | (~b & d); } else if (t <= 39) { return b ^ c ^ d; } else if (t <= 59) { return (b & c) | (b & d) | (c & d); } else { return b ^ c ^ d; } }
/** Initializes a hash instance. */ init(): SHA1 { // prettier-ignore this._H = new Uint32Array([ 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 ]);
this._bufIdx = 0; this._count = new Uint32Array(2); this._buf.fill(0); this._finalized = false;
return this; }
/** Updates a hash with additional message data. */ update(msg: string | Uint8Array, inputEncoding?: string): SHA1 { if (msg === null) { throw new TypeError("msg must be a string or Uint8Array."); } else if (typeof msg === "string") { msg = encode(msg, inputEncoding) as Uint8Array; }
// process the msg as many times as possible, the rest is stored in the buffer // message is processed in 512 bit (64 byte chunks) for (let i: number = 0; i < msg.length; i++) { this._buf[this._bufIdx++] = msg[i]; if (this._bufIdx === 64) { this.transform(); this._bufIdx = 0; } }
// counter update (number of message bits) const c: Uint32Array = this._count;
if ((c[0] += msg.length << 3) < msg.length << 3) { c[1]++; }
c[1] += msg.length >>> 29;
return this; }
/** Finalizes a hash with additional message data. */ digest(outputEncoding?: string): string | Uint8Array { if (this._finalized) { throw new Error("digest has already been called.") }
this._finalized = true;
// append '1' const b: Uint8Array = this._buf; let idx: number = this._bufIdx; b[idx++] = 0x80;
// zeropad up to byte pos 56 while (idx !== 56) { if (idx === 64) { this.transform(); idx = 0; } b[idx++] = 0; }
// append length in bits const c: Uint32Array = this._count;
b[56] = (c[1] >>> 24) & 0xff; b[57] = (c[1] >>> 16) & 0xff; b[58] = (c[1] >>> 8) & 0xff; b[59] = (c[1] >>> 0) & 0xff; b[60] = (c[0] >>> 24) & 0xff; b[61] = (c[0] >>> 16) & 0xff; b[62] = (c[0] >>> 8) & 0xff; b[63] = (c[0] >>> 0) & 0xff;
this.transform();
// return the hash as byte array (20 bytes) const hash: Uint8Array = new Uint8Array(BYTES);
for (let i: number = 0; i < 5; i++) { hash[(i << 2) + 0] = (this._H[i] >>> 24) & 0xff; hash[(i << 2) + 1] = (this._H[i] >>> 16) & 0xff; hash[(i << 2) + 2] = (this._H[i] >>> 8) & 0xff; hash[(i << 2) + 3] = (this._H[i] >>> 0) & 0xff; }
// clear internal states and prepare for new hash this.init();
return outputEncoding ? decode(hash, outputEncoding) : hash; }
/** Performs one transformation cycle. */ private transform(): void { const h: Uint32Array = this._H; let a: number = h[0]; let b: number = h[1]; let c: number = h[2]; let d: number = h[3]; let e: number = h[4];
// convert byte buffer to words const w: Uint32Array = new Uint32Array(80);
for (let i: number = 0; i < 16; i++) { w[i] = this._buf[(i << 2) + 3] | (this._buf[(i << 2) + 2] << 8) | (this._buf[(i << 2) + 1] << 16) | (this._buf[i << 2] << 24); }
for (let t: number = 0; t < 80; t++) { if (t >= 16) { w[t] = rotl(w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16], 1); }
const tmp: number = (rotl(a, 5) + SHA1.F(t, b, c, d) + e + w[t] + this._K[Math.floor(t / 20)]) | 0;
e = d; d = c; c = rotl(b, 30); b = a; a = tmp; }
h[0] = (h[0] + a) | 0; h[1] = (h[1] + b) | 0; h[2] = (h[2] + c) | 0; h[3] = (h[3] + d) | 0; h[4] = (h[4] + e) | 0; }}
/** Generates a SHA1 hash of the input data. */export function sha1( msg: string | Uint8Array, inputEncoding?: string, outputEncoding?: string): string | Uint8Array { return new SHA1().update(msg, inputEncoding).digest(outputEncoding);}