@wishknish/knishio-client-ts 0.9.6 → 0.9.8
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 +557 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +128 -7
- package/dist/index.d.ts +128 -7
- package/dist/index.iife.js +557 -9
- package/dist/index.iife.js.map +1 -1
- package/dist/index.js +550 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/KnishIOClient.ts +85 -7
- 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/mutation/MutationProposeMolecule.ts +7 -4
- 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(),
|
|
@@ -50849,6 +50902,13 @@ ${operationTypes.join("\n")}
|
|
|
50849
50902
|
molecule() {
|
|
50850
50903
|
return this.$__molecule.toJSON ? this.$__molecule.toJSON() : this.$__molecule;
|
|
50851
50904
|
}
|
|
50905
|
+
/**
|
|
50906
|
+
* Fills the molecule with specific mutation data.
|
|
50907
|
+
* Subclasses override to build domain-specific atoms.
|
|
50908
|
+
* Default implementation is a no-op for pre-assembled molecules.
|
|
50909
|
+
*/
|
|
50910
|
+
fillMolecule(_params) {
|
|
50911
|
+
}
|
|
50852
50912
|
};
|
|
50853
50913
|
|
|
50854
50914
|
// src/response/ResponseCreateToken.ts
|
|
@@ -51827,9 +51887,134 @@ ${operationTypes.join("\n")}
|
|
|
51827
51887
|
|
|
51828
51888
|
// src/KnishIOClient.ts
|
|
51829
51889
|
init_exception();
|
|
51890
|
+
|
|
51891
|
+
// src/storage/MemorySecretStorageProvider.ts
|
|
51892
|
+
init_SecretStorageException();
|
|
51893
|
+
|
|
51894
|
+
// src/libraries/secureMemory.ts
|
|
51895
|
+
var textEncoder = new TextEncoder();
|
|
51896
|
+
function zeroizeBytes(buffer) {
|
|
51897
|
+
if (buffer instanceof Uint8Array) {
|
|
51898
|
+
buffer.fill(0);
|
|
51899
|
+
} else if (Array.isArray(buffer)) {
|
|
51900
|
+
for (let i4 = 0; i4 < buffer.length; i4++) {
|
|
51901
|
+
buffer[i4] = 0;
|
|
51902
|
+
}
|
|
51903
|
+
}
|
|
51904
|
+
}
|
|
51905
|
+
async function withSecureBytes(bytes, fn) {
|
|
51906
|
+
try {
|
|
51907
|
+
return await fn(bytes);
|
|
51908
|
+
} finally {
|
|
51909
|
+
zeroizeBytes(bytes);
|
|
51910
|
+
}
|
|
51911
|
+
}
|
|
51912
|
+
async function withSecureString(secret, fn) {
|
|
51913
|
+
const bytes = textEncoder.encode(secret);
|
|
51914
|
+
try {
|
|
51915
|
+
return await fn(secret);
|
|
51916
|
+
} finally {
|
|
51917
|
+
zeroizeBytes(bytes);
|
|
51918
|
+
}
|
|
51919
|
+
}
|
|
51920
|
+
function constantTimeCompare(a3, b2) {
|
|
51921
|
+
const bytesA = typeof a3 === "string" ? textEncoder.encode(a3) : a3;
|
|
51922
|
+
const bytesB = typeof b2 === "string" ? textEncoder.encode(b2) : b2;
|
|
51923
|
+
let result = bytesA.length === bytesB.length ? 0 : 1;
|
|
51924
|
+
const len = Math.min(bytesA.length, bytesB.length);
|
|
51925
|
+
for (let i4 = 0; i4 < len; i4++) {
|
|
51926
|
+
const byteA = bytesA[i4] ?? 0;
|
|
51927
|
+
const byteB = bytesB[i4] ?? 0;
|
|
51928
|
+
result |= byteA ^ byteB;
|
|
51929
|
+
}
|
|
51930
|
+
if (typeof a3 === "string") zeroizeBytes(bytesA);
|
|
51931
|
+
if (typeof b2 === "string") zeroizeBytes(bytesB);
|
|
51932
|
+
return result === 0;
|
|
51933
|
+
}
|
|
51934
|
+
|
|
51935
|
+
// src/storage/MemorySecretStorageProvider.ts
|
|
51936
|
+
var MemorySecretStorageProvider = class {
|
|
51937
|
+
providerType = "memory";
|
|
51938
|
+
secrets = /* @__PURE__ */ new Map();
|
|
51939
|
+
/**
|
|
51940
|
+
* Memory storage is not hardware backed
|
|
51941
|
+
*/
|
|
51942
|
+
isHardwareBacked() {
|
|
51943
|
+
return false;
|
|
51944
|
+
}
|
|
51945
|
+
/**
|
|
51946
|
+
* Memory storage is always available
|
|
51947
|
+
*/
|
|
51948
|
+
async isAvailable() {
|
|
51949
|
+
return true;
|
|
51950
|
+
}
|
|
51951
|
+
/**
|
|
51952
|
+
* Store a secret in memory
|
|
51953
|
+
*/
|
|
51954
|
+
async storeSecret(bundleHash, secret, options) {
|
|
51955
|
+
if (!bundleHash) {
|
|
51956
|
+
throw new exports.SecretStorageException("Bundle hash cannot be empty");
|
|
51957
|
+
}
|
|
51958
|
+
if (!secret) {
|
|
51959
|
+
throw new exports.SecretStorageException("Secret cannot be empty");
|
|
51960
|
+
}
|
|
51961
|
+
const metadata = {
|
|
51962
|
+
bundleHash,
|
|
51963
|
+
label: options?.label,
|
|
51964
|
+
createdAt: Date.now(),
|
|
51965
|
+
hardwareBacked: false,
|
|
51966
|
+
providerType: this.providerType
|
|
51967
|
+
};
|
|
51968
|
+
this.secrets.set(bundleHash, { secret, metadata });
|
|
51969
|
+
}
|
|
51970
|
+
/**
|
|
51971
|
+
* Retrieve a secret from memory
|
|
51972
|
+
*/
|
|
51973
|
+
async retrieveSecret(bundleHash) {
|
|
51974
|
+
const entry = this.secrets.get(bundleHash);
|
|
51975
|
+
return entry ? entry.secret : null;
|
|
51976
|
+
}
|
|
51977
|
+
/**
|
|
51978
|
+
* Delete a stored secret
|
|
51979
|
+
*/
|
|
51980
|
+
async deleteSecret(bundleHash) {
|
|
51981
|
+
return this.secrets.delete(bundleHash);
|
|
51982
|
+
}
|
|
51983
|
+
/**
|
|
51984
|
+
* Check if a secret exists
|
|
51985
|
+
*/
|
|
51986
|
+
async hasSecret(bundleHash) {
|
|
51987
|
+
return this.secrets.has(bundleHash);
|
|
51988
|
+
}
|
|
51989
|
+
/**
|
|
51990
|
+
* List all stored secret metadata
|
|
51991
|
+
*/
|
|
51992
|
+
async listSecrets() {
|
|
51993
|
+
return Array.from(this.secrets.values()).map((entry) => ({ ...entry.metadata }));
|
|
51994
|
+
}
|
|
51995
|
+
/**
|
|
51996
|
+
* Execute callback with unwrapped secret and ensure cleanup
|
|
51997
|
+
*/
|
|
51998
|
+
async withSecret(bundleHash, fn) {
|
|
51999
|
+
const entry = this.secrets.get(bundleHash);
|
|
52000
|
+
if (!entry) {
|
|
52001
|
+
throw exports.SecretStorageException.notFound(bundleHash);
|
|
52002
|
+
}
|
|
52003
|
+
return withSecureString(entry.secret, fn);
|
|
52004
|
+
}
|
|
52005
|
+
/**
|
|
52006
|
+
* Clear all secrets from memory
|
|
52007
|
+
*/
|
|
52008
|
+
clear() {
|
|
52009
|
+
this.secrets.clear();
|
|
52010
|
+
}
|
|
52011
|
+
};
|
|
52012
|
+
|
|
52013
|
+
// src/KnishIOClient.ts
|
|
51830
52014
|
var KnishIOClient = class {
|
|
51831
52015
|
$__secret = "";
|
|
51832
52016
|
$__bundle = "";
|
|
52017
|
+
$__secretStorage = null;
|
|
51833
52018
|
$__cellSlug = null;
|
|
51834
52019
|
$__encrypt = false;
|
|
51835
52020
|
$__uris = [];
|
|
@@ -51889,6 +52074,9 @@ ${operationTypes.join("\n")}
|
|
|
51889
52074
|
logging,
|
|
51890
52075
|
defaultRequestPolicy
|
|
51891
52076
|
});
|
|
52077
|
+
if (config2.secretStorage) {
|
|
52078
|
+
this.$__secretStorage = config2.secretStorage;
|
|
52079
|
+
}
|
|
51892
52080
|
}
|
|
51893
52081
|
/**
|
|
51894
52082
|
* Initializes a new Knish.IO client session
|
|
@@ -51990,6 +52178,7 @@ ${operationTypes.join("\n")}
|
|
|
51990
52178
|
reset() {
|
|
51991
52179
|
this.$__secret = "";
|
|
51992
52180
|
this.$__bundle = "";
|
|
52181
|
+
this.$__secretStorage = null;
|
|
51993
52182
|
this.$__encrypt = false;
|
|
51994
52183
|
this.$__cellSlug = null;
|
|
51995
52184
|
this.$__authToken = null;
|
|
@@ -52051,7 +52240,7 @@ ${operationTypes.join("\n")}
|
|
|
52051
52240
|
* Returns whether a secret is stored for this session
|
|
52052
52241
|
*/
|
|
52053
52242
|
hasSecret() {
|
|
52054
|
-
return !!this.$__secret && this.$__secret.length > 0;
|
|
52243
|
+
return !!this.$__secret && this.$__secret.length > 0 || !!this.$__secretStorage && !!this.$__bundle && this.$__bundle.length > 0;
|
|
52055
52244
|
}
|
|
52056
52245
|
/**
|
|
52057
52246
|
* Returns the stored secret
|
|
@@ -52062,6 +52251,33 @@ ${operationTypes.join("\n")}
|
|
|
52062
52251
|
}
|
|
52063
52252
|
return this.$__secret;
|
|
52064
52253
|
}
|
|
52254
|
+
/**
|
|
52255
|
+
* Sets the secret storage provider and optionally sets the bundle hash
|
|
52256
|
+
*/
|
|
52257
|
+
setSecretStorage(storage, bundleHash) {
|
|
52258
|
+
this.$__secretStorage = storage;
|
|
52259
|
+
if (bundleHash) {
|
|
52260
|
+
this.$__bundle = bundleHash;
|
|
52261
|
+
}
|
|
52262
|
+
}
|
|
52263
|
+
/**
|
|
52264
|
+
* Returns current secret storage provider
|
|
52265
|
+
*/
|
|
52266
|
+
getSecretStorage() {
|
|
52267
|
+
return this.$__secretStorage;
|
|
52268
|
+
}
|
|
52269
|
+
/**
|
|
52270
|
+
* Asynchronously retrieves the secret from storage or returns in-memory secret
|
|
52271
|
+
*/
|
|
52272
|
+
async retrieveSecret(options) {
|
|
52273
|
+
if (this.$__secret && this.$__secret.length > 0) {
|
|
52274
|
+
return this.$__secret;
|
|
52275
|
+
}
|
|
52276
|
+
if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
|
|
52277
|
+
return await this.$__secretStorage.retrieveSecret(this.$__bundle, options);
|
|
52278
|
+
}
|
|
52279
|
+
return null;
|
|
52280
|
+
}
|
|
52065
52281
|
/**
|
|
52066
52282
|
* Returns whether a bundle hash is being stored for this session
|
|
52067
52283
|
*/
|
|
@@ -52100,6 +52316,13 @@ ${operationTypes.join("\n")}
|
|
|
52100
52316
|
remainderWallet = null
|
|
52101
52317
|
} = {}) {
|
|
52102
52318
|
this.log("info", "KnishIOClient::createMolecule() - Creating a new molecule...");
|
|
52319
|
+
if (!secret) {
|
|
52320
|
+
if (this.$__secret && this.$__secret.length > 0) {
|
|
52321
|
+
secret = this.getSecret();
|
|
52322
|
+
} else if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
|
|
52323
|
+
secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
|
|
52324
|
+
}
|
|
52325
|
+
}
|
|
52103
52326
|
secret = secret || this.getSecret();
|
|
52104
52327
|
bundle = bundle || this.getBundle();
|
|
52105
52328
|
let continuIdPosition = null;
|
|
@@ -52129,6 +52352,7 @@ ${operationTypes.join("\n")}
|
|
|
52129
52352
|
}));
|
|
52130
52353
|
return new Molecule({
|
|
52131
52354
|
secret,
|
|
52355
|
+
bundle,
|
|
52132
52356
|
sourceWallet,
|
|
52133
52357
|
remainderWallet: this.getRemainderWallet(),
|
|
52134
52358
|
cellSlug: this.getCellSlug(),
|
|
@@ -52175,8 +52399,9 @@ ${operationTypes.join("\n")}
|
|
|
52175
52399
|
async executeQuery(query, variables = null, context = {}) {
|
|
52176
52400
|
if (this.$__authToken && this.$__authToken.isExpired() && !this.$__authInProcess) {
|
|
52177
52401
|
this.log("info", "KnishIOClient::executeQuery() - Access token is expired. Getting new one...");
|
|
52402
|
+
const authSecret = this.$__secret || await this.retrieveSecret() || "";
|
|
52178
52403
|
await this.requestAuthToken({
|
|
52179
|
-
secret:
|
|
52404
|
+
secret: authSecret,
|
|
52180
52405
|
cellSlug: this.$__cellSlug,
|
|
52181
52406
|
encrypt: this.$__encrypt
|
|
52182
52407
|
});
|
|
@@ -52216,6 +52441,13 @@ ${operationTypes.join("\n")}
|
|
|
52216
52441
|
setSecret(secret) {
|
|
52217
52442
|
this.$__secret = secret;
|
|
52218
52443
|
this.$__bundle = generateBundleHash(secret);
|
|
52444
|
+
if (!this.$__secretStorage) {
|
|
52445
|
+
const memStorage = new MemorySecretStorageProvider();
|
|
52446
|
+
memStorage.storeSecret(this.$__bundle, secret);
|
|
52447
|
+
this.$__secretStorage = memStorage;
|
|
52448
|
+
} else {
|
|
52449
|
+
this.$__secretStorage.storeSecret(this.$__bundle, secret);
|
|
52450
|
+
}
|
|
52219
52451
|
}
|
|
52220
52452
|
/**
|
|
52221
52453
|
* Sets the auth token for this session
|
|
@@ -52399,6 +52631,9 @@ ${operationTypes.join("\n")}
|
|
|
52399
52631
|
if (secret === null && seed) {
|
|
52400
52632
|
secret = generateSecret(seed);
|
|
52401
52633
|
}
|
|
52634
|
+
if (secret === null && this.$__secretStorage && this.$__bundle) {
|
|
52635
|
+
secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
|
|
52636
|
+
}
|
|
52402
52637
|
if (cellSlug) {
|
|
52403
52638
|
this.setCellSlug(cellSlug);
|
|
52404
52639
|
}
|
|
@@ -53244,15 +53479,30 @@ ${operationTypes.join("\n")}
|
|
|
53244
53479
|
async createPolicy({
|
|
53245
53480
|
metaType,
|
|
53246
53481
|
metaId,
|
|
53247
|
-
policy =
|
|
53482
|
+
policy = {}
|
|
53248
53483
|
}) {
|
|
53249
53484
|
this.log("info", `KnishIOClient::createPolicy() - Creating policy for ${metaType}:${metaId}...`);
|
|
53250
|
-
|
|
53485
|
+
const molecule = await this.createMolecule({});
|
|
53486
|
+
molecule.addPolicyAtom({
|
|
53251
53487
|
metaType,
|
|
53252
53488
|
metaId,
|
|
53253
|
-
meta:
|
|
53254
|
-
policy
|
|
53489
|
+
meta: {},
|
|
53490
|
+
policy: policy || {}
|
|
53255
53491
|
});
|
|
53492
|
+
molecule.addContinuIdAtom();
|
|
53493
|
+
molecule.sign({
|
|
53494
|
+
bundle: this.getBundle()
|
|
53495
|
+
});
|
|
53496
|
+
molecule.check();
|
|
53497
|
+
const query = await this.createMoleculeMutation({
|
|
53498
|
+
mutationClass: MutationProposeMolecule,
|
|
53499
|
+
molecule
|
|
53500
|
+
});
|
|
53501
|
+
const response = await this.executeQuery(query);
|
|
53502
|
+
if (!response) {
|
|
53503
|
+
throw new CodeException("Policy creation failed");
|
|
53504
|
+
}
|
|
53505
|
+
return response;
|
|
53256
53506
|
}
|
|
53257
53507
|
/**
|
|
53258
53508
|
* Create an identifier
|
|
@@ -53536,7 +53786,297 @@ ${operationTypes.join("\n")}
|
|
|
53536
53786
|
// src/index.ts
|
|
53537
53787
|
init_Response();
|
|
53538
53788
|
init_exception();
|
|
53539
|
-
|
|
53789
|
+
|
|
53790
|
+
// src/storage/WebCryptoSecretStorageProvider.ts
|
|
53791
|
+
init_SecretStorageException();
|
|
53792
|
+
var MemoryStorageBackend = class {
|
|
53793
|
+
store = /* @__PURE__ */ new Map();
|
|
53794
|
+
getItem(key) {
|
|
53795
|
+
return this.store.get(key) ?? null;
|
|
53796
|
+
}
|
|
53797
|
+
setItem(key, value2) {
|
|
53798
|
+
this.store.set(key, value2);
|
|
53799
|
+
}
|
|
53800
|
+
removeItem(key) {
|
|
53801
|
+
return this.store.delete(key);
|
|
53802
|
+
}
|
|
53803
|
+
keys() {
|
|
53804
|
+
return Array.from(this.store.keys());
|
|
53805
|
+
}
|
|
53806
|
+
};
|
|
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
|
+
var textDecoder = new TextDecoder();
|
|
53829
|
+
var KEY_PREFIX = "knishio:secret:";
|
|
53830
|
+
var DEFAULT_ITERATIONS = 1e5;
|
|
53831
|
+
var WebCryptoSecretStorageProvider = class {
|
|
53832
|
+
providerType = "webcrypto-aes-gcm";
|
|
53833
|
+
backend;
|
|
53834
|
+
defaultPassphrase;
|
|
53835
|
+
hardwareBacked;
|
|
53836
|
+
constructor(options = {}) {
|
|
53837
|
+
this.backend = options.backend ?? new MemoryStorageBackend();
|
|
53838
|
+
this.defaultPassphrase = options.defaultPassphrase;
|
|
53839
|
+
this.hardwareBacked = options.hardwareBacked ?? false;
|
|
53840
|
+
}
|
|
53841
|
+
/**
|
|
53842
|
+
* Whether this provider is backed by hardware (e.g. WebAuthn PRF wrapping)
|
|
53843
|
+
*/
|
|
53844
|
+
isHardwareBacked() {
|
|
53845
|
+
return this.hardwareBacked;
|
|
53846
|
+
}
|
|
53847
|
+
/**
|
|
53848
|
+
* Check if WebCrypto subtle API is available
|
|
53849
|
+
*/
|
|
53850
|
+
async isAvailable() {
|
|
53851
|
+
return typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined";
|
|
53852
|
+
}
|
|
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
|
+
/**
|
|
53886
|
+
* Store and encrypt a master secret
|
|
53887
|
+
*/
|
|
53888
|
+
async storeSecret(bundleHash, secret, options) {
|
|
53889
|
+
if (!bundleHash) {
|
|
53890
|
+
throw new exports.SecretStorageException("Bundle hash cannot be empty");
|
|
53891
|
+
}
|
|
53892
|
+
if (!secret) {
|
|
53893
|
+
throw new exports.SecretStorageException("Secret cannot be empty");
|
|
53894
|
+
}
|
|
53895
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
53896
|
+
if (!passphrase) {
|
|
53897
|
+
throw new exports.SecretStorageException("Passphrase required for envelope encryption");
|
|
53898
|
+
}
|
|
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);
|
|
53905
|
+
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
|
+
const metadata = {
|
|
53916
|
+
bundleHash,
|
|
53917
|
+
label: options?.label,
|
|
53918
|
+
createdAt: Date.now(),
|
|
53919
|
+
hardwareBacked: this.hardwareBacked,
|
|
53920
|
+
providerType: this.providerType
|
|
53921
|
+
};
|
|
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
|
+
};
|
|
53931
|
+
await this.backend.setItem(`${KEY_PREFIX}${bundleHash}`, JSON.stringify(payload));
|
|
53932
|
+
} catch (err) {
|
|
53933
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
53934
|
+
throw new exports.SecretStorageException(`Encryption failed: ${msg}`);
|
|
53935
|
+
} finally {
|
|
53936
|
+
zeroizeBytes(secretBytes);
|
|
53937
|
+
}
|
|
53938
|
+
}
|
|
53939
|
+
/**
|
|
53940
|
+
* Retrieve and decrypt the master secret
|
|
53941
|
+
*/
|
|
53942
|
+
async retrieveSecret(bundleHash, options) {
|
|
53943
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
53944
|
+
if (!raw) {
|
|
53945
|
+
return null;
|
|
53946
|
+
}
|
|
53947
|
+
let payload;
|
|
53948
|
+
try {
|
|
53949
|
+
payload = JSON.parse(raw);
|
|
53950
|
+
} catch {
|
|
53951
|
+
throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
|
|
53952
|
+
}
|
|
53953
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
53954
|
+
if (!passphrase) {
|
|
53955
|
+
throw new exports.SecretStorageException("Passphrase required for secret decryption");
|
|
53956
|
+
}
|
|
53957
|
+
const salt = base64ToUint8Array2(payload.salt);
|
|
53958
|
+
const iv = base64ToUint8Array2(payload.iv);
|
|
53959
|
+
const ciphertext = base64ToUint8Array2(payload.ciphertext);
|
|
53960
|
+
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);
|
|
53971
|
+
try {
|
|
53972
|
+
return textDecoder.decode(decryptedBytes);
|
|
53973
|
+
} finally {
|
|
53974
|
+
zeroizeBytes(decryptedBytes);
|
|
53975
|
+
}
|
|
53976
|
+
} catch (err) {
|
|
53977
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
53978
|
+
throw exports.SecretStorageException.decryptionFailed(msg);
|
|
53979
|
+
}
|
|
53980
|
+
}
|
|
53981
|
+
/**
|
|
53982
|
+
* Delete a stored secret
|
|
53983
|
+
*/
|
|
53984
|
+
async deleteSecret(bundleHash) {
|
|
53985
|
+
const key = `${KEY_PREFIX}${bundleHash}`;
|
|
53986
|
+
const result = await this.backend.removeItem(key);
|
|
53987
|
+
return result !== false;
|
|
53988
|
+
}
|
|
53989
|
+
/**
|
|
53990
|
+
* Check if a secret exists
|
|
53991
|
+
*/
|
|
53992
|
+
async hasSecret(bundleHash) {
|
|
53993
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
53994
|
+
return raw !== null;
|
|
53995
|
+
}
|
|
53996
|
+
/**
|
|
53997
|
+
* List all stored secret metadata
|
|
53998
|
+
*/
|
|
53999
|
+
async listSecrets() {
|
|
54000
|
+
const keys = await this.backend.keys();
|
|
54001
|
+
const matchingKeys = keys.filter((k2) => k2.startsWith(KEY_PREFIX));
|
|
54002
|
+
const results = [];
|
|
54003
|
+
for (const key of matchingKeys) {
|
|
54004
|
+
const raw = await this.backend.getItem(key);
|
|
54005
|
+
if (raw) {
|
|
54006
|
+
try {
|
|
54007
|
+
const payload = JSON.parse(raw);
|
|
54008
|
+
if (payload.metadata) {
|
|
54009
|
+
results.push(payload.metadata);
|
|
54010
|
+
}
|
|
54011
|
+
} catch {
|
|
54012
|
+
}
|
|
54013
|
+
}
|
|
54014
|
+
}
|
|
54015
|
+
return results;
|
|
54016
|
+
}
|
|
54017
|
+
/**
|
|
54018
|
+
* Execute callback with unwrapped secret, zeroizing the decrypted buffer upon completion
|
|
54019
|
+
*/
|
|
54020
|
+
async withSecret(bundleHash, fn, options) {
|
|
54021
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
54022
|
+
if (!raw) {
|
|
54023
|
+
throw exports.SecretStorageException.notFound(bundleHash);
|
|
54024
|
+
}
|
|
54025
|
+
let payload;
|
|
54026
|
+
try {
|
|
54027
|
+
payload = JSON.parse(raw);
|
|
54028
|
+
} catch {
|
|
54029
|
+
throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
|
|
54030
|
+
}
|
|
54031
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
54032
|
+
if (!passphrase) {
|
|
54033
|
+
throw new exports.SecretStorageException("Passphrase required for secret decryption");
|
|
54034
|
+
}
|
|
54035
|
+
const salt = base64ToUint8Array2(payload.salt);
|
|
54036
|
+
const iv = base64ToUint8Array2(payload.iv);
|
|
54037
|
+
const ciphertext = base64ToUint8Array2(payload.ciphertext);
|
|
54038
|
+
try {
|
|
54039
|
+
const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
|
|
54040
|
+
const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
|
|
54041
|
+
{
|
|
54042
|
+
name: "AES-GCM",
|
|
54043
|
+
iv
|
|
54044
|
+
},
|
|
54045
|
+
key,
|
|
54046
|
+
ciphertext
|
|
54047
|
+
);
|
|
54048
|
+
const decryptedBytes = new Uint8Array(decryptedBuffer);
|
|
54049
|
+
return await withSecureBytes(decryptedBytes, async (bytes) => {
|
|
54050
|
+
const secretString = textDecoder.decode(bytes);
|
|
54051
|
+
return await fn(secretString);
|
|
54052
|
+
});
|
|
54053
|
+
} catch (err) {
|
|
54054
|
+
if (err instanceof exports.SecretStorageException) {
|
|
54055
|
+
throw err;
|
|
54056
|
+
}
|
|
54057
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
54058
|
+
throw exports.SecretStorageException.decryptionFailed(msg);
|
|
54059
|
+
}
|
|
54060
|
+
}
|
|
54061
|
+
};
|
|
54062
|
+
|
|
54063
|
+
// src/storage/index.ts
|
|
54064
|
+
function createDefaultSecretStorage(options = {}) {
|
|
54065
|
+
if (options.type === "memory") {
|
|
54066
|
+
return new MemorySecretStorageProvider();
|
|
54067
|
+
}
|
|
54068
|
+
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined") {
|
|
54069
|
+
return new WebCryptoSecretStorageProvider({
|
|
54070
|
+
backend: options.backend,
|
|
54071
|
+
defaultPassphrase: options.defaultPassphrase,
|
|
54072
|
+
hardwareBacked: options.hardwareBacked
|
|
54073
|
+
});
|
|
54074
|
+
}
|
|
54075
|
+
return new MemorySecretStorageProvider();
|
|
54076
|
+
}
|
|
54077
|
+
|
|
54078
|
+
// src/index.ts
|
|
54079
|
+
var SDK_VERSION = "0.9.8";
|
|
53540
54080
|
var SDK_NAME = "KnishIO-Client-TS";
|
|
53541
54081
|
var COMPATIBLE_SERVER_VERSIONS = [4, 5];
|
|
53542
54082
|
var SDK_INFO = {
|
|
@@ -53647,6 +54187,8 @@ ${operationTypes.join("\n")}
|
|
|
53647
54187
|
exports.GraphQLClient = GraphQLClient;
|
|
53648
54188
|
exports.KnishIO = KnishIO;
|
|
53649
54189
|
exports.KnishIOClient = KnishIOClient;
|
|
54190
|
+
exports.MemorySecretStorageProvider = MemorySecretStorageProvider;
|
|
54191
|
+
exports.MemoryStorageBackend = MemoryStorageBackend;
|
|
53650
54192
|
exports.Meta = Meta;
|
|
53651
54193
|
exports.Molecule = Molecule;
|
|
53652
54194
|
exports.Mutation = Mutation;
|
|
@@ -53692,6 +54234,7 @@ ${operationTypes.join("\n")}
|
|
|
53692
54234
|
exports.SDK_VERSION = SDK_VERSION;
|
|
53693
54235
|
exports.TokenUnit = TokenUnit;
|
|
53694
54236
|
exports.Wallet = Wallet;
|
|
54237
|
+
exports.WebCryptoSecretStorageProvider = WebCryptoSecretStorageProvider;
|
|
53695
54238
|
exports.base64ToHex = base64ToHex;
|
|
53696
54239
|
exports.bufferToHexString = bufferToHexString;
|
|
53697
54240
|
exports.capitalize = capitalize;
|
|
@@ -53699,8 +54242,10 @@ ${operationTypes.join("\n")}
|
|
|
53699
54242
|
exports.chunkArray = chunkArray;
|
|
53700
54243
|
exports.chunkSubstr = chunkSubstr;
|
|
53701
54244
|
exports.configureSDK = configureSDK;
|
|
54245
|
+
exports.constantTimeCompare = constantTimeCompare;
|
|
53702
54246
|
exports.convertToBase17 = convertToBase17;
|
|
53703
54247
|
exports.createBundleHash = createBundleHash;
|
|
54248
|
+
exports.createDefaultSecretStorage = createDefaultSecretStorage;
|
|
53704
54249
|
exports.createMolecularHash = createMolecularHash;
|
|
53705
54250
|
exports.createPosition = createPosition;
|
|
53706
54251
|
exports.createTokenSlug = createTokenSlug;
|
|
@@ -53742,6 +54287,9 @@ ${operationTypes.join("\n")}
|
|
|
53742
54287
|
exports.validateSecret = validateSecret;
|
|
53743
54288
|
exports.validateWalletAddress = validateWalletAddress;
|
|
53744
54289
|
exports.verifyOTSSignature = verifyOTSSignature;
|
|
54290
|
+
exports.withSecureBytes = withSecureBytes;
|
|
54291
|
+
exports.withSecureString = withSecureString;
|
|
54292
|
+
exports.zeroizeBytes = zeroizeBytes;
|
|
53745
54293
|
|
|
53746
54294
|
return exports;
|
|
53747
54295
|
|