@wishknish/knishio-client-ts 0.9.8 → 1.1.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.
@@ -20109,6 +20109,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20109
20109
  }
20110
20110
  );
20111
20111
  }
20112
+ /**
20113
+ * Validation error for storage options or parameters
20114
+ */
20115
+ static validationError(message) {
20116
+ return new _SecretStorageException(message, {
20117
+ code: "VALIDATION_ERROR"
20118
+ });
20119
+ }
20112
20120
  };
20113
20121
  }
20114
20122
  });
@@ -42562,8 +42570,14 @@ ${operationTypes.join("\n")}
42562
42570
  ...params
42563
42571
  });
42564
42572
  var ml_kem768 = /* @__PURE__ */ (() => mk(PARAMS[768]))();
42573
+ var ml_kem1024 = /* @__PURE__ */ (() => mk(PARAMS[1024]))();
42565
42574
 
42566
42575
  // src/core/Wallet.ts
42576
+ var ML_KEM_PARAMS = {
42577
+ 1024: { kem: ml_kem1024, pkBytes: 1568, skBytes: 3168, ctBytes: 1568 },
42578
+ 768: { kem: ml_kem768, pkBytes: 1184, skBytes: 2400, ctBytes: 1088 }
42579
+ };
42580
+ var DEFAULT_ML_KEM_PARAMETER_SET = 1024;
42567
42581
  var Wallet = class _Wallet {
42568
42582
  token;
42569
42583
  balance;
@@ -42578,6 +42592,7 @@ ${operationTypes.join("\n")}
42578
42592
  tokenUnits;
42579
42593
  tradeRates;
42580
42594
  molecules;
42595
+ mlKemParameterSet;
42581
42596
  // Token metadata (populated from query responses)
42582
42597
  tokenName;
42583
42598
  tokenAmount;
@@ -42591,8 +42606,14 @@ ${operationTypes.join("\n")}
42591
42606
  address = null,
42592
42607
  position = null,
42593
42608
  batchId = null,
42594
- characters = null
42609
+ characters = null,
42610
+ mlKemParameterSet = DEFAULT_ML_KEM_PARAMETER_SET
42595
42611
  } = {}) {
42612
+ const paramSetNum = Number(mlKemParameterSet);
42613
+ if (!ML_KEM_PARAMS[paramSetNum]) {
42614
+ throw new Error(`KnishIO: unsupported ML-KEM parameter set ${mlKemParameterSet}; expected 1024 or 768.`);
42615
+ }
42616
+ this.mlKemParameterSet = paramSetNum;
42596
42617
  this.token = token;
42597
42618
  this.balance = "0";
42598
42619
  this.molecules = {};
@@ -42628,7 +42649,8 @@ ${operationTypes.join("\n")}
42628
42649
  bundle = null,
42629
42650
  token = "USER",
42630
42651
  batchId = null,
42631
- characters = null
42652
+ characters = null,
42653
+ mlKemParameterSet = DEFAULT_ML_KEM_PARAMETER_SET
42632
42654
  }) {
42633
42655
  let position = null;
42634
42656
  if (!secret && !bundle) {
@@ -42644,7 +42666,8 @@ ${operationTypes.join("\n")}
42644
42666
  token,
42645
42667
  position,
42646
42668
  batchId,
42647
- characters
42669
+ characters,
42670
+ mlKemParameterSet
42648
42671
  });
42649
42672
  }
42650
42673
  /**
@@ -42803,20 +42826,80 @@ ${operationTypes.join("\n")}
42803
42826
  return (typeof this.position === "undefined" || this.position === null) && (typeof this.address === "undefined" || this.address === null);
42804
42827
  }
42805
42828
  // =============================================================================
42806
- // POST-QUANTUM CRYPTOGRAPHY - ML-KEM768 INTEGRATION
42829
+ // POST-QUANTUM CRYPTOGRAPHY - ML-KEM INTEGRATION
42807
42830
  // =============================================================================
42808
42831
  /**
42809
- * Initializes the ML-KEM key pair (matches JavaScript SDK exactly)
42832
+ * Derive an ML-KEM keypair for an arbitrary parameter set from the wallet's key seed,
42833
+ * without mutating the wallet. The 64-byte `d‖z` seed takes no parameter-set input — only
42834
+ * the final `keygen` call differs — so one KnishIO wallet owns both an ML-KEM-768 and an
42835
+ * ML-KEM-1024 identity and either can be reconstructed on demand.
42836
+ *
42837
+ * Returns `null` when the wallet holds no key — a secret-less wallet, which is what a molecule
42838
+ * deserializer builds for validation context. `generateSecret(null, …)` does NOT throw, so
42839
+ * without this the wallet would derive a plausible-looking identity from a bogus seed and fail
42840
+ * three layers down at AES-GCM instead of at the missing key. The guard lives here rather than
42841
+ * at each call site so a new caller cannot miss it.
42842
+ *
42843
+ * @param parameterSet - 1024 or 768
42810
42844
  */
