@smartledger/keys 3.1.1 → 4.0.0

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.
@@ -7848,14 +7848,19 @@ zoo`.split("\n"));
7848
7848
  publicKey
7849
7849
  };
7850
7850
  }
7851
- async sign(privateKey, message) {
7851
+ async sign(privateKey, message, options = {}) {
7852
+ if (options.context !== void 0 && !(options.context instanceof Uint8Array && options.context.length === 0)) {
7853
+ throw new Error("BsvEcdsaSuite: ECDSA has no context string; bind the context into the message instead");
7854
+ }
7852
7855
  try {
7853
7856
  return sign(message, privateKey, { prehash: false });
7854
7857
  } catch (error) {
7855
7858
  throw new Error(`BsvEcdsaSuite signing failed: ${error instanceof Error ? error.message : String(error)}`);
7856
7859
  }
7857
7860
  }
7858
- async verify(publicKey, message, signature) {
7861
+ async verify(publicKey, message, signature, options = {}) {
7862
+ if (options.context !== void 0 && !(options.context instanceof Uint8Array && options.context.length === 0))
7863
+ return false;
7859
7864
  try {
7860
7865
  return verify(signature, message, publicKey, { prehash: false }) === true;
7861
7866
  } catch (error) {
@@ -7920,6 +7925,16 @@ zoo`.split("\n"));
7920
7925
  }
7921
7926
  return ml_dsa87_lib;
7922
7927
  }
7928
+ function contextOption(options) {
7929
+ const context = options.context;
7930
+ if (context === void 0)
7931
+ return void 0;
7932
+ if (!(context instanceof Uint8Array))
7933
+ throw new Error("context must be a Uint8Array");
7934
+ if (context.length > 255)
7935
+ throw new Error("context must be at most 255 bytes");
7936
+ return context.length === 0 ? void 0 : { context };
7937
+ }
7923
7938
  var ML_DSA_SIZES = {
7924
7939
  "ml-dsa-44": { publicKey: 1312, privateKey: 2560, signature: 2420 },
7925
7940
  "ml-dsa-65": { publicKey: 1952, privateKey: 4032, signature: 3309 },
@@ -7944,21 +7959,21 @@ zoo`.split("\n"));
7944
7959
  throw new Error(`${this.suiteId} key generation failed: ${error instanceof Error ? error.message : String(error)}`);
7945
7960
  }
7946
7961
  }
7947
- async sign(privateKey, message) {
7962
+ async sign(privateKey, message, options = {}) {
7948
7963
  try {
7949
7964
  const lib = await this.getLib();
7950
- return lib.sign(message, privateKey);
7965
+ return lib.sign(message, privateKey, contextOption(options));
7951
7966
  } catch (error) {
7952
7967
  throw new Error(`${this.suiteId} signing failed: ${error instanceof Error ? error.message : String(error)}`);
7953
7968
  }
7954
7969
  }
7955
- async verify(publicKey, message, signature) {
7970
+ async verify(publicKey, message, signature, options = {}) {
7956
7971
  try {
7957
7972
  const lib = await this.getLib();
7958
7973
  if (publicKey.length !== this.sizes.publicKey || signature.length !== this.sizes.signature) {
7959
7974
  return false;
7960
7975
  }
7961
- return lib.verify(signature, message, publicKey) === true;
7976
+ return lib.verify(signature, message, publicKey, contextOption(options)) === true;
7962
7977
  } catch (error) {
7963
7978
  return false;
7964
7979
  }
@@ -8429,6 +8444,7 @@ zoo`.split("\n"));
8429
8444
  };
8430
8445
 
8431
8446
  // src/sdk.ts
8447
+ var DEFAULT_SIGNING_CONTEXT = "@smartledger/keys/4";
8432
8448
  var COUNTER_SUFFIX = /^pk-[^:-]+-(\d+)$/;
8433
8449
  var MAX_KEY_ID_ATTEMPTS = 8;
