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