42811
- initializeMLKEM() {
42845
+ deriveMlKemKeypair(parameterSet) {
42846
+ const params = ML_KEM_PARAMS[parameterSet];
42847
+ if (!params) {
42848
+ throw new Error(`KnishIO: unsupported ML-KEM parameter set ${parameterSet}; expected 1024 or 768.`);
42849
+ }
42850
+ if (!this.key) {
42851
+ return null;
42852
+ }
42812
42853
  const seedHex = generateSecret(this.key, 128);
42813
42854
  const seed = new Uint8Array(64);
42814
42855
  for (let i4 = 0; i4 < 64; i4++) {
42815
42856
  seed[i4] = parseInt(seedHex.substr(i4 * 2, 2), 16);
42816
42857
  }
42817
- const { publicKey, secretKey } = ml_kem768.keygen(seed);
42818
- this.pubkey = this.serializeKey(publicKey);
42819
- this.privkey = secretKey;
42858
+ const { publicKey, secretKey } = params.kem.keygen(seed);
42859
+ return {
42860
+ pubkey: this.serializeKey(publicKey),
42861
+ privkey: secretKey,
42862
+ params
42863
+ };
42864
+ }
42865
+ /**
42866
+ * ML-KEM parameter set implied by a serialized public key's raw byte length. FIPS 203's key
42867
+ * lengths are disjoint (1568 bytes → ML-KEM-1024, 1184 bytes → ML-KEM-768), so a stored peer
42868
+ * key recovers the parameter set of the session it belongs to without a wire-format change.
42869
+ * Used by AuthToken.restore to resolve a snapshot that predates the field.
42870
+ *
42871
+ * @param pubkey - Base64-serialized ML-KEM public key
42872
+ * @return 1024, 768, or null when the length matches neither
42873
+ */
42874
+ static mlKemParameterSetFromPubkey(pubkey) {
42875
+ if (!pubkey) {
42876
+ return null;
42877
+ }
42878
+ let byteLength;
42879
+ try {
42880
+ byteLength = typeof Buffer !== "undefined" ? Buffer.from(pubkey, "base64").length : atob(pubkey).length;
42881
+ } catch {
42882
+ return null;
42883
+ }
42884
+ if (byteLength === ML_KEM_PARAMS[1024].pkBytes) {
42885
+ return 1024;
42886
+ }
42887
+ if (byteLength === ML_KEM_PARAMS[768].pkBytes) {
42888
+ return 768;
42889
+ }
42890
+ return null;
42891
+ }
42892
+ /**
42893
+ * Initializes the ML-KEM key pair (matches JavaScript SDK exactly). Only ever reached from the
42894
+ * constructor's `secret` branch, so the derivation cannot come back empty here.
42895
+ */
42896
+ initializeMLKEM() {
42897
+ const derived = this.deriveMlKemKeypair(this.mlKemParameterSet);
42898
+ if (!derived) {
42899
+ return;
42900
+ }
42901
+ this.pubkey = derived.pubkey;
42902
+ this.privkey = derived.privkey;
42820
42903
  }
42821
42904
  // =============================================================================
42822
42905
  // HIGH-LEVEL MESSAGE ENCRYPTION (JavaScript SDK Compatibility)
@@ -42825,13 +42908,13 @@ ${operationTypes.join("\n")}
42825
42908
  const messageString = JSON.stringify(message);
42826
42909
  const messageUint8 = new TextEncoder().encode(messageString);
42827
42910
  const deserializedPubkey = this.deserializeKey(recipientPubkey);
42828
- const ML_KEM_768_PUBLIC_KEY_BYTES = 1184;
42829
- if (deserializedPubkey.length !== ML_KEM_768_PUBLIC_KEY_BYTES) {
42911
+ const params = ML_KEM_PARAMS[this.mlKemParameterSet];
42912
+ if (deserializedPubkey.length !== params.pkBytes) {
42830
42913
  throw new Error(
42831
- `KnishIO: cannot ML-KEM-encrypt \u2014 recipient public key is ${deserializedPubkey.length} bytes, expected ${ML_KEM_768_PUBLIC_KEY_BYTES} (ML-KEM-768). The node likely did not advertise an ML-KEM public key (upgrade the validator to a PQ-transport build), or authenticate with { encrypt: false }.`
42914
+ `KnishIO: cannot ML-KEM-encrypt \u2014 recipient public key is ${deserializedPubkey.length} bytes, expected ${params.pkBytes} (ML-KEM-${this.mlKemParameterSet}). The peer is not running ML-KEM-${this.mlKemParameterSet}; upgrade the peer, or step this client back to the other parameter set.`
42832
42915
  );
42833
42916
  }
42834
- const { cipherText, sharedSecret } = ml_kem768.encapsulate(deserializedPubkey);
42917
+ const { cipherText, sharedSecret } = params.kem.encapsulate(deserializedPubkey);
42835
42918
  const encryptedMessage = await this.encryptWithSharedSecret(messageUint8, sharedSecret);
42836
42919
  return {
42837
42920
  cipherText: this.serializeKey(cipherText),
@@ -42843,15 +42926,37 @@ ${operationTypes.join("\n")}
42843
42926
  return decryptedString === null ? null : JSON.parse(decryptedString);
42844
42927
  }
42845
42928
  /**
42846
- * ML-KEM768 decapsulate + AES-256-GCM decrypt → the RAW decrypted UTF-8 string (no JSON.parse).
42929
+ * ML-KEM decapsulate + AES-256-GCM decrypt → the RAW decrypted UTF-8 string (no JSON.parse).
42847
42930
  * Shared by {@link decryptMessage} (which JSON.parses the result) and the PQ CipherHash transport
42848
- * ({@link decryptMyMessageML768}, which needs the raw response JSON text). PQ-transport Phase E.
42931
+ * ({@link decryptMyMessageML}, which needs the raw response JSON text). PQ-transport Phase E.
42849
42932
  */
42850
42933
  async _mlkemDecryptToString(encryptedData) {
42851
42934
  const { cipherText, encryptedMessage } = encryptedData;
42935
+ const configuredParams = ML_KEM_PARAMS[this.mlKemParameterSet];
42936
+ const otherSet = this.mlKemParameterSet === 1024 ? 768 : 1024;
42937
+ const deserializedCipherText = this.deserializeKey(cipherText);
42938
+ let params = configuredParams;
42939
+ let decapsPrivkey = this.privkey;
42940
+ if (deserializedCipherText.length !== configuredParams.ctBytes) {
42941
+ if (deserializedCipherText.length !== ML_KEM_PARAMS[otherSet].ctBytes) {
42942
+ console.error(
42943
+ `Wallet::decryptMessage() - Ciphertext length mismatch: got ${deserializedCipherText.length}, expected ${configuredParams.ctBytes}`
42944
+ );
42945
+ return null;
42946
+ }
42947
+ const derived = this.deriveMlKemKeypair(otherSet);
42948
+ if (!derived) {
42949
+ console.error(
42950
+ `Wallet::decryptMessage() - cannot derive the ML-KEM-${otherSet} identity: wallet has no key`
42951
+ );
42952
+ return null;
42953
+ }
42954
+ params = derived.params;
42955
+ decapsPrivkey = derived.privkey;
42956
+ }
42852
42957
  let sharedSecret;
42853
42958
  try {
42854
- sharedSecret = ml_kem768.decapsulate(this.deserializeKey(cipherText), this.privkey);
42959
+ sharedSecret = params.kem.decapsulate(deserializedCipherText, decapsPrivkey);
42855
42960
  } catch (e4) {
42856
42961
  console.error("Wallet::decryptMessage() - Decapsulation failed", e4);
42857
42962
  console.info("Wallet::decryptMessage() - my public key", this.pubkey);
@@ -42901,11 +43006,11 @@ ${operationTypes.join("\n")}
42901
43006
  return this.serializeKey(bytes);
42902
43007
  }
42903
43008
  /**
42904
- * Post-quantum (ML-KEM768) CipherHash request envelope: a stringified single-recipient map
43009
+ * Post-quantum (ML-KEM) CipherHash request envelope: a stringified single-recipient map
42905
43010
  * `{ "<hashShare(recipientPubkey)>": {cipherText, encryptedMessage} }` (object-valued, via
42906
43011
  * {@link encryptMessage}). Matches the Rust validator's CipherHash handler. PQ-transport Phase E.
42907
43012
  */
42908
- async encryptStringML768(message, recipientPubkey) {
43013
+ async encryptStringML(message, recipientPubkey) {
42909
43014
  const envelope = await this.encryptMessage(message, recipientPubkey);
42910
43015
  return JSON.stringify({ [this.hashShare(recipientPubkey)]: envelope });
42911
43016
  }
@@ -42913,9 +43018,21 @@ ${operationTypes.join("\n")}
42913
43018
  * Decrypt a CipherHash response map addressed to THIS wallet's ML-KEM pubkey
42914
43019
  * (`hashShare(this.pubkey)`) → the RAW decrypted GraphQL response JSON text (NOT JSON.parsed;
42915
43020
  * it replaces the HTTP response body for the normal parser). `null` if no entry / decrypt fails.
43021
+ *
43022
+ * A pre-bump peer addressed its envelope to `hashShare(our_768_pubkey)`, which a wallet
43023
+ * configured at ML-KEM-1024 would never find — so the other identity's share is tried too.
43024
+ * Without this, the permissive length dispatch in {@link _mlkemDecryptToString} is
43025
+ * unreachable on the transport path.
42916
43026
  */
42917
- async decryptMyMessageML768(map3) {
42918
- const envelope = map3[this.hashShare(this.pubkey)];
43027
+ async decryptMyMessageML(map3) {
43028
+ let envelope = map3[this.hashShare(this.pubkey)];
43029
+ if (!envelope) {
43030
+ const otherSet = this.mlKemParameterSet === 1024 ? 768 : 1024;
43031
+ const other = this.deriveMlKemKeypair(otherSet);
43032
+ if (other) {
43033
+ envelope = map3[this.hashShare(other.pubkey)];
43034
+ }
43035
+ }
42919
43036
  if (!envelope) {
42920
43037
  return null;
42921
43038
  }
@@ -43072,7 +43189,8 @@ ${operationTypes.join("\n")}
43072
43189
  serverSdkVersion: external_exports.number().int().min(1).optional(),
43073
43190
  logging: external_exports.boolean().optional(),
43074
43191
  defaultRequestPolicy: external_exports.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional(),
43075
- secretStorage: external_exports.unknown().optional()
43192
+ secretStorage: external_exports.unknown().optional(),
43193
+ mlKemParameterSet: external_exports.union([external_exports.literal(1024), external_exports.literal(768)]).optional()
43076
43194
  }).strict();
43077
43195
  external_exports.object({
43078
43196
  token: external_exports.string().min(1, "Auth token cannot be empty"),
@@ -44162,6 +44280,7 @@ ${operationTypes.join("\n")}
44162
44280
  continuIdPosition;
44163
44281
  parentHashes;
44164
44282
  local;
44283
+ mlKemParameterSet = 1024;
44165
44284
  /**
44166
44285
  * Create new Molecule instance
44167
44286
  * Matches JavaScript SDK constructor signature
@@ -44173,7 +44292,8 @@ ${operationTypes.join("\n")}
44173
44292
  remainderWallet = null,
44174
44293
  cellSlug = null,
44175
44294
  version: version3 = null,
44176
- continuIdPosition = null
44295
+ continuIdPosition = null,
44296
+ mlKemParameterSet = null
44177
44297
  } = {}) {
44178
44298
  this.status = null;
44179
44299
  this.molecularHash = null;
@@ -44185,6 +44305,7 @@ ${operationTypes.join("\n")}
44185
44305
  this.continuIdPosition = continuIdPosition;
44186
44306
  this.atoms = [];
44187
44307
  this.parentHashes = [];
44308
+ this.mlKemParameterSet = mlKemParameterSet || sourceWallet?.mlKemParameterSet || 1024;
44188
44309
  const versionRegistry = versions_default;
44189
44310
  if (version3 !== null && Object.prototype.hasOwnProperty.call(versionRegistry, version3)) {
44190
44311
  this.version = String(version3);
@@ -44195,7 +44316,8 @@ ${operationTypes.join("\n")}
44195
44316
  bundle,
44196
44317
  token: sourceWallet.token,
44197
44318
  batchId: sourceWallet.batchId,
44198
- characters: sourceWallet.characters
44319
+ characters: sourceWallet.characters,
44320
+ mlKemParameterSet: this.mlKemParameterSet
44199
44321
  });
44200
44322
  } else {
44201
44323
  this.remainderWallet = null;
@@ -44256,7 +44378,8 @@ ${operationTypes.join("\n")}
44256
44378
  if (!this.remainderWallet || this.remainderWallet.token !== "USER") {
44257
44379
  this.remainderWallet = Wallet.create({
44258
44380
  secret: this.secret,
44259
- bundle: this.bundle
44381
+ bundle: this.bundle,
44382
+ mlKemParameterSet: this.mlKemParameterSet
44260
44383
  });
44261
44384
  }
44262
44385
  const continuIdMeta = {};
@@ -44643,7 +44766,8 @@ ${operationTypes.join("\n")}
44643
44766
  }
44644
44767
  const burnWallet = new Wallet({
44645
44768
  bundle: "0000000000000000000000000000000000000000000000000000000000000000",
44646
- token: this.sourceWallet.token
44769
+ token: this.sourceWallet.token,
44770
+ mlKemParameterSet: this.mlKemParameterSet
44647
44771
  });
44648
44772
  this.addAtom(Atom.create({
44649
44773
  isotope: "V",
@@ -44915,7 +45039,8 @@ ${operationTypes.join("\n")}
44915
45039
  position: data.sourceWallet.position,
44916
45040
  bundle: data.sourceWallet.bundle,
44917
45041
  batchId: data.sourceWallet.batchId,
44918
- characters: data.sourceWallet.characters
45042
+ characters: data.sourceWallet.characters,
45043
+ mlKemParameterSet: molecule.mlKemParameterSet
44919
45044
  });
44920
45045
  molecule.sourceWallet.balance = String(data.sourceWallet.balance != null ? data.sourceWallet.balance : 0);
44921
45046
  molecule.sourceWallet.address = data.sourceWallet.address;
@@ -44933,7 +45058,8 @@ ${operationTypes.join("\n")}
44933
45058
  position: data.remainderWallet.position,
44934
45059
  bundle: data.remainderWallet.bundle,
44935
45060
  batchId: data.remainderWallet.batchId,
44936
- characters: data.remainderWallet.characters
45061
+ characters: data.remainderWallet.characters,
45062
+ mlKemParameterSet: molecule.mlKemParameterSet
44937
45063
  });
44938
45064
  molecule.remainderWallet.balance = String(data.remainderWallet.balance != null ? data.remainderWallet.balance : 0);
44939
45065
  molecule.remainderWallet.address = data.remainderWallet.address;
@@ -45031,7 +45157,8 @@ ${operationTypes.join("\n")}
45031
45157
  secret: this.secret,
45032
45158
  bundle: this.bundle,
45033
45159
  token: this.sourceWallet.token,
45034
- batchId: this.sourceWallet.batchId
45160
+ batchId: this.sourceWallet.batchId,
45161
+ mlKemParameterSet: this.mlKemParameterSet
45035
45162
  });
45036
45163
  if (tradeRates) {
45037
45164
  bufferWallet.tradeRates = tradeRates;
@@ -47728,7 +47855,7 @@ ${operationTypes.join("\n")}
47728
47855
  let encryptedRequest = false;
47729
47856
  let requestInit = init;
47730
47857
  if (wallet && serverPubkey && init && typeof init.body === "string" && this.shouldEncrypt(init.body)) {
47731
- const hashVar = await wallet.encryptStringML768(init.body, serverPubkey);
47858
+ const hashVar = await wallet.encryptStringML(init.body, serverPubkey);
47732
47859
  requestInit = { ...init, body: JSON.stringify({ query: CIPHER_HASH_QUERY, variables: { Hash: hashVar } }) };
47733
47860
  encryptedRequest = true;
47734
47861
  }
@@ -47748,7 +47875,7 @@ ${operationTypes.join("\n")}
47748
47875
  if (typeof hash2 !== "string") {
47749
47876
  return new Response(text, init2);
47750
47877
  }
47751
- const decrypted = await wallet.decryptMyMessageML768(JSON.parse(hash2));
47878
+ const decrypted = await wallet.decryptMyMessageML(JSON.parse(hash2));
47752
47879
  return new Response(decrypted != null ? decrypted : text, init2);
47753
47880
  }
47754
47881
  setAuthData({
@@ -47917,6 +48044,22 @@ ${operationTypes.join("\n")}
47917
48044
  authToken.setWallet(wallet);
47918
48045
  return authToken;
47919
48046
  }
48047
+ /**
48048
+ * ML-KEM parameter set a restored session must use, resolved in three tiers:
48049
+ * an explicit snapshot field, then the stored validator key's length, then ML-KEM-768.
48050
+ *
48051
+ * The final tier is deliberately NOT the constructor default. A snapshot with neither an
48052
+ * explicit field nor a recognisable key can only have come from a pre-bump build, and every
48053
+ * pre-bump build was 768-only — defaulting to 1024 would make the restored wallet advertise
48054
+ * a public key the validator never recorded for that token.
48055
+ */
48056
+ static resolveMlKemParameterSet(snapshot) {
48057
+ const explicit = snapshot.wallet?.mlKemParameterSet;
48058
+ if (explicit) {
48059
+ return Number(explicit) === 768 ? 768 : 1024;
48060
+ }
48061
+ return Wallet.mlKemParameterSetFromPubkey(snapshot.pubkey) ?? 768;
48062
+ }
47920
48063
  /**
47921
48064
  * Restore AuthToken from snapshot
47922
48065
  */
@@ -47924,8 +48067,9 @@ ${operationTypes.join("\n")}
47924
48067
  const wallet = new Wallet({
47925
48068
  secret,
47926
48069
  token: "AUTH",
47927
- position: snapshot.wallet.position,
47928
- characters: snapshot.wallet.characters
48070
+ position: snapshot.wallet?.position ?? null,
48071
+ characters: snapshot.wallet?.characters ?? null,
48072
+ mlKemParameterSet: _AuthToken.resolveMlKemParameterSet(snapshot)
47929
48073
  });
47930
48074
  return _AuthToken.create({
47931
48075
  token: snapshot.token,
@@ -47993,7 +48137,9 @@ ${operationTypes.join("\n")}
47993
48137
  };
47994
48138
  }
47995
48139
  /**
47996
- * Create snapshot for persistence
48140
+ * Create snapshot for persistence. The wallet's ML-KEM parameter set is recorded beside its
48141
+ * position and characters so a stepped-back ML-KEM-768 session restores as 768 rather than
48142
+ * silently taking the constructor default.
47997
48143
  */
47998
48144
  toSnapshot() {
47999
48145
  return {
@@ -48004,7 +48150,8 @@ ${operationTypes.join("\n")}
48004
48150
  ...this.$__wallet ? {
48005
48151
  wallet: {
48006
48152
  position: this.$__wallet.position,
48007
- characters: this.$__wallet.characters
48153
+ characters: this.$__wallet.characters,
48154
+ mlKemParameterSet: this.$__wallet.mlKemParameterSet
48008
48155
  }
48009
48156
  } : {}
48010
48157
  };
@@ -48304,7 +48451,8 @@ ${operationTypes.join("\n")}
48304
48451
  // isn't rejected.
48305
48452
  defaultRequestPolicy: external_exports.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional(),
48306
48453
  // Pluggable hardware envelope encryption secret storage provider
48307
- secretStorage: external_exports.unknown().optional()
48454
+ secretStorage: external_exports.unknown().optional(),
48455
+ mlKemParameterSet: external_exports.union([external_exports.literal(1024), external_exports.literal(768)]).optional()
48308
48456
  }).strict();
48309
48457
  var EnvironmentConfigSchema = external_exports.object({
48310
48458
  NODE_ENV: external_exports.enum(["development", "production", "test"]).optional(),
@@ -51932,10 +52080,121 @@ ${operationTypes.join("\n")}
51932
52080
  return result === 0;
51933
52081
  }
51934
52082
 
52083
+ // src/storage/secretEnvelope.ts
52084
+ init_SecretStorageException();
52085
+ var ENVELOPE_ALGORITHM = "AES-GCM";
52086
+ var DEFAULT_ITERATIONS = 1e5;
52087
+ var SECRET_KEY_PREFIX = "knishio:secret:";
52088
+ var RECOVERY_KEY_PREFIX = "knishio:recovery:";
52089
+ var GCM_IV_LENGTH = 12;
52090
+ var SALT_LENGTH = 16;
52091
+ var textEncoder2 = new TextEncoder();
52092
+ function uint8ArrayToBase642(bytes) {
52093
+ let binary = "";
52094
+ const len = bytes.byteLength;
52095
+ for (let i4 = 0; i4 < len; i4++) {
52096
+ const byte = bytes[i4];
52097
+ if (byte !== void 0) {
52098
+ binary += String.fromCharCode(byte);
52099
+ }
52100
+ }
52101
+ return btoa(binary);
52102
+ }
52103
+ function base64ToUint8Array2(base643) {
52104
+ const binary = atob(base643);
52105
+ const len = binary.length;
52106
+ const bytes = new Uint8Array(len);
52107
+ for (let i4 = 0; i4 < len; i4++) {
52108
+ bytes[i4] = binary.charCodeAt(i4);
52109
+ }
52110
+ return bytes;
52111
+ }
52112
+ async function deriveEnvelopeKey(passphrase, salt, iterations = DEFAULT_ITERATIONS) {
52113
+ if (typeof globalThis.crypto === "undefined" || typeof globalThis.crypto.subtle === "undefined") {
52114
+ throw new exports.SecretStorageException("WebCrypto API is not available");
52115
+ }
52116
+ const passphraseBytes = textEncoder2.encode(passphrase);
52117
+ try {
52118
+ const baseKey = await globalThis.crypto.subtle.importKey(
52119
+ "raw",
52120
+ passphraseBytes,
52121
+ "PBKDF2",
52122
+ false,
52123
+ ["deriveKey"]
52124
+ );
52125
+ return await globalThis.crypto.subtle.deriveKey(
52126
+ {
52127
+ name: "PBKDF2",
52128
+ salt,
52129
+ iterations,
52130
+ hash: "SHA-256"
52131
+ },
52132
+ baseKey,
52133
+ { name: "AES-GCM", length: 256 },
52134
+ false,
52135
+ ["encrypt", "decrypt"]
52136
+ );
52137
+ } finally {
52138
+ zeroizeBytes(passphraseBytes);
52139
+ }
52140
+ }
52141
+ async function sealEnvelope(secret, passphrase, metadata) {
52142
+ if (typeof globalThis.crypto === "undefined" || typeof globalThis.crypto.subtle === "undefined") {
52143
+ throw new exports.SecretStorageException("WebCrypto API is not available");
52144
+ }
52145
+ const salt = new Uint8Array(SALT_LENGTH);
52146
+ const iv = new Uint8Array(GCM_IV_LENGTH);
52147
+ globalThis.crypto.getRandomValues(salt);
52148
+ globalThis.crypto.getRandomValues(iv);
52149
+ const key = await deriveEnvelopeKey(passphrase, salt, DEFAULT_ITERATIONS);
52150
+ const secretBytes = textEncoder2.encode(secret);
52151
+ try {
52152
+ const encryptedBuffer = await globalThis.crypto.subtle.encrypt(
52153
+ {
52154
+ name: ENVELOPE_ALGORITHM,
52155
+ iv
52156
+ },
52157
+ key,
52158
+ secretBytes
52159
+ );
52160
+ const ciphertext = uint8ArrayToBase642(new Uint8Array(encryptedBuffer));
52161
+ return {
52162
+ version: 1,
52163
+ ciphertext,
52164
+ iv: uint8ArrayToBase642(iv),
52165
+ salt: uint8ArrayToBase642(salt),
52166
+ algorithm: ENVELOPE_ALGORITHM,
52167
+ iterations: DEFAULT_ITERATIONS,
52168
+ metadata
52169
+ };
52170
+ } finally {
52171
+ zeroizeBytes(secretBytes);
52172
+ }
52173
+ }
52174
+ async function openEnvelope(payload, passphrase) {
52175
+ if (typeof globalThis.crypto === "undefined" || typeof globalThis.crypto.subtle === "undefined") {
52176
+ throw new exports.SecretStorageException("WebCrypto API is not available");
52177
+ }
52178
+ const salt = base64ToUint8Array2(payload.salt);
52179
+ const iv = base64ToUint8Array2(payload.iv);
52180
+ const ciphertext = base64ToUint8Array2(payload.ciphertext);
52181
+ const key = await deriveEnvelopeKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
52182
+ const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
52183
+ {
52184
+ name: ENVELOPE_ALGORITHM,
52185
+ iv
52186
+ },
52187
+ key,
52188
+ ciphertext
52189
+ );
52190
+ return new Uint8Array(decryptedBuffer);
52191
+ }
52192
+
51935
52193
  // src/storage/MemorySecretStorageProvider.ts
51936
52194
  var MemorySecretStorageProvider = class {
51937
52195
  providerType = "memory";
51938
52196
  secrets = /* @__PURE__ */ new Map();
52197
+ recoverySecrets = /* @__PURE__ */ new Map();
51939
52198
  /**
51940
52199
  * Memory storage is not hardware backed
51941
52200
  */
@@ -51966,6 +52225,17 @@ ${operationTypes.join("\n")}
51966
52225
  providerType: this.providerType
51967
52226
  };
51968
52227
  this.secrets.set(bundleHash, { secret, metadata });
52228
+ if (options?.recoveryPassphrase) {
52229
+ const recoveryMetadata = {
52230
+ bundleHash,
52231
+ label: options?.label,
52232
+ createdAt: Date.now(),
52233
+ hardwareBacked: false,
52234
+ providerType: "webcrypto-aes-gcm"
52235
+ };
52236
+ const recoveryPayload = await sealEnvelope(secret, options.recoveryPassphrase, recoveryMetadata);
52237
+ this.recoverySecrets.set(bundleHash, JSON.stringify(recoveryPayload));
52238
+ }
51969
52239
  }
51970
52240
  /**
51971
52241
  * Retrieve a secret from memory
@@ -51978,6 +52248,7 @@ ${operationTypes.join("\n")}
51978
52248
  * Delete a stored secret
51979
52249
  */
51980
52250
  async deleteSecret(bundleHash) {
52251
+ this.recoverySecrets.delete(bundleHash);
51981
52252
  return this.secrets.delete(bundleHash);
51982
52253
  }
51983
52254
  /**
@@ -52007,6 +52278,48 @@ ${operationTypes.join("\n")}
52007
52278
  */
52008
52279
  clear() {
52009
52280
  this.secrets.clear();
52281
+ this.recoverySecrets.clear();
52282
+ }
52283
+ /**
52284
+ * Recover a secret using its recovery envelope and restore it
52285
+ */
52286
+ async recoverSecret(bundleHash, recoveryPassphrase, options) {
52287
+ if (!bundleHash) {
52288
+ throw new exports.SecretStorageException("Bundle hash cannot be empty");
52289
+ }
52290
+ if (!recoveryPassphrase) {
52291
+ throw new exports.SecretStorageException("Recovery passphrase cannot be empty");
52292
+ }
52293
+ const raw = this.recoverySecrets.get(bundleHash);
52294
+ if (!raw) {
52295
+ throw exports.SecretStorageException.notFound(bundleHash);
52296
+ }
52297
+ let payload;
52298
+ try {
52299
+ payload = JSON.parse(raw);
52300
+ } catch {
52301
+ throw exports.SecretStorageException.decryptionFailed("Corrupted recovery payload format");
52302
+ }
52303
+ let decryptedBytes;
52304
+ try {
52305
+ decryptedBytes = await openEnvelope(payload, recoveryPassphrase);
52306
+ } catch (err) {
52307
+ if (err instanceof exports.SecretStorageException) {
52308
+ throw err;
52309
+ }
52310
+ const msg = err instanceof Error ? err.message : String(err);
52311
+ throw exports.SecretStorageException.decryptionFailed(msg);
52312
+ }
52313
+ let secretStr;
52314
+ try {
52315
+ secretStr = new TextDecoder().decode(decryptedBytes);
52316
+ } finally {
52317
+ zeroizeBytes(decryptedBytes);
52318
+ }
52319
+ await this.storeSecret(bundleHash, secretStr, {
52320
+ ...options,
52321
+ recoveryPassphrase
52322
+ });
52010
52323
  }
52011
52324
  };
52012
52325
 
@@ -52028,6 +52341,7 @@ ${operationTypes.join("\n")}
52028
52341
  $__authTokenObjects = {};
52029
52342
  $__authToken = null;
52030
52343
  $__authInProcess = false;
52344
+ $__mlKemParameterSet = 1024;
52031
52345
  $__remainderWallet = null;
52032
52346
  lastMoleculeQuery = null;
52033
52347
  abortControllers = /* @__PURE__ */ new Map();
@@ -52072,7 +52386,8 @@ ${operationTypes.join("\n")}
52072
52386
  client,
52073
52387
  serverSdkVersion,
52074
52388
  logging,
52075
- defaultRequestPolicy
52389
+ defaultRequestPolicy,
52390
+ mlKemParameterSet: config2.mlKemParameterSet ?? 1024
52076
52391
  });
52077
52392
  if (config2.secretStorage) {
52078
52393
  this.$__secretStorage = config2.secretStorage;
@@ -52088,10 +52403,12 @@ ${operationTypes.join("\n")}
52088
52403
  client = null,
52089
52404
  serverSdkVersion = 3,
52090
52405
  logging = false,
52091
- defaultRequestPolicy = null
52406
+ defaultRequestPolicy = null,
52407
+ mlKemParameterSet = 1024
52092
52408
  }) {
52093
52409
  this.reset();
52094
52410
  this.$__logging = logging;
52411
+ this.setMlKemParameterSet(mlKemParameterSet);
52095
52412
  this.$__authTokenObjects = {};
52096
52413
  this.setUri(uri);
52097
52414
  if (cellSlug) {
@@ -52112,6 +52429,17 @@ ${operationTypes.join("\n")}
52112
52429
  this.$__serverSdkVersion = serverSdkVersion;
52113
52430
  this.$__defaultRequestPolicy = defaultRequestPolicy;
52114
52431
  }
52432
+ getMlKemParameterSet() {
52433
+ return this.$__mlKemParameterSet || 1024;
52434
+ }
52435
+ setMlKemParameterSet(parameterSet) {
52436
+ const paramNum = Number(parameterSet);
52437
+ if (![1024, 768].includes(paramNum)) {
52438
+ throw new Error(`KnishIO: unsupported ML-KEM parameter set ${parameterSet}; expected 1024 or 768.`);
52439
+ }
52440
+ this.$__mlKemParameterSet = paramNum;
52441
+ return this;
52442
+ }
52115
52443
  /**
52116
52444
  * Get random uri from specified this.$__uris
52117
52445
  */
@@ -52348,7 +52676,8 @@ ${operationTypes.join("\n")}
52348
52676
  bundle,
52349
52677
  token: "USER",
52350
52678
  batchId: sourceWallet.batchId,
52351
- characters: sourceWallet.characters
52679
+ characters: sourceWallet.characters,
52680
+ mlKemParameterSet: this.getMlKemParameterSet()
52352
52681
  }));
