hmacSha256.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined
  3. * in FIPS 180-2
  4. * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
  5. * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
  6. * Distributed under the BSD License
  7. * See http://pajhome.org.uk/crypt/md5 for details.
  8. * Also http://anmar.eu.org/projects/jssha2/
  9. */
  10. /*
  11. * Configurable variables. You may need to tweak these to be compatible with
  12. * the server-side, but the defaults work in most cases.
  13. */
  14. export class hmacSha256 {
  15. constructor() {
  16. }
  17. private static instance: hmacSha256 = null
  18. static get Instance(): hmacSha256 {
  19. if (!this.instance) {
  20. this.instance = new hmacSha256()
  21. }
  22. return this.instance
  23. }
  24. private hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
  25. private b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
  26. /*
  27. * These are the functions you'll usually want to call
  28. * They take string arguments and return either hex or base-64 encoded strings
  29. */
  30. hex_sha256(s) { return this.rstr2hex(this.rstr_sha256(this.str2rstr_utf8(s))); }
  31. b64_sha256(s) { return this.rstr2b64(this.rstr_sha256(this.str2rstr_utf8(s))); }
  32. any_sha256(s, e) { return this.rstr2any(this.rstr_sha256(this.str2rstr_utf8(s)), e); }
  33. hex_hmac_sha256(k, d) { return this.rstr2hex(this.rstr_hmac_sha256(this.str2rstr_utf8(k), this.str2rstr_utf8(d))); }
  34. b64_hmac_sha256(k, d) { return this.rstr2b64(this.rstr_hmac_sha256(this.str2rstr_utf8(k), this.str2rstr_utf8(d))); }
  35. any_hmac_sha256(k, d, e) { return this.rstr2any(this.rstr_hmac_sha256(this.str2rstr_utf8(k), this.str2rstr_utf8(d)), e); }
  36. /*
  37. * Perform a simple self-test to see if the VM is working
  38. */
  39. sha256_vm_test() {
  40. return this.hex_sha256("abc").toLowerCase() ==
  41. "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
  42. }
  43. /*
  44. * Calculate the sha256 of a raw string
  45. */
  46. rstr_sha256(s) {
  47. return this.binb2rstr(this.binb_sha256(this.rstr2binb(s), s.length * 8));
  48. }
  49. /*
  50. * Calculate the HMAC-sha256 of a key and some data (raw strings)
  51. */
  52. rstr_hmac_sha256(key, data) {
  53. var bkey = this.rstr2binb(key);
  54. if (bkey.length > 16) bkey = this.binb_sha256(bkey, key.length * 8);
  55. var ipad = Array(16), opad = Array(16);
  56. for (var i = 0; i < 16; i++) {
  57. ipad[i] = bkey[i] ^ 0x36363636;
  58. opad[i] = bkey[i] ^ 0x5C5C5C5C;
  59. }
  60. var hash = this.binb_sha256(ipad.concat(this.rstr2binb(data)), 512 + data.length * 8);
  61. return this.binb2rstr(this.binb_sha256(opad.concat(hash), 512 + 256));
  62. }
  63. /*
  64. * Convert a raw string to a hex string
  65. */
  66. rstr2hex(input) {
  67. try { this.hexcase } catch (e) { this.hexcase = 0; }
  68. var hex_tab = this.hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  69. var output = "";
  70. var x;
  71. for (var i = 0; i < input.length; i++) {
  72. x = input.charCodeAt(i);
  73. output += hex_tab.charAt((x >>> 4) & 0x0F)
  74. + hex_tab.charAt(x & 0x0F);
  75. }
  76. return output;
  77. }
  78. /*
  79. * Convert a raw string to a base-64 string
  80. */
  81. rstr2b64(input) {
  82. try { this.b64pad } catch (e) { this.b64pad = ''; }
  83. var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  84. var output = "";
  85. var len = input.length;
  86. for (var i = 0; i < len; i += 3) {
  87. var triplet = (input.charCodeAt(i) << 16)
  88. | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0)
  89. | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
  90. for (var j = 0; j < 4; j++) {
  91. if (i * 8 + j * 6 > input.length * 8) output += this.b64pad;
  92. else output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
  93. }
  94. }
  95. return output;
  96. }
  97. /*
  98. * Convert a raw string to an arbitrary string encoding
  99. */
  100. rstr2any(input, encoding) {
  101. var divisor = encoding.length;
  102. var remainders = Array();
  103. var i, q, x, quotient;
  104. /* Convert to an array of 16-bit big-endian values, forming the dividend */
  105. var dividend = Array(Math.ceil(input.length / 2));
  106. for (i = 0; i < dividend.length; i++) {
  107. dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
  108. }
  109. /*
  110. * Repeatedly perform a long division. The binary array forms the dividend,
  111. * the length of the encoding is the divisor. Once computed, the quotient
  112. * forms the dividend for the next step. We stop when the dividend is zero.
  113. * All remainders are stored for later use.
  114. */
  115. while (dividend.length > 0) {
  116. quotient = Array();
  117. x = 0;
  118. for (i = 0; i < dividend.length; i++) {
  119. x = (x << 16) + dividend[i];
  120. q = Math.floor(x / divisor);
  121. x -= q * divisor;
  122. if (quotient.length > 0 || q > 0)
  123. quotient[quotient.length] = q;
  124. }
  125. remainders[remainders.length] = x;
  126. dividend = quotient;
  127. }
  128. /* Convert the remainders to the output string */
  129. var output = "";
  130. for (i = remainders.length - 1; i >= 0; i--)
  131. output += encoding.charAt(remainders[i]);
  132. /* Append leading zero equivalents */
  133. var full_length = Math.ceil(input.length * 8 /
  134. (Math.log(encoding.length) / Math.log(2)))
  135. for (i = output.length; i < full_length; i++)
  136. output = encoding[0] + output;
  137. return output;
  138. }
  139. /*
  140. * Encode a string as utf-8.
  141. * For efficiency, this assumes the input is valid utf-16.
  142. */
  143. str2rstr_utf8(input) {
  144. var output = "";
  145. var i = -1;
  146. var x, y;
  147. while (++i < input.length) {
  148. /* Decode utf-16 surrogate pairs */
  149. x = input.charCodeAt(i);
  150. y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
  151. if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
  152. x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
  153. i++;
  154. }
  155. /* Encode output as utf-8 */
  156. if (x <= 0x7F)
  157. output += String.fromCharCode(x);
  158. else if (x <= 0x7FF)
  159. output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F),
  160. 0x80 | (x & 0x3F));
  161. else if (x <= 0xFFFF)
  162. output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
  163. 0x80 | ((x >>> 6) & 0x3F),
  164. 0x80 | (x & 0x3F));
  165. else if (x <= 0x1FFFFF)
  166. output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
  167. 0x80 | ((x >>> 12) & 0x3F),
  168. 0x80 | ((x >>> 6) & 0x3F),
  169. 0x80 | (x & 0x3F));
  170. }
  171. return output;
  172. }
  173. /*
  174. * Encode a string as utf-16
  175. */
  176. str2rstr_utf16le(input) {
  177. var output = "";
  178. for (var i = 0; i < input.length; i++)
  179. output += String.fromCharCode(input.charCodeAt(i) & 0xFF,
  180. (input.charCodeAt(i) >>> 8) & 0xFF);
  181. return output;
  182. }
  183. str2rstr_utf16be(input) {
  184. var output = "";
  185. for (var i = 0; i < input.length; i++)
  186. output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
  187. input.charCodeAt(i) & 0xFF);
  188. return output;
  189. }
  190. /*
  191. * Convert a raw string to an array of big-endian words
  192. * Characters >255 have their high-byte silently ignored.
  193. */
  194. rstr2binb(input) {
  195. var output = Array(input.length >> 2);
  196. for (var i = 0; i < output.length; i++)
  197. output[i] = 0;
  198. for (var i = 0; i < input.length * 8; i += 8)
  199. output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
  200. return output;
  201. }
  202. /*
  203. * Convert an array of big-endian words to a string
  204. */
  205. binb2rstr(input) {
  206. var output = "";
  207. for (var i = 0; i < input.length * 32; i += 8)
  208. output += String.fromCharCode((input[i >> 5] >>> (24 - i % 32)) & 0xFF);
  209. return output;
  210. }
  211. /*
  212. * Main sha256 function, with its support functions
  213. */
  214. sha256_S(X, n) { return (X >>> n) | (X << (32 - n)); }
  215. sha256_R(X, n) { return (X >>> n); }
  216. sha256_Ch(x, y, z) { return ((x & y) ^ ((~x) & z)); }
  217. sha256_Maj(x, y, z) { return ((x & y) ^ (x & z) ^ (y & z)); }
  218. sha256_Sigma0256(x) { return (this.sha256_S(x, 2) ^ this.sha256_S(x, 13) ^ this.sha256_S(x, 22)); }
  219. sha256_Sigma1256(x) { return (this.sha256_S(x, 6) ^ this.sha256_S(x, 11) ^ this.sha256_S(x, 25)); }
  220. sha256_Gamma0256(x) { return (this.sha256_S(x, 7) ^ this.sha256_S(x, 18) ^ this.sha256_R(x, 3)); }
  221. sha256_Gamma1256(x) { return (this.sha256_S(x, 17) ^ this.sha256_S(x, 19) ^ this.sha256_R(x, 10)); }
  222. sha256_Sigma0512(x) { return (this.sha256_S(x, 28) ^ this.sha256_S(x, 34) ^ this.sha256_S(x, 39)); }
  223. sha256_Sigma1512(x) { return (this.sha256_S(x, 14) ^ this.sha256_S(x, 18) ^ this.sha256_S(x, 41)); }
  224. sha256_Gamma0512(x) { return (this.sha256_S(x, 1) ^ this.sha256_S(x, 8) ^ this.sha256_R(x, 7)); }
  225. sha256_Gamma1512(x) { return (this.sha256_S(x, 19) ^ this.sha256_S(x, 61) ^ this.sha256_R(x, 6)); }
  226. private sha256_K = new Array
  227. (
  228. 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
  229. -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
  230. 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
  231. 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
  232. -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
  233. 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
  234. 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
  235. -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
  236. 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
  237. 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
  238. -1866530822, -1538233109, -1090935817, -965641998
  239. );
  240. binb_sha256(m, l) {
  241. var HASH = new Array(1779033703, -1150833019, 1013904242, -1521486534,
  242. 1359893119, -1694144372, 528734635, 1541459225);
  243. var W = new Array(64);
  244. var a, b, c, d, e, f, g, h;
  245. var i, j, T1, T2;
  246. /* append padding */
  247. m[l >> 5] |= 0x80 << (24 - l % 32);
  248. m[((l + 64 >> 9) << 4) + 15] = l;
  249. for (i = 0; i < m.length; i += 16) {
  250. a = HASH[0];
  251. b = HASH[1];
  252. c = HASH[2];
  253. d = HASH[3];
  254. e = HASH[4];
  255. f = HASH[5];
  256. g = HASH[6];
  257. h = HASH[7];
  258. for (j = 0; j < 64; j++) {
  259. if (j < 16) W[j] = m[j + i];
  260. else W[j] = this.safe_add(this.safe_add(this.safe_add(this.sha256_Gamma1256(W[j - 2]), W[j - 7]),
  261. this.sha256_Gamma0256(W[j - 15])), W[j - 16]);
  262. T1 = this.safe_add(this.safe_add(this.safe_add(this.safe_add(h, this.sha256_Sigma1256(e)), this.sha256_Ch(e, f, g)),
  263. this.sha256_K[j]), W[j]);
  264. T2 = this.safe_add(this.sha256_Sigma0256(a), this.sha256_Maj(a, b, c));
  265. h = g;
  266. g = f;
  267. f = e;
  268. e = this.safe_add(d, T1);
  269. d = c;
  270. c = b;
  271. b = a;
  272. a = this.safe_add(T1, T2);
  273. }
  274. HASH[0] = this.safe_add(a, HASH[0]);
  275. HASH[1] = this.safe_add(b, HASH[1]);
  276. HASH[2] = this.safe_add(c, HASH[2]);
  277. HASH[3] = this.safe_add(d, HASH[3]);
  278. HASH[4] = this.safe_add(e, HASH[4]);
  279. HASH[5] = this.safe_add(f, HASH[5]);
  280. HASH[6] = this.safe_add(g, HASH[6]);
  281. HASH[7] = this.safe_add(h, HASH[7]);
  282. }
  283. return HASH;
  284. }
  285. safe_add(x, y) {
  286. var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  287. var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  288. return (msw << 16) | (lsw & 0xFFFF);
  289. }
  290. }