8434
8450
  function assertAgentId(agentId) {
@@ -8453,6 +8469,12 @@ zoo`.split("\n"));
8453
8469
  this.getOrCreateInFlight = /* @__PURE__ */ new Map();
8454
8470
  this.keyRegistry = config.keyRegistry;
8455
8471
  this.suiteRegistry = config.suiteRegistry;
8472
+ const context = config.signingContext ?? DEFAULT_SIGNING_CONTEXT;
8473
+ const bytes = typeof context === "string" ? new TextEncoder().encode(context) : Uint8Array.from(context);
8474
+ if (bytes.length < 1 || bytes.length > 255) {
8475
+ throw new Error("signingContext must be 1 to 255 bytes");
8476
+ }
8477
+ this.signingContext = bytes;
8456
8478
  }
8457
8479
  /**
8458
8480
  * Create a new key for an agent.
@@ -8518,9 +8540,29 @@ Consider using one of these suites or remove the derivationContext option.` : `N
8518
8540
  );
8519
8541
  }
8520
8542
  const suite = this.suiteRegistry.getSuite(result.meta.suiteId);
8521
- const hashAlg = this.getHashAlgorithm(result.meta.suiteId);
8522
- const messageHash = await hashBytes(message, hashAlg);
8523
- return await suite.sign(result.keypair.privateKey, messageHash);
8543
+ const { input, options } = await this.signingInput(result.meta.suiteId, message, "v4");
8544
+ return await suite.sign(result.keypair.privateKey, input, options);
8545
+ }
8546
+ /**
8547
+ * What a key of `suiteId` signs for `message` under a scheme.
8548
+ *
8549
+ * v4: ML-DSA signs the message itself in FIPS 204 pure mode with this SDK's
8550
+ * context string, so a standard verifier needs only the message and the
8551
+ * context, and the signature is not limited by an outside digest. ECDSA
8552
+ * signs SHA-256(message): ECDSA signs a digest by definition and has no
8553
+ * context, so callers who need domain separation for it put the purpose in
8554
+ * the message.
8555
+ *
8556
+ * v3-digest: what 3.x signed — SHA3-256(message) for ML-DSA, empty context.
8557
+ */
8558
+ async signingInput(suiteId, message, scheme) {
8559
+ if (scheme !== "v4" && scheme !== "v3-digest") {
8560
+ throw new Error(`Unknown signature scheme: ${String(scheme)}`);
8561
+ }
8562
+ if (suiteId.startsWith("ml-dsa")) {
8563
+ return scheme === "v4" ? { input: message, options: { context: this.signingContext } } : { input: await hashBytes(message, "sha3-256") };
8564
+ }
8565
+ return { input: await hashBytes(message, "sha256") };
8524
8566
  }
8525
8567
  /**
8526
8568
  * Verify a signature.
@@ -8538,15 +8580,14 @@ Consider using one of these suites or remove the derivationContext option.` : `N
8538
8580
  if (!statusAllows(result.meta, options.keyStatus ?? "any")) {
8539
8581
  return false;
8540
8582
  }
8541
- return this.verifyAgainst(result.meta, result.publicKey, message, signature);
8583
+ return this.verifyAgainst(result.meta, result.publicKey, message, signature, options.scheme ?? "v4");
8542
8584
  }
8543
8585
  /** The cryptographic check alone. Anything but a strict `true` is a failure. */
8544
- async verifyAgainst(meta, publicKey, message, signature) {
8586
+ async verifyAgainst(meta, publicKey, message, signature, scheme = "v4") {
8545
8587
  try {
8546
8588
  const suite = this.suiteRegistry.getSuite(meta.suiteId);
8547
- const hashAlg = this.getHashAlgorithm(meta.suiteId);
8548
- const messageHash = await hashBytes(message, hashAlg);
8549
- return await suite.verify(publicKey, messageHash, signature) === true;
8589
+ const { input, options } = await this.signingInput(meta.suiteId, message, scheme);
8590
+ return await suite.verify(publicKey, input, signature, options) === true;
8550
8591
  } catch {
8551
8592
  return false;
8552
8593
  }
@@ -8790,7 +8831,7 @@ Consider using one of these suites or remove the derivationContext option.` : `N
8790
8831
  fail("Signature must be a Uint8Array");
8791
8832
  continue;
8792
8833
  }
8793
- const valid = await this.verifyAgainst(key.meta, key.publicKey, message, entry.signature);
8834
+ const valid = await this.verifyAgainst(key.meta, key.publicKey, message, entry.signature, options.scheme ?? "v4");
8794
8835
  if (valid) {
8795
8836
  results.push({ suiteId, keyId, valid: true });
8796
8837
  } else {
@@ -8948,15 +8989,6 @@ Consider using one of these suites or remove the derivationContext option.` : `N
8948
8989
  options
8949
8990
  );
8950
8991
  }
8951
- /**
8952
- * Helper: Get hash algorithm for a suite.
8953
- */
8954
- getHashAlgorithm(suiteId) {
8955
- if (suiteId.startsWith("ml-dsa")) {
8956
- return "sha3-256";
8957
- }
8958
- return "sha256";
8959
- }
8960
8992
  /**
8961
8993
  * Helper: register a key under the agent's next free key ID.
8962
8994
  *