52353
52682
  return new Molecule({
52354
52683
  secret,
@@ -52357,7 +52686,8 @@ ${operationTypes.join("\n")}
52357
52686
  remainderWallet: this.getRemainderWallet(),
52358
52687
  cellSlug: this.getCellSlug(),
52359
52688
  version: this.getServerSdkVersion(),
52360
- continuIdPosition
52689
+ continuIdPosition,
52690
+ mlKemParameterSet: this.getMlKemParameterSet()
52361
52691
  });
52362
52692
  }
52363
52693
  /**
@@ -52469,7 +52799,8 @@ ${operationTypes.join("\n")}
52469
52799
  }))?.payload();
52470
52800
  if (!sourceWallet) {
52471
52801
  sourceWallet = new Wallet({
52472
- secret: this.getSecret()
52802
+ secret: this.getSecret(),
52803
+ mlKemParameterSet: this.getMlKemParameterSet()
52473
52804
  });
52474
52805
  } else {
52475
52806
  sourceWallet.key = Wallet.generateKey({
@@ -52512,7 +52843,8 @@ ${operationTypes.join("\n")}
52512
52843
  }
52513
52844
  const recipientWallet = Wallet.create({
52514
52845
  bundle: bundleHash,
52515
- token
52846
+ token,
52847
+ mlKemParameterSet: this.getMlKemParameterSet()
52516
52848
  });
52517
52849
  if (batchId !== null) {
52518
52850
  recipientWallet.batchId = batchId;
@@ -52579,7 +52911,8 @@ ${operationTypes.join("\n")}
52579
52911
  const recipientWallets = recipients.map((recipient) => {
52580
52912
  const recipientWallet = Wallet.create({
52581
52913
  bundle: recipient.bundleHash,
52582
- token
52914
+ token,
52915
+ mlKemParameterSet: this.getMlKemParameterSet()
52583
52916
  });
52584
52917
  if (recipient.batchId !== null && recipient.batchId !== void 0) {
52585
52918
  recipientWallet.batchId = recipient.batchId;
@@ -53203,7 +53536,8 @@ ${operationTypes.join("\n")}
53203
53536
  secret: this.getSecret(),
53204
53537
  bundle: this.getBundle(),
53205
53538
  token,
53206
- batchId
53539
+ batchId,
53540
+ mlKemParameterSet: this.getMlKemParameterSet()
53207
53541
  });
53208
53542
  await mutation.fillMolecule({
53209
53543
  recipientWallet,
@@ -53271,7 +53605,8 @@ ${operationTypes.join("\n")}
53271
53605
  const recipientWallet = new Wallet({
53272
53606
  secret: this.getSecret(),
53273
53607
  bundle: "0000000000000000000000000000000000000000000000000000000000000000",
53274
- token
53608
+ token,
53609
+ mlKemParameterSet: this.getMlKemParameterSet()
53275
53610
  });
53276
53611
  await mutation.fillMolecule({
53277
53612
  recipientWallet,
@@ -53348,7 +53683,8 @@ ${operationTypes.join("\n")}
53348
53683
  const newWallet = new Wallet({
53349
53684
  secret: this.getSecret(),
53350
53685
  bundle: this.getBundle(),
53351
- token
53686
+ token,
53687
+ mlKemParameterSet: this.getMlKemParameterSet()
53352
53688
  });
53353
53689
  await mutation.fillMolecule(newWallet);
53354
53690
  const response = await this.executeQuery(mutation);
@@ -53676,7 +54012,8 @@ ${operationTypes.join("\n")}
53676
54012
  this.setSecret(secret);
53677
54013
  const wallet = new Wallet({
53678
54014
  secret,
53679
- token: "AUTH"
54015
+ token: "AUTH",
54016
+ mlKemParameterSet: this.getMlKemParameterSet()
53680
54017
  });
53681
54018
  const molecule = await this.createMolecule({
53682
54019
  secret,
@@ -53804,45 +54141,25 @@ ${operationTypes.join("\n")}
53804
54141
  return Array.from(this.store.keys());
53805
54142
  }
53806
54143
  };
53807
- function uint8ArrayToBase642(bytes) {
53808
- let binary = "";
53809
- const len = bytes.byteLength;
53810
- for (let i4 = 0; i4 < len; i4++) {
53811
- const byte = bytes[i4];
53812
- if (byte !== void 0) {
53813
- binary += String.fromCharCode(byte);
53814
- }
53815
- }
53816
- return btoa(binary);
53817
- }
53818
- function base64ToUint8Array2(base643) {
53819
- const binary = atob(base643);
53820
- const len = binary.length;
53821
- const bytes = new Uint8Array(len);
53822
- for (let i4 = 0; i4 < len; i4++) {
53823
- bytes[i4] = binary.charCodeAt(i4);
53824
- }
53825
- return bytes;
53826
- }
53827
- var textEncoder2 = new TextEncoder();
53828
54144
  var textDecoder = new TextDecoder();
53829
- var KEY_PREFIX = "knishio:secret:";
53830
- var DEFAULT_ITERATIONS = 1e5;
54145
+ var KEY_PREFIX = SECRET_KEY_PREFIX;
53831
54146
  var WebCryptoSecretStorageProvider = class {
53832
54147
  providerType = "webcrypto-aes-gcm";
53833
54148
  backend;
53834
54149
  defaultPassphrase;
53835
- hardwareBacked;
53836
54150
  constructor(options = {}) {
53837
54151
  this.backend = options.backend ?? new MemoryStorageBackend();
53838
54152
  this.defaultPassphrase = options.defaultPassphrase;
53839
- this.hardwareBacked = options.hardwareBacked ?? false;
53840
54153
  }
53841
54154
  /**
53842
- * Whether this provider is backed by hardware (e.g. WebAuthn PRF wrapping)
54155
+ * True only when this provider holds a non-exportable key inside platform-secure
54156
+ * hardware (Android TEE/StrongBox, Secure Enclave, TPM) and learned that from the
54157
+ * platform itself — never from a caller argument. Software envelope providers
54158
+ * return false. The value is persisted as `metadata.hardwareBacked` in every
54159
+ * envelope this provider writes.
53843
54160
  */
