hkdf.js 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.hkdf = void 0;
  4. exports.extract = extract;
  5. exports.expand = expand;
  6. /**
  7. * HKDF (RFC 5869): extract + expand in one step.
  8. * See https://soatok.blog/2021/11/17/understanding-hkdf/.
  9. * @module
  10. */
  11. const hmac_ts_1 = require("./hmac.js");
  12. const utils_ts_1 = require("./utils.js");
  13. /**
  14. * HKDF-extract from spec. Less important part. `HKDF-Extract(IKM, salt) -> PRK`
  15. * Arguments position differs from spec (IKM is first one, since it is not optional)
  16. * @param hash - hash function that would be used (e.g. sha256)
  17. * @param ikm - input keying material, the initial key
  18. * @param salt - optional salt value (a non-secret random value)
  19. */
  20. function extract(hash, ikm, salt) {
  21. (0, utils_ts_1.ahash)(hash);
  22. // NOTE: some libraries treat zero-length array as 'not provided';
  23. // we don't, since we have undefined as 'not provided'
  24. // https://github.com/RustCrypto/KDFs/issues/15
  25. if (salt === undefined)
  26. salt = new Uint8Array(hash.outputLen);
  27. return (0, hmac_ts_1.hmac)(hash, (0, utils_ts_1.toBytes)(salt), (0, utils_ts_1.toBytes)(ikm));
  28. }
  29. const HKDF_COUNTER = /* @__PURE__ */ Uint8Array.from([0]);
  30. const EMPTY_BUFFER = /* @__PURE__ */ Uint8Array.of();
  31. /**
  32. * HKDF-expand from the spec. The most important part. `HKDF-Expand(PRK, info, L) -> OKM`
  33. * @param hash - hash function that would be used (e.g. sha256)
  34. * @param prk - a pseudorandom key of at least HashLen octets (usually, the output from the extract step)
  35. * @param info - optional context and application specific information (can be a zero-length string)
  36. * @param length - length of output keying material in bytes
  37. */
  38. function expand(hash, prk, info, length = 32) {
  39. (0, utils_ts_1.ahash)(hash);
  40. (0, utils_ts_1.anumber)(length);
  41. const olen = hash.outputLen;
  42. if (length > 255 * olen)
  43. throw new Error('Length should be <= 255*HashLen');
  44. const blocks = Math.ceil(length / olen);
  45. if (info === undefined)
  46. info = EMPTY_BUFFER;
  47. // first L(ength) octets of T
  48. const okm = new Uint8Array(blocks * olen);
  49. // Re-use HMAC instance between blocks
  50. const HMAC = hmac_ts_1.hmac.create(hash, prk);
  51. const HMACTmp = HMAC._cloneInto();
  52. const T = new Uint8Array(HMAC.outputLen);
  53. for (let counter = 0; counter < blocks; counter++) {
  54. HKDF_COUNTER[0] = counter + 1;
  55. // T(0) = empty string (zero length)
  56. // T(N) = HMAC-Hash(PRK, T(N-1) | info | N)
  57. HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T)
  58. .update(info)
  59. .update(HKDF_COUNTER)
  60. .digestInto(T);
  61. okm.set(T, olen * counter);
  62. HMAC._cloneInto(HMACTmp);
  63. }
  64. HMAC.destroy();
  65. HMACTmp.destroy();
  66. (0, utils_ts_1.clean)(T, HKDF_COUNTER);
  67. return okm.slice(0, length);
  68. }
  69. /**
  70. * HKDF (RFC 5869): derive keys from an initial input.
  71. * Combines hkdf_extract + hkdf_expand in one step
  72. * @param hash - hash function that would be used (e.g. sha256)
  73. * @param ikm - input keying material, the initial key
  74. * @param salt - optional salt value (a non-secret random value)
  75. * @param info - optional context and application specific information (can be a zero-length string)
  76. * @param length - length of output keying material in bytes
  77. * @example
  78. * import { hkdf } from '@noble/hashes/hkdf';
  79. * import { sha256 } from '@noble/hashes/sha2';
  80. * import { randomBytes } from '@noble/hashes/utils';
  81. * const inputKey = randomBytes(32);
  82. * const salt = randomBytes(32);
  83. * const info = 'application-key';
  84. * const hk1 = hkdf(sha256, inputKey, salt, info, 32);
  85. */
  86. const hkdf = (hash, ikm, salt, info, length) => expand(hash, extract(hash, ikm, salt), info, length);
  87. exports.hkdf = hkdf;
  88. //# sourceMappingURL=hkdf.js.map