_blake.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import { number, exists, output } from './_assert.js';
  2. import { Hash, toBytes, u32, isLE, byteSwap32, byteSwapIfBE } from './utils.js';
  3. // Blake is based on ChaCha permutation.
  4. // For BLAKE2b, the two extra permutations for rounds 10 and 11 are SIGMA[10..11] = SIGMA[0..1].
  5. // prettier-ignore
  6. export const SIGMA = /* @__PURE__ */ new Uint8Array([
  7. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
  8. 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
  9. 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
  10. 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
  11. 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
  12. 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
  13. 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
  14. 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
  15. 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
  16. 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
  17. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
  18. 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
  19. ]);
  20. export class BLAKE extends Hash {
  21. constructor(blockLen, outputLen, opts = {}, keyLen, saltLen, persLen) {
  22. super();
  23. this.blockLen = blockLen;
  24. this.outputLen = outputLen;
  25. this.length = 0;
  26. this.pos = 0;
  27. this.finished = false;
  28. this.destroyed = false;
  29. number(blockLen);
  30. number(outputLen);
  31. number(keyLen);
  32. if (outputLen < 0 || outputLen > keyLen)
  33. throw new Error('outputLen bigger than keyLen');
  34. if (opts.key !== undefined && (opts.key.length < 1 || opts.key.length > keyLen))
  35. throw new Error(`key must be up 1..${keyLen} byte long or undefined`);
  36. if (opts.salt !== undefined && opts.salt.length !== saltLen)
  37. throw new Error(`salt must be ${saltLen} byte long or undefined`);
  38. if (opts.personalization !== undefined && opts.personalization.length !== persLen)
  39. throw new Error(`personalization must be ${persLen} byte long or undefined`);
  40. this.buffer32 = u32((this.buffer = new Uint8Array(blockLen)));
  41. }
  42. update(data) {
  43. exists(this);
  44. // Main difference with other hashes: there is flag for last block,
  45. // so we cannot process current block before we know that there
  46. // is the next one. This significantly complicates logic and reduces ability
  47. // to do zero-copy processing
  48. const { blockLen, buffer, buffer32 } = this;
  49. data = toBytes(data);
  50. const len = data.length;
  51. const offset = data.byteOffset;
  52. const buf = data.buffer;
  53. for (let pos = 0; pos < len;) {
  54. // If buffer is full and we still have input (don't process last block, same as blake2s)
  55. if (this.pos === blockLen) {
  56. if (!isLE)
  57. byteSwap32(buffer32);
  58. this.compress(buffer32, 0, false);
  59. if (!isLE)
  60. byteSwap32(buffer32);
  61. this.pos = 0;
  62. }
  63. const take = Math.min(blockLen - this.pos, len - pos);
  64. const dataOffset = offset + pos;
  65. // full block && aligned to 4 bytes && not last in input
  66. if (take === blockLen && !(dataOffset % 4) && pos + take < len) {
  67. const data32 = new Uint32Array(buf, dataOffset, Math.floor((len - pos) / 4));
  68. if (!isLE)
  69. byteSwap32(data32);
  70. for (let pos32 = 0; pos + blockLen < len; pos32 += buffer32.length, pos += blockLen) {
  71. this.length += blockLen;
  72. this.compress(data32, pos32, false);
  73. }
  74. if (!isLE)
  75. byteSwap32(data32);
  76. continue;
  77. }
  78. buffer.set(data.subarray(pos, pos + take), this.pos);
  79. this.pos += take;
  80. this.length += take;
  81. pos += take;
  82. }
  83. return this;
  84. }
  85. digestInto(out) {
  86. exists(this);
  87. output(out, this);
  88. const { pos, buffer32 } = this;
  89. this.finished = true;
  90. // Padding
  91. this.buffer.subarray(pos).fill(0);
  92. if (!isLE)
  93. byteSwap32(buffer32);
  94. this.compress(buffer32, 0, true);
  95. if (!isLE)
  96. byteSwap32(buffer32);
  97. const out32 = u32(out);
  98. this.get().forEach((v, i) => (out32[i] = byteSwapIfBE(v)));
  99. }
  100. digest() {
  101. const { buffer, outputLen } = this;
  102. this.digestInto(buffer);
  103. const res = buffer.slice(0, outputLen);
  104. this.destroy();
  105. return res;
  106. }
  107. _cloneInto(to) {
  108. const { buffer, length, finished, destroyed, outputLen, pos } = this;
  109. to || (to = new this.constructor({ dkLen: outputLen }));
  110. to.set(...this.get());
  111. to.length = length;
  112. to.finished = finished;
  113. to.destroyed = destroyed;
  114. to.outputLen = outputLen;
  115. to.buffer.set(buffer);
  116. to.pos = pos;
  117. return to;
  118. }
  119. }
  120. //# sourceMappingURL=_blake.js.map