53844
54161
  isHardwareBacked() {
53845
- return this.hardwareBacked;
54162
+ return false;
53846
54163
  }
53847
54164
  /**
53848
54165
  * Check if WebCrypto subtle API is available
@@ -53850,38 +54167,6 @@ ${operationTypes.join("\n")}
53850
54167
  async isAvailable() {
53851
54168
  return typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined";
53852
54169
  }
53853
- /**
53854
- * Derive an AES-GCM CryptoKey from a passphrase and salt using PBKDF2
53855
- */
53856
- async deriveKey(passphrase, salt, iterations = DEFAULT_ITERATIONS) {
53857
- if (!await this.isAvailable()) {
53858
- throw exports.SecretStorageException.unavailable(this.providerType, "WebCrypto API is not available");
53859
- }
53860
- const passphraseBytes = textEncoder2.encode(passphrase);
53861
- try {
53862
- const baseKey = await globalThis.crypto.subtle.importKey(
53863
- "raw",
53864
- passphraseBytes,
53865
- "PBKDF2",
53866
- false,
53867
- ["deriveKey"]
53868
- );
53869
- return await globalThis.crypto.subtle.deriveKey(
53870
- {
53871
- name: "PBKDF2",
53872
- salt,
53873
- iterations,
53874
- hash: "SHA-256"
53875
- },
53876
- baseKey,
53877
- { name: "AES-GCM", length: 256 },
53878
- false,
53879
- ["encrypt", "decrypt"]
53880
- );
53881
- } finally {
53882
- zeroizeBytes(passphraseBytes);
53883
- }
53884
- }
53885
54170
  /**
53886
54171
  * Store and encrypt a master secret
53887
54172
  */
@@ -53896,44 +54181,36 @@ ${operationTypes.join("\n")}
53896
54181
  if (!passphrase) {
53897
54182
  throw new exports.SecretStorageException("Passphrase required for envelope encryption");
53898
54183
  }
53899
- const salt = new Uint8Array(16);
53900
- const iv = new Uint8Array(12);
53901
- globalThis.crypto.getRandomValues(salt);
53902
- globalThis.crypto.getRandomValues(iv);
53903
- const key = await this.deriveKey(passphrase, salt, DEFAULT_ITERATIONS);
53904
- const secretBytes = textEncoder2.encode(secret);
54184
+ if (!await this.isAvailable()) {
54185
+ throw exports.SecretStorageException.unavailable(this.providerType, "WebCrypto API is not available");
54186
+ }
53905
54187
  try {
53906
- const encryptedBuffer = await globalThis.crypto.subtle.encrypt(
53907
- {
53908
- name: "AES-GCM",
53909
- iv
53910
- },
53911
- key,
53912
- secretBytes
53913
- );
53914
- const ciphertext = uint8ArrayToBase642(new Uint8Array(encryptedBuffer));
53915
54188
  const metadata = {
53916
54189
  bundleHash,
53917
54190
  label: options?.label,
53918
54191
  createdAt: Date.now(),
53919
- hardwareBacked: this.hardwareBacked,
54192
+ hardwareBacked: false,
53920
54193
  providerType: this.providerType
53921
54194
  };
53922
- const payload = {
53923
- version: 1,
53924
- ciphertext,
53925
- iv: uint8ArrayToBase642(iv),
53926
- salt: uint8ArrayToBase642(salt),
53927
- algorithm: "AES-GCM",
53928
- iterations: DEFAULT_ITERATIONS,
53929
- metadata
53930
- };
54195
+ const payload = await sealEnvelope(secret, passphrase, metadata);
53931
54196
  await this.backend.setItem(`${KEY_PREFIX}${bundleHash}`, JSON.stringify(payload));
54197
+ if (options?.recoveryPassphrase) {
54198
+ const recoveryMetadata = {
54199
+ bundleHash,
54200
+ label: options?.label,
54201
+ createdAt: Date.now(),
54202
+ hardwareBacked: false,
54203
+ providerType: "webcrypto-aes-gcm"
54204
+ };
54205
+ const recoveryPayload = await sealEnvelope(secret, options.recoveryPassphrase, recoveryMetadata);
54206
+ await this.backend.setItem(`${RECOVERY_KEY_PREFIX}${bundleHash}`, JSON.stringify(recoveryPayload));
54207
+ }
53932
54208
  } catch (err) {
54209
+ if (err instanceof exports.SecretStorageException) {
54210
+ throw err;
54211
+ }
53933
54212
  const msg = err instanceof Error ? err.message : String(err);
53934
54213
  throw new exports.SecretStorageException(`Encryption failed: ${msg}`);
53935
- } finally {
53936
- zeroizeBytes(secretBytes);
53937
54214
  }
53938
54215
  }
53939
54216
  /**
@@ -53954,26 +54231,20 @@ ${operationTypes.join("\n")}
53954
54231
  if (!passphrase) {
53955
54232
  throw new exports.SecretStorageException("Passphrase required for secret decryption");
53956
54233
  }
53957
- const salt = base64ToUint8Array2(payload.salt);
53958
- const iv = base64ToUint8Array2(payload.iv);
53959
- const ciphertext = base64ToUint8Array2(payload.ciphertext);
54234
+ if (!await this.isAvailable()) {
54235
+ throw exports.SecretStorageException.unavailable(this.providerType, "WebCrypto API is not available");
54236
+ }
53960
54237
  try {
53961
- const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
53962
- const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
53963
- {
53964
- name: "AES-GCM",
53965
- iv
53966
- },
53967
- key,
53968
- ciphertext
53969
- );
53970
- const decryptedBytes = new Uint8Array(decryptedBuffer);
54238
+ const decryptedBytes = await openEnvelope(payload, passphrase);
53971
54239
  try {
53972
54240
  return textDecoder.decode(decryptedBytes);
53973
54241
  } finally {
53974
54242
  zeroizeBytes(decryptedBytes);
53975
54243
  }
53976
54244
  } catch (err) {
54245
+ if (err instanceof exports.SecretStorageException) {
54246
+ throw err;
54247
+ }
53977
54248
  const msg = err instanceof Error ? err.message : String(err);
53978
54249
  throw exports.SecretStorageException.decryptionFailed(msg);
53979
54250
  }
@@ -53983,7 +54254,9 @@ ${operationTypes.join("\n")}
53983
54254
  */
53984
54255
  async deleteSecret(bundleHash) {
53985
54256
  const key = `${KEY_PREFIX}${bundleHash}`;
54257
+ const recoveryKey = `${RECOVERY_KEY_PREFIX}${bundleHash}`;
53986
54258
  const result = await this.backend.removeItem(key);
54259
+ await this.backend.removeItem(recoveryKey);
53987
54260
  return result !== false;
53988
54261
  }
53989
54262
  /**
@@ -53998,7 +54271,7 @@ ${operationTypes.join("\n")}
53998
54271
  */
