@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.cjs
CHANGED
|
@@ -527,6 +527,55 @@ var init_TransferBalanceException = __esm({
|
|
|
527
527
|
}
|
|
528
528
|
});
|
|
529
529
|
|
|
530
|
+
// src/exception/SecretStorageException.ts
|
|
531
|
+
exports.SecretStorageException = void 0;
|
|
532
|
+
var init_SecretStorageException = __esm({
|
|
533
|
+
"src/exception/SecretStorageException.ts"() {
|
|
534
|
+
init_BaseException();
|
|
535
|
+
exports.SecretStorageException = class _SecretStorageException extends exports.BaseException {
|
|
536
|
+
constructor(message = "Secret storage operation failed", options = {}) {
|
|
537
|
+
super("WALLET_CREDENTIAL_ERROR", message, {
|
|
538
|
+
code: "SECRET_STORAGE_ERROR",
|
|
539
|
+
...options
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Secret not found for the requested bundle hash
|
|
544
|
+
*/
|
|
545
|
+
static notFound(bundleHash) {
|
|
546
|
+
return new _SecretStorageException(`Secret not found for bundle: ${bundleHash}`, {
|
|
547
|
+
code: "SECRET_NOT_FOUND",
|
|
548
|
+
details: { bundleHash }
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Decryption failed (wrong passphrase or corrupted payload)
|
|
553
|
+
*/
|
|
554
|
+
static decryptionFailed(reason) {
|
|
555
|
+
return new _SecretStorageException(
|
|
556
|
+
`Failed to decrypt master secret: ${reason || "Invalid passphrase or corrupted ciphertext"}`,
|
|
557
|
+
{
|
|
558
|
+
code: "DECRYPTION_FAILED",
|
|
559
|
+
details: { reason }
|
|
560
|
+
}
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Provider is unavailable in current platform
|
|
565
|
+
*/
|
|
566
|
+
static unavailable(provider, reason) {
|
|
567
|
+
return new _SecretStorageException(
|
|
568
|
+
`Secret storage provider '${provider}' is unavailable: ${reason || "Hardware or API not accessible"}`,
|
|
569
|
+
{
|
|
570
|
+
code: "STORAGE_UNAVAILABLE",
|
|
571
|
+
details: { provider, reason }
|
|
572
|
+
}
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
|
|
530
579
|
// src/exception/InvalidResponseException.ts
|
|
531
580
|
exports.InvalidResponseException = void 0;
|
|
532
581
|
var init_InvalidResponseException = __esm({
|
|
@@ -989,6 +1038,7 @@ var init_exception = __esm({
|
|
|
989
1038
|
init_SignatureMismatchException();
|
|
990
1039
|
init_TransferBalanceException();
|
|
991
1040
|
init_WalletCredentialException();
|
|
1041
|
+
init_SecretStorageException();
|
|
992
1042
|
init_InvalidResponseException();
|
|
993
1043
|
init_BalanceInsufficientException();
|
|
994
1044
|
init_BatchIdException();
|
|
@@ -4115,7 +4165,8 @@ zod.z.object({
|
|
|
4115
4165
|
socket: zod.z.unknown().optional(),
|
|
4116
4166
|
serverSdkVersion: zod.z.number().int().min(1).optional(),
|
|
4117
4167
|
logging: zod.z.boolean().optional(),
|
|
4118
|
-
defaultRequestPolicy: zod.z.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional()
|
|
4168
|
+
defaultRequestPolicy: zod.z.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional(),
|
|
4169
|
+
secretStorage: zod.z.unknown().optional()
|
|
4119
4170
|
}).strict();
|
|
4120
4171
|
zod.z.object({
|
|
4121
4172
|
token: zod.z.string().min(1, "Auth token cannot be empty"),
|
|
@@ -6915,7 +6966,9 @@ var KnishIOClientConfigSchema2 = zod.z.object({
|
|
|
6915
6966
|
// Optional default urql request policy for reads (server/sync clients pass
|
|
6916
6967
|
// 'network-only'). Permitted by the strict schema so the constructor option
|
|
6917
6968
|
// isn't rejected.
|
|
6918
|
-
defaultRequestPolicy: zod.z.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional()
|
|
6969
|
+
defaultRequestPolicy: zod.z.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional(),
|
|
6970
|
+
// Pluggable hardware envelope encryption secret storage provider
|
|
6971
|
+
secretStorage: zod.z.unknown().optional()
|
|
6919
6972
|
}).strict();
|
|
6920
6973
|
var EnvironmentConfigSchema = zod.z.object({
|
|
6921
6974
|
NODE_ENV: zod.z.enum(["development", "production", "test"]).optional(),
|
|
@@ -9489,6 +9542,13 @@ var MutationProposeMolecule = class extends Mutation {
|
|
|
9489
9542
|
molecule() {
|
|
9490
9543
|
return this.$__molecule.toJSON ? this.$__molecule.toJSON() : this.$__molecule;
|
|
9491
9544
|
}
|
|
9545
|
+
/**
|
|
9546
|
+
* Fills the molecule with specific mutation data.
|
|
9547
|
+
* Subclasses override to build domain-specific atoms.
|
|
9548
|
+
* Default implementation is a no-op for pre-assembled molecules.
|
|
9549
|
+
*/
|
|
9550
|
+
fillMolecule(_params) {
|
|
9551
|
+
}
|
|
9492
9552
|
};
|
|
9493
9553
|
|
|
9494
9554
|
// src/response/ResponseCreateToken.ts
|
|
@@ -10453,9 +10513,134 @@ var ActiveSessionSubscribe = class extends Subscribe {
|
|
|
10453
10513
|
|
|
10454
10514
|
// src/KnishIOClient.ts
|
|
10455
10515
|
init_exception();
|
|
10516
|
+
|
|
10517
|
+
// src/storage/MemorySecretStorageProvider.ts
|
|
10518
|
+
init_SecretStorageException();
|
|
10519
|
+
|
|
10520
|
+
// src/libraries/secureMemory.ts
|
|
10521
|
+
var textEncoder = new TextEncoder();
|
|
10522
|
+
function zeroizeBytes(buffer) {
|
|
10523
|
+
if (buffer instanceof Uint8Array) {
|
|
10524
|
+
buffer.fill(0);
|
|
10525
|
+
} else if (Array.isArray(buffer)) {
|
|
10526
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
10527
|
+
buffer[i] = 0;
|
|
10528
|
+
}
|
|
10529
|
+
}
|
|
10530
|
+
}
|
|
10531
|
+
async function withSecureBytes(bytes, fn) {
|
|
10532
|
+
try {
|
|
10533
|
+
return await fn(bytes);
|
|
10534
|
+
} finally {
|
|
10535
|
+
zeroizeBytes(bytes);
|
|
10536
|
+
}
|
|
10537
|
+
}
|
|
10538
|
+
async function withSecureString(secret, fn) {
|
|
10539
|
+
const bytes = textEncoder.encode(secret);
|
|
10540
|
+
try {
|
|
10541
|
+
return await fn(secret);
|
|
10542
|
+
} finally {
|
|
10543
|
+
zeroizeBytes(bytes);
|
|
10544
|
+
}
|
|
10545
|
+
}
|
|
10546
|
+
function constantTimeCompare(a, b) {
|
|
10547
|
+
const bytesA = typeof a === "string" ? textEncoder.encode(a) : a;
|
|
10548
|
+
const bytesB = typeof b === "string" ? textEncoder.encode(b) : b;
|
|
10549
|
+
let result = bytesA.length === bytesB.length ? 0 : 1;
|
|
10550
|
+
const len = Math.min(bytesA.length, bytesB.length);
|
|
10551
|
+
for (let i = 0; i < len; i++) {
|
|
10552
|
+
const byteA = bytesA[i] ?? 0;
|
|
10553
|
+
const byteB = bytesB[i] ?? 0;
|
|
10554
|
+
result |= byteA ^ byteB;
|
|
10555
|
+
}
|
|
10556
|
+
if (typeof a === "string") zeroizeBytes(bytesA);
|
|
10557
|
+
if (typeof b === "string") zeroizeBytes(bytesB);
|
|
10558
|
+
return result === 0;
|
|
10559
|
+
}
|
|
10560
|
+
|
|
10561
|
+
// src/storage/MemorySecretStorageProvider.ts
|
|
10562
|
+
var MemorySecretStorageProvider = class {
|
|
10563
|
+
providerType = "memory";
|
|
10564
|
+
secrets = /* @__PURE__ */ new Map();
|
|
10565
|
+
/**
|
|
10566
|
+
* Memory storage is not hardware backed
|
|
10567
|
+
*/
|
|
10568
|
+
isHardwareBacked() {
|
|
10569
|
+
return false;
|
|
10570
|
+
}
|
|
10571
|
+
/**
|
|
10572
|
+
* Memory storage is always available
|
|
10573
|
+
*/
|
|
10574
|
+
async isAvailable() {
|
|
10575
|
+
return true;
|
|
10576
|
+
}
|
|
10577
|
+
/**
|
|
10578
|
+
* Store a secret in memory
|
|
10579
|
+
*/
|
|
10580
|
+
async storeSecret(bundleHash, secret, options) {
|
|
10581
|
+
if (!bundleHash) {
|
|
10582
|
+
throw new exports.SecretStorageException("Bundle hash cannot be empty");
|
|
10583
|
+
}
|
|
10584
|
+
if (!secret) {
|
|
10585
|
+
throw new exports.SecretStorageException("Secret cannot be empty");
|
|
10586
|
+
}
|
|
10587
|
+
const metadata = {
|
|
10588
|
+
bundleHash,
|
|
10589
|
+
label: options?.label,
|
|
10590
|
+
createdAt: Date.now(),
|
|
10591
|
+
hardwareBacked: false,
|
|
10592
|
+
providerType: this.providerType
|
|
10593
|
+
};
|
|
10594
|
+
this.secrets.set(bundleHash, { secret, metadata });
|
|
10595
|
+
}
|
|
10596
|
+
/**
|
|
10597
|
+
* Retrieve a secret from memory
|
|
10598
|
+
*/
|
|
10599
|
+
async retrieveSecret(bundleHash) {
|
|
10600
|
+
const entry = this.secrets.get(bundleHash);
|
|
10601
|
+
return entry ? entry.secret : null;
|
|
10602
|
+
}
|
|
10603
|
+
/**
|
|
10604
|
+
* Delete a stored secret
|
|
10605
|
+
*/
|
|
10606
|
+
async deleteSecret(bundleHash) {
|
|
10607
|
+
return this.secrets.delete(bundleHash);
|
|
10608
|
+
}
|
|
10609
|
+
/**
|
|
10610
|
+
* Check if a secret exists
|
|
10611
|
+
*/
|
|
10612
|
+
async hasSecret(bundleHash) {
|
|
10613
|
+
return this.secrets.has(bundleHash);
|
|
10614
|
+
}
|
|
10615
|
+
/**
|
|
10616
|
+
* List all stored secret metadata
|
|
10617
|
+
*/
|
|
10618
|
+
async listSecrets() {
|
|
10619
|
+
return Array.from(this.secrets.values()).map((entry) => ({ ...entry.metadata }));
|
|
10620
|
+
}
|
|
10621
|
+
/**
|
|
10622
|
+
* Execute callback with unwrapped secret and ensure cleanup
|
|
10623
|
+
*/
|
|
10624
|
+
async withSecret(bundleHash, fn) {
|
|
10625
|
+
const entry = this.secrets.get(bundleHash);
|
|
10626
|
+
if (!entry) {
|
|
10627
|
+
throw exports.SecretStorageException.notFound(bundleHash);
|
|
10628
|
+
}
|
|
10629
|
+
return withSecureString(entry.secret, fn);
|
|
10630
|
+
}
|
|
10631
|
+
/**
|
|
10632
|
+
* Clear all secrets from memory
|
|
10633
|
+
*/
|
|
10634
|
+
clear() {
|
|
10635
|
+
this.secrets.clear();
|
|
10636
|
+
}
|
|
10637
|
+
};
|
|
10638
|
+
|
|
10639
|
+
// src/KnishIOClient.ts
|
|
10456
10640
|
var KnishIOClient = class {
|
|
10457
10641
|
$__secret = "";
|
|
10458
10642
|
$__bundle = "";
|
|
10643
|
+
$__secretStorage = null;
|
|
10459
10644
|
$__cellSlug = null;
|
|
10460
10645
|
$__encrypt = false;
|
|
10461
10646
|
$__uris = [];
|
|
@@ -10515,6 +10700,9 @@ var KnishIOClient = class {
|
|
|
10515
10700
|
logging,
|
|
10516
10701
|
defaultRequestPolicy
|
|
10517
10702
|
});
|
|
10703
|
+
if (config.secretStorage) {
|
|
10704
|
+
this.$__secretStorage = config.secretStorage;
|
|
10705
|
+
}
|
|
10518
10706
|
}
|
|
10519
10707
|
/**
|
|
10520
10708
|
* Initializes a new Knish.IO client session
|
|
@@ -10616,6 +10804,7 @@ var KnishIOClient = class {
|
|
|
10616
10804
|
reset() {
|
|
10617
10805
|
this.$__secret = "";
|
|
10618
10806
|
this.$__bundle = "";
|
|
10807
|
+
this.$__secretStorage = null;
|
|
10619
10808
|
this.$__encrypt = false;
|
|
10620
10809
|
this.$__cellSlug = null;
|
|
10621
10810
|
this.$__authToken = null;
|
|
@@ -10677,7 +10866,7 @@ var KnishIOClient = class {
|
|
|
10677
10866
|
* Returns whether a secret is stored for this session
|
|
10678
10867
|
*/
|
|
10679
10868
|
hasSecret() {
|
|
10680
|
-
return !!this.$__secret && this.$__secret.length > 0;
|
|
10869
|
+
return !!this.$__secret && this.$__secret.length > 0 || !!this.$__secretStorage && !!this.$__bundle && this.$__bundle.length > 0;
|
|
10681
10870
|
}
|
|
10682
10871
|
/**
|
|
10683
10872
|
* Returns the stored secret
|
|
@@ -10688,6 +10877,33 @@ var KnishIOClient = class {
|
|
|
10688
10877
|
}
|
|
10689
10878
|
return this.$__secret;
|
|
10690
10879
|
}
|
|
10880
|
+
/**
|
|
10881
|
+
* Sets the secret storage provider and optionally sets the bundle hash
|
|
10882
|
+
*/
|
|
10883
|
+
setSecretStorage(storage, bundleHash) {
|
|
10884
|
+
this.$__secretStorage = storage;
|
|
10885
|
+
if (bundleHash) {
|
|
10886
|
+
this.$__bundle = bundleHash;
|
|
10887
|
+
}
|
|
10888
|
+
}
|
|
10889
|
+
/**
|
|
10890
|
+
* Returns current secret storage provider
|
|
10891
|
+
*/
|
|
10892
|
+
getSecretStorage() {
|
|
10893
|
+
return this.$__secretStorage;
|
|
10894
|
+
}
|
|
10895
|
+
/**
|
|
10896
|
+
* Asynchronously retrieves the secret from storage or returns in-memory secret
|
|
10897
|
+
*/
|
|
10898
|
+
async retrieveSecret(options) {
|
|
10899
|
+
if (this.$__secret && this.$__secret.length > 0) {
|
|
10900
|
+
return this.$__secret;
|
|
10901
|
+
}
|
|
10902
|
+
if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
|
|
10903
|
+
return await this.$__secretStorage.retrieveSecret(this.$__bundle, options);
|
|
10904
|
+
}
|
|
10905
|
+
return null;
|
|
10906
|
+
}
|
|
10691
10907
|
/**
|
|
10692
10908
|
* Returns whether a bundle hash is being stored for this session
|
|
10693
10909
|
*/
|
|
@@ -10726,6 +10942,13 @@ var KnishIOClient = class {
|
|
|
10726
10942
|
remainderWallet = null
|
|
10727
10943
|
} = {}) {
|
|
10728
10944
|
this.log("info", "KnishIOClient::createMolecule() - Creating a new molecule...");
|
|
10945
|
+
if (!secret) {
|
|
10946
|
+
if (this.$__secret && this.$__secret.length > 0) {
|
|
10947
|
+
secret = this.getSecret();
|
|
10948
|
+
} else if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
|
|
10949
|
+
secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
|
|
10950
|
+
}
|
|
10951
|
+
}
|
|
10729
10952
|
secret = secret || this.getSecret();
|
|
10730
10953
|
bundle = bundle || this.getBundle();
|
|
10731
10954
|
let continuIdPosition = null;
|
|
@@ -10755,6 +10978,7 @@ var KnishIOClient = class {
|
|
|
10755
10978
|
}));
|
|
10756
10979
|
return new Molecule({
|
|
10757
10980
|
secret,
|
|
10981
|
+
bundle,
|
|
10758
10982
|
sourceWallet,
|
|
10759
10983
|
remainderWallet: this.getRemainderWallet(),
|
|
10760
10984
|
cellSlug: this.getCellSlug(),
|
|
@@ -10801,8 +11025,9 @@ var KnishIOClient = class {
|
|
|
10801
11025
|
async executeQuery(query, variables = null, context = {}) {
|
|
10802
11026
|
if (this.$__authToken && this.$__authToken.isExpired() && !this.$__authInProcess) {
|
|
10803
11027
|
this.log("info", "KnishIOClient::executeQuery() - Access token is expired. Getting new one...");
|
|
11028
|
+
const authSecret = this.$__secret || await this.retrieveSecret() || "";
|
|
10804
11029
|
await this.requestAuthToken({
|
|
10805
|
-
secret:
|
|
11030
|
+
secret: authSecret,
|
|
10806
11031
|
cellSlug: this.$__cellSlug,
|
|
10807
11032
|
encrypt: this.$__encrypt
|
|
10808
11033
|
});
|
|
@@ -10842,6 +11067,13 @@ var KnishIOClient = class {
|
|
|
10842
11067
|
setSecret(secret) {
|
|
10843
11068
|
this.$__secret = secret;
|
|
10844
11069
|
this.$__bundle = generateBundleHash(secret);
|
|
11070
|
+
if (!this.$__secretStorage) {
|
|
11071
|
+
const memStorage = new MemorySecretStorageProvider();
|
|
11072
|
+
memStorage.storeSecret(this.$__bundle, secret);
|
|
11073
|
+
this.$__secretStorage = memStorage;
|
|
11074
|
+
} else {
|
|
11075
|
+
this.$__secretStorage.storeSecret(this.$__bundle, secret);
|
|
11076
|
+
}
|
|
10845
11077
|
}
|
|
10846
11078
|
/**
|
|
10847
11079
|
* Sets the auth token for this session
|
|
@@ -11025,6 +11257,9 @@ var KnishIOClient = class {
|
|
|
11025
11257
|
if (secret === null && seed) {
|
|
11026
11258
|
secret = generateSecret(seed);
|
|
11027
11259
|
}
|
|
11260
|
+
if (secret === null && this.$__secretStorage && this.$__bundle) {
|
|
11261
|
+
secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
|
|
11262
|
+
}
|
|
11028
11263
|
if (cellSlug) {
|
|
11029
11264
|
this.setCellSlug(cellSlug);
|
|
11030
11265
|
}
|
|
@@ -11870,15 +12105,30 @@ var KnishIOClient = class {
|
|
|
11870
12105
|
async createPolicy({
|
|
11871
12106
|
metaType,
|
|
11872
12107
|
metaId,
|
|
11873
|
-
policy =
|
|
12108
|
+
policy = {}
|
|
11874
12109
|
}) {
|
|
11875
12110
|
this.log("info", `KnishIOClient::createPolicy() - Creating policy for ${metaType}:${metaId}...`);
|
|
11876
|
-
|
|
12111
|
+
const molecule = await this.createMolecule({});
|
|
12112
|
+
molecule.addPolicyAtom({
|
|
11877
12113
|
metaType,
|
|
11878
12114
|
metaId,
|
|
11879
|
-
meta:
|
|
11880
|
-
policy
|
|
12115
|
+
meta: {},
|
|
12116
|
+
policy: policy || {}
|
|
12117
|
+
});
|
|
12118
|
+
molecule.addContinuIdAtom();
|
|
12119
|
+
molecule.sign({
|
|
12120
|
+
bundle: this.getBundle()
|
|
11881
12121
|
});
|
|
12122
|
+
molecule.check();
|
|
12123
|
+
const query = await this.createMoleculeMutation({
|
|
12124
|
+
mutationClass: MutationProposeMolecule,
|
|
12125
|
+
molecule
|
|
12126
|
+
});
|
|
12127
|
+
const response = await this.executeQuery(query);
|
|
12128
|
+
if (!response) {
|
|
12129
|
+
throw new CodeException("Policy creation failed");
|
|
12130
|
+
}
|
|
12131
|
+
return response;
|
|
11882
12132
|
}
|
|
11883
12133
|
/**
|
|
11884
12134
|
* Create an identifier
|
|
@@ -12162,7 +12412,297 @@ var KnishIOClient = class {
|
|
|
12162
12412
|
// src/index.ts
|
|
12163
12413
|
init_Response();
|
|
12164
12414
|
init_exception();
|
|
12165
|
-
|
|
12415
|
+
|
|
12416
|
+
// src/storage/WebCryptoSecretStorageProvider.ts
|
|
12417
|
+
init_SecretStorageException();
|
|
12418
|
+
var MemoryStorageBackend = class {
|
|
12419
|
+
store = /* @__PURE__ */ new Map();
|
|
12420
|
+
getItem(key) {
|
|
12421
|
+
return this.store.get(key) ?? null;
|
|
12422
|
+
}
|
|
12423
|
+
setItem(key, value) {
|
|
12424
|
+
this.store.set(key, value);
|
|
12425
|
+
}
|
|
12426
|
+
removeItem(key) {
|
|
12427
|
+
return this.store.delete(key);
|
|
12428
|
+
}
|
|
12429
|
+
keys() {
|
|
12430
|
+
return Array.from(this.store.keys());
|
|
12431
|
+
}
|
|
12432
|
+
};
|
|
12433
|
+
function uint8ArrayToBase64(bytes) {
|
|
12434
|
+
let binary = "";
|
|
12435
|
+
const len = bytes.byteLength;
|
|
12436
|
+
for (let i = 0; i < len; i++) {
|
|
12437
|
+
const byte = bytes[i];
|
|
12438
|
+
if (byte !== void 0) {
|
|
12439
|
+
binary += String.fromCharCode(byte);
|
|
12440
|
+
}
|
|
12441
|
+
}
|
|
12442
|
+
return btoa(binary);
|
|
12443
|
+
}
|
|
12444
|
+
function base64ToUint8Array(base64) {
|
|
12445
|
+
const binary = atob(base64);
|
|
12446
|
+
const len = binary.length;
|
|
12447
|
+
const bytes = new Uint8Array(len);
|
|
12448
|
+
for (let i = 0; i < len; i++) {
|
|
12449
|
+
bytes[i] = binary.charCodeAt(i);
|
|
12450
|
+
}
|
|
12451
|
+
return bytes;
|
|
12452
|
+
}
|
|
12453
|
+
var textEncoder2 = new TextEncoder();
|
|
12454
|
+
var textDecoder = new TextDecoder();
|
|
12455
|
+
var KEY_PREFIX = "knishio:secret:";
|
|
12456
|
+
var DEFAULT_ITERATIONS = 1e5;
|
|
12457
|
+
var WebCryptoSecretStorageProvider = class {
|
|
12458
|
+
providerType = "webcrypto-aes-gcm";
|
|
12459
|
+
backend;
|
|
12460
|
+
defaultPassphrase;
|
|
12461
|
+
hardwareBacked;
|
|
12462
|
+
constructor(options = {}) {
|
|
12463
|
+
this.backend = options.backend ?? new MemoryStorageBackend();
|
|
12464
|
+
this.defaultPassphrase = options.defaultPassphrase;
|
|
12465
|
+
this.hardwareBacked = options.hardwareBacked ?? false;
|
|
12466
|
+
}
|
|
12467
|
+
/**
|
|
12468
|
+
* Whether this provider is backed by hardware (e.g. WebAuthn PRF wrapping)
|
|
12469
|
+
*/
|
|
12470
|
+
isHardwareBacked() {
|
|
12471
|
+
return this.hardwareBacked;
|
|
12472
|
+
}
|
|
12473
|
+
/**
|
|
12474
|
+
* Check if WebCrypto subtle API is available
|
|
12475
|
+
*/
|
|
12476
|
+
async isAvailable() {
|
|
12477
|
+
return typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined";
|
|
12478
|
+
}
|
|
12479
|
+
/**
|
|
12480
|
+
* Derive an AES-GCM CryptoKey from a passphrase and salt using PBKDF2
|
|
12481
|
+
*/
|
|
12482
|
+
async deriveKey(passphrase, salt, iterations = DEFAULT_ITERATIONS) {
|
|
12483
|
+
if (!await this.isAvailable()) {
|
|
12484
|
+
throw exports.SecretStorageException.unavailable(this.providerType, "WebCrypto API is not available");
|
|
12485
|
+
}
|
|
12486
|
+
const passphraseBytes = textEncoder2.encode(passphrase);
|
|
12487
|
+
try {
|
|
12488
|
+
const baseKey = await globalThis.crypto.subtle.importKey(
|
|
12489
|
+
"raw",
|
|
12490
|
+
passphraseBytes,
|
|
12491
|
+
"PBKDF2",
|
|
12492
|
+
false,
|
|
12493
|
+
["deriveKey"]
|
|
12494
|
+
);
|
|
12495
|
+
return await globalThis.crypto.subtle.deriveKey(
|
|
12496
|
+
{
|
|
12497
|
+
name: "PBKDF2",
|
|
12498
|
+
salt,
|
|
12499
|
+
iterations,
|
|
12500
|
+
hash: "SHA-256"
|
|
12501
|
+
},
|
|
12502
|
+
baseKey,
|
|
12503
|
+
{ name: "AES-GCM", length: 256 },
|
|
12504
|
+
false,
|
|
12505
|
+
["encrypt", "decrypt"]
|
|
12506
|
+
);
|
|
12507
|
+
} finally {
|
|
12508
|
+
zeroizeBytes(passphraseBytes);
|
|
12509
|
+
}
|
|
12510
|
+
}
|
|
12511
|
+
/**
|
|
12512
|
+
* Store and encrypt a master secret
|
|
12513
|
+
*/
|
|
12514
|
+
async storeSecret(bundleHash, secret, options) {
|
|
12515
|
+
if (!bundleHash) {
|
|
12516
|
+
throw new exports.SecretStorageException("Bundle hash cannot be empty");
|
|
12517
|
+
}
|
|
12518
|
+
if (!secret) {
|
|
12519
|
+
throw new exports.SecretStorageException("Secret cannot be empty");
|
|
12520
|
+
}
|
|
12521
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
12522
|
+
if (!passphrase) {
|
|
12523
|
+
throw new exports.SecretStorageException("Passphrase required for envelope encryption");
|
|
12524
|
+
}
|
|
12525
|
+
const salt = new Uint8Array(16);
|
|
12526
|
+
const iv = new Uint8Array(12);
|
|
12527
|
+
globalThis.crypto.getRandomValues(salt);
|
|
12528
|
+
globalThis.crypto.getRandomValues(iv);
|
|
12529
|
+
const key = await this.deriveKey(passphrase, salt, DEFAULT_ITERATIONS);
|
|
12530
|
+
const secretBytes = textEncoder2.encode(secret);
|
|
12531
|
+
try {
|
|
12532
|
+
const encryptedBuffer = await globalThis.crypto.subtle.encrypt(
|
|
12533
|
+
{
|
|
12534
|
+
name: "AES-GCM",
|
|
12535
|
+
iv
|
|
12536
|
+
},
|
|
12537
|
+
key,
|
|
12538
|
+
secretBytes
|
|
12539
|
+
);
|
|
12540
|
+
const ciphertext = uint8ArrayToBase64(new Uint8Array(encryptedBuffer));
|
|
12541
|
+
const metadata = {
|
|
12542
|
+
bundleHash,
|
|
12543
|
+
label: options?.label,
|
|
12544
|
+
createdAt: Date.now(),
|
|
12545
|
+
hardwareBacked: this.hardwareBacked,
|
|
12546
|
+
providerType: this.providerType
|
|
12547
|
+
};
|
|
12548
|
+
const payload = {
|
|
12549
|
+
version: 1,
|
|
12550
|
+
ciphertext,
|
|
12551
|
+
iv: uint8ArrayToBase64(iv),
|
|
12552
|
+
salt: uint8ArrayToBase64(salt),
|
|
12553
|
+
algorithm: "AES-GCM",
|
|
12554
|
+
iterations: DEFAULT_ITERATIONS,
|
|
12555
|
+
metadata
|
|
12556
|
+
};
|
|
12557
|
+
await this.backend.setItem(`${KEY_PREFIX}${bundleHash}`, JSON.stringify(payload));
|
|
12558
|
+
} catch (err) {
|
|
12559
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12560
|
+
throw new exports.SecretStorageException(`Encryption failed: ${msg}`);
|
|
12561
|
+
} finally {
|
|
12562
|
+
zeroizeBytes(secretBytes);
|
|
12563
|
+
}
|
|
12564
|
+
}
|
|
12565
|
+
/**
|
|
12566
|
+
* Retrieve and decrypt the master secret
|
|
12567
|
+
*/
|
|
12568
|
+
async retrieveSecret(bundleHash, options) {
|
|
12569
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
12570
|
+
if (!raw) {
|
|
12571
|
+
return null;
|
|
12572
|
+
}
|
|
12573
|
+
let payload;
|
|
12574
|
+
try {
|
|
12575
|
+
payload = JSON.parse(raw);
|
|
12576
|
+
} catch {
|
|
12577
|
+
throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
|
|
12578
|
+
}
|
|
12579
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
12580
|
+
if (!passphrase) {
|
|
12581
|
+
throw new exports.SecretStorageException("Passphrase required for secret decryption");
|
|
12582
|
+
}
|
|
12583
|
+
const salt = base64ToUint8Array(payload.salt);
|
|
12584
|
+
const iv = base64ToUint8Array(payload.iv);
|
|
12585
|
+
const ciphertext = base64ToUint8Array(payload.ciphertext);
|
|
12586
|
+
try {
|
|
12587
|
+
const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
|
|
12588
|
+
const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
|
|
12589
|
+
{
|
|
12590
|
+
name: "AES-GCM",
|
|
12591
|
+
iv
|
|
12592
|
+
},
|
|
12593
|
+
key,
|
|
12594
|
+
ciphertext
|
|
12595
|
+
);
|
|
12596
|
+
const decryptedBytes = new Uint8Array(decryptedBuffer);
|
|
12597
|
+
try {
|
|
12598
|
+
return textDecoder.decode(decryptedBytes);
|
|
12599
|
+
} finally {
|
|
12600
|
+
zeroizeBytes(decryptedBytes);
|
|
12601
|
+
}
|
|
12602
|
+
} catch (err) {
|
|
12603
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12604
|
+
throw exports.SecretStorageException.decryptionFailed(msg);
|
|
12605
|
+
}
|
|
12606
|
+
}
|
|
12607
|
+
/**
|
|
12608
|
+
* Delete a stored secret
|
|
12609
|
+
*/
|
|
12610
|
+
async deleteSecret(bundleHash) {
|
|
12611
|
+
const key = `${KEY_PREFIX}${bundleHash}`;
|
|
12612
|
+
const result = await this.backend.removeItem(key);
|
|
12613
|
+
return result !== false;
|
|
12614
|
+
}
|
|
12615
|
+
/**
|
|
12616
|
+
* Check if a secret exists
|
|
12617
|
+
*/
|
|
12618
|
+
async hasSecret(bundleHash) {
|
|
12619
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
12620
|
+
return raw !== null;
|
|
12621
|
+
}
|
|
12622
|
+
/**
|
|
12623
|
+
* List all stored secret metadata
|
|
12624
|
+
*/
|
|
12625
|
+
async listSecrets() {
|
|
12626
|
+
const keys = await this.backend.keys();
|
|
12627
|
+
const matchingKeys = keys.filter((k) => k.startsWith(KEY_PREFIX));
|
|
12628
|
+
const results = [];
|
|
12629
|
+
for (const key of matchingKeys) {
|
|
12630
|
+
const raw = await this.backend.getItem(key);
|
|
12631
|
+
if (raw) {
|
|
12632
|
+
try {
|
|
12633
|
+
const payload = JSON.parse(raw);
|
|
12634
|
+
if (payload.metadata) {
|
|
12635
|
+
results.push(payload.metadata);
|
|
12636
|
+
}
|
|
12637
|
+
} catch {
|
|
12638
|
+
}
|
|
12639
|
+
}
|
|
12640
|
+
}
|
|
12641
|
+
return results;
|
|
12642
|
+
}
|
|
12643
|
+
/**
|
|
12644
|
+
* Execute callback with unwrapped secret, zeroizing the decrypted buffer upon completion
|
|
12645
|
+
*/
|
|
12646
|
+
async withSecret(bundleHash, fn, options) {
|
|
12647
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
12648
|
+
if (!raw) {
|
|
12649
|
+
throw exports.SecretStorageException.notFound(bundleHash);
|
|
12650
|
+
}
|
|
12651
|
+
let payload;
|
|
12652
|
+
try {
|
|
12653
|
+
payload = JSON.parse(raw);
|
|
12654
|
+
} catch {
|
|
12655
|
+
throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
|
|
12656
|
+
}
|
|
12657
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
12658
|
+
if (!passphrase) {
|
|
12659
|
+
throw new exports.SecretStorageException("Passphrase required for secret decryption");
|
|
12660
|
+
}
|
|
12661
|
+
const salt = base64ToUint8Array(payload.salt);
|
|
12662
|
+
const iv = base64ToUint8Array(payload.iv);
|
|
12663
|
+
const ciphertext = base64ToUint8Array(payload.ciphertext);
|
|
12664
|
+
try {
|
|
12665
|
+
const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
|
|
12666
|
+
const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
|
|
12667
|
+
{
|
|
12668
|
+
name: "AES-GCM",
|
|
12669
|
+
iv
|
|
12670
|
+
},
|
|
12671
|
+
key,
|
|
12672
|
+
ciphertext
|
|
12673
|
+
);
|
|
12674
|
+
const decryptedBytes = new Uint8Array(decryptedBuffer);
|
|
12675
|
+
return await withSecureBytes(decryptedBytes, async (bytes) => {
|
|
12676
|
+
const secretString = textDecoder.decode(bytes);
|
|
12677
|
+
return await fn(secretString);
|
|
12678
|
+
});
|
|
12679
|
+
} catch (err) {
|
|
12680
|
+
if (err instanceof exports.SecretStorageException) {
|
|
12681
|
+
throw err;
|
|
12682
|
+
}
|
|
12683
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12684
|
+
throw exports.SecretStorageException.decryptionFailed(msg);
|
|
12685
|
+
}
|
|
12686
|
+
}
|
|
12687
|
+
};
|
|
12688
|
+
|
|
12689
|
+
// src/storage/index.ts
|
|
12690
|
+
function createDefaultSecretStorage(options = {}) {
|
|
12691
|
+
if (options.type === "memory") {
|
|
12692
|
+
return new MemorySecretStorageProvider();
|
|
12693
|
+
}
|
|
12694
|
+
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined") {
|
|
12695
|
+
return new WebCryptoSecretStorageProvider({
|
|
12696
|
+
backend: options.backend,
|
|
12697
|
+
defaultPassphrase: options.defaultPassphrase,
|
|
12698
|
+
hardwareBacked: options.hardwareBacked
|
|
12699
|
+
});
|
|
12700
|
+
}
|
|
12701
|
+
return new MemorySecretStorageProvider();
|
|
12702
|
+
}
|
|
12703
|
+
|
|
12704
|
+
// src/index.ts
|
|
12705
|
+
var SDK_VERSION = "0.9.8";
|
|
12166
12706
|
var SDK_NAME = "KnishIO-Client-TS";
|
|
12167
12707
|
var COMPATIBLE_SERVER_VERSIONS = [4, 5];
|
|
12168
12708
|
var SDK_INFO = {
|
|
@@ -12263,6 +12803,8 @@ exports.EXTENDED_COMPATIBILITY_TEST_VECTORS = EXTENDED_COMPATIBILITY_TEST_VECTOR
|
|
|
12263
12803
|
exports.GraphQLClient = GraphQLClient;
|
|
12264
12804
|
exports.KnishIO = KnishIO;
|
|
12265
12805
|
exports.KnishIOClient = KnishIOClient;
|
|
12806
|
+
exports.MemorySecretStorageProvider = MemorySecretStorageProvider;
|
|
12807
|
+
exports.MemoryStorageBackend = MemoryStorageBackend;
|
|
12266
12808
|
exports.Meta = Meta;
|
|
12267
12809
|
exports.Molecule = Molecule;
|
|
12268
12810
|
exports.Mutation = Mutation;
|
|
@@ -12308,6 +12850,7 @@ exports.SDK_NAME = SDK_NAME;
|
|
|
12308
12850
|
exports.SDK_VERSION = SDK_VERSION;
|
|
12309
12851
|
exports.TokenUnit = TokenUnit;
|
|
12310
12852
|
exports.Wallet = Wallet;
|
|
12853
|
+
exports.WebCryptoSecretStorageProvider = WebCryptoSecretStorageProvider;
|
|
12311
12854
|
exports.base64ToHex = base64ToHex;
|
|
12312
12855
|
exports.bufferToHexString = bufferToHexString;
|
|
12313
12856
|
exports.capitalize = capitalize;
|
|
@@ -12315,8 +12858,10 @@ exports.charsetBaseConvert = charsetBaseConvert;
|
|
|
12315
12858
|
exports.chunkArray = chunkArray;
|
|
12316
12859
|
exports.chunkSubstr = chunkSubstr;
|
|
12317
12860
|
exports.configureSDK = configureSDK;
|
|
12861
|
+
exports.constantTimeCompare = constantTimeCompare;
|
|
12318
12862
|
exports.convertToBase17 = convertToBase17;
|
|
12319
12863
|
exports.createBundleHash = createBundleHash;
|
|
12864
|
+
exports.createDefaultSecretStorage = createDefaultSecretStorage;
|
|
12320
12865
|
exports.createMolecularHash = createMolecularHash;
|
|
12321
12866
|
exports.createPosition = createPosition;
|
|
12322
12867
|
exports.createTokenSlug = createTokenSlug;
|
|
@@ -12358,5 +12903,8 @@ exports.validatePosition = validatePosition;
|
|
|
12358
12903
|
exports.validateSecret = validateSecret;
|
|
12359
12904
|
exports.validateWalletAddress = validateWalletAddress;
|
|
12360
12905
|
exports.verifyOTSSignature = verifyOTSSignature;
|
|
12906
|
+
exports.withSecureBytes = withSecureBytes;
|
|
12907
|
+
exports.withSecureString = withSecureString;
|
|
12908
|
+
exports.zeroizeBytes = zeroizeBytes;
|
|
12361
12909
|
//# sourceMappingURL=index.cjs.map
|
|
12362
12910
|
//# sourceMappingURL=index.cjs.map
|