hmac.js 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import { hash as assertHash, bytes as assertBytes, exists as assertExists } from './_assert.js';
  2. import { Hash, toBytes } from './utils.js';
  3. // HMAC (RFC 2104)
  4. export class HMAC extends Hash {
  5. constructor(hash, _key) {
  6. super();
  7. this.finished = false;
  8. this.destroyed = false;
  9. assertHash(hash);
  10. const key = toBytes(_key);
  11. this.iHash = hash.create();
  12. if (typeof this.iHash.update !== 'function')
  13. throw new Error('Expected instance of class which extends utils.Hash');
  14. this.blockLen = this.iHash.blockLen;
  15. this.outputLen = this.iHash.outputLen;
  16. const blockLen = this.blockLen;
  17. const pad = new Uint8Array(blockLen);
  18. // blockLen can be bigger than outputLen
  19. pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
  20. for (let i = 0; i < pad.length; i++)
  21. pad[i] ^= 0x36;
  22. this.iHash.update(pad);
  23. // By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone
  24. this.oHash = hash.create();
  25. // Undo internal XOR && apply outer XOR
  26. for (let i = 0; i < pad.length; i++)
  27. pad[i] ^= 0x36 ^ 0x5c;
  28. this.oHash.update(pad);
  29. pad.fill(0);
  30. }
  31. update(buf) {
  32. assertExists(this);
  33. this.iHash.update(buf);
  34. return this;
  35. }
  36. digestInto(out) {
  37. assertExists(this);
  38. assertBytes(out, this.outputLen);
  39. this.finished = true;
  40. this.iHash.digestInto(out);
  41. this.oHash.update(out);
  42. this.oHash.digestInto(out);
  43. this.destroy();
  44. }
  45. digest() {
  46. const out = new Uint8Array(this.oHash.outputLen);
  47. this.digestInto(out);
  48. return out;
  49. }
  50. _cloneInto(to) {
  51. // Create new instance without calling constructor since key already in state and we don't know it.
  52. to || (to = Object.create(Object.getPrototypeOf(this), {}));
  53. const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
  54. to = to;
  55. to.finished = finished;
  56. to.destroyed = destroyed;
  57. to.blockLen = blockLen;
  58. to.outputLen = outputLen;
  59. to.oHash = oHash._cloneInto(to.oHash);
  60. to.iHash = iHash._cloneInto(to.iHash);
  61. return to;
  62. }
  63. destroy() {
  64. this.destroyed = true;
  65. this.oHash.destroy();
  66. this.iHash.destroy();
  67. }
  68. }
  69. /**
  70. * HMAC: RFC2104 message authentication code.
  71. * @param hash - function that would be used e.g. sha256
  72. * @param key - message key
  73. * @param message - message data
  74. */
  75. export const hmac = (hash, key, message) => new HMAC(hash, key).update(message).digest();
  76. hmac.create = (hash, key) => new HMAC(hash, key);
  77. //# sourceMappingURL=hmac.js.map