53999
54272
  async listSecrets() {
54000
54273
  const keys = await this.backend.keys();
54001
- const matchingKeys = keys.filter((k2) => k2.startsWith(KEY_PREFIX));
54274
+ const matchingKeys = keys.filter((k2) => k2.startsWith(KEY_PREFIX) && !k2.startsWith(RECOVERY_KEY_PREFIX));
54002
54275
  const results = [];
54003
54276
  for (const key of matchingKeys) {
54004
54277
  const raw = await this.backend.getItem(key);
@@ -54032,22 +54305,1008 @@ ${operationTypes.join("\n")}
54032
54305
  if (!passphrase) {
54033
54306
  throw new exports.SecretStorageException("Passphrase required for secret decryption");
54034
54307
  }
54035
- const salt = base64ToUint8Array2(payload.salt);
54036
- const iv = base64ToUint8Array2(payload.iv);
54037
- const ciphertext = base64ToUint8Array2(payload.ciphertext);
54308
+ if (!await this.isAvailable()) {
54309
+ throw exports.SecretStorageException.unavailable(this.providerType, "WebCrypto API is not available");
54310
+ }
54311
+ try {
54312
+ const decryptedBytes = await openEnvelope(payload, passphrase);
54313
+ return await withSecureBytes(decryptedBytes, async (bytes) => {
54314
+ const secretString = textDecoder.decode(bytes);
54315
+ return await fn(secretString);
54316
+ });
54317
+ } catch (err) {
54318
+ if (err instanceof exports.SecretStorageException) {
54319
+ throw err;
54320
+ }
54321
+ const msg = err instanceof Error ? err.message : String(err);
54322
+ throw exports.SecretStorageException.decryptionFailed(msg);
54323
+ }
54324
+ }
54325
+ /**
54326
+ * Recover a secret using its recovery envelope and re-enroll it
54327
+ */
54328
+ async recoverSecret(bundleHash, recoveryPassphrase, options) {
54329
+ if (!bundleHash) {
54330
+ throw new exports.SecretStorageException("Bundle hash cannot be empty");
54331
+ }
54332
+ if (!recoveryPassphrase) {
54333
+ throw new exports.SecretStorageException("Recovery passphrase cannot be empty");
54334
+ }
54335
+ const raw = await this.backend.getItem(`${RECOVERY_KEY_PREFIX}${bundleHash}`);
54336
+ if (!raw) {
54337
+ throw exports.SecretStorageException.notFound(bundleHash);
54338
+ }
54339
+ let payload;
54340
+ try {
54341
+ payload = JSON.parse(raw);
54342
+ } catch {
54343
+ throw exports.SecretStorageException.decryptionFailed("Corrupted recovery payload format");
54344
+ }
54345
+ let decryptedBytes;
54346
+ try {
54347
+ decryptedBytes = await openEnvelope(payload, recoveryPassphrase);
54348
+ } catch (err) {
54349
+ if (err instanceof exports.SecretStorageException) {
54350
+ throw err;
54351
+ }
54352
+ const msg = err instanceof Error ? err.message : String(err);
54353
+ throw exports.SecretStorageException.decryptionFailed(msg);
54354
+ }
54355
+ let secretStr;
54356
+ try {
54357
+ secretStr = textDecoder.decode(decryptedBytes);
54358
+ } finally {
54359
+ zeroizeBytes(decryptedBytes);
54360
+ }
54361
+ const storePassphrase = options?.passphrase ?? this.defaultPassphrase ?? recoveryPassphrase;
54362
+ await this.storeSecret(bundleHash, secretStr, {
54363
+ ...options,
54364
+ passphrase: storePassphrase,
54365
+ recoveryPassphrase
54366
+ });
54367
+ }
54368
+ };
54369
+
54370
+ // src/storage/FileStorageBackend.ts
54371
+ init_SecretStorageException();
54372
+ var FileStorageBackend = class {
54373
+ filePath;
54374
+ store = /* @__PURE__ */ new Map();
54375
+ loaded = false;
54376
+ constructor(filePath) {
54377
+ if (!filePath) {
54378
+ throw new exports.SecretStorageException("Storage file path cannot be empty");
54379
+ }
54380
+ this.filePath = filePath;
54381
+ }
54382
+ async getFs() {
54383
+ try {
54384
+ const fs = await import('fs/promises');
54385
+ const path = await import('path');
54386
+ return { fs, path };
54387
+ } catch {
54388
+ throw exports.SecretStorageException.unavailable(
54389
+ "file-storage",
54390
+ "FileStorageBackend is only supported in Node.js environments with node:fs access"
54391
+ );
54392
+ }
54393
+ }
54394
+ async ensureLoaded() {
54395
+ if (this.loaded) {
54396
+ return this.store;
54397
+ }
54398
+ const { fs } = await this.getFs();
54399
+ try {
54400
+ const content = await fs.readFile(this.filePath, "utf8");
54401
+ let parsed;
54402
+ try {
54403
+ parsed = JSON.parse(content);
54404
+ } catch {
54405
+ throw exports.SecretStorageException.decryptionFailed("Corrupted storage file format");
54406
+ }
54407
+ if (parsed && typeof parsed === "object") {
54408
+ this.store = new Map(Object.entries(parsed).map(([k2, v3]) => [k2, String(v3)]));
54409
+ }
54410
+ } catch (err) {
54411
+ if (err instanceof exports.SecretStorageException) {
54412
+ throw err;
54413
+ }
54414
+ const nodeErr = err;
54415
+ if (nodeErr?.code !== "ENOENT") {
54416
+ const msg = err instanceof Error ? err.message : String(err);
54417
+ throw new exports.SecretStorageException(`Failed to read storage file: ${msg}`);
54418
+ }
54419
+ this.store = /* @__PURE__ */ new Map();
54420
+ }
54421
+ this.loaded = true;
54422
+ return this.store;
54423
+ }
54424
+ async persist() {
54425
+ const { fs, path } = await this.getFs();
54426
+ const dir = path.dirname(this.filePath);
54427
+ if (dir && dir !== ".") {
54428
+ await fs.mkdir(dir, { recursive: true });
54429
+ }
54430
+ const tmpPath = `${this.filePath}.tmp.${Date.now()}_${Math.random().toString(36).slice(2)}`;
54431
+ const data = JSON.stringify(Object.fromEntries(this.store), null, 2);
54432
+ try {
54433
+ await fs.writeFile(tmpPath, data, { mode: 384, encoding: "utf8" });
54434
+ if (typeof process !== "undefined" && process.platform !== "win32") {
54435
+ try {
54436
+ await fs.chmod(tmpPath, 384);
54437
+ } catch {
54438
+ }
54439
+ }
54440
+ await fs.rename(tmpPath, this.filePath);
54441
+ } catch (err) {
54442
+ try {
54443
+ await fs.unlink(tmpPath);
54444
+ } catch {
54445
+ }
54446
+ const msg = err instanceof Error ? err.message : String(err);
54447
+ throw new exports.SecretStorageException(`Failed to persist storage file: ${msg}`);
54448
+ }
54449
+ }
54450
+ async getItem(key) {
54451
+ await this.ensureLoaded();
54452
+ return this.store.get(key) ?? null;
54453
+ }
54454
+ async setItem(key, value2) {
54455
+ await this.ensureLoaded();
54456
+ this.store.set(key, value2);
54457
+ await this.persist();
54458
+ }
54459
+ async removeItem(key) {
54460
+ await this.ensureLoaded();
54461
+ const existed = this.store.delete(key);
54462
+ if (existed) {
54463
+ await this.persist();
54464
+ }
54465
+ return existed;
54466
+ }
54467
+ async keys() {
54468
+ await this.ensureLoaded();
54469
+ return Array.from(this.store.keys());
54470
+ }
54471
+ };
54472
+
54473
+ // src/storage/WebStorageBackend.ts
54474
+ init_SecretStorageException();
54475
+ var WebStorageBackend = class {
54476
+ storage;
54477
+ prefix;
54478
+ constructor(storage, prefix = "knishio:") {
54479
+ if (storage) {
54480
+ this.storage = storage;
54481
+ } else if (typeof globalThis !== "undefined" && globalThis.localStorage) {
54482
+ this.storage = globalThis.localStorage;
54483
+ } else {
54484
+ throw exports.SecretStorageException.unavailable(
54485
+ "web-storage",
54486
+ "WebStorageBackend requires a Storage object or global localStorage"
54487
+ );
54488
+ }
54489
+ this.prefix = prefix;
54490
+ }
54491
+ getItem(key) {
54492
+ return this.storage.getItem(key);
54493
+ }
54494
+ setItem(key, value2) {
54495
+ this.storage.setItem(key, value2);
54496
+ }
54497
+ removeItem(key) {
54498
+ const existed = this.storage.getItem(key) !== null;
54499
+ this.storage.removeItem(key);
54500
+ return existed;
54501
+ }
54502
+ keys() {
54503
+ const result = [];
54504
+ const len = this.storage.length;
54505
+ for (let i4 = 0; i4 < len; i4++) {
54506
+ const k2 = this.storage.key(i4);
54507
+ if (k2 !== null) {
54508
+ if (!this.prefix || k2.startsWith(this.prefix)) {
54509
+ result.push(k2);
54510
+ }
54511
+ }
54512
+ }
54513
+ return result;
54514
+ }
54515
+ };
54516
+
54517
+ // src/storage/WebAuthnPrfSecretStorageProvider.ts
54518
+ init_SecretStorageException();
54519
+ var PRF_SALT_LABEL = "knishio:secret-storage:webauthn-prf:v1";
54520
+ var KEK_INFO = "knishio:secret-storage:kek:v1";
54521
+ var KEY_PREFIX2 = SECRET_KEY_PREFIX;
54522
+ var GCM_IV_LENGTH2 = 12;
54523
+ var textEncoder3 = new TextEncoder();
54524
+ var textDecoder2 = new TextDecoder();
54525
+ function base64UrlEncode(bytes) {
54526
+ return uint8ArrayToBase642(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
54527
+ }
54528
+ function base64UrlDecode(str) {
54529
+ let base643 = str.replace(/-/g, "+").replace(/_/g, "/");
54530
+ while (base643.length % 4 !== 0) {
54531
+ base643 += "=";
54532
+ }
54533
+ return base64ToUint8Array2(base643);
54534
+ }
54535
+ function toUint8Array(buf) {
54536
+ if (buf instanceof Uint8Array) {
54537
+ return buf;
54538
+ }
54539
+ if (ArrayBuffer.isView(buf)) {
54540
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
54541
+ }
54542
+ return new Uint8Array(buf);
54543
+ }
54544
+ async function computePrfSalt() {
54545
+ const hash2 = await globalThis.crypto.subtle.digest("SHA-256", textEncoder3.encode(PRF_SALT_LABEL));
54546
+ return new Uint8Array(hash2);
54547
+ }
54548
+ async function deriveKekFromPrf(prfOutput, prfSalt) {
54549
+ const hkdfKey = await globalThis.crypto.subtle.importKey(
54550
+ "raw",
54551
+ prfOutput,
54552
+ "HKDF",
54553
+ false,
54554
+ ["deriveKey"]
54555
+ );
54556
+ return await globalThis.crypto.subtle.deriveKey(
54557
+ {
54558
+ name: "HKDF",
54559
+ hash: "SHA-256",
54560
+ salt: prfSalt,
54561
+ info: textEncoder3.encode(KEK_INFO)
54562
+ },
54563
+ hkdfKey,
54564
+ { name: "AES-GCM", length: 256 },
54565
+ false,
54566
+ ["encrypt", "decrypt"]
54567
+ );
54568
+ }
54569
+ var WebAuthnPrfSecretStorageProvider = class {
54570
+ providerType = "webauthn-prf";
54571
+ backend;
54572
+ rp;
54573
+ user;
54574
+ credentialsContainer;
54575
+ alias;
54576
+ cachedPassphrase;
54577
+ constructor(options) {
54578
+ this.backend = options.backend;
54579
+ this.rp = options.rp;
54580
+ this.user = options.user;
54581
+ this.credentialsContainer = options.credentials;
54582
+ this.alias = options.alias ?? "default";
54583
+ }
54584
+ get credentials() {
54585
+ if (this.credentialsContainer) {
54586
+ return this.credentialsContainer;
54587
+ }
54588
+ if (typeof globalThis.navigator !== "undefined" && globalThis.navigator.credentials) {
54589
+ return globalThis.navigator.credentials;
54590
+ }
54591
+ throw exports.SecretStorageException.unavailable(
54592
+ this.providerType,
54593
+ "WebAuthn credentials container is not available"
54594
+ );
54595
+ }
54596
+ get recordKey() {
54597
+ return `knishio:webauthn-prf:${this.alias}`;
54598
+ }
54599
+ isHardwareBacked() {
54600
+ return false;
54601
+ }
54602
+ async isAvailable() {
54603
+ const hasWebCrypto = typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined";
54604
+ const hasCredentials = Boolean(
54605
+ this.credentialsContainer || typeof globalThis.navigator !== "undefined" && globalThis.navigator.credentials && typeof globalThis.PublicKeyCredential !== "undefined"
54606
+ );
54607
+ return hasWebCrypto && hasCredentials;
54608
+ }
54609
+ /**
54610
+ * Enroll a new passkey credential with PRF support and wrap a random device passphrase
54611
+ */
54612
+ async enroll() {
54613
+ const existing = await this.backend.getItem(this.recordKey);
54614
+ if (existing) {
54615
+ return;
54616
+ }
54617
+ if (!await this.isAvailable()) {
54618
+ throw exports.SecretStorageException.unavailable(this.providerType, "WebAuthn PRF is not available");
54619
+ }
54620
+ const challenge = new Uint8Array(32);
54621
+ globalThis.crypto.getRandomValues(challenge);
54622
+ const credential = await this.credentials.create({
54623
+ publicKey: {
54624
+ rp: this.rp,
54625
+ user: {
54626
+ id: this.user.id,
54627
+ name: this.user.name,
54628
+ displayName: this.user.displayName
54629
+ },
54630
+ challenge,
54631
+ pubKeyCredParams: [
54632
+ { type: "public-key", alg: -7 },
54633
+ { type: "public-key", alg: -257 }
54634
+ ],
54635
+ authenticatorSelection: {
54636
+ residentKey: "required",
54637
+ userVerification: "required"
54638
+ },
54639
+ extensions: {
54640
+ prf: {}
54641
+ }
54642
+ }
54643
+ });
54644
+ if (!credential) {
54645
+ throw exports.SecretStorageException.unavailable(this.providerType, "Authenticator creation returned null");
54646
+ }
54647
+ const extResults = credential.getClientExtensionResults?.();
54648
+ if (extResults?.prf?.enabled !== true) {
54649
+ throw exports.SecretStorageException.unavailable(
54650
+ this.providerType,
54651
+ "authenticator does not support the PRF extension"
54652
+ );
54653
+ }
54654
+ const credentialIdBytes = new Uint8Array(credential.rawId);
54655
+ const prfSalt = await computePrfSalt();
54656
+ const getChallenge = new Uint8Array(32);
54657
+ globalThis.crypto.getRandomValues(getChallenge);
54658
+ let assertion;
54659
+ try {
54660
+ assertion = await this.credentials.get({
54661
+ publicKey: {
54662
+ challenge: getChallenge,
54663
+ rpId: this.rp.id,
54664
+ allowCredentials: [
54665
+ {
54666
+ type: "public-key",
54667
+ id: credentialIdBytes
54668
+ }
54669
+ ],
54670
+ userVerification: "required",
54671
+ extensions: {
54672
+ prf: {
54673
+ eval: {
54674
+ first: prfSalt
54675
+ }
54676
+ }
54677
+ }
54678
+ }
54679
+ });
54680
+ } catch (err) {
54681
+ const isNotAllowed = err instanceof Error && err.name === "NotAllowedError" || err?.name === "NotAllowedError";
54682
+ if (isNotAllowed) {
54683
+ throw exports.SecretStorageException.unavailable(
54684
+ this.providerType,
54685
+ "authenticator refused or credential missing"
54686
+ );
54687
+ }
54688
+ throw err;
54689
+ }
54690
+ if (!assertion) {
54691
+ throw exports.SecretStorageException.unavailable(
54692
+ this.providerType,
54693
+ "authenticator refused or credential missing"
54694
+ );
54695
+ }
54696
+ const getExtResults = assertion?.getClientExtensionResults?.();
54697
+ const firstOutput = getExtResults?.prf?.results?.first;
54698
+ if (!firstOutput) {
54699
+ throw exports.SecretStorageException.unavailable(
54700
+ this.providerType,
54701
+ "authenticator returned no PRF result"
54702
+ );
54703
+ }
54704
+ const prfBytes = toUint8Array(firstOutput);
54705
+ const kek = await deriveKekFromPrf(prfBytes, prfSalt);
54706
+ const devicePassphraseBytes = new Uint8Array(32);
54707
+ globalThis.crypto.getRandomValues(devicePassphraseBytes);
54708
+ const devicePassphrase = uint8ArrayToBase642(devicePassphraseBytes);
54709
+ const iv = new Uint8Array(GCM_IV_LENGTH2);
54710
+ globalThis.crypto.getRandomValues(iv);
54711
+ const passphraseBytes = textEncoder3.encode(devicePassphrase);
54712
+ try {
54713
+ const encryptedBuffer = await globalThis.crypto.subtle.encrypt(
54714
+ {
54715
+ name: "AES-GCM",
54716
+ iv
54717
+ },
54718
+ kek,
54719
+ passphraseBytes
54720
+ );
54721
+ const record2 = {
54722
+ version: 1,
54723
+ credentialId: base64UrlEncode(credentialIdBytes),
54724
+ iv: uint8ArrayToBase642(iv),
54725
+ ciphertext: uint8ArrayToBase642(new Uint8Array(encryptedBuffer))
54726
+ };
54727
+ await this.backend.setItem(this.recordKey, JSON.stringify(record2));
54728
+ this.cachedPassphrase = devicePassphrase;
54729
+ } finally {
54730
+ zeroizeBytes(passphraseBytes);
54731
+ zeroizeBytes(devicePassphraseBytes);
54732
+ }
54733
+ }
54734
+ /**
54735
+ * Unlock the device passphrase using the enrolled WebAuthn PRF credential
54736
+ */
54737
+ async unlock() {
54738
+ if (this.cachedPassphrase) {
54739
+ return this.cachedPassphrase;
54740
+ }
54741
+ const rawRecord = await this.backend.getItem(this.recordKey);
54742
+ if (!rawRecord) {
54743
+ throw exports.SecretStorageException.unavailable(
54744
+ this.providerType,
54745
+ "no enrolled credential; call enroll() first"
54746
+ );
54747
+ }
54748
+ let record2;
54749
+ try {
54750
+ record2 = JSON.parse(rawRecord);
54751
+ } catch {
54752
+ throw exports.SecretStorageException.decryptionFailed("Corrupted PRF record format");
54753
+ }
54754
+ const credentialIdBytes = base64UrlDecode(record2.credentialId);
54755
+ const prfSalt = await computePrfSalt();
54756
+ const challenge = new Uint8Array(32);
54757
+ globalThis.crypto.getRandomValues(challenge);
54758
+ let assertion;
54759
+ try {
54760
+ assertion = await this.credentials.get({
54761
+ publicKey: {
54762
+ challenge,
54763
+ rpId: this.rp.id,
54764
+ allowCredentials: [
54765
+ {
54766
+ type: "public-key",
54767
+ id: credentialIdBytes
54768
+ }
54769
+ ],
54770
+ userVerification: "required",
54771
+ extensions: {
54772
+ prf: {
54773
+ eval: {
54774
+ first: prfSalt
54775
+ }
54776
+ }
54777
+ }
54778
+ }
54779
+ });
54780
+ } catch (err) {
54781
+ const isNotAllowed = err instanceof Error && err.name === "NotAllowedError" || err?.name === "NotAllowedError";
54782
+ if (isNotAllowed) {
54783
+ throw exports.SecretStorageException.unavailable(
54784
+ this.providerType,
54785
+ "authenticator refused or credential missing"
54786
+ );
54787
+ }
54788
+ throw err;
54789
+ }
54790
+ if (!assertion) {
54791
+ throw exports.SecretStorageException.unavailable(
54792
+ this.providerType,
54793
+ "authenticator refused or credential missing"
54794
+ );
54795
+ }
54796
+ const extResults = assertion?.getClientExtensionResults?.();
54797
+ const firstOutput = extResults?.prf?.results?.first;
54798
+ if (!firstOutput) {
54799
+ throw exports.SecretStorageException.unavailable(
54800
+ this.providerType,
54801
+ "authenticator returned no PRF result"
54802
+ );
54803
+ }
54804
+ const prfBytes = toUint8Array(firstOutput);
54805
+ const kek = await deriveKekFromPrf(prfBytes, prfSalt);
54806
+ const iv = base64ToUint8Array2(record2.iv);
54807
+ const ciphertext = base64ToUint8Array2(record2.ciphertext);
54038
54808
  try {
54039
- const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
54040
54809
  const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
54041
54810
  {
54042
54811
  name: "AES-GCM",
54043
54812
  iv
54044
54813
  },
54045
- key,
54814
+ kek,
54046
54815
  ciphertext
54047
54816
  );
54048
54817
  const decryptedBytes = new Uint8Array(decryptedBuffer);
54818
+ try {
54819
+ this.cachedPassphrase = textDecoder2.decode(decryptedBytes);
54820
+ return this.cachedPassphrase;
54821
+ } finally {
54822
+ zeroizeBytes(decryptedBytes);
54823
+ }
54824
+ } catch {
54825
+ throw exports.SecretStorageException.decryptionFailed(
54826
+ "wrapped device passphrase failed authentication under the enrolled credential"
54827
+ );
54828
+ }
54829
+ }
54830
+ /**
54831
+ * Lock the provider by clearing cached passphrase material
54832
+ */
54833
+ lock() {
54834
+ this.cachedPassphrase = void 0;
54835
+ }
54836
+ /**
54837
+ * Unenroll the current credential, removing the stored PRF record and clearing cached passphrase
54838
+ */
54839
+ async unenroll() {
54840
+ this.lock();
54841
+ await this.backend.removeItem(this.recordKey);
54842
+ }
54843
+ async storeSecret(bundleHash, secret, options) {
54844
+ if (!bundleHash) {
54845
+ throw new exports.SecretStorageException("Bundle hash cannot be empty");
54846
+ }
54847
+ if (!secret) {
54848
+ throw new exports.SecretStorageException("Secret cannot be empty");
54849
+ }
54850
+ if (options?.passphrase) {
54851
+ throw new exports.SecretStorageException(
54852
+ "WebAuthnPrfSecretStorageProvider derives its passphrase from the authenticator; options.passphrase is not accepted"
54853
+ );
54854
+ }
54855
+ if (!options?.recoveryPassphrase && !options?.allowUnrecoverable) {
54856
+ throw exports.SecretStorageException.validationError(
54857
+ "Recovery passphrase required for non-exportable hardware key unless allowUnrecoverable is true"
54858
+ );
54859
+ }
54860
+ const passphrase = await this.unlock();
54861
+ const metadata = {
54862
+ bundleHash,
54863
+ label: options?.label,
54864
+ createdAt: Date.now(),
54865
+ hardwareBacked: false,
54866
+ providerType: this.providerType
54867
+ };
54868
+ const payload = await sealEnvelope(secret, passphrase, metadata);
54869
+ await this.backend.setItem(`${KEY_PREFIX2}${bundleHash}`, JSON.stringify(payload));
54870
+ if (options?.recoveryPassphrase) {
54871
+ const recoveryMetadata = {
54872
+ bundleHash,
54873
+ label: options?.label,
54874
+ createdAt: Date.now(),
54875
+ hardwareBacked: false,
54876
+ providerType: "webcrypto-aes-gcm"
54877
+ };
54878
+ const recoveryPayload = await sealEnvelope(secret, options.recoveryPassphrase, recoveryMetadata);
54879
+ await this.backend.setItem(`${RECOVERY_KEY_PREFIX}${bundleHash}`, JSON.stringify(recoveryPayload));
54880
+ }
54881
+ }
54882
+ async retrieveSecret(bundleHash, options) {
54883
+ if (options?.passphrase) {
54884
+ throw new exports.SecretStorageException(
54885
+ "WebAuthnPrfSecretStorageProvider derives its passphrase from the authenticator; options.passphrase is not accepted"
54886
+ );
54887
+ }
54888
+ const raw = await this.backend.getItem(`${KEY_PREFIX2}${bundleHash}`);
54889
+ if (!raw) {
54890
+ return null;
54891
+ }
54892
+ let payload;
54893
+ try {
54894
+ payload = JSON.parse(raw);
54895
+ } catch {
54896
+ throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
54897
+ }
54898
+ const passphrase = await this.unlock();
54899
+ try {
54900
+ const decryptedBytes = await openEnvelope(payload, passphrase);
54901
+ try {
54902
+ return textDecoder2.decode(decryptedBytes);
54903
+ } finally {
54904
+ zeroizeBytes(decryptedBytes);
54905
+ }
54906
+ } catch (err) {
54907
+ if (err instanceof exports.SecretStorageException) {
54908
+ throw err;
54909
+ }
54910
+ const msg = err instanceof Error ? err.message : String(err);
54911
+ throw exports.SecretStorageException.decryptionFailed(msg);
54912
+ }
54913
+ }
54914
+ async withSecret(bundleHash, fn, options) {
54915
+ if (options?.passphrase) {
54916
+ throw new exports.SecretStorageException(
54917
+ "WebAuthnPrfSecretStorageProvider derives its passphrase from the authenticator; options.passphrase is not accepted"
54918
+ );
54919
+ }
54920
+ const raw = await this.backend.getItem(`${KEY_PREFIX2}${bundleHash}`);
54921
+ if (!raw) {
54922
+ throw exports.SecretStorageException.notFound(bundleHash);
54923
+ }
54924
+ let payload;
54925
+ try {
54926
+ payload = JSON.parse(raw);
54927
+ } catch {
54928
+ throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
54929
+ }
54930
+ const passphrase = await this.unlock();
54931
+ try {
54932
+ const decryptedBytes = await openEnvelope(payload, passphrase);
54049
54933
  return await withSecureBytes(decryptedBytes, async (bytes) => {
54050
- const secretString = textDecoder.decode(bytes);
54934
+ const secretString = textDecoder2.decode(bytes);
54935
+ return await fn(secretString);
54936
+ });
54937
+ } catch (err) {
54938
+ if (err instanceof exports.SecretStorageException) {
54939
+ throw err;
54940
+ }
54941
+ const msg = err instanceof Error ? err.message : String(err);
54942
+ throw exports.SecretStorageException.decryptionFailed(msg);
54943
+ }
54944
+ }
54945
+ async deleteSecret(bundleHash) {
54946
+ const key = `${KEY_PREFIX2}${bundleHash}`;
54947
+ const recoveryKey = `${RECOVERY_KEY_PREFIX}${bundleHash}`;
54948
+ const result = await this.backend.removeItem(key);
54949
+ await this.backend.removeItem(recoveryKey);
54950
+ return result !== false;
54951
+ }
54952
+ async hasSecret(bundleHash) {
54953
+ const raw = await this.backend.getItem(`${KEY_PREFIX2}${bundleHash}`);
54954
+ return raw !== null;
54955
+ }
54956
+ async listSecrets() {
54957
+ const keys = await this.backend.keys();
54958
+ const matchingKeys = keys.filter((k2) => k2.startsWith(KEY_PREFIX2) && !k2.startsWith(RECOVERY_KEY_PREFIX));
54959
+ const results = [];
54960
+ for (const key of matchingKeys) {
54961
+ const raw = await this.backend.getItem(key);
54962
+ if (raw) {
54963
+ try {
54964
+ const payload = JSON.parse(raw);
54965
+ if (payload.metadata) {
54966
+ results.push(payload.metadata);
54967
+ }
54968
+ } catch {
54969
+ }
54970
+ }
54971
+ }
54972
+ return results;
54973
+ }
54974
+ /**
54975
+ * Recover a secret using its recovery envelope and re-enroll it under a fresh WebAuthn PRF credential
54976
+ */
54977
+ async recoverSecret(bundleHash, recoveryPassphrase, options) {
54978
+ if (!bundleHash) {
54979
+ throw new exports.SecretStorageException("Bundle hash cannot be empty");
54980
+ }
54981
+ if (!recoveryPassphrase) {
54982
+ throw new exports.SecretStorageException("Recovery passphrase cannot be empty");
54983
+ }
54984
+ const raw = await this.backend.getItem(`${RECOVERY_KEY_PREFIX}${bundleHash}`);
54985
+ if (!raw) {
54986
+ throw exports.SecretStorageException.notFound(bundleHash);
54987
+ }
54988
+ let payload;
54989
+ try {
54990
+ payload = JSON.parse(raw);
54991
+ } catch {
54992
+ throw exports.SecretStorageException.decryptionFailed("Corrupted recovery payload format");
54993
+ }
54994
+ let decryptedBytes;
54995
+ try {
54996
+ decryptedBytes = await openEnvelope(payload, recoveryPassphrase);
54997
+ } catch (err) {
54998
+ if (err instanceof exports.SecretStorageException) {
54999
+ throw err;
55000
+ }
55001
+ const msg = err instanceof Error ? err.message : String(err);
55002
+ throw exports.SecretStorageException.decryptionFailed(msg);
55003
+ }
55004
+ let secretStr;
55005
+ try {
55006
+ secretStr = textDecoder2.decode(decryptedBytes);
55007
+ } finally {
55008
+ zeroizeBytes(decryptedBytes);
55009
+ }
55010
+ await this.storeSecret(bundleHash, secretStr, {
55011
+ ...options,
55012
+ recoveryPassphrase
55013
+ });
55014
+ }
55015
+ };
55016
+
55017
+ // src/storage/NonExtractableKeySecretStorageProvider.ts
55018
+ init_SecretStorageException();
55019
+ var KEY_PREFIX3 = SECRET_KEY_PREFIX;
55020
+ var GCM_IV_LENGTH3 = 12;
55021
+ var textEncoder4 = new TextEncoder();
55022
+ var textDecoder3 = new TextDecoder();
55023
+ var MemoryKeyStore = class {
55024
+ keys = /* @__PURE__ */ new Map();
55025
+ async get(name2) {
55026
+ return this.keys.get(name2);
55027
+ }
55028
+ async put(name2, key) {
55029
+ this.keys.set(name2, key);
55030
+ }
55031
+ async delete(name2) {
55032
+ return this.keys.delete(name2);
55033
+ }
55034
+ };
55035
+ var IndexedDbKeyStore = class {
55036
+ dbName;
55037
+ storeName = "keys";
55038
+ constructor(dbName = "knishio-secret-storage") {
55039
+ this.dbName = dbName;
55040
+ }
55041
+ // Executor form intentionally retained for browser runtime compatibility with ES2022 / browsers without Promise.withResolvers polyfill
55042
+ async getDb() {
55043
+ if (typeof globalThis.indexedDB === "undefined") {
55044
+ throw exports.SecretStorageException.unavailable(
55045
+ "webcrypto-nonextractable",
55046
+ "IndexedDB is not available"
55047
+ );
55048
+ }
55049
+ return new Promise((resolve, reject) => {
55050
+ const request = globalThis.indexedDB.open(this.dbName, 1);
55051
+ request.onupgradeneeded = () => {
55052
+ const db = request.result;
55053
+ if (!db.objectStoreNames.contains(this.storeName)) {
55054
+ db.createObjectStore(this.storeName);
55055
+ }
55056
+ };
55057
+ request.onsuccess = () => resolve(request.result);
55058
+ request.onerror = () => reject(request.error);
55059
+ });
55060
+ }
55061
+ async get(name2) {
55062
+ const db = await this.getDb();
55063
+ return new Promise((resolve, reject) => {
55064
+ const tx = db.transaction(this.storeName, "readonly");
55065
+ const store = tx.objectStore(this.storeName);
55066
+ const request = store.get(name2);
55067
+ request.onsuccess = () => resolve(request.result);
55068
+ request.onerror = () => reject(request.error);
55069
+ });
55070
+ }
55071
+ async put(name2, key) {
55072
+ const db = await this.getDb();
55073
+ return new Promise((resolve, reject) => {
55074
+ const tx = db.transaction(this.storeName, "readwrite");
55075
+ const store = tx.objectStore(this.storeName);
55076
+ const request = store.put(key, name2);
55077
+ request.onsuccess = () => resolve();
55078
+ request.onerror = () => reject(request.error);
55079
+ });
55080
+ }
55081
+ async delete(name2) {
55082
+ const db = await this.getDb();
55083
+ return new Promise((resolve, reject) => {
55084
+ const tx = db.transaction(this.storeName, "readwrite");
55085
+ const store = tx.objectStore(this.storeName);
55086
+ const request = store.delete(name2);
55087
+ request.onsuccess = () => resolve(true);
55088
+ request.onerror = () => reject(request.error);
55089
+ });
55090
+ }
55091
+ };
55092
+ var NonExtractableKeySecretStorageProvider = class {
55093
+ providerType = "webcrypto-nonextractable";
55094
+ backend;
55095
+ keyStore;
55096
+ alias;
55097
+ cachedPassphrase;
55098
+ constructor(options) {
55099
+ this.backend = options.backend;
55100
+ this.keyStore = options.keyStore ?? new IndexedDbKeyStore();
55101
+ this.alias = options.alias ?? "default";
55102
+ }
55103
+ get recordKey() {
55104
+ return `knishio:kek:webcrypto-nonextractable:${this.alias}`;
55105
+ }
55106
+ get kekStoreKey() {
55107
+ return `knishio:kek:${this.alias}`;
55108
+ }
55109
+ isHardwareBacked() {
55110
+ return false;
55111
+ }
55112
+ async isAvailable() {
55113
+ return typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined";
55114
+ }
55115
+ /**
55116
+ * Unlock or initialize the device passphrase using the non-extractable KEK
55117
+ */
55118
+ async unlock() {
55119
+ if (this.cachedPassphrase) {
55120
+ return this.cachedPassphrase;
55121
+ }
55122
+ if (!await this.isAvailable()) {
55123
+ throw exports.SecretStorageException.unavailable(
55124
+ this.providerType,
55125
+ "WebCrypto API is not available"
55126
+ );
55127
+ }
55128
+ const rawRecord = await this.backend.getItem(this.recordKey);
55129
+ if (!rawRecord) {
55130
+ let kek2 = await this.keyStore.get(this.kekStoreKey);
55131
+ if (!kek2) {
55132
+ kek2 = await globalThis.crypto.subtle.generateKey(
55133
+ { name: "AES-GCM", length: 256 },
55134
+ false,
55135
+ ["encrypt", "decrypt"]
55136
+ );
55137
+ await this.keyStore.put(this.kekStoreKey, kek2);
55138
+ }
55139
+ const devicePassphraseBytes = new Uint8Array(32);
55140
+ globalThis.crypto.getRandomValues(devicePassphraseBytes);
55141
+ const devicePassphrase = uint8ArrayToBase642(devicePassphraseBytes);
55142
+ const iv2 = new Uint8Array(GCM_IV_LENGTH3);
55143
+ globalThis.crypto.getRandomValues(iv2);
55144
+ const passphraseBytes = textEncoder4.encode(devicePassphrase);
55145
+ try {
55146
+ const encryptedBuffer = await globalThis.crypto.subtle.encrypt(
55147
+ {
55148
+ name: "AES-GCM",
55149
+ iv: iv2
55150
+ },
55151
+ kek2,
55152
+ passphraseBytes
55153
+ );
55154
+ const record3 = {
55155
+ version: 1,
55156
+ iv: uint8ArrayToBase642(iv2),
55157
+ ciphertext: uint8ArrayToBase642(new Uint8Array(encryptedBuffer))
55158
+ };
55159
+ await this.backend.setItem(this.recordKey, JSON.stringify(record3));
55160
+ this.cachedPassphrase = devicePassphrase;
55161
+ return devicePassphrase;
55162
+ } finally {
55163
+ zeroizeBytes(passphraseBytes);
55164
+ zeroizeBytes(devicePassphraseBytes);
55165
+ }
55166
+ }
55167
+ let record2;
55168
+ try {
55169
+ record2 = JSON.parse(rawRecord);
55170
+ } catch {
55171
+ throw exports.SecretStorageException.decryptionFailed("Corrupted key record format");
55172
+ }
55173
+ const kek = await this.keyStore.get(this.kekStoreKey);
55174
+ if (!kek) {
55175
+ throw exports.SecretStorageException.unavailable(
55176
+ this.providerType,
55177
+ `no non-extractable key found for alias '${this.alias}'`
55178
+ );
55179
+ }
55180
+ const iv = base64ToUint8Array2(record2.iv);
55181
+ const ciphertext = base64ToUint8Array2(record2.ciphertext);
55182
+ try {
55183
+ const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
55184
+ {
55185
+ name: "AES-GCM",
55186
+ iv
55187
+ },
55188
+ kek,
55189
+ ciphertext
55190
+ );
55191
+ const decryptedBytes = new Uint8Array(decryptedBuffer);
55192
+ try {
55193
+ this.cachedPassphrase = textDecoder3.decode(decryptedBytes);
55194
+ return this.cachedPassphrase;
55195
+ } finally {
55196
+ zeroizeBytes(decryptedBytes);
55197
+ }
55198
+ } catch {
55199
+ throw exports.SecretStorageException.decryptionFailed(
55200
+ "wrapped device passphrase failed authentication under non-extractable key"
55201
+ );
55202
+ }
55203
+ }
55204
+ /**
55205
+ * Lock the provider by clearing cached passphrase material
55206
+ */
55207
+ lock() {
55208
+ this.cachedPassphrase = void 0;
55209
+ }
55210
+ /**
55211
+ * Unenroll the non-extractable key, removing the stored wrapped record and KEK
55212
+ */
55213
+ async unenroll() {
55214
+ this.lock();
55215
+ await this.backend.removeItem(this.recordKey);
55216
+ await this.keyStore.delete(this.kekStoreKey);
55217
+ }
55218
+ async storeSecret(bundleHash, secret, options) {
55219
+ if (!bundleHash) {
55220
+ throw new exports.SecretStorageException("Bundle hash cannot be empty");
55221
+ }
55222
+ if (!secret) {
55223
+ throw new exports.SecretStorageException("Secret cannot be empty");
55224
+ }
55225
+ if (options?.passphrase) {
55226
+ throw new exports.SecretStorageException(
55227
+ "NonExtractableKeySecretStorageProvider derives its passphrase from the non-extractable device key; options.passphrase is not accepted"
55228
+ );
55229
+ }
55230
+ if (!options?.recoveryPassphrase && !options?.allowUnrecoverable) {
55231
+ throw exports.SecretStorageException.validationError(
55232
+ "Recovery passphrase required for non-exportable hardware key unless allowUnrecoverable is true"
55233
+ );
55234
+ }
55235
+ const passphrase = await this.unlock();
55236
+ const metadata = {
55237
+ bundleHash,
55238
+ label: options?.label,
55239
+ createdAt: Date.now(),
55240
+ hardwareBacked: false,
55241
+ providerType: this.providerType
55242
+ };
55243
+ const payload = await sealEnvelope(secret, passphrase, metadata);
55244
+ await this.backend.setItem(`${KEY_PREFIX3}${bundleHash}`, JSON.stringify(payload));
55245
+ if (options?.recoveryPassphrase) {
55246
+ const recoveryMetadata = {
55247
+ bundleHash,
55248
+ label: options?.label,
55249
+ createdAt: Date.now(),
55250
+ hardwareBacked: false,
55251
+ providerType: "webcrypto-aes-gcm"
55252
+ };
55253
+ const recoveryPayload = await sealEnvelope(secret, options.recoveryPassphrase, recoveryMetadata);
55254
+ await this.backend.setItem(`${RECOVERY_KEY_PREFIX}${bundleHash}`, JSON.stringify(recoveryPayload));
55255
+ }
55256
+ }
55257
+ async retrieveSecret(bundleHash, options) {
55258
+ if (options?.passphrase) {
55259
+ throw new exports.SecretStorageException(
55260
+ "NonExtractableKeySecretStorageProvider derives its passphrase from the non-extractable device key; options.passphrase is not accepted"
55261
+ );
55262
+ }
55263
+ const raw = await this.backend.getItem(`${KEY_PREFIX3}${bundleHash}`);
55264
+ if (!raw) {
55265
+ return null;
55266
+ }
55267
+ let payload;
55268
+ try {
55269
+ payload = JSON.parse(raw);
55270
+ } catch {
55271
+ throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
55272
+ }
55273
+ const passphrase = await this.unlock();
55274
+ try {
55275
+ const decryptedBytes = await openEnvelope(payload, passphrase);
55276
+ try {
55277
+ return textDecoder3.decode(decryptedBytes);
55278
+ } finally {
55279
+ zeroizeBytes(decryptedBytes);
55280
+ }
55281
+ } catch (err) {
55282
+ if (err instanceof exports.SecretStorageException) {
55283
+ throw err;
55284
+ }
55285
+ const msg = err instanceof Error ? err.message : String(err);
55286
+ throw exports.SecretStorageException.decryptionFailed(msg);
55287
+ }
55288
+ }
55289
+ async withSecret(bundleHash, fn, options) {
55290
+ if (options?.passphrase) {
55291
+ throw new exports.SecretStorageException(
55292
+ "NonExtractableKeySecretStorageProvider derives its passphrase from the non-extractable device key; options.passphrase is not accepted"
55293
+ );
55294
+ }
55295
+ const raw = await this.backend.getItem(`${KEY_PREFIX3}${bundleHash}`);
55296
+ if (!raw) {
55297
+ throw exports.SecretStorageException.notFound(bundleHash);
55298
+ }
55299
+ let payload;
55300
+ try {
55301
+ payload = JSON.parse(raw);
55302
+ } catch {
55303
+ throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
55304
+ }
55305
+ const passphrase = await this.unlock();
55306
+ try {
55307
+ const decryptedBytes = await openEnvelope(payload, passphrase);
55308
+ return await withSecureBytes(decryptedBytes, async (bytes) => {
55309
+ const secretString = textDecoder3.decode(bytes);
54051
55310
  return await fn(secretString);
54052
55311
  });
54053
55312
  } catch (err) {
@@ -54058,6 +55317,76 @@ ${operationTypes.join("\n")}
54058
55317
  throw exports.SecretStorageException.decryptionFailed(msg);
54059
55318
  }
54060
55319
  }
