mirror of
https://github.com/swc-project/swc.git
synced 2024-12-21 12:41:54 +03:00
195 lines
4.8 KiB
TypeScript
195 lines
4.8 KiB
TypeScript
|
// Loaded from https://denopkg.com/chiefbiiko/sha1@v1.0.3/mod.ts
|
||
|
|
||
|
|
||
|
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);
|
||
|
}
|