@wishknish/knishio-client-ts 0.9.6 → 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +531 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +126 -5
- package/dist/index.d.ts +126 -5
- package/dist/index.iife.js +531 -5
- package/dist/index.iife.js.map +1 -1
- package/dist/index.js +524 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/KnishIOClient.ts +63 -2
- package/src/exception/SecretStorageException.ts +97 -0
- package/src/exception/index.ts +1 -0
- package/src/index.ts +28 -1
- package/src/libraries/secureMemory.ts +121 -0
- package/src/schemas/index.ts +2 -1
- package/src/storage/MemorySecretStorageProvider.ts +156 -0
- package/src/storage/WebCryptoSecretStorageProvider.ts +407 -0
- package/src/storage/index.ts +87 -0
- package/src/types/index.ts +1 -1
- package/src/types/storage.ts +129 -0
- package/src/validation/schemas.ts +3 -1
package/dist/index.iife.js
CHANGED
|
@@ -20064,6 +20064,55 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
20064
20064
|
}
|
|
20065
20065
|
});
|
|
20066
20066
|
|
|
20067
|
+
// src/exception/SecretStorageException.ts
|
|
20068
|
+
exports.SecretStorageException = void 0;
|
|
20069
|
+
var init_SecretStorageException = __esm({
|
|
20070
|
+
"src/exception/SecretStorageException.ts"() {
|
|
20071
|
+
init_BaseException();
|
|
20072
|
+
exports.SecretStorageException = class _SecretStorageException extends exports.BaseException {
|
|
20073
|
+
constructor(message = "Secret storage operation failed", options = {}) {
|
|
20074
|
+
super("WALLET_CREDENTIAL_ERROR", message, {
|
|
20075
|
+
code: "SECRET_STORAGE_ERROR",
|
|
20076
|
+
...options
|
|
20077
|
+
});
|
|
20078
|
+
}
|
|
20079
|
+
/**
|
|
20080
|
+
* Secret not found for the requested bundle hash
|
|
20081
|
+
*/
|
|
20082
|
+
static notFound(bundleHash) {
|
|
20083
|
+
return new _SecretStorageException(`Secret not found for bundle: ${bundleHash}`, {
|
|
20084
|
+
code: "SECRET_NOT_FOUND",
|
|
20085
|
+
details: { bundleHash }
|
|
20086
|
+
});
|
|
20087
|
+
}
|
|
20088
|
+
/**
|
|
20089
|
+
* Decryption failed (wrong passphrase or corrupted payload)
|
|
20090
|
+
*/
|
|
20091
|
+
static decryptionFailed(reason) {
|
|
20092
|
+
return new _SecretStorageException(
|
|
20093
|
+
`Failed to decrypt master secret: ${reason || "Invalid passphrase or corrupted ciphertext"}`,
|
|
20094
|
+
{
|
|
20095
|
+
code: "DECRYPTION_FAILED",
|
|
20096
|
+
details: { reason }
|
|
20097
|
+
}
|
|
20098
|
+
);
|
|
20099
|
+
}
|
|
20100
|
+
/**
|
|
20101
|
+
* Provider is unavailable in current platform
|
|
20102
|
+
*/
|
|
20103
|
+
static unavailable(provider, reason) {
|
|
20104
|
+
return new _SecretStorageException(
|
|
20105
|
+
`Secret storage provider '${provider}' is unavailable: ${reason || "Hardware or API not accessible"}`,
|
|
20106
|
+
{
|
|
20107
|
+
code: "STORAGE_UNAVAILABLE",
|
|
20108
|
+
details: { provider, reason }
|
|
20109
|
+
}
|
|
20110
|
+
);
|
|
20111
|
+
}
|
|
20112
|
+
};
|
|
20113
|
+
}
|
|
20114
|
+
});
|
|
20115
|
+
|
|
20067
20116
|
// src/exception/InvalidResponseException.ts
|
|
20068
20117
|
exports.InvalidResponseException = void 0;
|
|
20069
20118
|
var init_InvalidResponseException = __esm({
|
|
@@ -20526,6 +20575,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
20526
20575
|
init_SignatureMismatchException();
|
|
20527
20576
|
init_TransferBalanceException();
|
|
20528
20577
|
init_WalletCredentialException();
|
|
20578
|
+
init_SecretStorageException();
|
|
20529
20579
|
init_InvalidResponseException();
|
|
20530
20580
|
init_BalanceInsufficientException();
|
|
20531
20581
|
init_BatchIdException();
|
|
@@ -43021,7 +43071,8 @@ ${operationTypes.join("\n")}
|
|
|
43021
43071
|
socket: external_exports.unknown().optional(),
|
|
43022
43072
|
serverSdkVersion: external_exports.number().int().min(1).optional(),
|
|
43023
43073
|
logging: external_exports.boolean().optional(),
|
|
43024
|
-
defaultRequestPolicy: external_exports.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional()
|
|
43074
|
+
defaultRequestPolicy: external_exports.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional(),
|
|
43075
|
+
secretStorage: external_exports.unknown().optional()
|
|
43025
43076
|
}).strict();
|
|
43026
43077
|
external_exports.object({
|
|
43027
43078
|
token: external_exports.string().min(1, "Auth token cannot be empty"),
|
|
@@ -48251,7 +48302,9 @@ ${operationTypes.join("\n")}
|
|
|
48251
48302
|
// Optional default urql request policy for reads (server/sync clients pass
|
|
48252
48303
|
// 'network-only'). Permitted by the strict schema so the constructor option
|
|
48253
48304
|
// isn't rejected.
|
|
48254
|
-
defaultRequestPolicy: external_exports.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional()
|
|
48305
|
+
defaultRequestPolicy: external_exports.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional(),
|
|
48306
|
+
// Pluggable hardware envelope encryption secret storage provider
|
|
48307
|
+
secretStorage: external_exports.unknown().optional()
|
|
48255
48308
|
}).strict();
|
|
48256
48309
|
var EnvironmentConfigSchema = external_exports.object({
|
|
48257
48310
|
NODE_ENV: external_exports.enum(["development", "production", "test"]).optional(),
|
|
@@ -51827,9 +51880,134 @@ ${operationTypes.join("\n")}
|
|
|
51827
51880
|
|
|
51828
51881
|
// src/KnishIOClient.ts
|
|
51829
51882
|
init_exception();
|
|
51883
|
+
|
|
51884
|
+
// src/storage/MemorySecretStorageProvider.ts
|
|
51885
|
+
init_SecretStorageException();
|
|
51886
|
+
|
|
51887
|
+
// src/libraries/secureMemory.ts
|
|
51888
|
+
var textEncoder = new TextEncoder();
|
|
51889
|
+
function zeroizeBytes(buffer) {
|
|
51890
|
+
if (buffer instanceof Uint8Array) {
|
|
51891
|
+
buffer.fill(0);
|
|
51892
|
+
} else if (Array.isArray(buffer)) {
|
|
51893
|
+
for (let i4 = 0; i4 < buffer.length; i4++) {
|
|
51894
|
+
buffer[i4] = 0;
|
|
51895
|
+
}
|
|
51896
|
+
}
|
|
51897
|
+
}
|
|
51898
|
+
async function withSecureBytes(bytes, fn) {
|
|
51899
|
+
try {
|
|
51900
|
+
return await fn(bytes);
|
|
51901
|
+
} finally {
|
|
51902
|
+
zeroizeBytes(bytes);
|
|
51903
|
+
}
|
|
51904
|
+
}
|
|
51905
|
+
async function withSecureString(secret, fn) {
|
|
51906
|
+
const bytes = textEncoder.encode(secret);
|
|
51907
|
+
try {
|
|
51908
|
+
return await fn(secret);
|
|
51909
|
+
} finally {
|
|
51910
|
+
zeroizeBytes(bytes);
|
|
51911
|
+
}
|
|
51912
|
+
}
|
|
51913
|
+
function constantTimeCompare(a3, b2) {
|
|
51914
|
+
const bytesA = typeof a3 === "string" ? textEncoder.encode(a3) : a3;
|
|
51915
|
+
const bytesB = typeof b2 === "string" ? textEncoder.encode(b2) : b2;
|
|
51916
|
+
let result = bytesA.length === bytesB.length ? 0 : 1;
|
|
51917
|
+
const len = Math.min(bytesA.length, bytesB.length);
|
|
51918
|
+
for (let i4 = 0; i4 < len; i4++) {
|
|
51919
|
+
const byteA = bytesA[i4] ?? 0;
|
|
51920
|
+
const byteB = bytesB[i4] ?? 0;
|
|
51921
|
+
result |= byteA ^ byteB;
|
|
51922
|
+
}
|
|
51923
|
+
if (typeof a3 === "string") zeroizeBytes(bytesA);
|
|
51924
|
+
if (typeof b2 === "string") zeroizeBytes(bytesB);
|
|
51925
|
+
return result === 0;
|
|
51926
|
+
}
|
|
51927
|
+
|
|
51928
|
+
// src/storage/MemorySecretStorageProvider.ts
|
|
51929
|
+
var MemorySecretStorageProvider = class {
|
|
51930
|
+
providerType = "memory";
|
|
51931
|
+
secrets = /* @__PURE__ */ new Map();
|
|
51932
|
+
/**
|
|
51933
|
+
* Memory storage is not hardware backed
|
|
51934
|
+
*/
|
|
51935
|
+
isHardwareBacked() {
|
|
51936
|
+
return false;
|
|
51937
|
+
}
|
|
51938
|
+
/**
|
|
51939
|
+
* Memory storage is always available
|
|
51940
|
+
*/
|
|
51941
|
+
async isAvailable() {
|
|
51942
|
+
return true;
|
|
51943
|
+
}
|
|
51944
|
+
/**
|
|
51945
|
+
* Store a secret in memory
|
|
51946
|
+
*/
|
|
51947
|
+
async storeSecret(bundleHash, secret, options) {
|
|
51948
|
+
if (!bundleHash) {
|
|
51949
|
+
throw new exports.SecretStorageException("Bundle hash cannot be empty");
|
|
51950
|
+
}
|
|
51951
|
+
if (!secret) {
|
|
51952
|
+
throw new exports.SecretStorageException("Secret cannot be empty");
|
|
51953
|
+
}
|
|
51954
|
+
const metadata = {
|
|
51955
|
+
bundleHash,
|
|
51956
|
+
label: options?.label,
|
|
51957
|
+
createdAt: Date.now(),
|
|
51958
|
+
hardwareBacked: false,
|
|
51959
|
+
providerType: this.providerType
|
|
51960
|
+
};
|
|
51961
|
+
this.secrets.set(bundleHash, { secret, metadata });
|
|
51962
|
+
}
|
|
51963
|
+
/**
|
|
51964
|
+
* Retrieve a secret from memory
|
|
51965
|
+
*/
|
|
51966
|
+
async retrieveSecret(bundleHash) {
|
|
51967
|
+
const entry = this.secrets.get(bundleHash);
|
|
51968
|
+
return entry ? entry.secret : null;
|
|
51969
|
+
}
|
|
51970
|
+
/**
|
|
51971
|
+
* Delete a stored secret
|
|
51972
|
+
*/
|
|
51973
|
+
async deleteSecret(bundleHash) {
|
|
51974
|
+
return this.secrets.delete(bundleHash);
|
|
51975
|
+
}
|
|
51976
|
+
/**
|
|
51977
|
+
* Check if a secret exists
|
|
51978
|
+
*/
|
|
51979
|
+
async hasSecret(bundleHash) {
|
|
51980
|
+
return this.secrets.has(bundleHash);
|
|
51981
|
+
}
|
|
51982
|
+
/**
|
|
51983
|
+
* List all stored secret metadata
|
|
51984
|
+
*/
|
|
51985
|
+
async listSecrets() {
|
|
51986
|
+
return Array.from(this.secrets.values()).map((entry) => ({ ...entry.metadata }));
|
|
51987
|
+
}
|
|
51988
|
+
/**
|
|
51989
|
+
* Execute callback with unwrapped secret and ensure cleanup
|
|
51990
|
+
*/
|
|
51991
|
+
async withSecret(bundleHash, fn) {
|
|
51992
|
+
const entry = this.secrets.get(bundleHash);
|
|
51993
|
+
if (!entry) {
|
|
51994
|
+
throw exports.SecretStorageException.notFound(bundleHash);
|
|
51995
|
+
}
|
|
51996
|
+
return withSecureString(entry.secret, fn);
|
|
51997
|
+
}
|
|
51998
|
+
/**
|
|
51999
|
+
* Clear all secrets from memory
|
|
52000
|
+
*/
|
|
52001
|
+
clear() {
|
|
52002
|
+
this.secrets.clear();
|
|
52003
|
+
}
|
|
52004
|
+
};
|
|
52005
|
+
|
|
52006
|
+
// src/KnishIOClient.ts
|
|
51830
52007
|
var KnishIOClient = class {
|
|
51831
52008
|
$__secret = "";
|
|
51832
52009
|
$__bundle = "";
|
|
52010
|
+
$__secretStorage = null;
|
|
51833
52011
|
$__cellSlug = null;
|
|
51834
52012
|
$__encrypt = false;
|
|
51835
52013
|
$__uris = [];
|
|
@@ -51889,6 +52067,9 @@ ${operationTypes.join("\n")}
|
|
|
51889
52067
|
logging,
|
|
51890
52068
|
defaultRequestPolicy
|
|
51891
52069
|
});
|
|
52070
|
+
if (config2.secretStorage) {
|
|
52071
|
+
this.$__secretStorage = config2.secretStorage;
|
|
52072
|
+
}
|
|
51892
52073
|
}
|
|
51893
52074
|
/**
|
|
51894
52075
|
* Initializes a new Knish.IO client session
|
|
@@ -51990,6 +52171,7 @@ ${operationTypes.join("\n")}
|
|
|
51990
52171
|
reset() {
|
|
51991
52172
|
this.$__secret = "";
|
|
51992
52173
|
this.$__bundle = "";
|
|
52174
|
+
this.$__secretStorage = null;
|
|
51993
52175
|
this.$__encrypt = false;
|
|
51994
52176
|
this.$__cellSlug = null;
|
|
51995
52177
|
this.$__authToken = null;
|
|
@@ -52051,7 +52233,7 @@ ${operationTypes.join("\n")}
|
|
|
52051
52233
|
* Returns whether a secret is stored for this session
|
|
52052
52234
|
*/
|
|
52053
52235
|
hasSecret() {
|
|
52054
|
-
return !!this.$__secret && this.$__secret.length > 0;
|
|
52236
|
+
return !!this.$__secret && this.$__secret.length > 0 || !!this.$__secretStorage && !!this.$__bundle && this.$__bundle.length > 0;
|
|
52055
52237
|
}
|
|
52056
52238
|
/**
|
|
52057
52239
|
* Returns the stored secret
|
|
@@ -52062,6 +52244,33 @@ ${operationTypes.join("\n")}
|
|
|
52062
52244
|
}
|
|
52063
52245
|
return this.$__secret;
|
|
52064
52246
|
}
|
|
52247
|
+
/**
|
|
52248
|
+
* Sets the secret storage provider and optionally sets the bundle hash
|
|
52249
|
+
*/
|
|
52250
|
+
setSecretStorage(storage, bundleHash) {
|
|
52251
|
+
this.$__secretStorage = storage;
|
|
52252
|
+
if (bundleHash) {
|
|
52253
|
+
this.$__bundle = bundleHash;
|
|
52254
|
+
}
|
|
52255
|
+
}
|
|
52256
|
+
/**
|
|
52257
|
+
* Returns current secret storage provider
|
|
52258
|
+
*/
|
|
52259
|
+
getSecretStorage() {
|
|
52260
|
+
return this.$__secretStorage;
|
|
52261
|
+
}
|
|
52262
|
+
/**
|
|
52263
|
+
* Asynchronously retrieves the secret from storage or returns in-memory secret
|
|
52264
|
+
*/
|
|
52265
|
+
async retrieveSecret(options) {
|
|
52266
|
+
if (this.$__secret && this.$__secret.length > 0) {
|
|
52267
|
+
return this.$__secret;
|
|
52268
|
+
}
|
|
52269
|
+
if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
|
|
52270
|
+
return await this.$__secretStorage.retrieveSecret(this.$__bundle, options);
|
|
52271
|
+
}
|
|
52272
|
+
return null;
|
|
52273
|
+
}
|
|
52065
52274
|
/**
|
|
52066
52275
|
* Returns whether a bundle hash is being stored for this session
|
|
52067
52276
|
*/
|
|
@@ -52100,6 +52309,13 @@ ${operationTypes.join("\n")}
|
|
|
52100
52309
|
remainderWallet = null
|
|
52101
52310
|
} = {}) {
|
|
52102
52311
|
this.log("info", "KnishIOClient::createMolecule() - Creating a new molecule...");
|
|
52312
|
+
if (!secret) {
|
|
52313
|
+
if (this.$__secret && this.$__secret.length > 0) {
|
|
52314
|
+
secret = this.getSecret();
|
|
52315
|
+
} else if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
|
|
52316
|
+
secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
|
|
52317
|
+
}
|
|
52318
|
+
}
|
|
52103
52319
|
secret = secret || this.getSecret();
|
|
52104
52320
|
bundle = bundle || this.getBundle();
|
|
52105
52321
|
let continuIdPosition = null;
|
|
@@ -52129,6 +52345,7 @@ ${operationTypes.join("\n")}
|
|
|
52129
52345
|
}));
|
|
52130
52346
|
return new Molecule({
|
|
52131
52347
|
secret,
|
|
52348
|
+
bundle,
|
|
52132
52349
|
sourceWallet,
|
|
52133
52350
|
remainderWallet: this.getRemainderWallet(),
|
|
52134
52351
|
cellSlug: this.getCellSlug(),
|
|
@@ -52175,8 +52392,9 @@ ${operationTypes.join("\n")}
|
|
|
52175
52392
|
async executeQuery(query, variables = null, context = {}) {
|
|
52176
52393
|
if (this.$__authToken && this.$__authToken.isExpired() && !this.$__authInProcess) {
|
|
52177
52394
|
this.log("info", "KnishIOClient::executeQuery() - Access token is expired. Getting new one...");
|
|
52395
|
+
const authSecret = this.$__secret || await this.retrieveSecret() || "";
|
|
52178
52396
|
await this.requestAuthToken({
|
|
52179
|
-
secret:
|
|
52397
|
+
secret: authSecret,
|
|
52180
52398
|
cellSlug: this.$__cellSlug,
|
|
52181
52399
|
encrypt: this.$__encrypt
|
|
52182
52400
|
});
|
|
@@ -52216,6 +52434,13 @@ ${operationTypes.join("\n")}
|
|
|
52216
52434
|
setSecret(secret) {
|
|
52217
52435
|
this.$__secret = secret;
|
|
52218
52436
|
this.$__bundle = generateBundleHash(secret);
|
|
52437
|
+
if (!this.$__secretStorage) {
|
|
52438
|
+
const memStorage = new MemorySecretStorageProvider();
|
|
52439
|
+
memStorage.storeSecret(this.$__bundle, secret);
|
|
52440
|
+
this.$__secretStorage = memStorage;
|
|
52441
|
+
} else {
|
|
52442
|
+
this.$__secretStorage.storeSecret(this.$__bundle, secret);
|
|
52443
|
+
}
|
|
52219
52444
|
}
|
|
52220
52445
|
/**
|
|
52221
52446
|
* Sets the auth token for this session
|
|
@@ -52399,6 +52624,9 @@ ${operationTypes.join("\n")}
|
|
|
52399
52624
|
if (secret === null && seed) {
|
|
52400
52625
|
secret = generateSecret(seed);
|
|
52401
52626
|
}
|
|
52627
|
+
if (secret === null && this.$__secretStorage && this.$__bundle) {
|
|
52628
|
+
secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
|
|
52629
|
+
}
|
|
52402
52630
|
if (cellSlug) {
|
|
52403
52631
|
this.setCellSlug(cellSlug);
|
|
52404
52632
|
}
|
|
@@ -53536,7 +53764,297 @@ ${operationTypes.join("\n")}
|
|
|
53536
53764
|
// src/index.ts
|
|
53537
53765
|
init_Response();
|
|
53538
53766
|
init_exception();
|
|
53539
|
-
|
|
53767
|
+
|
|
53768
|
+
// src/storage/WebCryptoSecretStorageProvider.ts
|
|
53769
|
+
init_SecretStorageException();
|
|
53770
|
+
var MemoryStorageBackend = class {
|
|
53771
|
+
store = /* @__PURE__ */ new Map();
|
|
53772
|
+
getItem(key) {
|
|
53773
|
+
return this.store.get(key) ?? null;
|
|
53774
|
+
}
|
|
53775
|
+
setItem(key, value2) {
|
|
53776
|
+
this.store.set(key, value2);
|
|
53777
|
+
}
|
|
53778
|
+
removeItem(key) {
|
|
53779
|
+
return this.store.delete(key);
|
|
53780
|
+
}
|
|
53781
|
+
keys() {
|
|
53782
|
+
return Array.from(this.store.keys());
|
|
53783
|
+
}
|
|
53784
|
+
};
|
|
53785
|
+
function uint8ArrayToBase642(bytes) {
|
|
53786
|
+
let binary = "";
|
|
53787
|
+
const len = bytes.byteLength;
|
|
53788
|
+
for (let i4 = 0; i4 < len; i4++) {
|
|
53789
|
+
const byte = bytes[i4];
|
|
53790
|
+
if (byte !== void 0) {
|
|
53791
|
+
binary += String.fromCharCode(byte);
|
|
53792
|
+
}
|
|
53793
|
+
}
|
|
53794
|
+
return btoa(binary);
|
|
53795
|
+
}
|
|
53796
|
+
function base64ToUint8Array2(base643) {
|
|
53797
|
+
const binary = atob(base643);
|
|
53798
|
+
const len = binary.length;
|
|
53799
|
+
const bytes = new Uint8Array(len);
|
|
53800
|
+
for (let i4 = 0; i4 < len; i4++) {
|
|
53801
|
+
bytes[i4] = binary.charCodeAt(i4);
|
|
53802
|
+
}
|
|
53803
|
+
return bytes;
|
|
53804
|
+
}
|
|
53805
|
+
var textEncoder2 = new TextEncoder();
|
|
53806
|
+
var textDecoder = new TextDecoder();
|
|
53807
|
+
var KEY_PREFIX = "knishio:secret:";
|
|
53808
|
+
var DEFAULT_ITERATIONS = 1e5;
|
|
53809
|
+
var WebCryptoSecretStorageProvider = class {
|
|
53810
|
+
providerType = "webcrypto-aes-gcm";
|
|
53811
|
+
backend;
|
|
53812
|
+
defaultPassphrase;
|
|
53813
|
+
hardwareBacked;
|
|
53814
|
+
constructor(options = {}) {
|
|
53815
|
+
this.backend = options.backend ?? new MemoryStorageBackend();
|
|
53816
|
+
this.defaultPassphrase = options.defaultPassphrase;
|
|
53817
|
+
this.hardwareBacked = options.hardwareBacked ?? false;
|
|
53818
|
+
}
|
|
53819
|
+
/**
|
|
53820
|
+
* Whether this provider is backed by hardware (e.g. WebAuthn PRF wrapping)
|
|
53821
|
+
*/
|
|
53822
|
+
isHardwareBacked() {
|
|
53823
|
+
return this.hardwareBacked;
|
|
53824
|
+
}
|
|
53825
|
+
/**
|
|
53826
|
+
* Check if WebCrypto subtle API is available
|
|
53827
|
+
*/
|
|
53828
|
+
async isAvailable() {
|
|
53829
|
+
return typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined";
|
|
53830
|
+
}
|
|
53831
|
+
/**
|
|
53832
|
+
* Derive an AES-GCM CryptoKey from a passphrase and salt using PBKDF2
|
|
53833
|
+
*/
|
|
53834
|
+
async deriveKey(passphrase, salt, iterations = DEFAULT_ITERATIONS) {
|
|
53835
|
+
if (!await this.isAvailable()) {
|
|
53836
|
+
throw exports.SecretStorageException.unavailable(this.providerType, "WebCrypto API is not available");
|
|
53837
|
+
}
|
|
53838
|
+
const passphraseBytes = textEncoder2.encode(passphrase);
|
|
53839
|
+
try {
|
|
53840
|
+
const baseKey = await globalThis.crypto.subtle.importKey(
|
|
53841
|
+
"raw",
|
|
53842
|
+
passphraseBytes,
|
|
53843
|
+
"PBKDF2",
|
|
53844
|
+
false,
|
|
53845
|
+
["deriveKey"]
|
|
53846
|
+
);
|
|
53847
|
+
return await globalThis.crypto.subtle.deriveKey(
|
|
53848
|
+
{
|
|
53849
|
+
name: "PBKDF2",
|
|
53850
|
+
salt,
|
|
53851
|
+
iterations,
|
|
53852
|
+
hash: "SHA-256"
|
|
53853
|
+
},
|
|
53854
|
+
baseKey,
|
|
53855
|
+
{ name: "AES-GCM", length: 256 },
|
|
53856
|
+
false,
|
|
53857
|
+
["encrypt", "decrypt"]
|
|
53858
|
+
);
|
|
53859
|
+
} finally {
|
|
53860
|
+
zeroizeBytes(passphraseBytes);
|
|
53861
|
+
}
|
|
53862
|
+
}
|
|
53863
|
+
/**
|
|
53864
|
+
* Store and encrypt a master secret
|
|
53865
|
+
*/
|
|
53866
|
+
async storeSecret(bundleHash, secret, options) {
|
|
53867
|
+
if (!bundleHash) {
|
|
53868
|
+
throw new exports.SecretStorageException("Bundle hash cannot be empty");
|
|
53869
|
+
}
|
|
53870
|
+
if (!secret) {
|
|
53871
|
+
throw new exports.SecretStorageException("Secret cannot be empty");
|
|
53872
|
+
}
|
|
53873
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
53874
|
+
if (!passphrase) {
|
|
53875
|
+
throw new exports.SecretStorageException("Passphrase required for envelope encryption");
|
|
53876
|
+
}
|
|
53877
|
+
const salt = new Uint8Array(16);
|
|
53878
|
+
const iv = new Uint8Array(12);
|
|
53879
|
+
globalThis.crypto.getRandomValues(salt);
|
|
53880
|
+
globalThis.crypto.getRandomValues(iv);
|
|
53881
|
+
const key = await this.deriveKey(passphrase, salt, DEFAULT_ITERATIONS);
|
|
53882
|
+
const secretBytes = textEncoder2.encode(secret);
|
|
53883
|
+
try {
|
|
53884
|
+
const encryptedBuffer = await globalThis.crypto.subtle.encrypt(
|
|
53885
|
+
{
|
|
53886
|
+
name: "AES-GCM",
|
|
53887
|
+
iv
|
|
53888
|
+
},
|
|
53889
|
+
key,
|
|
53890
|
+
secretBytes
|
|
53891
|
+
);
|
|
53892
|
+
const ciphertext = uint8ArrayToBase642(new Uint8Array(encryptedBuffer));
|
|
53893
|
+
const metadata = {
|
|
53894
|
+
bundleHash,
|
|
53895
|
+
label: options?.label,
|
|
53896
|
+
createdAt: Date.now(),
|
|
53897
|
+
hardwareBacked: this.hardwareBacked,
|
|
53898
|
+
providerType: this.providerType
|
|
53899
|
+
};
|
|
53900
|
+
const payload = {
|
|
53901
|
+
version: 1,
|
|
53902
|
+
ciphertext,
|
|
53903
|
+
iv: uint8ArrayToBase642(iv),
|
|
53904
|
+
salt: uint8ArrayToBase642(salt),
|
|
53905
|
+
algorithm: "AES-GCM",
|
|
53906
|
+
iterations: DEFAULT_ITERATIONS,
|
|
53907
|
+
metadata
|
|
53908
|
+
};
|
|
53909
|
+
await this.backend.setItem(`${KEY_PREFIX}${bundleHash}`, JSON.stringify(payload));
|
|
53910
|
+
} catch (err) {
|
|
53911
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
53912
|
+
throw new exports.SecretStorageException(`Encryption failed: ${msg}`);
|
|
53913
|
+
} finally {
|
|
53914
|
+
zeroizeBytes(secretBytes);
|
|
53915
|
+
}
|
|
53916
|
+
}
|
|
53917
|
+
/**
|
|
53918
|
+
* Retrieve and decrypt the master secret
|
|
53919
|
+
*/
|
|
53920
|
+
async retrieveSecret(bundleHash, options) {
|
|
53921
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
53922
|
+
if (!raw) {
|
|
53923
|
+
return null;
|
|
53924
|
+
}
|
|
53925
|
+
let payload;
|
|
53926
|
+
try {
|
|
53927
|
+
payload = JSON.parse(raw);
|
|
53928
|
+
} catch {
|
|
53929
|
+
throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
|
|
53930
|
+
}
|
|
53931
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
53932
|
+
if (!passphrase) {
|
|
53933
|
+
throw new exports.SecretStorageException("Passphrase required for secret decryption");
|
|
53934
|
+
}
|
|
53935
|
+
const salt = base64ToUint8Array2(payload.salt);
|
|
53936
|
+
const iv = base64ToUint8Array2(payload.iv);
|
|
53937
|
+
const ciphertext = base64ToUint8Array2(payload.ciphertext);
|
|
53938
|
+
try {
|
|
53939
|
+
const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
|
|
53940
|
+
const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
|
|
53941
|
+
{
|
|
53942
|
+
name: "AES-GCM",
|
|
53943
|
+
iv
|
|
53944
|
+
},
|
|
53945
|
+
key,
|
|
53946
|
+
ciphertext
|
|
53947
|
+
);
|
|
53948
|
+
const decryptedBytes = new Uint8Array(decryptedBuffer);
|
|
53949
|
+
try {
|
|
53950
|
+
return textDecoder.decode(decryptedBytes);
|
|
53951
|
+
} finally {
|
|
53952
|
+
zeroizeBytes(decryptedBytes);
|
|
53953
|
+
}
|
|
53954
|
+
} catch (err) {
|
|
53955
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
53956
|
+
throw exports.SecretStorageException.decryptionFailed(msg);
|
|
53957
|
+
}
|
|
53958
|
+
}
|
|
53959
|
+
/**
|
|
53960
|
+
* Delete a stored secret
|
|
53961
|
+
*/
|
|
53962
|
+
async deleteSecret(bundleHash) {
|
|
53963
|
+
const key = `${KEY_PREFIX}${bundleHash}`;
|
|
53964
|
+
const result = await this.backend.removeItem(key);
|
|
53965
|
+
return result !== false;
|
|
53966
|
+
}
|
|
53967
|
+
/**
|
|
53968
|
+
* Check if a secret exists
|
|
53969
|
+
*/
|
|
53970
|
+
async hasSecret(bundleHash) {
|
|
53971
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
53972
|
+
return raw !== null;
|
|
53973
|
+
}
|
|
53974
|
+
/**
|
|
53975
|
+
* List all stored secret metadata
|
|
53976
|
+
*/
|
|
53977
|
+
async listSecrets() {
|
|
53978
|
+
const keys = await this.backend.keys();
|
|
53979
|
+
const matchingKeys = keys.filter((k2) => k2.startsWith(KEY_PREFIX));
|
|
53980
|
+
const results = [];
|
|
53981
|
+
for (const key of matchingKeys) {
|
|
53982
|
+
const raw = await this.backend.getItem(key);
|
|
53983
|
+
if (raw) {
|
|
53984
|
+
try {
|
|
53985
|
+
const payload = JSON.parse(raw);
|
|
53986
|
+
if (payload.metadata) {
|
|
53987
|
+
results.push(payload.metadata);
|
|
53988
|
+
}
|
|
53989
|
+
} catch {
|
|
53990
|
+
}
|
|
53991
|
+
}
|
|
53992
|
+
}
|
|
53993
|
+
return results;
|
|
53994
|
+
}
|
|
53995
|
+
/**
|
|
53996
|
+
* Execute callback with unwrapped secret, zeroizing the decrypted buffer upon completion
|
|
53997
|
+
*/
|
|
53998
|
+
async withSecret(bundleHash, fn, options) {
|
|
53999
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
54000
|
+
if (!raw) {
|
|
54001
|
+
throw exports.SecretStorageException.notFound(bundleHash);
|
|
54002
|
+
}
|
|
54003
|
+
let payload;
|
|
54004
|
+
try {
|
|
54005
|
+
payload = JSON.parse(raw);
|
|
54006
|
+
} catch {
|
|
54007
|
+
throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
|
|
54008
|
+
}
|
|
54009
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
54010
|
+
if (!passphrase) {
|
|
54011
|
+
throw new exports.SecretStorageException("Passphrase required for secret decryption");
|
|
54012
|
+
}
|
|
54013
|
+
const salt = base64ToUint8Array2(payload.salt);
|
|
54014
|
+
const iv = base64ToUint8Array2(payload.iv);
|
|
54015
|
+
const ciphertext = base64ToUint8Array2(payload.ciphertext);
|
|
54016
|
+
try {
|
|
54017
|
+
const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
|
|
54018
|
+
const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
|
|
54019
|
+
{
|
|
54020
|
+
name: "AES-GCM",
|
|
54021
|
+
iv
|
|
54022
|
+
},
|
|
54023
|
+
key,
|
|
54024
|
+
ciphertext
|
|
54025
|
+
);
|
|
54026
|
+
const decryptedBytes = new Uint8Array(decryptedBuffer);
|
|
54027
|
+
return await withSecureBytes(decryptedBytes, async (bytes) => {
|
|
54028
|
+
const secretString = textDecoder.decode(bytes);
|
|
54029
|
+
return await fn(secretString);
|
|
54030
|
+
});
|
|
54031
|
+
} catch (err) {
|
|
54032
|
+
if (err instanceof exports.SecretStorageException) {
|
|
54033
|
+
throw err;
|
|
54034
|
+
}
|
|
54035
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
54036
|
+
throw exports.SecretStorageException.decryptionFailed(msg);
|
|
54037
|
+
}
|
|
54038
|
+
}
|
|
54039
|
+
};
|
|
54040
|
+
|
|
54041
|
+
// src/storage/index.ts
|
|
54042
|
+
function createDefaultSecretStorage(options = {}) {
|
|
54043
|
+
if (options.type === "memory") {
|
|
54044
|
+
return new MemorySecretStorageProvider();
|
|
54045
|
+
}
|
|
54046
|
+
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined") {
|
|
54047
|
+
return new WebCryptoSecretStorageProvider({
|
|
54048
|
+
backend: options.backend,
|
|
54049
|
+
defaultPassphrase: options.defaultPassphrase,
|
|
54050
|
+
hardwareBacked: options.hardwareBacked
|
|
54051
|
+
});
|
|
54052
|
+
}
|
|
54053
|
+
return new MemorySecretStorageProvider();
|
|
54054
|
+
}
|
|
54055
|
+
|
|
54056
|
+
// src/index.ts
|
|
54057
|
+
var SDK_VERSION = "0.9.7";
|
|
53540
54058
|
var SDK_NAME = "KnishIO-Client-TS";
|
|
53541
54059
|
var COMPATIBLE_SERVER_VERSIONS = [4, 5];
|
|
53542
54060
|
var SDK_INFO = {
|
|
@@ -53647,6 +54165,8 @@ ${operationTypes.join("\n")}
|
|
|
53647
54165
|
exports.GraphQLClient = GraphQLClient;
|
|
53648
54166
|
exports.KnishIO = KnishIO;
|
|
53649
54167
|
exports.KnishIOClient = KnishIOClient;
|
|
54168
|
+
exports.MemorySecretStorageProvider = MemorySecretStorageProvider;
|
|
54169
|
+
exports.MemoryStorageBackend = MemoryStorageBackend;
|
|
53650
54170
|
exports.Meta = Meta;
|
|
53651
54171
|
exports.Molecule = Molecule;
|
|
53652
54172
|
exports.Mutation = Mutation;
|
|
@@ -53692,6 +54212,7 @@ ${operationTypes.join("\n")}
|
|
|
53692
54212
|
exports.SDK_VERSION = SDK_VERSION;
|
|
53693
54213
|
exports.TokenUnit = TokenUnit;
|
|
53694
54214
|
exports.Wallet = Wallet;
|
|
54215
|
+
exports.WebCryptoSecretStorageProvider = WebCryptoSecretStorageProvider;
|
|
53695
54216
|
exports.base64ToHex = base64ToHex;
|
|
53696
54217
|
exports.bufferToHexString = bufferToHexString;
|
|
53697
54218
|
exports.capitalize = capitalize;
|
|
@@ -53699,8 +54220,10 @@ ${operationTypes.join("\n")}
|
|
|
53699
54220
|
exports.chunkArray = chunkArray;
|
|
53700
54221
|
exports.chunkSubstr = chunkSubstr;
|
|
53701
54222
|
exports.configureSDK = configureSDK;
|
|
54223
|
+
exports.constantTimeCompare = constantTimeCompare;
|
|
53702
54224
|
exports.convertToBase17 = convertToBase17;
|
|
53703
54225
|
exports.createBundleHash = createBundleHash;
|
|
54226
|
+
exports.createDefaultSecretStorage = createDefaultSecretStorage;
|
|
53704
54227
|
exports.createMolecularHash = createMolecularHash;
|
|
53705
54228
|
exports.createPosition = createPosition;
|
|
53706
54229
|
exports.createTokenSlug = createTokenSlug;
|
|
@@ -53742,6 +54265,9 @@ ${operationTypes.join("\n")}
|
|
|
53742
54265
|
exports.validateSecret = validateSecret;
|
|
53743
54266
|
exports.validateWalletAddress = validateWalletAddress;
|
|
53744
54267
|
exports.verifyOTSSignature = verifyOTSSignature;
|
|
54268
|
+
exports.withSecureBytes = withSecureBytes;
|
|
54269
|
+
exports.withSecureString = withSecureString;
|
|
54270
|
+
exports.zeroizeBytes = zeroizeBytes;
|
|
53745
54271
|
|
|
53746
54272
|
return exports;
|
|
53747
54273
|
|