55320
+ async deleteSecret(bundleHash) {
55321
+ const key = `${KEY_PREFIX3}${bundleHash}`;
55322
+ const recoveryKey = `${RECOVERY_KEY_PREFIX}${bundleHash}`;
55323
+ const result = await this.backend.removeItem(key);
55324
+ await this.backend.removeItem(recoveryKey);
55325
+ return result !== false;
55326
+ }
55327
+ async hasSecret(bundleHash) {
55328
+ const raw = await this.backend.getItem(`${KEY_PREFIX3}${bundleHash}`);
55329
+ return raw !== null;
55330
+ }
55331
+ async listSecrets() {
55332
+ const keys = await this.backend.keys();
55333
+ const matchingKeys = keys.filter((k2) => k2.startsWith(KEY_PREFIX3) && !k2.startsWith(RECOVERY_KEY_PREFIX));
55334
+ const results = [];
55335
+ for (const key of matchingKeys) {
55336
+ const raw = await this.backend.getItem(key);
55337
+ if (raw) {
55338
+ try {
55339
+ const payload = JSON.parse(raw);
55340
+ if (payload.metadata) {
55341
+ results.push(payload.metadata);
55342
+ }
55343
+ } catch {
55344
+ }
55345
+ }
55346
+ }
55347
+ return results;
55348
+ }
55349
+ /**
55350
+ * Recover a secret using its recovery envelope and re-enroll it under a fresh non-extractable KEK
55351
+ */
55352
+ async recoverSecret(bundleHash, recoveryPassphrase, options) {
55353
+ if (!bundleHash) {
55354
+ throw new exports.SecretStorageException("Bundle hash cannot be empty");
55355
+ }
55356
+ if (!recoveryPassphrase) {
55357
+ throw new exports.SecretStorageException("Recovery passphrase cannot be empty");
55358
+ }
55359
+ const raw = await this.backend.getItem(`${RECOVERY_KEY_PREFIX}${bundleHash}`);
55360
+ if (!raw) {
55361
+ throw exports.SecretStorageException.notFound(bundleHash);
55362
+ }
55363
+ let payload;
55364
+ try {
55365
+ payload = JSON.parse(raw);
55366
+ } catch {
55367
+ throw exports.SecretStorageException.decryptionFailed("Corrupted recovery payload format");
55368
+ }
55369
+ let decryptedBytes;
55370
+ try {
55371
+ decryptedBytes = await openEnvelope(payload, recoveryPassphrase);
55372
+ } catch (err) {
55373
+ if (err instanceof exports.SecretStorageException) {
55374
+ throw err;
55375
+ }
55376
+ const msg = err instanceof Error ? err.message : String(err);
55377
+ throw exports.SecretStorageException.decryptionFailed(msg);
55378
+ }
55379
+ let secretStr;
55380
+ try {
55381
+ secretStr = textDecoder3.decode(decryptedBytes);
55382
+ } finally {
55383
+ zeroizeBytes(decryptedBytes);
55384
+ }
55385
+ await this.storeSecret(bundleHash, secretStr, {
55386
+ ...options,
55387
+ recoveryPassphrase
55388
+ });
55389
+ }
54061
55390
  };
