@crossdyne/security 0.5.0-beta.1 → 0.5.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -74,20 +74,31 @@ var SecurityUtils = class {
74
74
  diff |= a[i] ^ b[i];
75
75
  return diff === 0;
76
76
  }
77
- /** Modular exponentiation (base^exp mod mod) using binary exponentiation. */
78
- static expMod(base, exp, mod) {
77
+ /**
78
+ * Async modular exponentiation with event-loop yielding.
79
+ * @param base - The base value.
80
+ * @param exp - The exponent.
81
+ * @param mod - The modulus.
82
+ * @param yieldEvery - Number of iterations before yielding (default 64).
83
+ */
84
+ static async expModAsync(base, exp, mod, yieldEvery = 64) {
79
85
  if (mod === 1n) return 0n;
80
86
  let res = 1n;
81
87
  base = base % mod;
88
+ let i = 0;
82
89
  while (exp > 0n) {
83
90
  if ((exp & 1n) === 1n)
84
91
  res = res * base % mod;
85
92
  exp >>= 1n;
86
93
  if (exp > 0n)
87
94
  base = base * base % mod;
95
+ if (++i % yieldEvery === 0) {
96
+ await new Promise((resolve) => setTimeout(resolve, 0));
97
+ }
88
98
  }
89
99
  return res;
90
100
  }
101
+ /** Converts bigint to minimal-length big-endian bytes (no padding). */
91
102
  static bigIntToRawBytes(bn) {
92
103
  if (bn === 0n)
93
104
  return new Uint8Array([0]);
@@ -253,7 +264,6 @@ var CryptoService = class {
253
264
  * Encrypts a serializable object to a Base64 string.
254
265
  * @param dataModel - Object or Uint8Array to encrypt.
255
266
  * @param key - AES-256 key (32 bytes).
256
- * @param options - AES-GCM configuration; uses default if omitted.
257
267
  * @returns Base64-encoded ciphertext with prepended nonce.
258
268
  */
259
269
  async encryptData(dataModel, key, version = 1 /* V1 */) {
@@ -300,7 +310,6 @@ var CryptoService = class {
300
310
  * Decrypts a Base64-encoded ciphertext back to the original object.
301
311
  * @param encryptedBase64 - The encrypted data.
302
312
  * @param key - AES-256 key (32 bytes).
303
- * @param options - AES-GCM configuration; uses default if omitted.
304
313
  * @returns Deserialized object, or null if input is empty.
305
314
  * @throws If authentication tag mismatch or corrupted data.
306
315
  */
@@ -355,15 +364,14 @@ var CryptoService = class {
355
364
  // src/crypto/key-derivation.service.ts
356
365
  var KeyDerivationService = class {
357
366
  /**
358
- * Derives KEK and Base64 AuthHash. Identity is hashed as-is — caller must
359
- * normalize (trim, lowercase, etc.) before calling.
360
- * @param identity - User identity (pre-normalized by caller).
361
- * @param identity - User identity (email, username).
362
- * @param password - User password.
363
- * @param salt - Random salt.
364
- * @param options - KDF configuration; uses default if omitted.
365
- * @returns Object with `kek` (Uint8Array) and `authHash` (Base64 string).
366
- */
367
+ * Derives KEK and Base64 AuthHash. Identity is used as-is in the
368
+ * combined salt string — caller must normalize (trim, lowercase, etc.) before calling.
369
+ * @param identity - User identity (email, username). Must be pre-normalized by caller.
370
+ * @param password - User password.
371
+ * @param salt - Random salt (minimum 16 bytes).
372
+ * @param version - Crypto version for profile selection.
373
+ * @returns Object with `kek` (Uint8Array) and `authHash` (Base64 string).
374
+ */
367
375
  async deriveKeysFromPassword(identity, password, salt, version) {
368
376
  if (!identity || identity.length === 0)
369
377
  throw new Error("Identity cannot be null or empty.");
@@ -402,63 +410,6 @@ var KeyDerivationService = class {
402
410
  authHash: SecurityUtils.toBase64(new Uint8Array(authBytes))
403
411
  };
404
412
  }
405
- /**
406
- * Derives an SRP-compatible authentication hash (output size = hash output length).
407
- * Identity is hashed as-is — caller must normalize (trim, lowercase, etc.) before calling.
408
- * @param identity - User identity (pre-normalized by caller).
409
- * @param identity - User identity.
410
- * @param password - User password.
411
- * @param salt - Random salt.
412
- * @param srpHashAlgorithm - SRP hash algorithm (SHA-256/384/512).
413
- * @param options - KDF configuration; uses default if omitted.
414
- * @returns Raw hash bytes for use as SRP verifier input (x).
415
- */
416
- async deriveAuthHashForSrp(identity, password, salt, srpHashAlgorithm, version) {
417
- if (!identity || identity.trim().length === 0)
418
- throw new Error("Identity cannot be null or empty.");
419
- if (!password || password.trim().length === 0)
420
- throw new Error("Password cannot be null or empty.");
421
- if (!salt || salt.length < 16)
422
- throw new Error("Salt must be at least 16 bytes.");
423
- const profile = CryptoProfileRegistry.getProfile(version);
424
- const opts = profile.kdfOptions;
425
- opts.validate();
426
- const srpHashSize = HashSizes[srpHashAlgorithm];
427
- const combinedPassword = `${identity}:${password}`;
428
- const passwordBytes = new TextEncoder().encode(combinedPassword);
429
- const baseKey = await crypto.subtle.importKey(
430
- "raw",
431
- passwordBytes,
432
- "PBKDF2",
433
- false,
434
- ["deriveBits"]
435
- );
436
- const masterKeyBits = await crypto.subtle.deriveBits({
437
- name: "PBKDF2",
438
- salt,
439
- iterations: opts.pbkdf2Iterations,
440
- hash: srpHashAlgorithm
441
- }, baseKey, srpHashSize * 8);
442
- const masterKey = await crypto.subtle.importKey(
443
- "raw",
444
- masterKeyBits,
445
- "HKDF",
446
- false,
447
- ["deriveBits"]
448
- );
449
- const info = new TextEncoder().encode("SRP-AUTH-HASH-v1");
450
- const authBytes = await crypto.subtle.deriveBits(
451
- {
452
- name: "HKDF",
453
- hash: srpHashAlgorithm,
454
- salt: new Uint8Array(0),
455
- info
456
- },
457
- masterKey,
458
- srpHashSize * 8
459
- );
460
- return new Uint8Array(authBytes);
461
- }
462
413
  };
463
414
 
464
415
  // src/utils/srp-encoding.ts
@@ -482,7 +433,7 @@ var SrpEncoding = class {
482
433
  * Follows RFC 5054 / SRP-6a:
483
434
  * - H(N) and H(g) are hashed as modulus-sized values.
484
435
  * - Identity (I) is hashed as raw UTF-8 bytes.
485
- * - A and B are padded to the modulus size before hashing.
436
+ * - A and B are zero-padded to the modulus size before hashing.
486
437
  * - K is the session key (H(S) without padding).
487
438
  *
488
439
  * @param ctx - SRP context containing N, g, hash algorithm, and modulus size.
@@ -513,7 +464,10 @@ var SrpEncoding = class {
513
464
  sessionKeyK
514
465
  );
515
466
  }
516
- /** Computes M2 = H(A || M1 || sessionKeyK). */
467
+ /**
468
+ * Computes M2 = H( PAD(A) || M1 || sessionKeyK ).
469
+ * A is zero-padded to the modulus size before hashing.
470
+ */
517
471
  static async computeM2(ctx, A, m1Bytes, sessionKeyK) {
518
472
  return this.computeHash(
519
473
  ctx.hashAlgorithmName,
@@ -525,10 +479,14 @@ var SrpEncoding = class {
525
479
  /** Computes session key K = H(S). */
526
480
  static async computeSessionKey(ctx, S) {
527
481
  const sBytes = SecurityUtils.bigIntToRawBytes(S);
528
- const hashBuffer = await crypto.subtle.digest(ctx.hashAlgorithmName, sBytes.buffer);
482
+ const safeBuffer = sBytes.buffer.slice(
483
+ sBytes.byteOffset,
484
+ sBytes.byteOffset + sBytes.byteLength
485
+ );
486
+ const hashBuffer = await crypto.subtle.digest(ctx.hashAlgorithmName, safeBuffer);
529
487
  return new Uint8Array(hashBuffer);
530
488
  }
531
- /** Hashes bytes and returns raw Uint8Array (для M1/M2). */
489
+ /** Hashes concatenated byte arrays and returns raw Uint8Array. */
532
490
  static async computeHash(algo, ...buffers) {
533
491
  const totalLen = buffers.reduce((sum, b) => sum + b.length, 0);
534
492
  const combined = new Uint8Array(totalLen);
@@ -554,35 +512,159 @@ var SrpEncoding = class {
554
512
  }
555
513
  };
556
514
 
557
- // src/srp/srp-client-service.ts
558
- var SrpClientService = class {
515
+ // src/srp/srp-group.ts
516
+ var SrpGroup = /* @__PURE__ */ ((SrpGroup2) => {
517
+ SrpGroup2[SrpGroup2["Rfc5054_1024"] = 1] = "Rfc5054_1024";
518
+ SrpGroup2[SrpGroup2["Rfc5054_1536"] = 2] = "Rfc5054_1536";
519
+ SrpGroup2[SrpGroup2["Rfc5054_2048"] = 3] = "Rfc5054_2048";
520
+ SrpGroup2[SrpGroup2["Rfc5054_3072"] = 4] = "Rfc5054_3072";
521
+ SrpGroup2[SrpGroup2["Rfc5054_4096"] = 5] = "Rfc5054_4096";
522
+ SrpGroup2[SrpGroup2["Rfc5054_6144"] = 6] = "Rfc5054_6144";
523
+ SrpGroup2[SrpGroup2["Rfc5054_8192"] = 7] = "Rfc5054_8192";
524
+ SrpGroup2[SrpGroup2["Custom"] = 99] = "Custom";
525
+ return SrpGroup2;
526
+ })(SrpGroup || {});
527
+
528
+ // src/srp/srp-group-params.ts
529
+ var SrpGroupParams = class {
530
+ /**
531
+ * Returns the prime modulus N for the given SRP group.
532
+ * @param group - SRP group identifier.
533
+ * @returns N as a bigint.
534
+ */
535
+ static getN(group) {
536
+ switch (group) {
537
+ case 1 /* Rfc5054_1024 */:
538
+ return BigInt("0xEEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2FC0EB06E3");
539
+ case 2 /* Rfc5054_1536 */:
540
+ return BigInt("0x9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA9614B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F84380B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0BE3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF56EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734AF7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB");
541
+ case 3 /* Rfc5054_2048 */:
542
+ return BigInt("0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73");
543
+ case 4 /* Rfc5054_3072 */:
544
+ return BigInt("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF");
545
+ case 5 /* Rfc5054_4096 */:
546
+ return BigInt("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF");
547
+ case 6 /* Rfc5054_6144 */:
548
+ return BigInt("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF");
549
+ case 7 /* Rfc5054_8192 */:
550
+ return BigInt("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF");
551
+ default:
552
+ throw new Error("Unsupported group");
553
+ }
554
+ }
555
+ /**
556
+ * Returns the generator g for the given SRP group.
557
+ * @param group - SRP group identifier.
558
+ * @returns g as a bigint (2, 5, or 19).
559
+ */
560
+ static getG(group) {
561
+ switch (group) {
562
+ case 1 /* Rfc5054_1024 */:
563
+ return BigInt(2);
564
+ case 2 /* Rfc5054_1536 */:
565
+ return BigInt(2);
566
+ case 3 /* Rfc5054_2048 */:
567
+ return BigInt(2);
568
+ case 4 /* Rfc5054_3072 */:
569
+ return BigInt(5);
570
+ case 5 /* Rfc5054_4096 */:
571
+ return BigInt(5);
572
+ case 6 /* Rfc5054_6144 */:
573
+ return BigInt(5);
574
+ case 7 /* Rfc5054_8192 */:
575
+ return BigInt(19);
576
+ default:
577
+ throw new Error("Unsupported group");
578
+ }
579
+ }
580
+ };
581
+
582
+ // src/srp/srp-options.ts
583
+ var SrpOptions = class {
559
584
  constructor() {
560
- this.keyDerivation = new KeyDerivationService();
585
+ /** Diffie-Hellman group. Default Rfc5054_3072 (g=5). */
586
+ this.group = 4 /* Rfc5054_3072 */;
587
+ /** Hash algorithm for SRP computations. Default SHA-256. */
588
+ this.hashAlgorithmName = "SHA-256";
589
+ /** Salt size in bytes. Default 32. */
590
+ this.saltSize = 32;
591
+ }
592
+ /** Prime modulus N for the selected group. */
593
+ get N() {
594
+ return SrpGroupParams.getN(this.group);
595
+ }
596
+ /** Generator g for the selected group (2, 5, or 19). */
597
+ get g() {
598
+ return SrpGroupParams.getG(this.group);
599
+ }
600
+ /** Byte length of N (ceil(bitLength / 8)). */
601
+ get modulusSize() {
602
+ return Math.floor((this.N.toString(2).length + 7) / 8);
561
603
  }
604
+ /**
605
+ * Computes the multiplier parameter k = H(PAD(N) || PAD(g)) (RFC 5054, 2.5.3).
606
+ * @returns k as a bigint.
607
+ */
608
+ async computeK() {
609
+ const nBytes = SecurityUtils.bigIntToFixedBytes(this.N, this.modulusSize);
610
+ const gBytes = SecurityUtils.bigIntToFixedBytes(this.g, this.modulusSize);
611
+ const combined = new Uint8Array(nBytes.length + gBytes.length);
612
+ combined.set(nBytes, 0);
613
+ combined.set(gBytes, nBytes.length);
614
+ const hash = await crypto.subtle.digest(this.hashAlgorithmName, combined);
615
+ return SecurityUtils.bytesToBigInt(new Uint8Array(hash));
616
+ }
617
+ };
618
+
619
+ // src/srp/srp-context-factory.ts
620
+ var SrpContextFactory = class {
621
+ /**
622
+ * Creates an SRP context with the specified group.
623
+ * @param group - SRP group (default Rfc5054_3072).
624
+ * @returns A ready-to-use SrpContext.
625
+ */
626
+ static async create(group = 4 /* Rfc5054_3072 */) {
627
+ const opts = new SrpOptions();
628
+ opts.group = group;
629
+ opts.hashAlgorithmName = group === 5 /* Rfc5054_4096 */ || group === 6 /* Rfc5054_6144 */ || group === 7 /* Rfc5054_8192 */ ? "SHA-384" : "SHA-256";
630
+ return {
631
+ N: opts.N,
632
+ g: opts.g,
633
+ k: await opts.computeK(),
634
+ modulusSize: opts.modulusSize,
635
+ hashAlgorithmName: opts.hashAlgorithmName,
636
+ hashSize: opts.hashAlgorithmName === "SHA-256" ? 32 : opts.hashAlgorithmName === "SHA-384" ? 48 : 64
637
+ };
638
+ }
639
+ };
640
+
641
+ // src/srp/srp-client-service.ts
642
+ var SrpClientService = class {
562
643
  /**
563
644
  * Computes SRP verifier v = g^x mod N from the authentication hash.
564
645
  * @param authHash - Auth hash (Base64).
565
- * @param ctx - SRP context (N, g, hash algorithm, etc.).
646
+ * @param group - SRP group (determines modulus N, generator g, hash).
566
647
  * @returns Verifier as Base64 string.
567
648
  */
568
- async generateSrpVerifier(authHash, ctx) {
649
+ async generateSrpVerifier(authHash, group) {
650
+ const ctx = await SrpContextFactory.create(group);
569
651
  const x = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(authHash));
570
- const v = SecurityUtils.expMod(ctx.g, x, ctx.N);
652
+ const v = await SecurityUtils.expModAsync(ctx.g, x, ctx.N);
571
653
  return SecurityUtils.toBase64(SrpEncoding.toModulusBytes(ctx, v));
572
654
  }
573
655
  /**
574
656
  * Generates client proof (A, M1, session key S) from server challenge.
575
657
  * @param login - User login.
576
- * @param password - Plaintext password.
658
+ * @param authHashBytes - SRP private exponent x as raw bytes (derived from auth hash).
577
659
  * @param saltBase64 - Server salt (standard Base64).
578
660
  * @param B_base64 - Server public ephemeral B (standard Base64).
579
- * @param ctx - SRP context.
661
+ * @param group - SRP group (determines modulus N, generator g, hash).
580
662
  * @returns Object with A and M1 as standard Base64; SessionKeyK as raw bytes.
581
663
  */
582
- async generateSrpProof(login, password, saltBase64, B_base64, ctx, version) {
664
+ async generateSrpProof(login, authHashBytes, saltBase64, B_base64, group) {
665
+ const ctx = await SrpContextFactory.create(group);
583
666
  const salt = SecurityUtils.fromBase64(saltBase64);
584
- const authHash = await this.keyDerivation.deriveAuthHashForSrp(login, password, salt, ctx.hashAlgorithmName, version);
585
- const x = SecurityUtils.bytesToBigInt(authHash);
667
+ const x = SecurityUtils.bytesToBigInt(authHashBytes);
586
668
  const privateKeySize = Math.max(32, Math.floor(ctx.modulusSize / 2));
587
669
  let aBytes;
588
670
  let a;
@@ -590,7 +672,7 @@ var SrpClientService = class {
590
672
  aBytes = crypto.getRandomValues(new Uint8Array(privateKeySize));
591
673
  a = SecurityUtils.bytesToBigInt(aBytes);
592
674
  } while (a === 0n);
593
- const A = SecurityUtils.expMod(ctx.g, a, ctx.N);
675
+ const A = await SecurityUtils.expModAsync(ctx.g, a, ctx.N);
594
676
  if (A <= 0n || A >= ctx.N)
595
677
  throw new Error("Invalid client public key A.");
596
678
  const B = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(B_base64));
@@ -599,11 +681,11 @@ var SrpClientService = class {
599
681
  const u = await SrpEncoding.hashModuli(ctx, A, B);
600
682
  if (u === 0n)
601
683
  throw new Error("\u041D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 u");
602
- const gX = SecurityUtils.expMod(ctx.g, x, ctx.N);
684
+ const gX = await SecurityUtils.expModAsync(ctx.g, x, ctx.N);
603
685
  const term = ctx.k * gX % ctx.N;
604
686
  const base = (B - term + ctx.N) % ctx.N;
605
687
  const exponent = a + u * x;
606
- const S = SecurityUtils.expMod(base, exponent, ctx.N);
688
+ const S = await SecurityUtils.expModAsync(base, exponent, ctx.N);
607
689
  if (S === 0n)
608
690
  throw new Error("Critical error: S === 0");
609
691
  const sessionKeyK = await SrpEncoding.computeSessionKey(ctx, S);
@@ -618,12 +700,13 @@ var SrpClientService = class {
618
700
  * Validates the server proof M2 to authenticate the server.
619
701
  * @param A_b64 - Client public A (Base64).
620
702
  * @param M1_b64 - Client proof M1 (Base64).
621
- * @param S_b64 - Session key S (Base64).
703
+ * @param sessionKeyK - Session key K as raw bytes.
622
704
  * @param serverM2_b64 - Server proof M2 (Base64).
623
- * @param ctx - SRP context.
705
+ * @param group - SRP group (determines modulus N, generator g, hash).
624
706
  * @returns True if the server proof is valid.
625
707
  */
626
- async verifyServerM2(A_b64, M1_b64, sessionKeyK, serverM2_b64, ctx) {
708
+ async verifyServerM2(A_b64, M1_b64, sessionKeyK, serverM2_b64, group) {
709
+ const ctx = await SrpContextFactory.create(group);
627
710
  const A = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(A_b64));
628
711
  const M1 = SecurityUtils.fromBase64(M1_b64);
629
712
  const computedM2 = await SrpEncoding.computeM2(ctx, A, M1, sessionKeyK);
@@ -638,14 +721,16 @@ var SrpServerService = class {
638
721
  * Generates server challenge B and session state from verifier.
639
722
  * @param login - User login.
640
723
  * @param verifierBytes - Stored verifier v as byte array.
641
- * @param ctx - SRP context (hash, N, g, etc.).
642
- * @returns Session state with private b, verifier, and public B.
724
+ * @param salt - User salt (raw bytes).
725
+ * @param group - SRP group (determines modulus N, generator g, hash).
726
+ * @returns Session state with private b, verifier, public B, and salt.
643
727
  */
644
- async getSrpChallenge(login, verifierBytes, salt, ctx) {
728
+ async getSrpChallenge(login, verifierBytes, salt, group) {
645
729
  if (!login || login.trim().length === 0)
646
730
  throw new Error("Login cannot be null or empty.");
647
731
  if (!verifierBytes || verifierBytes.length === 0)
648
732
  throw new Error("Verifier cannot be null or empty.");
733
+ const ctx = await SrpContextFactory.create(group);
649
734
  const v = SecurityUtils.bytesToBigInt(verifierBytes);
650
735
  if (v <= 0n || v >= ctx.N)
651
736
  throw new Error("The verifier is corrupted");
@@ -656,7 +741,7 @@ var SrpServerService = class {
656
741
  if (b === 0n) {
657
742
  continue;
658
743
  }
659
- const gB = SecurityUtils.expMod(ctx.g, b, ctx.N);
744
+ const gB = await SecurityUtils.expModAsync(ctx.g, b, ctx.N);
660
745
  const B = (ctx.k * v + gB) % ctx.N;
661
746
  if (B !== 0n) {
662
747
  return {
@@ -674,11 +759,12 @@ var SrpServerService = class {
674
759
  * @param sessionState - Server session state.
675
760
  * @param a - Client public A (Base64).
676
761
  * @param m1 - Client proof M1 (Base64).
677
- * @param ctx - SRP context.
762
+ * @param group - SRP group (determines modulus N, generator g, hash).
678
763
  * @returns Server proof M2 as Base64 string.
679
764
  * @throws If verification fails or input is invalid.
680
765
  */
681
- async verifySrpProof(sessionState, a, m1, ctx) {
766
+ async verifySrpProof(sessionState, a, m1, group) {
767
+ const ctx = await SrpContextFactory.create(group);
682
768
  const A = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(a));
683
769
  const M1_client = SecurityUtils.fromBase64(m1);
684
770
  const b = SecurityUtils.bytesToBigInt(sessionState.privateKeyB);
@@ -695,8 +781,8 @@ var SrpServerService = class {
695
781
  const u = await SrpEncoding.hashModuli(ctx, A, B);
696
782
  if (u === 0n)
697
783
  throw new Error("Error in calculating the parameter u");
698
- const vU = SecurityUtils.expMod(v, u, ctx.N);
699
- const S = SecurityUtils.expMod(A * vU % ctx.N, b, ctx.N);
784
+ const vU = await SecurityUtils.expModAsync(v, u, ctx.N);
785
+ const S = await SecurityUtils.expModAsync(A * vU % ctx.N, b, ctx.N);
700
786
  if (S === 0n)
701
787
  throw new Error("Critical error: shared secret S is zero (possible malicious A).");
702
788
  const sessionKeyK = await SrpEncoding.computeSessionKey(ctx, S);
@@ -708,129 +794,66 @@ var SrpServerService = class {
708
794
  }
709
795
  };
710
796
 
711
- // src/srp/srp-group.ts
712
- var SrpGroup = /* @__PURE__ */ ((SrpGroup2) => {
713
- SrpGroup2[SrpGroup2["Rfc5054_1024"] = 1] = "Rfc5054_1024";
714
- SrpGroup2[SrpGroup2["Rfc5054_1536"] = 2] = "Rfc5054_1536";
715
- SrpGroup2[SrpGroup2["Rfc5054_2048"] = 3] = "Rfc5054_2048";
716
- SrpGroup2[SrpGroup2["Rfc5054_3072"] = 4] = "Rfc5054_3072";
717
- SrpGroup2[SrpGroup2["Rfc5054_4096"] = 5] = "Rfc5054_4096";
718
- SrpGroup2[SrpGroup2["Rfc5054_6144"] = 6] = "Rfc5054_6144";
719
- SrpGroup2[SrpGroup2["Rfc5054_8192"] = 7] = "Rfc5054_8192";
720
- SrpGroup2[SrpGroup2["Custom"] = 99] = "Custom";
721
- return SrpGroup2;
722
- })(SrpGroup || {});
723
-
724
- // src/srp/srp-group-params.ts
725
- var SrpGroupParams = class {
797
+ // src/srp/srp-key-derivation.service.ts
798
+ var SrpKeyDerivationService = class {
726
799
  /**
727
- * Returns the prime modulus N for the given SRP group.
728
- * @param group - SRP group identifier.
729
- * @returns N as a bigint.
800
+ * Derives an SRP-compatible authentication hash (output size = hash output length).
801
+ * Identity is used as-is in the combined string — caller must normalize
802
+ * (trim, lowercase, etc.) before calling.
803
+ * @param identity - User identity (email, username). Must be pre-normalized by caller.
804
+ * @param password - User password.
805
+ * @param salt - Random salt (minimum 16 bytes).
806
+ * @param srpGroup - SRP group (determines hash algorithm and modulus).
807
+ * @param version - Crypto version for KDF profile selection.
808
+ * @returns Raw hash bytes for use as SRP verifier input (x).
730
809
  */
731
- static getN(group) {
732
- switch (group) {
733
- case 1 /* Rfc5054_1024 */:
734
- return BigInt("0xEEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2FC0EB06E3");
735
- case 2 /* Rfc5054_1536 */:
736
- return BigInt("0x9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA9614B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F84380B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0BE3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF56EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734AF7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB");
737
- case 3 /* Rfc5054_2048 */:
738
- return BigInt("0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73");
739
- case 4 /* Rfc5054_3072 */:
740
- return BigInt("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF");
741
- case 5 /* Rfc5054_4096 */:
742
- return BigInt("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF");
743
- case 6 /* Rfc5054_6144 */:
744
- return BigInt("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF");
745
- case 7 /* Rfc5054_8192 */:
746
- return BigInt("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF");
747
- default:
748
- throw new Error("Unsupported group");
749
- }
750
- }
751
- /**
752
- * Returns the generator g for the given SRP group.
753
- * @param group - SRP group identifier.
754
- * @returns g as a bigint (2, 5, or 19).
755
- */
756
- static getG(group) {
757
- switch (group) {
758
- case 1 /* Rfc5054_1024 */:
759
- return BigInt(2);
760
- case 2 /* Rfc5054_1536 */:
761
- return BigInt(2);
762
- case 3 /* Rfc5054_2048 */:
763
- return BigInt(2);
764
- case 4 /* Rfc5054_3072 */:
765
- return BigInt(5);
766
- case 5 /* Rfc5054_4096 */:
767
- return BigInt(5);
768
- case 6 /* Rfc5054_6144 */:
769
- return BigInt(5);
770
- case 7 /* Rfc5054_8192 */:
771
- return BigInt(19);
772
- default:
773
- throw new Error("Unsupported group");
774
- }
775
- }
776
- };
777
-
778
- // src/srp/srp-options.ts
779
- var SrpOptions = class {
780
- constructor() {
781
- /** Diffie-Hellman group. Default Rfc5054_3072 (g=5). */
782
- this.group = 4 /* Rfc5054_3072 */;
783
- /** Hash algorithm for SRP computations. Default SHA-256. */
784
- this.hashAlgorithmName = "SHA-256";
785
- /** Salt size in bytes. Default 32. */
786
- this.saltSize = 32;
787
- }
788
- /** Prime modulus N for the selected group. */
789
- get N() {
790
- return SrpGroupParams.getN(this.group);
791
- }
792
- /** Generator g for the selected group (2, 5, or 19). */
793
- get g() {
794
- return SrpGroupParams.getG(this.group);
795
- }
796
- /** Byte length of N (ceil(bitLength / 8)). */
797
- get modulusSize() {
798
- return Math.floor((this.N.toString(2).length + 7) / 8);
799
- }
800
- /**
801
- * Computes the multiplier parameter k = H(PAD(N) || PAD(g)) (RFC 5054, 2.5.3).
802
- * @returns k as a bigint.
803
- */
804
- async computeK() {
805
- const nBytes = SecurityUtils.bigIntToFixedBytes(this.N, this.modulusSize);
806
- const gBytes = SecurityUtils.bigIntToFixedBytes(this.g, this.modulusSize);
807
- const combined = new Uint8Array(nBytes.length + gBytes.length);
808
- combined.set(nBytes, 0);
809
- combined.set(gBytes, nBytes.length);
810
- const hash = await crypto.subtle.digest(this.hashAlgorithmName, combined);
811
- return SecurityUtils.bytesToBigInt(new Uint8Array(hash));
812
- }
813
- };
814
-
815
- // src/srp/srp-context-factory.ts
816
- var SrpContextFactory = class {
817
- /**
818
- * Creates an SRP context with the specified group.
819
- * @param group - SRP group (default Rfc5054_3072).
820
- * @returns A ready-to-use SrpContext.
821
- */
822
- static async create(group = 4 /* Rfc5054_3072 */) {
823
- const opts = new SrpOptions();
824
- opts.group = group;
825
- opts.hashAlgorithmName = group === 5 /* Rfc5054_4096 */ || group === 6 /* Rfc5054_6144 */ || group === 7 /* Rfc5054_8192 */ ? "SHA-384" : "SHA-256";
826
- return {
827
- N: opts.N,
828
- g: opts.g,
829
- k: await opts.computeK(),
830
- modulusSize: opts.modulusSize,
831
- hashAlgorithmName: opts.hashAlgorithmName,
832
- hashSize: opts.hashAlgorithmName === "SHA-256" ? 32 : opts.hashAlgorithmName === "SHA-384" ? 48 : 64
833
- };
810
+ async deriveAuthHashForSrp(identity, password, salt, srpGroup, version) {
811
+ if (!identity || identity.trim().length === 0)
812
+ throw new Error("Identity cannot be null or empty.");
813
+ if (!password || password.trim().length === 0)
814
+ throw new Error("Password cannot be null or empty.");
815
+ if (!salt || salt.length < 16)
816
+ throw new Error("Salt must be at least 16 bytes.");
817
+ const profile = CryptoProfileRegistry.getProfile(version);
818
+ const opts = profile.kdfOptions;
819
+ opts.validate();
820
+ const ctx = await SrpContextFactory.create(srpGroup);
821
+ const srpHashAlgorithm = ctx.hashAlgorithmName;
822
+ const srpHashSize = HashSizes[srpHashAlgorithm];
823
+ const combinedPassword = `${identity}:${password}`;
824
+ const passwordBytes = new TextEncoder().encode(combinedPassword);
825
+ const baseKey = await crypto.subtle.importKey(
826
+ "raw",
827
+ passwordBytes,
828
+ "PBKDF2",
829
+ false,
830
+ ["deriveBits"]
831
+ );
832
+ const masterKeyBits = await crypto.subtle.deriveBits({
833
+ name: "PBKDF2",
834
+ salt,
835
+ iterations: opts.pbkdf2Iterations,
836
+ hash: srpHashAlgorithm
837
+ }, baseKey, srpHashSize * 8);
838
+ const masterKey = await crypto.subtle.importKey(
839
+ "raw",
840
+ masterKeyBits,
841
+ "HKDF",
842
+ false,
843
+ ["deriveBits"]
844
+ );
845
+ const info = new TextEncoder().encode("SRP-AUTH-HASH-v1");
846
+ const authBytes = await crypto.subtle.deriveBits(
847
+ {
848
+ name: "HKDF",
849
+ hash: srpHashAlgorithm,
850
+ salt: new Uint8Array(0),
851
+ info
852
+ },
853
+ masterKey,
854
+ srpHashSize * 8
855
+ );
856
+ return new Uint8Array(authBytes);
834
857
  }
835
858
  };
836
859
 
@@ -848,6 +871,7 @@ exports.SrpContextFactory = SrpContextFactory;
848
871
  exports.SrpEncoding = SrpEncoding;
849
872
  exports.SrpGroup = SrpGroup;
850
873
  exports.SrpGroupParams = SrpGroupParams;
874
+ exports.SrpKeyDerivationService = SrpKeyDerivationService;
851
875
  exports.SrpOptions = SrpOptions;
852
876
  exports.SrpServerService = SrpServerService;
853
877
  exports.SupportedHashAlgorithms = SupportedHashAlgorithms;