sha256.ts 6.1 KB

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