UNPKG

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