dbadia

ctr_drbg.ts

Dec 29th, 2024 (edited)
116
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.25 KB | None | 0 0
  1. /*!
  2. * ctr-drbg.js - ctr-drbg implementation for bcrypto
  3. * Copyright (c) 2019, Christopher Jeffrey (MIT License).
  4. * https://github.com/bcoin-org/bcrypto
  5. *
  6. * Parts of this software are based on google/boringssl:
  7. * https://github.com/google/boringssl
  8. *
  9. * Resources:
  10. * https://csrc.nist.gov/publications/detail/sp/800-90a/archive/2012-01-23
  11. * https://github.com/google/boringssl/blob/master/crypto/fipsmodule/rand/ctrdrbg.c
  12. * https://github.com/google/boringssl/blob/master/crypto/fipsmodule/rand/internal.h
  13. * https://github.com/openssl/openssl/blob/master/crypto/rand/drbg_lib.c
  14. * https://github.com/cryptocoinjs/drbg.js/blob/master/ctr.js
  15. * https://github.com/netroby/jdk9-dev/blob/master/jdk/src/java.base/share/classes/sun/security/provider/CtrDrbg.java
  16. */
  17.  
  18. import { increment } from "./bufferUtils";
  19. import { encryptAES128ECB } from "./crypto";
  20.  
  21. const MAX_GENERATE_LENGTH = 65536;
  22. // const RESEED_INTERVAL = 0x1000000000000;
  23.  
  24. export class CtrDRBG {
  25. constructor(
  26. bits: 128,
  27. derivation: boolean,
  28. entropy?: Buffer,
  29. nonce?: Buffer,
  30. pers?: Buffer,
  31. ) {
  32. this.ctr = Buffer.alloc(16, 0);
  33. this.keySize = bits >>> 3;
  34. this.blkSize = 16;
  35. this.entSize = this.keySize + this.blkSize;
  36. this.slab = Buffer.alloc(this.entSize);
  37. this.K = this.slab.subarray(0, this.keySize);
  38. this.V = this.slab.subarray(this.keySize);
  39. this.derivation = derivation;
  40. // this.rounds = 0;
  41. this.initialized = false;
  42.  
  43. if (entropy) this.init(entropy, nonce, pers);
  44. }
  45.  
  46. /** The internal counter */
  47. private ctr: Buffer;
  48. private readonly keySize: number;
  49. private readonly blkSize: number;
  50. private readonly entSize: number;
  51. private slab: Buffer;
  52. private K: Buffer;
  53. private V: Buffer;
  54. private readonly derivation: boolean;
  55. // private rounds: number;
  56. private initialized: boolean;
  57.  
  58. init(
  59. entropy: Buffer,
  60. nonce: Buffer = Buffer.alloc(0),
  61. pers: Buffer = Buffer.alloc(0),
  62. ): this {
  63. let seed: Buffer;
  64.  
  65. if (this.derivation) {
  66. seed = this.derive(entropy, nonce, pers);
  67. } else {
  68. if (entropy.length + nonce.length > this.entSize) {
  69. throw new Error("Entropy is too long.");
  70. }
  71.  
  72. if (pers.length > this.entSize) {
  73. throw new Error("Personalization string is too long.");
  74. }
  75.  
  76. seed = Buffer.alloc(this.entSize, 0);
  77.  
  78. entropy.copy(seed, 0);
  79. nonce.copy(seed, entropy.length);
  80.  
  81. for (let i = 0; i < pers.length; i++) seed[i] ^= pers[i];
  82. }
  83.  
  84. this.slab.fill(0);
  85. this.V.copy(this.ctr, 0);
  86. this.update(seed);
  87. this.initialized = true;
  88. // this.rounds = 1;
  89.  
  90. return this;
  91. }
  92.  
  93. reseed(entropy: Buffer, add: Buffer = Buffer.alloc(0)): this {
  94. // if (this.rounds === 0)
  95. if (!this.initialized) throw new Error("DRBG not initialized.");
  96.  
  97. let seed: Buffer;
  98.  
  99. if (this.derivation) {
  100. seed = this.derive(entropy, add);
  101. } else {
  102. if (add.length > this.entSize) {
  103. throw new Error("Additional data is too long.");
  104. }
  105.  
  106. seed = Buffer.alloc(this.entSize, 0x00);
  107. entropy.copy(seed, 0);
  108. for (let i = 0; i < add.length; i++) seed[i] ^= add[i];
  109. }
  110.  
  111. this.update(seed);
  112. // this.rounds = 1;
  113.  
  114. return this;
  115. }
  116.  
  117. private next(): Buffer {
  118. increment(this.ctr);
  119. return encryptAES128ECB(this.ctr, this.K);
  120. }
  121.  
  122. generate(len: number, add?: Buffer): Buffer {
  123. // if (this.rounds === 0)
  124. if (!this.initialized) throw new Error("DRBG not initialized.");
  125.  
  126. // if (this.rounds > RESEED_INTERVAL)
  127. // throw new Error("Reseed is required.");
  128.  
  129. if (len > MAX_GENERATE_LENGTH) {
  130. throw new Error("Requested length is too long.");
  131. }
  132.  
  133. if (add && add.length > 0) {
  134. if (this.derivation) add = this.derive(add);
  135.  
  136. this.update(add);
  137. }
  138.  
  139. const blocks = Math.ceil(len / this.blkSize);
  140. const out = Buffer.alloc(blocks * this.blkSize);
  141.  
  142. for (let i = 0; i < blocks; i++) {
  143. const ciphertext = this.next();
  144. ciphertext.copy(out, i * this.blkSize);
  145. }
  146.  
  147. this.update(add);
  148. // this.rounds += 1;
  149. this.initialized = true;
  150.  
  151. return out.subarray(0, len);
  152. }
  153.  
  154. /*
  155. * Helpers
  156. */
  157.  
  158. update(seed: Buffer = Buffer.alloc(0)): this {
  159. if (seed.length > this.entSize) throw new Error("Seed is too long.");
  160.  
  161. const newSlab = Buffer.alloc(this.slab.length, 0);
  162. // this.slab.fill(0);
  163.  
  164. for (let i = 0; i < this.entSize; i += this.blkSize) {
  165. this.next().copy(newSlab, i);
  166. // ciphertext.copy(this.slab, i);
  167. }
  168.  
  169. // for (let i = 0; i < seed.length; i++) this.slab[i] ^= seed[i];
  170. for (let i = 0; i < seed.length; i++) newSlab[i] ^= seed[i];
  171.  
  172. newSlab.copy(this.slab, 0);
  173. this.V.copy(this.ctr, 0);
  174.  
  175. return this;
  176. }
  177.  
  178. serialize(...input: Buffer[]): Buffer {
  179. const N = this.entSize;
  180.  
  181. let L = 0;
  182.  
  183. for (const item of input) L += item.length;
  184.  
  185. let size = this.blkSize + 4 + 4 + L + 1;
  186.  
  187. if (size % this.blkSize) size += this.blkSize - (size % this.blkSize);
  188.  
  189. // S = IV || (L || N || input || 0x80 || 0x00...)
  190. const S = Buffer.alloc(size, 0x00);
  191.  
  192. let pos = this.blkSize;
  193. S.writeUInt32BE(L, pos);
  194. pos += 4;
  195. S.writeUInt32BE(N, pos);
  196. pos += 4;
  197.  
  198. for (const item of input) pos += item.copy(S, pos);
  199.  
  200. S[pos++] = 0x80;
  201.  
  202. return S;
  203. }
  204.  
  205. derive(...input: Buffer[]): Buffer {
  206. const S = this.serialize(...input);
  207. const N = S.length / this.blkSize;
  208. const K = Buffer.alloc(this.keySize);
  209. const blocks = Math.ceil(this.entSize / this.blkSize);
  210. const slab = Buffer.alloc(blocks * this.blkSize);
  211. const out = Buffer.alloc(blocks * this.blkSize);
  212. const chain = Buffer.alloc(this.blkSize);
  213.  
  214. for (let i = 0; i < K.length; i++) K[i] = i;
  215.  
  216. for (let i = 0; i < blocks; i++) {
  217. chain.fill(0);
  218.  
  219. S.writeUInt32BE(i, 0);
  220.  
  221. // chain = BCC(K, IV || S)
  222. for (let j = 0; j < N; j++) {
  223. for (let k = 0; k < chain.length; k++) {
  224. chain[k] ^= S[j * this.blkSize + k];
  225. }
  226.  
  227. // encrypt in-place
  228. encryptAES128ECB(chain, K).copy(chain, 0);
  229. // ctx.encrypt(chain, 0, chain, 0);
  230. }
  231.  
  232. chain.copy(slab, i * this.blkSize);
  233. }
  234.  
  235. const k = slab.subarray(0, this.keySize);
  236. const x = slab.subarray(this.keySize, this.entSize);
  237.  
  238. for (let i = 0; i < blocks; i++) {
  239. // encrypt in-place
  240. encryptAES128ECB(x, k).copy(x, 0);
  241. // ctx.encrypt(x, 0, x, 0);
  242. x.copy(out, i * this.blkSize);
  243. }
  244.  
  245. return out.subarray(0, this.entSize);
  246. }
  247. }
  248.  
Advertisement
Add Comment
Please, Sign In to add comment