@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.js
CHANGED
|
@@ -521,6 +521,55 @@ var init_TransferBalanceException = __esm({
|
|
|
521
521
|
}
|
|
522
522
|
});
|
|
523
523
|
|
|
524
|
+
// src/exception/SecretStorageException.ts
|
|
525
|
+
var SecretStorageException;
|
|
526
|
+
var init_SecretStorageException = __esm({
|
|
527
|
+
"src/exception/SecretStorageException.ts"() {
|
|
528
|
+
init_BaseException();
|
|
529
|
+
SecretStorageException = class _SecretStorageException extends BaseException {
|
|
530
|
+
constructor(message = "Secret storage operation failed", options = {}) {
|
|
531
|
+
super("WALLET_CREDENTIAL_ERROR", message, {
|
|
532
|
+
code: "SECRET_STORAGE_ERROR",
|
|
533
|
+
...options
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Secret not found for the requested bundle hash
|
|
538
|
+
*/
|
|
539
|
+
static notFound(bundleHash) {
|
|
540
|
+
return new _SecretStorageException(`Secret not found for bundle: ${bundleHash}`, {
|
|
541
|
+
code: "SECRET_NOT_FOUND",
|
|
542
|
+
details: { bundleHash }
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Decryption failed (wrong passphrase or corrupted payload)
|
|
547
|
+
*/
|
|
548
|
+
static decryptionFailed(reason) {
|
|
549
|
+
return new _SecretStorageException(
|
|
550
|
+
`Failed to decrypt master secret: ${reason || "Invalid passphrase or corrupted ciphertext"}`,
|
|
551
|
+
{
|
|
552
|
+
code: "DECRYPTION_FAILED",
|
|
553
|
+
details: { reason }
|
|
554
|
+
}
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Provider is unavailable in current platform
|
|
559
|
+
*/
|
|
560
|
+
static unavailable(provider, reason) {
|
|
561
|
+
return new _SecretStorageException(
|
|
562
|
+
`Secret storage provider '${provider}' is unavailable: ${reason || "Hardware or API not accessible"}`,
|
|
563
|
+
{
|
|
564
|
+
code: "STORAGE_UNAVAILABLE",
|
|
565
|
+
details: { provider, reason }
|
|
566
|
+
}
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
|
|
524
573
|
// src/exception/InvalidResponseException.ts
|
|
525
574
|
var InvalidResponseException;
|
|
526
575
|
var init_InvalidResponseException = __esm({
|
|
@@ -983,6 +1032,7 @@ var init_exception = __esm({
|
|
|
983
1032
|
init_SignatureMismatchException();
|
|
984
1033
|
init_TransferBalanceException();
|
|
985
1034
|
init_WalletCredentialException();
|
|
1035
|
+
init_SecretStorageException();
|
|
986
1036
|
init_InvalidResponseException();
|
|
987
1037
|
init_BalanceInsufficientException();
|
|
988
1038
|
init_BatchIdException();
|
|
@@ -4109,7 +4159,8 @@ z.object({
|
|
|
4109
4159
|
socket: z.unknown().optional(),
|
|
4110
4160
|
serverSdkVersion: z.number().int().min(1).optional(),
|
|
4111
4161
|
logging: z.boolean().optional(),
|
|
4112
|
-
defaultRequestPolicy: z.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional()
|
|
4162
|
+
defaultRequestPolicy: z.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional(),
|
|
4163
|
+
secretStorage: z.unknown().optional()
|
|
4113
4164
|
}).strict();
|
|
4114
4165
|
z.object({
|
|
4115
4166
|
token: z.string().min(1, "Auth token cannot be empty"),
|
|
@@ -6909,7 +6960,9 @@ var KnishIOClientConfigSchema2 = z.object({
|
|
|
6909
6960
|
// Optional default urql request policy for reads (server/sync clients pass
|
|
6910
6961
|
// 'network-only'). Permitted by the strict schema so the constructor option
|
|
6911
6962
|
// isn't rejected.
|
|
6912
|
-
defaultRequestPolicy: z.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional()
|
|
6963
|
+
defaultRequestPolicy: z.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional(),
|
|
6964
|
+
// Pluggable hardware envelope encryption secret storage provider
|
|
6965
|
+
secretStorage: z.unknown().optional()
|
|
6913
6966
|
}).strict();
|
|
6914
6967
|
var EnvironmentConfigSchema = z.object({
|
|
6915
6968
|
NODE_ENV: z.enum(["development", "production", "test"]).optional(),
|
|
@@ -9483,6 +9536,13 @@ var MutationProposeMolecule = class extends Mutation {
|
|
|
9483
9536
|
molecule() {
|
|
9484
9537
|
return this.$__molecule.toJSON ? this.$__molecule.toJSON() : this.$__molecule;
|
|
9485
9538
|
}
|
|
9539
|
+
/**
|
|
9540
|
+
* Fills the molecule with specific mutation data.
|
|
9541
|
+
* Subclasses override to build domain-specific atoms.
|
|
9542
|
+
* Default implementation is a no-op for pre-assembled molecules.
|
|
9543
|
+
*/
|
|
9544
|
+
fillMolecule(_params) {
|
|
9545
|
+
}
|
|
9486
9546
|
};
|
|
9487
9547
|
|
|
9488
9548
|
// src/response/ResponseCreateToken.ts
|
|
@@ -10447,9 +10507,134 @@ var ActiveSessionSubscribe = class extends Subscribe {
|
|
|
10447
10507
|
|
|
10448
10508
|
// src/KnishIOClient.ts
|
|
10449
10509
|
init_exception();
|
|
10510
|
+
|
|
10511
|
+
// src/storage/MemorySecretStorageProvider.ts
|
|
10512
|
+
init_SecretStorageException();
|
|
10513
|
+
|
|
10514
|
+
// src/libraries/secureMemory.ts
|
|
10515
|
+
var textEncoder = new TextEncoder();
|
|
10516
|
+
function zeroizeBytes(buffer) {
|
|
10517
|
+
if (buffer instanceof Uint8Array) {
|
|
10518
|
+
buffer.fill(0);
|
|
10519
|
+
} else if (Array.isArray(buffer)) {
|
|
10520
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
10521
|
+
buffer[i] = 0;
|
|
10522
|
+
}
|
|
10523
|
+
}
|
|
10524
|
+
}
|
|
10525
|
+
async function withSecureBytes(bytes, fn) {
|
|
10526
|
+
try {
|
|
10527
|
+
return await fn(bytes);
|
|
10528
|
+
} finally {
|
|
10529
|
+
zeroizeBytes(bytes);
|
|
10530
|
+
}
|
|
10531
|
+
}
|
|
10532
|
+
async function withSecureString(secret, fn) {
|
|
10533
|
+
const bytes = textEncoder.encode(secret);
|
|
10534
|
+
try {
|
|
10535
|
+
return await fn(secret);
|
|
10536
|
+
} finally {
|
|
10537
|
+
zeroizeBytes(bytes);
|
|
10538
|
+
}
|
|
10539
|
+
}
|
|
10540
|
+
function constantTimeCompare(a, b) {
|
|
10541
|
+
const bytesA = typeof a === "string" ? textEncoder.encode(a) : a;
|
|
10542
|
+
const bytesB = typeof b === "string" ? textEncoder.encode(b) : b;
|
|
10543
|
+
let result = bytesA.length === bytesB.length ? 0 : 1;
|
|
10544
|
+
const len = Math.min(bytesA.length, bytesB.length);
|
|
10545
|
+
for (let i = 0; i < len; i++) {
|
|
10546
|
+
const byteA = bytesA[i] ?? 0;
|
|
10547
|
+
const byteB = bytesB[i] ?? 0;
|
|
10548
|
+
result |= byteA ^ byteB;
|
|
10549
|
+
}
|
|
10550
|
+
if (typeof a === "string") zeroizeBytes(bytesA);
|
|
10551
|
+
if (typeof b === "string") zeroizeBytes(bytesB);
|
|
10552
|
+
return result === 0;
|
|
10553
|
+
}
|
|
10554
|
+
|
|
10555
|
+
// src/storage/MemorySecretStorageProvider.ts
|
|
10556
|
+
var MemorySecretStorageProvider = class {
|
|
10557
|
+
providerType = "memory";
|
|
10558
|
+
secrets = /* @__PURE__ */ new Map();
|
|
10559
|
+
/**
|
|
10560
|
+
* Memory storage is not hardware backed
|
|
10561
|
+
*/
|
|
10562
|
+
isHardwareBacked() {
|
|
10563
|
+
return false;
|
|
10564
|
+
}
|
|
10565
|
+
/**
|
|
10566
|
+
* Memory storage is always available
|
|
10567
|
+
*/
|
|
10568
|
+
async isAvailable() {
|
|
10569
|
+
return true;
|
|
10570
|
+
}
|
|
10571
|
+
/**
|
|
10572
|
+
* Store a secret in memory
|
|
10573
|
+
*/
|
|
10574
|
+
async storeSecret(bundleHash, secret, options) {
|
|
10575
|
+
if (!bundleHash) {
|
|
10576
|
+
throw new SecretStorageException("Bundle hash cannot be empty");
|
|
10577
|
+
}
|
|
10578
|
+
if (!secret) {
|
|
10579
|
+
throw new SecretStorageException("Secret cannot be empty");
|
|
10580
|
+
}
|
|
10581
|
+
const metadata = {
|
|
10582
|
+
bundleHash,
|
|
10583
|
+
label: options?.label,
|
|
10584
|
+
createdAt: Date.now(),
|
|
10585
|
+
hardwareBacked: false,
|
|
10586
|
+
providerType: this.providerType
|
|
10587
|
+
};
|
|
10588
|
+
this.secrets.set(bundleHash, { secret, metadata });
|
|
10589
|
+
}
|
|
10590
|
+
/**
|
|
10591
|
+
* Retrieve a secret from memory
|
|
10592
|
+
*/
|
|
10593
|
+
async retrieveSecret(bundleHash) {
|
|
10594
|
+
const entry = this.secrets.get(bundleHash);
|
|
10595
|
+
return entry ? entry.secret : null;
|
|
10596
|
+
}
|
|
10597
|
+
/**
|
|
10598
|
+
* Delete a stored secret
|
|
10599
|
+
*/
|
|
10600
|
+
async deleteSecret(bundleHash) {
|
|
10601
|
+
return this.secrets.delete(bundleHash);
|
|
10602
|
+
}
|
|
10603
|
+
/**
|
|
10604
|
+
* Check if a secret exists
|
|
10605
|
+
*/
|
|
10606
|
+
async hasSecret(bundleHash) {
|
|
10607
|
+
return this.secrets.has(bundleHash);
|
|
10608
|
+
}
|
|
10609
|
+
/**
|
|
10610
|
+
* List all stored secret metadata
|
|
10611
|
+
*/
|
|
10612
|
+
async listSecrets() {
|
|
10613
|
+
return Array.from(this.secrets.values()).map((entry) => ({ ...entry.metadata }));
|
|
10614
|
+
}
|
|
10615
|
+
/**
|
|
10616
|
+
* Execute callback with unwrapped secret and ensure cleanup
|
|
10617
|
+
*/
|
|
10618
|
+
async withSecret(bundleHash, fn) {
|
|
10619
|
+
const entry = this.secrets.get(bundleHash);
|
|
10620
|
+
if (!entry) {
|
|
10621
|
+
throw SecretStorageException.notFound(bundleHash);
|
|
10622
|
+
}
|
|
10623
|
+
return withSecureString(entry.secret, fn);
|
|
10624
|
+
}
|
|
10625
|
+
/**
|
|
10626
|
+
* Clear all secrets from memory
|
|
10627
|
+
*/
|
|
10628
|
+
clear() {
|
|
10629
|
+
this.secrets.clear();
|
|
10630
|
+
}
|
|
10631
|
+
};
|
|
10632
|
+
|
|
10633
|
+
// src/KnishIOClient.ts
|
|
10450
10634
|
var KnishIOClient = class {
|
|
10451
10635
|
$__secret = "";
|
|
10452
10636
|
$__bundle = "";
|
|
10637
|
+
$__secretStorage = null;
|
|
10453
10638
|
$__cellSlug = null;
|
|
10454
10639
|
$__encrypt = false;
|
|
10455
10640
|
$__uris = [];
|
|
@@ -10509,6 +10694,9 @@ var KnishIOClient = class {
|
|
|
10509
10694
|
logging,
|
|
10510
10695
|
defaultRequestPolicy
|
|
10511
10696
|
});
|
|
10697
|
+
if (config.secretStorage) {
|
|
10698
|
+
this.$__secretStorage = config.secretStorage;
|
|
10699
|
+
}
|
|
10512
10700
|
}
|
|
10513
10701
|
/**
|
|
10514
10702
|
* Initializes a new Knish.IO client session
|
|
@@ -10610,6 +10798,7 @@ var KnishIOClient = class {
|
|
|
10610
10798
|
reset() {
|
|
10611
10799
|
this.$__secret = "";
|
|
10612
10800
|
this.$__bundle = "";
|
|
10801
|
+
this.$__secretStorage = null;
|
|
10613
10802
|
this.$__encrypt = false;
|
|
10614
10803
|
this.$__cellSlug = null;
|
|
10615
10804
|
this.$__authToken = null;
|
|
@@ -10671,7 +10860,7 @@ var KnishIOClient = class {
|
|
|
10671
10860
|
* Returns whether a secret is stored for this session
|
|
10672
10861
|
*/
|
|
10673
10862
|
hasSecret() {
|
|
10674
|
-
return !!this.$__secret && this.$__secret.length > 0;
|
|
10863
|
+
return !!this.$__secret && this.$__secret.length > 0 || !!this.$__secretStorage && !!this.$__bundle && this.$__bundle.length > 0;
|
|
10675
10864
|
}
|
|
10676
10865
|
/**
|
|
10677
10866
|
* Returns the stored secret
|
|
@@ -10682,6 +10871,33 @@ var KnishIOClient = class {
|
|
|
10682
10871
|
}
|
|
10683
10872
|
return this.$__secret;
|
|
10684
10873
|
}
|
|
10874
|
+
/**
|
|
10875
|
+
* Sets the secret storage provider and optionally sets the bundle hash
|
|
10876
|
+
*/
|
|
10877
|
+
setSecretStorage(storage, bundleHash) {
|
|
10878
|
+
this.$__secretStorage = storage;
|
|
10879
|
+
if (bundleHash) {
|
|
10880
|
+
this.$__bundle = bundleHash;
|
|
10881
|
+
}
|
|
10882
|
+
}
|
|
10883
|
+
/**
|
|
10884
|
+
* Returns current secret storage provider
|
|
10885
|
+
*/
|
|
10886
|
+
getSecretStorage() {
|
|
10887
|
+
return this.$__secretStorage;
|
|
10888
|
+
}
|
|
10889
|
+
/**
|
|
10890
|
+
* Asynchronously retrieves the secret from storage or returns in-memory secret
|
|
10891
|
+
*/
|
|
10892
|
+
async retrieveSecret(options) {
|
|
10893
|
+
if (this.$__secret && this.$__secret.length > 0) {
|
|
10894
|
+
return this.$__secret;
|
|
10895
|
+
}
|
|
10896
|
+
if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
|
|
10897
|
+
return await this.$__secretStorage.retrieveSecret(this.$__bundle, options);
|
|
10898
|
+
}
|
|
10899
|
+
return null;
|
|
10900
|
+
}
|
|
10685
10901
|
/**
|
|
10686
10902
|
* Returns whether a bundle hash is being stored for this session
|
|
10687
10903
|
*/
|
|
@@ -10720,6 +10936,13 @@ var KnishIOClient = class {
|
|
|
10720
10936
|
remainderWallet = null
|
|
10721
10937
|
} = {}) {
|
|
10722
10938
|
this.log("info", "KnishIOClient::createMolecule() - Creating a new molecule...");
|
|
10939
|
+
if (!secret) {
|
|
10940
|
+
if (this.$__secret && this.$__secret.length > 0) {
|
|
10941
|
+
secret = this.getSecret();
|
|
10942
|
+
} else if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
|
|
10943
|
+
secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
|
|
10944
|
+
}
|
|
10945
|
+
}
|
|
10723
10946
|
secret = secret || this.getSecret();
|
|
10724
10947
|
bundle = bundle || this.getBundle();
|
|
10725
10948
|
let continuIdPosition = null;
|
|
@@ -10749,6 +10972,7 @@ var KnishIOClient = class {
|
|
|
10749
10972
|
}));
|
|
10750
10973
|
return new Molecule({
|
|
10751
10974
|
secret,
|
|
10975
|
+
bundle,
|
|
10752
10976
|
sourceWallet,
|
|
10753
10977
|
remainderWallet: this.getRemainderWallet(),
|
|
10754
10978
|
cellSlug: this.getCellSlug(),
|
|
@@ -10795,8 +11019,9 @@ var KnishIOClient = class {
|
|
|
10795
11019
|
async executeQuery(query, variables = null, context = {}) {
|
|
10796
11020
|
if (this.$__authToken && this.$__authToken.isExpired() && !this.$__authInProcess) {
|
|
10797
11021
|
this.log("info", "KnishIOClient::executeQuery() - Access token is expired. Getting new one...");
|
|
11022
|
+
const authSecret = this.$__secret || await this.retrieveSecret() || "";
|
|
10798
11023
|
await this.requestAuthToken({
|
|
10799
|
-
secret:
|
|
11024
|
+
secret: authSecret,
|
|
10800
11025
|
cellSlug: this.$__cellSlug,
|
|
10801
11026
|
encrypt: this.$__encrypt
|
|
10802
11027
|
});
|
|
@@ -10836,6 +11061,13 @@ var KnishIOClient = class {
|
|
|
10836
11061
|
setSecret(secret) {
|
|
10837
11062
|
this.$__secret = secret;
|
|
10838
11063
|
this.$__bundle = generateBundleHash(secret);
|
|
11064
|
+
if (!this.$__secretStorage) {
|
|
11065
|
+
const memStorage = new MemorySecretStorageProvider();
|
|
11066
|
+
memStorage.storeSecret(this.$__bundle, secret);
|
|
11067
|
+
this.$__secretStorage = memStorage;
|
|
11068
|
+
} else {
|
|
11069
|
+
this.$__secretStorage.storeSecret(this.$__bundle, secret);
|
|
11070
|
+
}
|
|
10839
11071
|
}
|
|
10840
11072
|
/**
|
|
10841
11073
|
* Sets the auth token for this session
|
|
@@ -11019,6 +11251,9 @@ var KnishIOClient = class {
|
|
|
11019
11251
|
if (secret === null && seed) {
|
|
11020
11252
|
secret = generateSecret(seed);
|
|
11021
11253
|
}
|
|
11254
|
+
if (secret === null && this.$__secretStorage && this.$__bundle) {
|
|
11255
|
+
secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
|
|
11256
|
+
}
|
|
11022
11257
|
if (cellSlug) {
|
|
11023
11258
|
this.setCellSlug(cellSlug);
|
|
11024
11259
|
}
|
|
@@ -11864,15 +12099,30 @@ var KnishIOClient = class {
|
|
|
11864
12099
|
async createPolicy({
|
|
11865
12100
|
metaType,
|
|
11866
12101
|
metaId,
|
|
11867
|
-
policy =
|
|
12102
|
+
policy = {}
|
|
11868
12103
|
}) {
|
|
11869
12104
|
this.log("info", `KnishIOClient::createPolicy() - Creating policy for ${metaType}:${metaId}...`);
|
|
11870
|
-
|
|
12105
|
+
const molecule = await this.createMolecule({});
|
|
12106
|
+
molecule.addPolicyAtom({
|
|
11871
12107
|
metaType,
|
|
11872
12108
|
metaId,
|
|
11873
|
-
meta:
|
|
11874
|
-
policy
|
|
12109
|
+
meta: {},
|
|
12110
|
+
policy: policy || {}
|
|
12111
|
+
});
|
|
12112
|
+
molecule.addContinuIdAtom();
|
|
12113
|
+
molecule.sign({
|
|
12114
|
+
bundle: this.getBundle()
|
|
11875
12115
|
});
|
|
12116
|
+
molecule.check();
|
|
12117
|
+
const query = await this.createMoleculeMutation({
|
|
12118
|
+
mutationClass: MutationProposeMolecule,
|
|
12119
|
+
molecule
|
|
12120
|
+
});
|
|
12121
|
+
const response = await this.executeQuery(query);
|
|
12122
|
+
if (!response) {
|
|
12123
|
+
throw new CodeException("Policy creation failed");
|
|
12124
|
+
}
|
|
12125
|
+
return response;
|
|
11876
12126
|
}
|
|
11877
12127
|
/**
|
|
11878
12128
|
* Create an identifier
|
|
@@ -12156,7 +12406,297 @@ var KnishIOClient = class {
|
|
|
12156
12406
|
// src/index.ts
|
|
12157
12407
|
init_Response();
|
|
12158
12408
|
init_exception();
|
|
12159
|
-
|
|
12409
|
+
|
|
12410
|
+
// src/storage/WebCryptoSecretStorageProvider.ts
|
|
12411
|
+
init_SecretStorageException();
|
|
12412
|
+
var MemoryStorageBackend = class {
|
|
12413
|
+
store = /* @__PURE__ */ new Map();
|
|
12414
|
+
getItem(key) {
|
|
12415
|
+
return this.store.get(key) ?? null;
|
|
12416
|
+
}
|
|
12417
|
+
setItem(key, value) {
|
|
12418
|
+
this.store.set(key, value);
|
|
12419
|
+
}
|
|
12420
|
+
removeItem(key) {
|
|
12421
|
+
return this.store.delete(key);
|
|
12422
|
+
}
|
|
12423
|
+
keys() {
|
|
12424
|
+
return Array.from(this.store.keys());
|
|
12425
|
+
}
|
|
12426
|
+
};
|
|
12427
|
+
function uint8ArrayToBase64(bytes) {
|
|
12428
|
+
let binary = "";
|
|
12429
|
+
const len = bytes.byteLength;
|
|
12430
|
+
for (let i = 0; i < len; i++) {
|
|
12431
|
+
const byte = bytes[i];
|
|
12432
|
+
if (byte !== void 0) {
|
|
12433
|
+
binary += String.fromCharCode(byte);
|
|
12434
|
+
}
|
|
12435
|
+
}
|
|
12436
|
+
return btoa(binary);
|
|
12437
|
+
}
|
|
12438
|
+
function base64ToUint8Array(base64) {
|
|
12439
|
+
const binary = atob(base64);
|
|
12440
|
+
const len = binary.length;
|
|
12441
|
+
const bytes = new Uint8Array(len);
|
|
12442
|
+
for (let i = 0; i < len; i++) {
|
|
12443
|
+
bytes[i] = binary.charCodeAt(i);
|
|
12444
|
+
}
|
|
12445
|
+
return bytes;
|
|
12446
|
+
}
|
|
12447
|
+
var textEncoder2 = new TextEncoder();
|
|
12448
|
+
var textDecoder = new TextDecoder();
|
|
12449
|
+
var KEY_PREFIX = "knishio:secret:";
|
|
12450
|
+
var DEFAULT_ITERATIONS = 1e5;
|
|
12451
|
+
var WebCryptoSecretStorageProvider = class {
|
|
12452
|
+
providerType = "webcrypto-aes-gcm";
|
|
12453
|
+
backend;
|
|
12454
|
+
defaultPassphrase;
|
|
12455
|
+
hardwareBacked;
|
|
12456
|
+
constructor(options = {}) {
|
|
12457
|
+
this.backend = options.backend ?? new MemoryStorageBackend();
|
|
12458
|
+
this.defaultPassphrase = options.defaultPassphrase;
|
|
12459
|
+
this.hardwareBacked = options.hardwareBacked ?? false;
|
|
12460
|
+
}
|
|
12461
|
+
/**
|
|
12462
|
+
* Whether this provider is backed by hardware (e.g. WebAuthn PRF wrapping)
|
|
12463
|
+
*/
|
|
12464
|
+
isHardwareBacked() {
|
|
12465
|
+
return this.hardwareBacked;
|
|
12466
|
+
}
|
|
12467
|
+
/**
|
|
12468
|
+
* Check if WebCrypto subtle API is available
|
|
12469
|
+
*/
|
|
12470
|
+
async isAvailable() {
|
|
12471
|
+
return typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined";
|
|
12472
|
+
}
|
|
12473
|
+
/**
|
|
12474
|
+
* Derive an AES-GCM CryptoKey from a passphrase and salt using PBKDF2
|
|
12475
|
+
*/
|
|
12476
|
+
async deriveKey(passphrase, salt, iterations = DEFAULT_ITERATIONS) {
|
|
12477
|
+
if (!await this.isAvailable()) {
|
|
12478
|
+
throw SecretStorageException.unavailable(this.providerType, "WebCrypto API is not available");
|
|
12479
|
+
}
|
|
12480
|
+
const passphraseBytes = textEncoder2.encode(passphrase);
|
|
12481
|
+
try {
|
|
12482
|
+
const baseKey = await globalThis.crypto.subtle.importKey(
|
|
12483
|
+
"raw",
|
|
12484
|
+
passphraseBytes,
|
|
12485
|
+
"PBKDF2",
|
|
12486
|
+
false,
|
|
12487
|
+
["deriveKey"]
|
|
12488
|
+
);
|
|
12489
|
+
return await globalThis.crypto.subtle.deriveKey(
|
|
12490
|
+
{
|
|
12491
|
+
name: "PBKDF2",
|
|
12492
|
+
salt,
|
|
12493
|
+
iterations,
|
|
12494
|
+
hash: "SHA-256"
|
|
12495
|
+
},
|
|
12496
|
+
baseKey,
|
|
12497
|
+
{ name: "AES-GCM", length: 256 },
|
|
12498
|
+
false,
|
|
12499
|
+
["encrypt", "decrypt"]
|
|
12500
|
+
);
|
|
12501
|
+
} finally {
|
|
12502
|
+
zeroizeBytes(passphraseBytes);
|
|
12503
|
+
}
|
|
12504
|
+
}
|
|
12505
|
+
/**
|
|
12506
|
+
* Store and encrypt a master secret
|
|
12507
|
+
*/
|
|
12508
|
+
async storeSecret(bundleHash, secret, options) {
|
|
12509
|
+
if (!bundleHash) {
|
|
12510
|
+
throw new SecretStorageException("Bundle hash cannot be empty");
|
|
12511
|
+
}
|
|
12512
|
+
if (!secret) {
|
|
12513
|
+
throw new SecretStorageException("Secret cannot be empty");
|
|
12514
|
+
}
|
|
12515
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
12516
|
+
if (!passphrase) {
|
|
12517
|
+
throw new SecretStorageException("Passphrase required for envelope encryption");
|
|
12518
|
+
}
|
|
12519
|
+
const salt = new Uint8Array(16);
|
|
12520
|
+
const iv = new Uint8Array(12);
|
|
12521
|
+
globalThis.crypto.getRandomValues(salt);
|
|
12522
|
+
globalThis.crypto.getRandomValues(iv);
|
|
12523
|
+
const key = await this.deriveKey(passphrase, salt, DEFAULT_ITERATIONS);
|
|
12524
|
+
const secretBytes = textEncoder2.encode(secret);
|
|
12525
|
+
try {
|
|
12526
|
+
const encryptedBuffer = await globalThis.crypto.subtle.encrypt(
|
|
12527
|
+
{
|
|
12528
|
+
name: "AES-GCM",
|
|
12529
|
+
iv
|
|
12530
|
+
},
|
|
12531
|
+
key,
|
|
12532
|
+
secretBytes
|
|
12533
|
+
);
|
|
12534
|
+
const ciphertext = uint8ArrayToBase64(new Uint8Array(encryptedBuffer));
|
|
12535
|
+
const metadata = {
|
|
12536
|
+
bundleHash,
|
|
12537
|
+
label: options?.label,
|
|
12538
|
+
createdAt: Date.now(),
|
|
12539
|
+
hardwareBacked: this.hardwareBacked,
|
|
12540
|
+
providerType: this.providerType
|
|
12541
|
+
};
|
|
12542
|
+
const payload = {
|
|
12543
|
+
version: 1,
|
|
12544
|
+
ciphertext,
|
|
12545
|
+
iv: uint8ArrayToBase64(iv),
|
|
12546
|
+
salt: uint8ArrayToBase64(salt),
|
|
12547
|
+
algorithm: "AES-GCM",
|
|
12548
|
+
iterations: DEFAULT_ITERATIONS,
|
|
12549
|
+
metadata
|
|
12550
|
+
};
|
|
12551
|
+
await this.backend.setItem(`${KEY_PREFIX}${bundleHash}`, JSON.stringify(payload));
|
|
12552
|
+
} catch (err) {
|
|
12553
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12554
|
+
throw new SecretStorageException(`Encryption failed: ${msg}`);
|
|
12555
|
+
} finally {
|
|
12556
|
+
zeroizeBytes(secretBytes);
|
|
12557
|
+
}
|
|
12558
|
+
}
|
|
12559
|
+
/**
|
|
12560
|
+
* Retrieve and decrypt the master secret
|
|
12561
|
+
*/
|
|
12562
|
+
async retrieveSecret(bundleHash, options) {
|
|
12563
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
12564
|
+
if (!raw) {
|
|
12565
|
+
return null;
|
|
12566
|
+
}
|
|
12567
|
+
let payload;
|
|
12568
|
+
try {
|
|
12569
|
+
payload = JSON.parse(raw);
|
|
12570
|
+
} catch {
|
|
12571
|
+
throw SecretStorageException.decryptionFailed("Corrupted payload format");
|
|
12572
|
+
}
|
|
12573
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
12574
|
+
if (!passphrase) {
|
|
12575
|
+
throw new SecretStorageException("Passphrase required for secret decryption");
|
|
12576
|
+
}
|
|
12577
|
+
const salt = base64ToUint8Array(payload.salt);
|
|
12578
|
+
const iv = base64ToUint8Array(payload.iv);
|
|
12579
|
+
const ciphertext = base64ToUint8Array(payload.ciphertext);
|
|
12580
|
+
try {
|
|
12581
|
+
const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
|
|
12582
|
+
const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
|
|
12583
|
+
{
|
|
12584
|
+
name: "AES-GCM",
|
|
12585
|
+
iv
|
|
12586
|
+
},
|
|
12587
|
+
key,
|
|
12588
|
+
ciphertext
|
|
12589
|
+
);
|
|
12590
|
+
const decryptedBytes = new Uint8Array(decryptedBuffer);
|
|
12591
|
+
try {
|
|
12592
|
+
return textDecoder.decode(decryptedBytes);
|
|
12593
|
+
} finally {
|
|
12594
|
+
zeroizeBytes(decryptedBytes);
|
|
12595
|
+
}
|
|
12596
|
+
} catch (err) {
|
|
12597
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12598
|
+
throw SecretStorageException.decryptionFailed(msg);
|
|
12599
|
+
}
|
|
12600
|
+
}
|
|
12601
|
+
/**
|
|
12602
|
+
* Delete a stored secret
|
|
12603
|
+
*/
|
|
12604
|
+
async deleteSecret(bundleHash) {
|
|
12605
|
+
const key = `${KEY_PREFIX}${bundleHash}`;
|
|
12606
|
+
const result = await this.backend.removeItem(key);
|
|
12607
|
+
return result !== false;
|
|
12608
|
+
}
|
|
12609
|
+
/**
|
|
12610
|
+
* Check if a secret exists
|
|
12611
|
+
*/
|
|
12612
|
+
async hasSecret(bundleHash) {
|
|
12613
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
12614
|
+
return raw !== null;
|
|
12615
|
+
}
|
|
12616
|
+
/**
|
|
12617
|
+
* List all stored secret metadata
|
|
12618
|
+
*/
|
|
12619
|
+
async listSecrets() {
|
|
12620
|
+
const keys = await this.backend.keys();
|
|
12621
|
+
const matchingKeys = keys.filter((k) => k.startsWith(KEY_PREFIX));
|
|
12622
|
+
const results = [];
|
|
12623
|
+
for (const key of matchingKeys) {
|
|
12624
|
+
const raw = await this.backend.getItem(key);
|
|
12625
|
+
if (raw) {
|
|
12626
|
+
try {
|
|
12627
|
+
const payload = JSON.parse(raw);
|
|
12628
|
+
if (payload.metadata) {
|
|
12629
|
+
results.push(payload.metadata);
|
|
12630
|
+
}
|
|
12631
|
+
} catch {
|
|
12632
|
+
}
|
|
12633
|
+
}
|
|
12634
|
+
}
|
|
12635
|
+
return results;
|
|
12636
|
+
}
|
|
12637
|
+
/**
|
|
12638
|
+
* Execute callback with unwrapped secret, zeroizing the decrypted buffer upon completion
|
|
12639
|
+
*/
|
|
12640
|
+
async withSecret(bundleHash, fn, options) {
|
|
12641
|
+
const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
|
|
12642
|
+
if (!raw) {
|
|
12643
|
+
throw SecretStorageException.notFound(bundleHash);
|
|
12644
|
+
}
|
|
12645
|
+
let payload;
|
|
12646
|
+
try {
|
|
12647
|
+
payload = JSON.parse(raw);
|
|
12648
|
+
} catch {
|
|
12649
|
+
throw SecretStorageException.decryptionFailed("Corrupted payload format");
|
|
12650
|
+
}
|
|
12651
|
+
const passphrase = options?.passphrase ?? this.defaultPassphrase;
|
|
12652
|
+
if (!passphrase) {
|
|
12653
|
+
throw new SecretStorageException("Passphrase required for secret decryption");
|
|
12654
|
+
}
|
|
12655
|
+
const salt = base64ToUint8Array(payload.salt);
|
|
12656
|
+
const iv = base64ToUint8Array(payload.iv);
|
|
12657
|
+
const ciphertext = base64ToUint8Array(payload.ciphertext);
|
|
12658
|
+
try {
|
|
12659
|
+
const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
|
|
12660
|
+
const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
|
|
12661
|
+
{
|
|
12662
|
+
name: "AES-GCM",
|
|
12663
|
+
iv
|
|
12664
|
+
},
|
|
12665
|
+
key,
|
|
12666
|
+
ciphertext
|
|
12667
|
+
);
|
|
12668
|
+
const decryptedBytes = new Uint8Array(decryptedBuffer);
|
|
12669
|
+
return await withSecureBytes(decryptedBytes, async (bytes) => {
|
|
12670
|
+
const secretString = textDecoder.decode(bytes);
|
|
12671
|
+
return await fn(secretString);
|
|
12672
|
+
});
|
|
12673
|
+
} catch (err) {
|
|
12674
|
+
if (err instanceof SecretStorageException) {
|
|
12675
|
+
throw err;
|
|
12676
|
+
}
|
|
12677
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12678
|
+
throw SecretStorageException.decryptionFailed(msg);
|
|
12679
|
+
}
|
|
12680
|
+
}
|
|
12681
|
+
};
|
|
12682
|
+
|
|
12683
|
+
// src/storage/index.ts
|
|
12684
|
+
function createDefaultSecretStorage(options = {}) {
|
|
12685
|
+
if (options.type === "memory") {
|
|
12686
|
+
return new MemorySecretStorageProvider();
|
|
12687
|
+
}
|
|
12688
|
+
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined") {
|
|
12689
|
+
return new WebCryptoSecretStorageProvider({
|
|
12690
|
+
backend: options.backend,
|
|
12691
|
+
defaultPassphrase: options.defaultPassphrase,
|
|
12692
|
+
hardwareBacked: options.hardwareBacked
|
|
12693
|
+
});
|
|
12694
|
+
}
|
|
12695
|
+
return new MemorySecretStorageProvider();
|
|
12696
|
+
}
|
|
12697
|
+
|
|
12698
|
+
// src/index.ts
|
|
12699
|
+
var SDK_VERSION = "0.9.8";
|
|
12160
12700
|
var SDK_NAME = "KnishIO-Client-TS";
|
|
12161
12701
|
var COMPATIBLE_SERVER_VERSIONS = [4, 5];
|
|
12162
12702
|
var SDK_INFO = {
|
|
@@ -12246,6 +12786,6 @@ var KnishIO = {
|
|
|
12246
12786
|
SDK_INFO
|
|
12247
12787
|
};
|
|
12248
12788
|
|
|
12249
|
-
export { Atom, AtomIndexException, AtomMeta, AtomsMissingException, AuthToken, BaseException, COMPATIBLE_SERVER_VERSIONS, CRYPTO_CONSTANTS, CheckMolecule, DevUtils, Dot, EXCEPTION_CODES, EXCEPTION_TYPES, EXTENDED_COMPATIBILITY_TEST_VECTORS, ExceptionFactory, GraphQLClient, InvalidResponseException, KnishIO, KnishIOClient, Meta, MolecularHashMismatchException, Molecule, Mutation, MutationAppendRequest, MutationCreateMeta, MutationCreateToken, MutationCreateWallet, MutationPeering, MutationProposeMolecule, MutationRequestAuthorization, MutationRequestTokens, MutationTransferTokens, PolicyMeta, Query, QueryAtom, QueryBalance, QueryBatch, QueryContinuId, QueryEmbeddingStatus, QueryMetaType, QueryMetaTypeViaAtom, QueryWalletBundle, QueryWalletList, Response2 as Response, ResponseAppendRequest, ResponseAtom, ResponseBalance, ResponseContinuId, ResponseCreateMeta, ResponseCreateToken, ResponseCreateWallet, ResponseEmbeddingStatus, ResponseMetaType, ResponseMetaTypeViaAtom, ResponsePeering, ResponseProposeMolecule, ResponseRequestAuthorization, ResponseRequestTokens, ResponseTransferTokens, ResponseWalletBundle, ResponseWalletList, SDK_INFO, SDK_NAME, SDK_VERSION, SignatureMismatchException, TokenUnit, TransferBalanceException, Wallet, WalletCredentialException, base64ToHex, bufferToHexString, capitalize, charsetBaseConvert, chunkArray, chunkSubstr, configureSDK, convertToBase17, createBundleHash, createMolecularHash, createPosition, createTokenSlug, createWalletAddress, deepCloning, diff, enumerateMolecularHash, generateBatchId, generateBundleHash, generateOTSSignature, generatePosition, generateSecret, generateWalletAddress, generateWalletKey, getSDKConfig, hexStringToBuffer, hexToBase64, intersect, isAtomIsotope, isBundleHash, isHex, isHexString, isMolecularHash, isNumeric, isPosition2 as isPosition, isWalletAddress2 as isWalletAddress, normalizeMolecularHash, randomString, runCompatibilityTests, runExtendedCompatibilityTests, shake256, toCamelCase, toSnakeCase, truncate, validateBundleHash, validateMolecularHashForSignature, validateOTSSignature, validatePosition, validateSecret, validateWalletAddress, verifyOTSSignature };
|
|
12789
|
+
export { Atom, AtomIndexException, AtomMeta, AtomsMissingException, AuthToken, BaseException, COMPATIBLE_SERVER_VERSIONS, CRYPTO_CONSTANTS, CheckMolecule, DevUtils, Dot, EXCEPTION_CODES, EXCEPTION_TYPES, EXTENDED_COMPATIBILITY_TEST_VECTORS, ExceptionFactory, GraphQLClient, InvalidResponseException, KnishIO, KnishIOClient, MemorySecretStorageProvider, MemoryStorageBackend, Meta, MolecularHashMismatchException, Molecule, Mutation, MutationAppendRequest, MutationCreateMeta, MutationCreateToken, MutationCreateWallet, MutationPeering, MutationProposeMolecule, MutationRequestAuthorization, MutationRequestTokens, MutationTransferTokens, PolicyMeta, Query, QueryAtom, QueryBalance, QueryBatch, QueryContinuId, QueryEmbeddingStatus, QueryMetaType, QueryMetaTypeViaAtom, QueryWalletBundle, QueryWalletList, Response2 as Response, ResponseAppendRequest, ResponseAtom, ResponseBalance, ResponseContinuId, ResponseCreateMeta, ResponseCreateToken, ResponseCreateWallet, ResponseEmbeddingStatus, ResponseMetaType, ResponseMetaTypeViaAtom, ResponsePeering, ResponseProposeMolecule, ResponseRequestAuthorization, ResponseRequestTokens, ResponseTransferTokens, ResponseWalletBundle, ResponseWalletList, SDK_INFO, SDK_NAME, SDK_VERSION, SecretStorageException, SignatureMismatchException, TokenUnit, TransferBalanceException, Wallet, WalletCredentialException, WebCryptoSecretStorageProvider, base64ToHex, bufferToHexString, capitalize, charsetBaseConvert, chunkArray, chunkSubstr, configureSDK, constantTimeCompare, convertToBase17, createBundleHash, createDefaultSecretStorage, createMolecularHash, createPosition, createTokenSlug, createWalletAddress, deepCloning, diff, enumerateMolecularHash, generateBatchId, generateBundleHash, generateOTSSignature, generatePosition, generateSecret, generateWalletAddress, generateWalletKey, getSDKConfig, hexStringToBuffer, hexToBase64, intersect, isAtomIsotope, isBundleHash, isHex, isHexString, isMolecularHash, isNumeric, isPosition2 as isPosition, isWalletAddress2 as isWalletAddress, normalizeMolecularHash, randomString, runCompatibilityTests, runExtendedCompatibilityTests, shake256, toCamelCase, toSnakeCase, truncate, validateBundleHash, validateMolecularHashForSignature, validateOTSSignature, validatePosition, validateSecret, validateWalletAddress, verifyOTSSignature, withSecureBytes, withSecureString, zeroizeBytes };
|
|
12250
12790
|
//# sourceMappingURL=index.js.map
|
|
12251
12791
|
//# sourceMappingURL=index.js.map
|