@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.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(),
|
|
@@ -10453,9 +10506,134 @@ var ActiveSessionSubscribe = class extends Subscribe {
|
|
|
10453
10506
|
|
|
10454
10507
|
// src/KnishIOClient.ts
|
|
10455
10508
|
init_exception();
|
|
10509
|
+
|
|
10510
|
+
// src/storage/MemorySecretStorageProvider.ts
|
|
10511
|
+
init_SecretStorageException();
|
|
10512
|
+
|
|
10513
|
+
// src/libraries/secureMemory.ts
|
|
10514
|
+
var textEncoder = new TextEncoder();
|
|
10515
|
+
function zeroizeBytes(buffer) {
|
|
10516
|
+
if (buffer instanceof Uint8Array) {
|
|
10517
|
+
buffer.fill(0);
|
|
10518
|
+
} else if (Array.isArray(buffer)) {
|
|
10519
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
10520
|
+
buffer[i] = 0;
|
|
10521
|
+
}
|
|
10522
|
+
}
|
|
10523
|
+
}
|
|
10524
|
+
async function withSecureBytes(bytes, fn) {
|
|
10525
|
+
try {
|
|
10526
|
+
return await fn(bytes);
|
|
10527
|
+
} finally {
|
|
10528
|
+
zeroizeBytes(bytes);
|
|
10529
|
+
}
|
|
10530
|
+
}
|
|
10531
|
+
async function withSecureString(secret, fn) {
|
|
10532
|
+
const bytes = textEncoder.encode(secret);
|
|
10533
|
+
try {
|
|
10534
|
+
return await fn(secret);
|
|
10535
|
+
} finally {
|
|
10536
|
+
zeroizeBytes(bytes);
|
|
10537
|
+
}
|
|
10538
|
+
}
|
|
10539
|
+
function constantTimeCompare(a, b) {
|
|
10540
|
+
const bytesA = typeof a === "string" ? textEncoder.encode(a) : a;
|
|
10541
|
+
const bytesB = typeof b === "string" ? textEncoder.encode(b) : b;
|
|
10542
|
+
let result = bytesA.length === bytesB.length ? 0 : 1;
|
|
10543
|
+
const len = Math.min(bytesA.length, bytesB.length);
|
|
10544
|
+
for (let i = 0; i < len; i++) {
|
|
10545
|
+
const byteA = bytesA[i] ?? 0;
|
|
10546
|
+
const byteB = bytesB[i] ?? 0;
|
|
10547
|
+
result |= byteA ^ byteB;
|
|
10548
|
+
}
|
|
10549
|
+
if (typeof a === "string") zeroizeBytes(bytesA);
|
|
10550
|
+
if (typeof b === "string") zeroizeBytes(bytesB);
|
|
10551
|
+
return result === 0;
|
|
10552
|
+
}
|
|
10553
|
+
|
|
10554
|
+
// src/storage/MemorySecretStorageProvider.ts
|
|
10555
|
+
var MemorySecretStorageProvider = class {
|
|
10556
|
+
providerType = "memory";
|
|
10557
|
+
secrets = /* @__PURE__ */ new Map();
|
|
10558
|
+
/**
|
|
10559
|
+
* Memory storage is not hardware backed
|
|
10560
|
+
*/
|
|
10561
|
+
isHardwareBacked() {
|
|
10562
|
+
return false;
|
|
10563
|
+
}
|
|
10564
|
+
/**
|
|
10565
|
+
* Memory storage is always available
|
|
10566
|
+
*/
|
|
10567
|
+
async isAvailable() {
|
|
10568
|
+
return true;
|
|
10569
|
+
}
|
|
10570
|
+
/**
|
|
10571
|
+
* Store a secret in memory
|
|
10572
|
+
*/
|
|
10573
|
+
async storeSecret(bundleHash, secret, options) {
|
|
10574
|
+
if (!bundleHash) {
|
|
10575
|
+
throw new exports.SecretStorageException("Bundle hash cannot be empty");
|
|
10576
|
+
}
|
|
10577
|
+
if (!secret) {
|
|
10578
|
+
throw new exports.SecretStorageException("Secret cannot be empty");
|
|
10579
|
+
}
|
|
10580
|
+
const metadata = {
|
|
10581
|
+
bundleHash,
|
|
10582
|
+
label: options?.label,
|
|
10583
|
+
createdAt: Date.now(),
|
|
10584
|
+
hardwareBacked: false,
|
|
10585
|
+
providerType: this.providerType
|
|
10586
|
+
};
|
|
10587
|
+
this.secrets.set(bundleHash, { secret, metadata });
|
|
10588
|
+
}
|
|
10589
|
+
/**
|
|
10590
|
+
* Retrieve a secret from memory
|
|
10591
|
+
*/
|
|
10592
|
+
async retrieveSecret(bundleHash) {
|
|
10593
|
+
const entry = this.secrets.get(bundleHash);
|
|
10594
|
+
return entry ? entry.secret : null;
|
|
10595
|
+
}
|
|
10596
|
+
/**
|
|
10597
|
+
* Delete a stored secret
|
|
10598
|
+
*/
|
|
10599
|
+
async deleteSecret(bundleHash) {
|
|
10600
|
+
return this.secrets.delete(bundleHash);
|
|
10601
|
+
}
|
|
10602
|
+
/**
|
|
10603
|
+
* Check if a secret exists
|
|
10604
|
+
*/
|
|
10605
|
+
async hasSecret(bundleHash) {
|
|
10606
|
+
return this.secrets.has(bundleHash);
|
|
10607
|
+
}
|
|
10608
|
+
/**
|
|
10609
|
+
* List all stored secret metadata
|
|
10610
|
+
*/
|
|
10611
|
+
async listSecrets() {
|
|
10612
|
+
return Array.from(this.secrets.values()).map((entry) => ({ ...entry.metadata }));
|
|
10613
|
+
}
|
|
10614
|
+
/**
|
|
10615
|
+
* Execute callback with unwrapped secret and ensure cleanup
|
|
10616
|
+
*/
|
|
10617
|
+
async withSecret(bundleHash, fn) {
|
|
10618
|
+
const entry = this.secrets.get(bundleHash);
|
|
10619
|
+
if (!entry) {
|
|
10620
|
+
throw exports.SecretStorageException.notFound(bundleHash);
|
|
10621
|
+
}
|
|
10622
|
+
return withSecureString(entry.secret, fn);
|
|
10623
|
+
}
|
|
10624
|
+
/**
|
|
10625
|
+
* Clear all secrets from memory
|
|
10626
|
+
*/
|
|
10627
|
+
clear() {
|
|
10628
|
+
this.secrets.clear();
|
|
10629
|
+
}
|
|
10630
|
+
};
|
|
10631
|
+
|
|
10632
|
+
// src/KnishIOClient.ts
|
|
10456
10633
|
var KnishIOClient = class {
|
|
10457
10634
|
$__secret = "";
|
|
10458
10635
|
$__bundle = "";
|
|
10636
|
+
$__secretStorage = null;
|
|
10459
10637
|
$__cellSlug = null;
|
|
10460
10638
|
$__encrypt = false;
|
|
10461
10639
|
$__uris = [];
|
|
@@ -10515,6 +10693,9 @@ var KnishIOClient = class {
|
|
|
10515
10693
|
logging,
|
|
10516
10694
|
defaultRequestPolicy
|
|
10517
10695
|
});
|
|
10696
|
+
if (config.secretStorage) {
|
|
10697
|
+
this.$__secretStorage = config.secretStorage;
|
|
10698
|
+
}
|
|
10518
10699
|
}
|
|
10519
10700
|
/**
|
|
10520
10701
|
* Initializes a new Knish.IO client session
|
|
@@ -10616,6 +10797,7 @@ var KnishIOClient = class {
|
|
|
10616
10797
|
reset() {
|
|
10617
10798
|
this.$__secret = "";
|
|
10618
10799
|
this.$__bundle = "";
|
|
10800
|
+
this.$__secretStorage = null;
|
|
10619
10801
|
this.$__encrypt = false;
|
|
10620
10802
|
this.$__cellSlug = null;
|
|
10621
10803
|
this.$__authToken = null;
|
|
@@ -10677,7 +10859,7 @@ var KnishIOClient = class {
|
|
|
10677
10859
|
* Returns whether a secret is stored for this session
|
|
10678
10860
|
*/
|
|
10679
10861
|
hasSecret() {
|
|
10680
|
-
return !!this.$__secret && this.$__secret.length > 0;
|
|
10862
|
+
return !!this.$__secret && this.$__secret.length > 0 || !!this.$__secretStorage && !!this.$__bundle && this.$__bundle.length > 0;
|
|
10681
10863
|
}
|
|
10682
10864
|
/**
|
|
10683
10865
|
* Returns the stored secret
|
|
@@ -10688,6 +10870,33 @@ var KnishIOClient = class {
|
|
|
10688
10870
|
}
|
|
10689
10871
|
return this.$__secret;
|
|
10690
10872
|
}
|
|
10873
|
+
/**
|
|
10874
|
+
* Sets the secret storage provider and optionally sets the bundle hash
|
|
10875
|
+
*/
|
|
10876
|
+
setSecretStorage(storage, bundleHash) {
|
|
10877
|
+
this.$__secretStorage = storage;
|
|
10878
|
+
if (bundleHash) {
|
|
10879
|
+
this.$__bundle = bundleHash;
|
|
10880
|
+
}
|
|
10881
|
+
}
|
|
10882
|
+
/**
|
|
10883
|
+
* Returns current secret storage provider
|
|
10884
|
+
*/
|
|
10885
|
+
getSecretStorage() {
|
|
10886
|
+
return this.$__secretStorage;
|
|
10887
|
+
}
|
|
10888
|
+
/**
|
|
10889
|
+
* Asynchronously retrieves the secret from storage or returns in-memory secret
|
|
10890
|
+
*/
|
|
10891
|
+
async retrieveSecret(options) {
|
|
10892
|
+
if (this.$__secret && this.$__secret.length > 0) {
|
|
10893
|
+
return this.$__secret;
|
|
10894
|
+
}
|
|
10895
|
+
if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
|
|
10896
|
+
return await this.$__secretStorage.retrieveSecret(this.$__bundle, options);
|
|
10897
|
+
}
|
|
10898
|
+
return null;
|
|
10899
|
+
}
|
|
10691
10900
|
/**
|
|
10692
10901
|
* Returns whether a bundle hash is being stored for this session
|
|
10693
10902
|
*/
|
|
@@ -10726,6 +10935,13 @@ var KnishIOClient = class {
|
|
|
10726
10935
|
remainderWallet = null
|
|
10727
10936
|
} = {}) {
|
|
10728
10937
|
this.log("info", "KnishIOClient::createMolecule() - Creating a new molecule...");
|
|
10938
|
+
if (!secret) {
|
|
10939
|
+
if (this.$__secret && this.$__secret.length > 0) {
|
|
10940
|
+
secret = this.getSecret();
|
|
10941
|
+
} else if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
|
|
10942
|
+
secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
|
|
10943
|
+
}
|
|
10944
|
+
}
|
|
10729
10945
|
secret = secret || this.getSecret();
|
|
10730
10946
|
bundle = bundle || this.getBundle();
|
|
10731
10947
|
let continuIdPosition = null;
|
|
@@ -10755,6 +10971,7 @@ var KnishIOClient = class {
|
|
|
10755
10971
|
}));
|
|
10756
10972
|
return new Molecule({
|
|
10757
10973
|
secret,
|
|
10974
|
+
bundle,
|
|
10758
10975
|
sourceWallet,
|
|
10759
10976
|
remainderWallet: this.getRemainderWallet(),
|
|
10760
10977
|
cellSlug: this.getCellSlug(),
|
|
@@ -10801,8 +11018,9 @@ var KnishIOClient = class {
|
|
|
10801
11018
|
async executeQuery(query, variables = null, context = {}) {
|
|
10802
11019
|
if (this.$__authToken && this.$__authToken.isExpired() && !this.$__authInProcess) {
|
|
10803
11020
|
this.log("info", "KnishIOClient::executeQuery() - Access token is expired. Getting new one...");
|
|
11021
|
+
const authSecret = this.$__secret || await this.retrieveSecret() || "";
|
|
10804
11022
|
await this.requestAuthToken({
|
|
10805
|
-
secret:
|
|
11023
|
+
secret: authSecret,
|
|
10806
11024
|
cellSlug: this.$__cellSlug,
|
|
10807
11025
|
encrypt: this.$__encrypt
|
|
10808
11026
|
});
|
|
@@ -10842,6 +11060,13 @@ var KnishIOClient = class {
|
|
|
10842
11060
|
setSecret(secret) {
|
|
10843
11061
|
this.$__secret = secret;
|
|
10844
11062
|
this.$__bundle = generateBundleHash(secret);
|
|
11063
|
+
if (!this.$__secretStorage) {
|
|
11064
|
+
const memStorage = new MemorySecretStorageProvider();
|
|
11065
|
+
memStorage.storeSecret(this.$__bundle, secret);
|
|
11066
|
+
this.$__secretStorage = memStorage;
|
|
11067
|
+
} else {
|
|
11068
|
+
this.$__secretStorage.storeSecret(this.$__bundle, secret);
|
|
11069
|
+
}
|
|
10845
11070
|
}
|
|
10846
11071
|
/**
|
|
10847
11072
|
* Sets the auth token for this session
|
|
@@ -11025,6 +11250,9 @@ var KnishIOClient = class {
|
|
|
11025
11250
|
if (secret === null && seed) {
|
|
11026
11251
|
secret = generateSecret(seed);
|
|
11027
11252
|
}
|
|
11253
|
+
if (secret === null && this.$__secretStorage && this.$__bundle) {
|
|
11254
|
+
secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
|
|
11255
|
+
}
|
|
11028
11256
|
if (cellSlug) {
|
|
11029
11257
|
this.setCellSlug(cellSlug);
|
|
11030
11258
|
}
|
|
@@ -12162,7 +12390,297 @@ var KnishIOClient = class {
|
|
|
12162
12390
|
// src/index.ts
|
|
12163
12391
|
init_Response();
|
|
12164
12392
|
init_exception();
|
|
12165
|
-
|
|
12393
|
+
|
|
12394
|
+
// src/storage/WebCryptoSecretStorageProvider.ts
|
|
12395
|
+
init_SecretStorageException();
|
|
12396
|
+
var MemoryStorageBackend = class {
|
|
12397
|
+
store = /* @__PURE__ */ new Map();
|
|
12398
|
+
getItem(key) {
|
|
12399
|
+
return this.store.get(key) ?? null;
|
|
12400
|
+
}
|
|
12401
|
+
setItem(key, value) {
|
|
12402
|
+
this.store.set(key, value);
|
|
12403
|
+
}
|
|
12404
|
+
removeItem(key) {
|
|
12405
|
+
return this.store.delete(key);
|
|
12406
|
+
}
|
|
12407
|
+
keys() {
|
|
12408
|
+
return Array.from(this.store.keys());
|
|
12409
|
+
}
|
|
12410
|
+
};
|
|
12411
|
+
function uint8ArrayToBase64(bytes) {
|
|
12412
|
+
let binary = "";
|
|
12413
|
+
const len = bytes.byteLength;
|
|
12414
|
+
for (let i = 0; i < len; i++) {
|
|
12415
|
+
const byte = bytes[i];
|
|
12416
|
+
if (byte !== void 0) {
|
|
12417
|
+
binary += String.fromCharCode(byte);
|
|
12418
|
+
}
|
|
12419
|
+
}
|
|
12420
|
+
return btoa(binary);
|
|
12421
|
+
}
|
|
12422
|
+
function base64ToUint8Array(base64) {
|
|
12423
|
+
const binary = atob(base64);
|
|
12424
|
+
const len = binary.length;
|
|
12425
|
+
const bytes = new Uint8Array(len);
|
|
12426
|
+
for (let i = 0; i < len; i++) {
|
|
12427
|
+
bytes[i] = binary.charCodeAt(i);
|
|
12428
|
+
}
|
|
12429
|
+
return bytes;
|
|
12430
|
+
}
|
|
12431
|
+
var textEncoder2 = new TextEncoder();
|
|
12432
|
+
var textDecoder = new TextDecoder();
|
|
12433
|
+
var KEY_PREFIX = "knishio:secret:";
|
|
12434
|
+
var DEFAULT_ITERATIONS = 1e5;
|
|
12435
|
+
var WebCryptoSecretStorageProvider = class {
|
|
12436
|
+
providerType = "webcrypto-aes-gcm";
|
|
12437
|
+
backend;
|
|
12438
|
+
defaultPassphrase;
|
|
12439
|
+
hardwareBacked;
|
|
12440
|
+
constructor(options = {}) {
|
|
12441
|
+
this.backend = options.backend ?? new MemoryStorageBackend();
|
|
12442
|
+
this.defaultPassphrase = options.defaultPassphrase;
|
|
12443
|
+
this.hardwareBacked = options.hardwareBacked ?? false;
|
|
12444
|
+
}
|
|
12445
|
+
/**
|
|
12446
|
+
* Whether this provider is backed by hardware (e.g. WebAuthn PRF wrapping)
|
|
12447
|
+
*/
|
|
12448
|
+
isHardwareBacked() {
|
|
12449
|
+
return this.hardwareBacked;
|
|
12450
|
+
}
|
|
12451
|
+
/**
|
|
12452
|
+
* Check if WebCrypto subtle API is available
|
|
12453
|
+
*/
|
|
12454
|
+
async isAvailable() {
|
|
12455
|
+
return typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined";
|
|
12456
|
+
}
|
|
12457
|
+
/**
|
|
12458
|
+
* Derive an AES-GCM CryptoKey from a passphrase and salt using PBKDF2
|
|
12459
|
+
*/
|
|
12460
|
+
async deriveKey(passphrase, salt, iterations = DEFAULT_ITERATIONS) {
|
|
12461
|
+
if (!await this.isAvailable()) {
|
|
12462
|
+
throw exports.SecretStorageException.unavailable(this.providerType, "WebCrypto API is not available");
|
|
12463
|
+
}
|
|
12464
|
+
const passphraseBytes = textEncoder2.encode(passphrase);
|
|
12465
|
+
try {
|
|
12466
|
+
const baseKey = await globalThis.crypto.subtle.importKey(
|
|
12467
|
+
"raw",
|
|
12468
|
+
passphraseBytes,
|
|
12469
|
+
"PBKDF2",
|
|
12470
|
+
false,
|
|
12471
|
+
["deriveKey"]
|
|
12472
|
+
);
|
|
12473
|
+
return await globalThis.crypto.subtle.deriveKey(
|
|
12474
|
+
{
|
|
12475
|
+
name: "PBKDF2",
|
|
12476
|
+
salt,
|
|
12477
|
+
iterations,
|
|
12478
|
+
hash: "SHA-256"
|
|
12479
|
+
},
|
|
12480
|
+
baseKey,
|
|
12481
|
+
{ name: "AES-GCM", length: 256 },
|
|
12482
|
+
false,
|
|
12483
|
+
["encrypt", "decrypt"]
|
|
12484
|
+
);
|
|
12485
|
+
} finally {
|
|
12486
|
+
zeroizeBytes(passphraseBytes);
|
|
12487
|
+
}
|
|
12488
|
+
}
|
|
12489
|
+
/**
|
|
12490
|
+
* Store and encrypt a master secret
|
|
12491
|
+
*/
|
|
12492
|
+
async storeSecret(bundleHash, secret, options) {
|
|
12493
|
+
if (!bundleHash) {
|
|
12494
|
+
throw new exports.SecretStorageException("Bundle hash cannot be empty");
|
|
12495
|
+
}
|
|
12496
|
+
if (!secret) {
|
|
12497
|
+
throw new exports.SecretStorageException("Secret cannot be empty");
|
|
12498
|
+
}
|
|
12499
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
12500
|
+
if (!passphrase) {
|
|
12501
|
+
throw new exports.SecretStorageException("Passphrase required for envelope encryption");
|
|
12502
|
+
}
|
|
12503
|
+
const salt = new Uint8Array(16);
|
|
12504
|
+
const iv = new Uint8Array(12);
|
|
12505
|
+
globalThis.crypto.getRandomValues(salt);
|
|
12506
|
+
globalThis.crypto.getRandomValues(iv);
|
|
12507
|
+
const key = await this.deriveKey(passphrase, salt, DEFAULT_ITERATIONS);
|
|
12508
|
+
const secretBytes = textEncoder2.encode(secret);
|
|
12509
|
+
try {
|
|
12510
|
+
const encryptedBuffer = await globalThis.crypto.subtle.encrypt(
|
|
12511
|
+
{
|
|
12512
|
+
name: "AES-GCM",
|
|
12513
|
+
iv
|
|
12514
|
+
},
|
|
12515
|
+
key,
|
|
12516
|
+
secretBytes
|
|
12517
|
+
);
|
|
12518
|
+
const ciphertext = uint8ArrayToBase64(new Uint8Array(encryptedBuffer));
|
|
12519
|
+
const metadata = {
|
|
12520
|
+
bundleHash,
|
|
12521
|
+
label: options?.label,
|
|
12522
|
+
createdAt: Date.now(),
|
|
12523
|
+
hardwareBacked: this.hardwareBacked,
|
|
12524
|
+
providerType: this.providerType
|
|
12525
|
+
};
|
|
12526
|
+
const payload = {
|
|
12527
|
+
version: 1,
|
|
12528
|
+
ciphertext,
|
|
12529
|
+
iv: uint8ArrayToBase64(iv),
|
|
12530
|
+
salt: uint8ArrayToBase64(salt),
|
|
12531
|
+
algorithm: "AES-GCM",
|
|
12532
|
+
iterations: DEFAULT_ITERATIONS,
|
|
12533
|
+
metadata
|
|
12534
|
+
};
|
|
12535
|
+
await this.backend.setItem(`${KEY_PREFIX}${bundleHash}`, JSON.stringify(payload));
|
|
12536
|
+
} catch (err) {
|
|
12537
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12538
|
+
throw new exports.SecretStorageException(`Encryption failed: ${msg}`);
|
|
12539
|
+
} finally {
|
|
12540
|
+
zeroizeBytes(secretBytes);
|
|
12541
|
+
}
|
|
12542
|
+
}
|
|
12543
|
+
/**
|
|
12544
|
+
* Retrieve and decrypt the master secret
|
|
12545
|
+
*/
|
|
12546
|
+
async retrieveSecret(bundleHash, options) {
|
|
12547
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
12548
|
+
if (!raw) {
|
|
12549
|
+
return null;
|
|
12550
|
+
}
|
|
12551
|
+
let payload;
|
|
12552
|
+
try {
|
|
12553
|
+
payload = JSON.parse(raw);
|
|
12554
|
+
} catch {
|
|
12555
|
+
throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
|
|
12556
|
+
}
|
|
12557
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
12558
|
+
if (!passphrase) {
|
|
12559
|
+
throw new exports.SecretStorageException("Passphrase required for secret decryption");
|
|
12560
|
+
}
|
|
12561
|
+
const salt = base64ToUint8Array(payload.salt);
|
|
12562
|
+
const iv = base64ToUint8Array(payload.iv);
|
|
12563
|
+
const ciphertext = base64ToUint8Array(payload.ciphertext);
|
|
12564
|
+
try {
|
|
12565
|
+
const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
|
|
12566
|
+
const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
|
|
12567
|
+
{
|
|
12568
|
+
name: "AES-GCM",
|
|
12569
|
+
iv
|
|
12570
|
+
},
|
|
12571
|
+
key,
|
|
12572
|
+
ciphertext
|
|
12573
|
+
);
|
|
12574
|
+
const decryptedBytes = new Uint8Array(decryptedBuffer);
|
|
12575
|
+
try {
|
|
12576
|
+
return textDecoder.decode(decryptedBytes);
|
|
12577
|
+
} finally {
|
|
12578
|
+
zeroizeBytes(decryptedBytes);
|
|
12579
|
+
}
|
|
12580
|
+
} catch (err) {
|
|
12581
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12582
|
+
throw exports.SecretStorageException.decryptionFailed(msg);
|
|
12583
|
+
}
|
|
12584
|
+
}
|
|
12585
|
+
/**
|
|
12586
|
+
* Delete a stored secret
|
|
12587
|
+
*/
|
|
12588
|
+
async deleteSecret(bundleHash) {
|
|
12589
|
+
const key = `${KEY_PREFIX}${bundleHash}`;
|
|
12590
|
+
const result = await this.backend.removeItem(key);
|
|
12591
|
+
return result !== false;
|
|
12592
|
+
}
|
|
12593
|
+
/**
|
|
12594
|
+
* Check if a secret exists
|
|
12595
|
+
*/
|
|
12596
|
+
async hasSecret(bundleHash) {
|
|
12597
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
12598
|
+
return raw !== null;
|
|
12599
|
+
}
|
|
12600
|
+
/**
|
|
12601
|
+
* List all stored secret metadata
|
|
12602
|
+
*/
|
|
12603
|
+
async listSecrets() {
|
|
12604
|
+
const keys = await this.backend.keys();
|
|
12605
|
+
const matchingKeys = keys.filter((k) => k.startsWith(KEY_PREFIX));
|
|
12606
|
+
const results = [];
|
|
12607
|
+
for (const key of matchingKeys) {
|
|
12608
|
+
const raw = await this.backend.getItem(key);
|
|
12609
|
+
if (raw) {
|
|
12610
|
+
try {
|
|
12611
|
+
const payload = JSON.parse(raw);
|
|
12612
|
+
if (payload.metadata) {
|
|
12613
|
+
results.push(payload.metadata);
|
|
12614
|
+
}
|
|
12615
|
+
} catch {
|
|
12616
|
+
}
|
|
12617
|
+
}
|
|
12618
|
+
}
|
|
12619
|
+
return results;
|
|
12620
|
+
}
|
|
12621
|
+
/**
|
|
12622
|
+
* Execute callback with unwrapped secret, zeroizing the decrypted buffer upon completion
|
|
12623
|
+
*/
|
|
12624
|
+
async withSecret(bundleHash, fn, options) {
|
|
12625
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
12626
|
+
if (!raw) {
|
|
12627
|
+
throw exports.SecretStorageException.notFound(bundleHash);
|
|
12628
|
+
}
|
|
12629
|
+
let payload;
|
|
12630
|
+
try {
|
|
12631
|
+
payload = JSON.parse(raw);
|
|
12632
|
+
} catch {
|
|
12633
|
+
throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
|
|
12634
|
+
}
|
|
12635
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
12636
|
+
if (!passphrase) {
|
|
12637
|
+
throw new exports.SecretStorageException("Passphrase required for secret decryption");
|
|
12638
|
+
}
|
|
12639
|
+
const salt = base64ToUint8Array(payload.salt);
|
|
12640
|
+
const iv = base64ToUint8Array(payload.iv);
|
|
12641
|
+
const ciphertext = base64ToUint8Array(payload.ciphertext);
|
|
12642
|
+
try {
|
|
12643
|
+
const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
|
|
12644
|
+
const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
|
|
12645
|
+
{
|
|
12646
|
+
name: "AES-GCM",
|
|
12647
|
+
iv
|
|
12648
|
+
},
|
|
12649
|
+
key,
|
|
12650
|
+
ciphertext
|
|
12651
|
+
);
|
|
12652
|
+
const decryptedBytes = new Uint8Array(decryptedBuffer);
|
|
12653
|
+
return await withSecureBytes(decryptedBytes, async (bytes) => {
|
|
12654
|
+
const secretString = textDecoder.decode(bytes);
|
|
12655
|
+
return await fn(secretString);
|
|
12656
|
+
});
|
|
12657
|
+
} catch (err) {
|
|
12658
|
+
if (err instanceof exports.SecretStorageException) {
|
|
12659
|
+
throw err;
|
|
12660
|
+
}
|
|
12661
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12662
|
+
throw exports.SecretStorageException.decryptionFailed(msg);
|
|
12663
|
+
}
|
|
12664
|
+
}
|
|
12665
|
+
};
|
|
12666
|
+
|
|
12667
|
+
// src/storage/index.ts
|
|
12668
|
+
function createDefaultSecretStorage(options = {}) {
|
|
12669
|
+
if (options.type === "memory") {
|
|
12670
|
+
return new MemorySecretStorageProvider();
|
|
12671
|
+
}
|
|
12672
|
+
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined") {
|
|
12673
|
+
return new WebCryptoSecretStorageProvider({
|
|
12674
|
+
backend: options.backend,
|
|
12675
|
+
defaultPassphrase: options.defaultPassphrase,
|
|
12676
|
+
hardwareBacked: options.hardwareBacked
|
|
12677
|
+
});
|
|
12678
|
+
}
|
|
12679
|
+
return new MemorySecretStorageProvider();
|
|
12680
|
+
}
|
|
12681
|
+
|
|
12682
|
+
// src/index.ts
|
|
12683
|
+
var SDK_VERSION = "0.9.7";
|
|
12166
12684
|
var SDK_NAME = "KnishIO-Client-TS";
|
|
12167
12685
|
var COMPATIBLE_SERVER_VERSIONS = [4, 5];
|
|
12168
12686
|
var SDK_INFO = {
|
|
@@ -12263,6 +12781,8 @@ exports.EXTENDED_COMPATIBILITY_TEST_VECTORS = EXTENDED_COMPATIBILITY_TEST_VECTOR
|
|
|
12263
12781
|
exports.GraphQLClient = GraphQLClient;
|
|
12264
12782
|
exports.KnishIO = KnishIO;
|
|
12265
12783
|
exports.KnishIOClient = KnishIOClient;
|
|
12784
|
+
exports.MemorySecretStorageProvider = MemorySecretStorageProvider;
|
|
12785
|
+
exports.MemoryStorageBackend = MemoryStorageBackend;
|
|
12266
12786
|
exports.Meta = Meta;
|
|
12267
12787
|
exports.Molecule = Molecule;
|
|
12268
12788
|
exports.Mutation = Mutation;
|
|
@@ -12308,6 +12828,7 @@ exports.SDK_NAME = SDK_NAME;
|
|
|
12308
12828
|
exports.SDK_VERSION = SDK_VERSION;
|
|
12309
12829
|
exports.TokenUnit = TokenUnit;
|
|
12310
12830
|
exports.Wallet = Wallet;
|
|
12831
|
+
exports.WebCryptoSecretStorageProvider = WebCryptoSecretStorageProvider;
|
|
12311
12832
|
exports.base64ToHex = base64ToHex;
|
|
12312
12833
|
exports.bufferToHexString = bufferToHexString;
|
|
12313
12834
|
exports.capitalize = capitalize;
|
|
@@ -12315,8 +12836,10 @@ exports.charsetBaseConvert = charsetBaseConvert;
|
|
|
12315
12836
|
exports.chunkArray = chunkArray;
|
|
12316
12837
|
exports.chunkSubstr = chunkSubstr;
|
|
12317
12838
|
exports.configureSDK = configureSDK;
|
|
12839
|
+
exports.constantTimeCompare = constantTimeCompare;
|
|
12318
12840
|
exports.convertToBase17 = convertToBase17;
|
|
12319
12841
|
exports.createBundleHash = createBundleHash;
|
|
12842
|
+
exports.createDefaultSecretStorage = createDefaultSecretStorage;
|
|
12320
12843
|
exports.createMolecularHash = createMolecularHash;
|
|
12321
12844
|
exports.createPosition = createPosition;
|
|
12322
12845
|
exports.createTokenSlug = createTokenSlug;
|
|
@@ -12358,5 +12881,8 @@ exports.validatePosition = validatePosition;
|
|
|
12358
12881
|
exports.validateSecret = validateSecret;
|
|
12359
12882
|
exports.validateWalletAddress = validateWalletAddress;
|
|
12360
12883
|
exports.verifyOTSSignature = verifyOTSSignature;
|
|
12884
|
+
exports.withSecureBytes = withSecureBytes;
|
|
12885
|
+
exports.withSecureString = withSecureString;
|
|
12886
|
+
exports.zeroizeBytes = zeroizeBytes;
|
|
12361
12887
|
//# sourceMappingURL=index.cjs.map
|
|
12362
12888
|
//# sourceMappingURL=index.cjs.map
|