54062
55391
 
54063
55392
  // src/storage/index.ts
@@ -54068,15 +55397,14 @@ ${operationTypes.join("\n")}
54068
55397
  if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined") {
54069
55398
  return new WebCryptoSecretStorageProvider({
54070
55399
  backend: options.backend,
54071
- defaultPassphrase: options.defaultPassphrase,
54072
- hardwareBacked: options.hardwareBacked
55400
+ defaultPassphrase: options.defaultPassphrase
54073
55401
  });
54074
55402
  }
54075
55403
  return new MemorySecretStorageProvider();
54076
55404
  }
54077
55405
 
54078
55406
  // src/index.ts
54079
- var SDK_VERSION = "0.9.8";
55407
+ var SDK_VERSION = "1.1.0";
54080
55408
  var SDK_NAME = "KnishIO-Client-TS";
54081
55409
  var COMPATIBLE_SERVER_VERSIONS = [4, 5];
54082
55410
  var SDK_INFO = {
@@ -54085,7 +55413,7 @@ ${operationTypes.join("\n")}
54085
55413
  description: "TypeScript SDK for Knish.IO post-blockchain distributed ledger",
54086
55414
  compatibleServerVersions: COMPATIBLE_SERVER_VERSIONS,
54087
55415
  features: [
54088
- "Post-quantum cryptography (XMSS, ML-KEM768)",
55416
+ "Post-quantum cryptography (XMSS, ML-KEM-1024)",
54089
55417
  "Cross-platform compatibility",
54090
55418
  "Type-safe APIs",
54091
55419
  "DAG-based transaction processing",
@@ -54184,9 +55512,12 @@ ${operationTypes.join("\n")}
54184
55512
  exports.CheckMolecule = CheckMolecule;
54185
55513
  exports.DevUtils = DevUtils;
54186
55514
  exports.EXTENDED_COMPATIBILITY_TEST_VECTORS = EXTENDED_COMPATIBILITY_TEST_VECTORS;
55515
+ exports.FileStorageBackend = FileStorageBackend;
54187
55516
  exports.GraphQLClient = GraphQLClient;
55517
+ exports.IndexedDbKeyStore = IndexedDbKeyStore;
54188
55518
  exports.KnishIO = KnishIO;
54189
55519
  exports.KnishIOClient = KnishIOClient;
55520
+ exports.MemoryKeyStore = MemoryKeyStore;
54190
55521
  exports.MemorySecretStorageProvider = MemorySecretStorageProvider;
54191
55522
  exports.MemoryStorageBackend = MemoryStorageBackend;
54192
55523
  exports.Meta = Meta;
@@ -54201,6 +55532,7 @@ ${operationTypes.join("\n")}
54201
55532
  exports.MutationRequestAuthorization = MutationRequestAuthorization;
54202
55533
  exports.MutationRequestTokens = MutationRequestTokens;
54203
55534
  exports.MutationTransferTokens = MutationTransferTokens;
55535
+ exports.NonExtractableKeySecretStorageProvider = NonExtractableKeySecretStorageProvider;
54204
55536
  exports.PolicyMeta = PolicyMeta;
54205
55537
  exports.Query = Query;
54206
55538
  exports.QueryAtom = QueryAtom;
@@ -54212,6 +55544,7 @@ ${operationTypes.join("\n")}
54212
55544
  exports.QueryMetaTypeViaAtom = QueryMetaTypeViaAtom;
54213
55545
  exports.QueryWalletBundle = QueryWalletBundle;
54214
55546
  exports.QueryWalletList = QueryWalletList;
55547
+ exports.RECOVERY_KEY_PREFIX = RECOVERY_KEY_PREFIX;
54215
55548
  exports.ResponseAppendRequest = ResponseAppendRequest;
54216
55549
  exports.ResponseAtom = ResponseAtom;
54217
55550
  exports.ResponseBalance = ResponseBalance;
@@ -54232,9 +55565,12 @@ ${operationTypes.join("\n")}
54232
55565
  exports.SDK_INFO = SDK_INFO;
54233
55566
  exports.SDK_NAME = SDK_NAME;
54234
55567
  exports.SDK_VERSION = SDK_VERSION;
55568
+ exports.SECRET_KEY_PREFIX = SECRET_KEY_PREFIX;
54235
55569
  exports.TokenUnit = TokenUnit;
54236
55570
  exports.Wallet = Wallet;
55571
+ exports.WebAuthnPrfSecretStorageProvider = WebAuthnPrfSecretStorageProvider;
54237
55572
  exports.WebCryptoSecretStorageProvider = WebCryptoSecretStorageProvider;
55573
+ exports.WebStorageBackend = WebStorageBackend;
54238
55574
  exports.base64ToHex = base64ToHex;
54239
55575
  exports.bufferToHexString = bufferToHexString;
54240
55576
  exports.capitalize = capitalize;
@@ -54273,9 +55609,11 @@ ${operationTypes.join("\n")}
54273
55609
  exports.isPosition = isPosition2;
54274
55610
  exports.isWalletAddress = isWalletAddress2;
54275
55611
  exports.normalizeMolecularHash = normalizeMolecularHash;
55612
+ exports.openEnvelope = openEnvelope;
54276
55613
  exports.randomString = randomString;
54277
55614
  exports.runCompatibilityTests = runCompatibilityTests;
54278
55615
  exports.runExtendedCompatibilityTests = runExtendedCompatibilityTests;
55616
+ exports.sealEnvelope = sealEnvelope;
54279
55617
  exports.shake256 = shake256;
54280
55618
  exports.toCamelCase = toCamelCase;
54281
55619
  exports.toSnakeCase = toSnakeCase;