UNPKG

5.62 kBPlain TextView Raw
1import { jsSHABase, TWO_PWR_32, sha_variant_error, parseInputOption } from "./common";
2import {
3 packedValue,
4 FixedLengthOptionsEncodingType,
5 FixedLengthOptionsNoEncodingType,
6 FormatNoTextType,
7} from "./custom_types";
8import { getStrConverter } from "./converters";
9import { ch_32, parity_32, maj_32, rotl_32, safeAdd_32_2, safeAdd_32_5 } from "./primitives_32";
10
11/**
12 * Gets the state values for the specified SHA variant.
13 *
14 * @param _variant: Unused
15 * @returns The initial state values.
16 */
17function getNewState(_variant: "SHA-1"): number[] {
18 return [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];
19}
20
21/**
22 * Performs a round of SHA-1 hashing over a 512-byte block. This clobbers `H`.
23 *
24 * @param block The binary array representation of the block to hash.
25 * @param H The intermediate H values from a previous round.
26 * @returns The resulting H values.
27 */
28function roundSHA1(block: number[], H: number[]): number[] {
29 let a, b, c, d, e, T, t;
30 const W: number[] = [];
31
32 a = H[0];
33 b = H[1];
34 c = H[2];
35 d = H[3];
36 e = H[4];
37
38 for (t = 0; t < 80; t += 1) {
39 if (t < 16) {
40 W[t] = block[t];
41 } else {
42 W[t] = rotl_32(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);
43 }
44
45 if (t < 20) {
46 T = safeAdd_32_5(rotl_32(a, 5), ch_32(b, c, d), e, 0x5a827999, W[t]);
47 } else if (t < 40) {
48 T = safeAdd_32_5(rotl_32(a, 5), parity_32(b, c, d), e, 0x6ed9eba1, W[t]);
49 } else if (t < 60) {
50 T = safeAdd_32_5(rotl_32(a, 5), maj_32(b, c, d), e, 0x8f1bbcdc, W[t]);
51 } else {
52 T = safeAdd_32_5(rotl_32(a, 5), parity_32(b, c, d), e, 0xca62c1d6, W[t]);
53 }
54
55 e = d;
56 d = c;
57 c = rotl_32(b, 30);
58 b = a;
59 a = T;
60 }
61
62 H[0] = safeAdd_32_2(a, H[0]);
63 H[1] = safeAdd_32_2(b, H[1]);
64 H[2] = safeAdd_32_2(c, H[2]);
65 H[3] = safeAdd_32_2(d, H[3]);
66 H[4] = safeAdd_32_2(e, H[4]);
67
68 return H;
69}
70
71/**
72 * Finalizes the SHA-1 hash. This clobbers `remainder` and `H`.
73 *
74 * @param remainder Any leftover unprocessed packed ints that still need to be processed.
75 * @param remainderBinLen The number of bits in `remainder`.
76 * @param processedBinLen The number of bits already processed.
77 * @param H The intermediate H values from a previous round.
78 * @returns The array of integers representing the SHA-1 hash of message.
79 */
80function finalizeSHA1(remainder: number[], remainderBinLen: number, processedBinLen: number, H: number[]): number[] {
81 let i;
82
83 /* The 65 addition is a hack but it works. The correct number is
84 actually 72 (64 + 8) but the below math fails if
85 remainderBinLen + 72 % 512 = 0. Since remainderBinLen % 8 = 0,
86 "shorting" the addition is OK. */
87 const offset = (((remainderBinLen + 65) >>> 9) << 4) + 15,
88 totalLen = remainderBinLen + processedBinLen;
89 while (remainder.length <= offset) {
90 remainder.push(0);
91 }
92 /* Append '1' at the end of the binary string */
93 remainder[remainderBinLen >>> 5] |= 0x80 << (24 - (remainderBinLen % 32));
94
95 /* Append length of binary string in the position such that the new
96 * length is a multiple of 512. Logic does not work for even multiples
97 * of 512 but there can never be even multiples of 512. JavaScript
98 * numbers are limited to 2^53 so it's "safe" to treat the totalLen as
99 * a 64-bit integer. */
100 remainder[offset] = totalLen & 0xffffffff;
101
102 /* Bitwise operators treat the operand as a 32-bit number so need to
103 * use hacky division and round to get access to upper 32-ish bits */
104 remainder[offset - 1] = (totalLen / TWO_PWR_32) | 0;
105
106 /* This will always be at least 1 full chunk */
107 for (i = 0; i < remainder.length; i += 16) {
108 H = roundSHA1(remainder.slice(i, i + 16), H);
109 }
110
111 return H;
112}
113
114export default class jsSHA extends jsSHABase<number[], "SHA-1"> {
115 intermediateState: number[];
116 variantBlockSize: number;
117 bigEndianMod: -1 | 1;
118 outputBinLen: number;
119 isVariableLen: boolean;
120 HMACSupported: boolean;
121
122 /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
123 converterFunc: (input: any, existingBin: number[], existingBinLen: number) => packedValue;
124 roundFunc: (block: number[], H: number[]) => number[];
125 finalizeFunc: (remainder: number[], remainderBinLen: number, processedBinLen: number, H: number[]) => number[];
126 stateCloneFunc: (state: number[]) => number[];
127 newStateFunc: (variant: "SHA-1") => number[];
128 getMAC: () => number[];
129
130 constructor(variant: "SHA-1", inputFormat: "TEXT", options?: FixedLengthOptionsEncodingType);
131 constructor(variant: "SHA-1", inputFormat: FormatNoTextType, options?: FixedLengthOptionsNoEncodingType);
132 // eslint-disable-next-line @typescript-eslint/no-explicit-any
133 constructor(variant: any, inputFormat: any, options?: any) {
134 if ("SHA-1" !== variant) {
135 throw new Error(sha_variant_error);
136 }
137 super(variant, inputFormat, options);
138 const resolvedOptions = options || {};
139
140 this.HMACSupported = true;
141 // eslint-disable-next-line @typescript-eslint/unbound-method
142 this.getMAC = this._getHMAC;
143 this.bigEndianMod = -1;
144 this.converterFunc = getStrConverter(this.inputFormat, this.utfType, this.bigEndianMod);
145 this.roundFunc = roundSHA1;
146 this.stateCloneFunc = function (state: number[]): number[] {
147 return state.slice();
148 };
149 this.newStateFunc = getNewState;
150 this.finalizeFunc = finalizeSHA1;
151
152 this.intermediateState = getNewState(variant);
153 this.variantBlockSize = 512;
154 this.outputBinLen = 160;
155 this.isVariableLen = false;
156
157 if (resolvedOptions["hmacKey"]) {
158 this._setHMACKey(parseInputOption("hmacKey", resolvedOptions["hmacKey"], this.bigEndianMod));
159 }
160 }
161}