@tinycloud/sdk-services 2.2.0-beta.13 → 2.2.0
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/{BaseService-BiS6HRwE.d.cts → BaseService-C_iXlTeN.d.cts} +6 -1
- package/dist/{BaseService-BiS6HRwE.d.ts → BaseService-C_iXlTeN.d.ts} +6 -1
- package/dist/encryption/index.cjs +1340 -0
- package/dist/encryption/index.cjs.map +1 -0
- package/dist/encryption/index.d.cts +802 -0
- package/dist/encryption/index.d.ts +802 -0
- package/dist/encryption/index.js +1274 -0
- package/dist/encryption/index.js.map +1 -0
- package/dist/index.cjs +1321 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -5
- package/dist/index.d.ts +26 -5
- package/dist/index.js +1280 -4
- package/dist/index.js.map +1 -1
- package/dist/kv/index.cjs.map +1 -1
- package/dist/kv/index.d.cts +1 -1
- package/dist/kv/index.d.ts +1 -1
- package/dist/kv/index.js.map +1 -1
- package/dist/sql/index.cjs.map +1 -1
- package/dist/sql/index.d.cts +1 -1
- package/dist/sql/index.d.ts +1 -1
- package/dist/sql/index.js.map +1 -1
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -2937,6 +2937,12 @@ function base64Decode(str) {
|
|
|
2937
2937
|
}
|
|
2938
2938
|
return bytes;
|
|
2939
2939
|
}
|
|
2940
|
+
function unwrapKVData(value) {
|
|
2941
|
+
if (value !== null && typeof value === "object" && "data" in value) {
|
|
2942
|
+
return value.data;
|
|
2943
|
+
}
|
|
2944
|
+
return value;
|
|
2945
|
+
}
|
|
2940
2946
|
function isUnlockSigner(signer) {
|
|
2941
2947
|
return typeof signer === "object" && signer !== null && typeof signer.signMessage === "function";
|
|
2942
2948
|
}
|
|
@@ -2991,13 +2997,16 @@ var DataVaultService = class extends BaseService {
|
|
|
2991
2997
|
* Whether the vault is currently unlocked.
|
|
2992
2998
|
*/
|
|
2993
2999
|
get isUnlocked() {
|
|
2994
|
-
return this._isUnlocked;
|
|
3000
|
+
return this.usesNetworkEncryption || this._isUnlocked;
|
|
2995
3001
|
}
|
|
2996
3002
|
/**
|
|
2997
3003
|
* The vault's public encryption key (X25519).
|
|
2998
3004
|
* Throws if vault is locked.
|
|
2999
3005
|
*/
|
|
3000
3006
|
get publicKey() {
|
|
3007
|
+
if (this.usesNetworkEncryption) {
|
|
3008
|
+
throw new Error("Network-encrypted vaults do not expose a local public key");
|
|
3009
|
+
}
|
|
3001
3010
|
if (!this.encryptionIdentity) {
|
|
3002
3011
|
throw new Error("Vault is locked");
|
|
3003
3012
|
}
|
|
@@ -3015,12 +3024,51 @@ var DataVaultService = class extends BaseService {
|
|
|
3015
3024
|
get tc() {
|
|
3016
3025
|
return this.vaultConfig.tc;
|
|
3017
3026
|
}
|
|
3027
|
+
get networkEncryption() {
|
|
3028
|
+
return this.vaultConfig.encryption;
|
|
3029
|
+
}
|
|
3030
|
+
get usesNetworkEncryption() {
|
|
3031
|
+
return this.networkEncryption !== void 0;
|
|
3032
|
+
}
|
|
3018
3033
|
/**
|
|
3019
3034
|
* Get the host URL.
|
|
3020
3035
|
*/
|
|
3021
3036
|
get host() {
|
|
3022
3037
|
return this.tc.hosts[0];
|
|
3023
3038
|
}
|
|
3039
|
+
async decryptCapabilityProof() {
|
|
3040
|
+
const proof = this.networkEncryption?.decryptCapabilityProof;
|
|
3041
|
+
if (typeof proof === "function") {
|
|
3042
|
+
return await proof();
|
|
3043
|
+
}
|
|
3044
|
+
return proof ?? { proofs: [] };
|
|
3045
|
+
}
|
|
3046
|
+
serializeValue(value, options) {
|
|
3047
|
+
let plaintext;
|
|
3048
|
+
if (value instanceof Uint8Array) {
|
|
3049
|
+
plaintext = value;
|
|
3050
|
+
} else if (options?.serialize) {
|
|
3051
|
+
plaintext = options.serialize(value);
|
|
3052
|
+
} else if (typeof value === "string") {
|
|
3053
|
+
plaintext = toBytes(value);
|
|
3054
|
+
} else {
|
|
3055
|
+
plaintext = toBytes(JSON.stringify(value));
|
|
3056
|
+
}
|
|
3057
|
+
const contentType = options?.contentType ?? (value instanceof Uint8Array ? "application/octet-stream" : "application/json");
|
|
3058
|
+
return { plaintext, contentType };
|
|
3059
|
+
}
|
|
3060
|
+
deserializeValue(plaintext, contentType, options) {
|
|
3061
|
+
if (options?.raw) {
|
|
3062
|
+
return plaintext;
|
|
3063
|
+
}
|
|
3064
|
+
if (options?.deserialize) {
|
|
3065
|
+
return options.deserialize(plaintext);
|
|
3066
|
+
}
|
|
3067
|
+
if (contentType === "application/json") {
|
|
3068
|
+
return JSON.parse(fromBytes(plaintext));
|
|
3069
|
+
}
|
|
3070
|
+
return plaintext;
|
|
3071
|
+
}
|
|
3024
3072
|
// =========================================================================
|
|
3025
3073
|
// Phase 1: Core Operations
|
|
3026
3074
|
// =========================================================================
|
|
@@ -3037,6 +3085,10 @@ var DataVaultService = class extends BaseService {
|
|
|
3037
3085
|
* signatures exist (browser only).
|
|
3038
3086
|
*/
|
|
3039
3087
|
async unlock(signer) {
|
|
3088
|
+
if (this.usesNetworkEncryption) {
|
|
3089
|
+
this._isUnlocked = true;
|
|
3090
|
+
return { ok: true, data: void 0 };
|
|
3091
|
+
}
|
|
3040
3092
|
const unlockSigner = isUnlockSigner(signer) ? signer : void 0;
|
|
3041
3093
|
if (this._isUnlocked && this.masterKey && (this.encryptionIdentity || !unlockSigner)) {
|
|
3042
3094
|
return { ok: true, data: void 0 };
|
|
@@ -3163,6 +3215,131 @@ var DataVaultService = class extends BaseService {
|
|
|
3163
3215
|
this.lock();
|
|
3164
3216
|
super.onSignOut();
|
|
3165
3217
|
}
|
|
3218
|
+
async putNetworkEncrypted(key, value, options) {
|
|
3219
|
+
const config = this.networkEncryption;
|
|
3220
|
+
if (!config) {
|
|
3221
|
+
return vaultError({
|
|
3222
|
+
code: "VAULT_LOCKED",
|
|
3223
|
+
message: "Network encryption is not configured"
|
|
3224
|
+
});
|
|
3225
|
+
}
|
|
3226
|
+
if (!this.requireAuth()) {
|
|
3227
|
+
return vaultError({
|
|
3228
|
+
code: "VAULT_LOCKED",
|
|
3229
|
+
message: "Authentication required"
|
|
3230
|
+
});
|
|
3231
|
+
}
|
|
3232
|
+
try {
|
|
3233
|
+
const { plaintext, contentType } = this.serializeValue(value, options);
|
|
3234
|
+
const metadata = {
|
|
3235
|
+
[VaultHeaders.VERSION]: "2",
|
|
3236
|
+
[VaultHeaders.CIPHER]: "tinycloud-network-envelope",
|
|
3237
|
+
[VaultHeaders.CONTENT_TYPE]: contentType,
|
|
3238
|
+
...options?.metadata ?? {}
|
|
3239
|
+
};
|
|
3240
|
+
const aad = toBytes(`tinycloud.vault:${this.vaultConfig.spaceId}:${key}`);
|
|
3241
|
+
const envelopeResult = await config.service.encryptToNetwork(
|
|
3242
|
+
config.networkId,
|
|
3243
|
+
plaintext,
|
|
3244
|
+
{ aad, metadata }
|
|
3245
|
+
);
|
|
3246
|
+
if (!envelopeResult.ok) {
|
|
3247
|
+
return vaultError({
|
|
3248
|
+
code: "STORAGE_ERROR",
|
|
3249
|
+
cause: new Error(envelopeResult.error.message)
|
|
3250
|
+
});
|
|
3251
|
+
}
|
|
3252
|
+
const valuePutResult = await this.tc.kv.put(
|
|
3253
|
+
`vault/${key}`,
|
|
3254
|
+
JSON.stringify(envelopeResult.data)
|
|
3255
|
+
);
|
|
3256
|
+
if (!valuePutResult.ok) {
|
|
3257
|
+
return vaultError({
|
|
3258
|
+
code: "STORAGE_ERROR",
|
|
3259
|
+
cause: new Error(
|
|
3260
|
+
`Failed to store encrypted value: ${valuePutResult.error.message}`
|
|
3261
|
+
)
|
|
3262
|
+
});
|
|
3263
|
+
}
|
|
3264
|
+
return { ok: true, data: void 0 };
|
|
3265
|
+
} catch (error) {
|
|
3266
|
+
return vaultError({
|
|
3267
|
+
code: "STORAGE_ERROR",
|
|
3268
|
+
cause: toError(error)
|
|
3269
|
+
});
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
async getNetworkEncrypted(key, options) {
|
|
3273
|
+
const config = this.networkEncryption;
|
|
3274
|
+
if (!config) {
|
|
3275
|
+
return vaultError({
|
|
3276
|
+
code: "VAULT_LOCKED",
|
|
3277
|
+
message: "Network encryption is not configured"
|
|
3278
|
+
});
|
|
3279
|
+
}
|
|
3280
|
+
if (!this.requireAuth()) {
|
|
3281
|
+
return vaultError({
|
|
3282
|
+
code: "VAULT_LOCKED",
|
|
3283
|
+
message: "Authentication required"
|
|
3284
|
+
});
|
|
3285
|
+
}
|
|
3286
|
+
try {
|
|
3287
|
+
const valueResult = await this.tc.kv.get(`vault/${key}`, {
|
|
3288
|
+
raw: true
|
|
3289
|
+
});
|
|
3290
|
+
if (!valueResult.ok) {
|
|
3291
|
+
return vaultError({ code: "KEY_NOT_FOUND", key });
|
|
3292
|
+
}
|
|
3293
|
+
const rawEnvelope = unwrapKVData(valueResult.data);
|
|
3294
|
+
const envelope = typeof rawEnvelope === "string" ? JSON.parse(rawEnvelope) : rawEnvelope;
|
|
3295
|
+
const proof = await this.decryptCapabilityProof();
|
|
3296
|
+
const plaintextResult = await config.service.decryptEnvelope(envelope, proof);
|
|
3297
|
+
if (!plaintextResult.ok) {
|
|
3298
|
+
return vaultError({
|
|
3299
|
+
code: "DECRYPTION_FAILED",
|
|
3300
|
+
message: plaintextResult.error.message
|
|
3301
|
+
});
|
|
3302
|
+
}
|
|
3303
|
+
const metadata = envelope.metadata ?? {};
|
|
3304
|
+
const contentType = metadata[VaultHeaders.CONTENT_TYPE] ?? "application/json";
|
|
3305
|
+
const keyId = metadata[VaultHeaders.KEY_ID] ?? envelope.encryptedSymmetricKeyHash.slice(0, 16);
|
|
3306
|
+
const value = this.deserializeValue(
|
|
3307
|
+
plaintextResult.data,
|
|
3308
|
+
contentType,
|
|
3309
|
+
options
|
|
3310
|
+
);
|
|
3311
|
+
return { ok: true, data: { value, metadata, keyId } };
|
|
3312
|
+
} catch (error) {
|
|
3313
|
+
return vaultError({
|
|
3314
|
+
code: "STORAGE_ERROR",
|
|
3315
|
+
cause: toError(error)
|
|
3316
|
+
});
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
async headNetworkEncrypted(key) {
|
|
3320
|
+
if (!this.requireAuth()) {
|
|
3321
|
+
return vaultError({
|
|
3322
|
+
code: "VAULT_LOCKED",
|
|
3323
|
+
message: "Authentication required"
|
|
3324
|
+
});
|
|
3325
|
+
}
|
|
3326
|
+
try {
|
|
3327
|
+
const valueResult = await this.tc.kv.get(`vault/${key}`, {
|
|
3328
|
+
raw: true
|
|
3329
|
+
});
|
|
3330
|
+
if (!valueResult.ok) {
|
|
3331
|
+
return vaultError({ code: "KEY_NOT_FOUND", key });
|
|
3332
|
+
}
|
|
3333
|
+
const rawEnvelope = unwrapKVData(valueResult.data);
|
|
3334
|
+
const envelope = typeof rawEnvelope === "string" ? JSON.parse(rawEnvelope) : rawEnvelope;
|
|
3335
|
+
return { ok: true, data: envelope.metadata ?? {} };
|
|
3336
|
+
} catch (error) {
|
|
3337
|
+
return vaultError({
|
|
3338
|
+
code: "STORAGE_ERROR",
|
|
3339
|
+
cause: toError(error)
|
|
3340
|
+
});
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3166
3343
|
/**
|
|
3167
3344
|
* Encrypt and store a value at the given key.
|
|
3168
3345
|
*
|
|
@@ -3172,6 +3349,9 @@ var DataVaultService = class extends BaseService {
|
|
|
3172
3349
|
*/
|
|
3173
3350
|
async put(key, value, options) {
|
|
3174
3351
|
return this.withTelemetry("put", key, async () => {
|
|
3352
|
+
if (this.usesNetworkEncryption) {
|
|
3353
|
+
return this.putNetworkEncrypted(key, value, options);
|
|
3354
|
+
}
|
|
3175
3355
|
if (!this._isUnlocked || !this.masterKey) {
|
|
3176
3356
|
return vaultError({
|
|
3177
3357
|
code: "VAULT_LOCKED",
|
|
@@ -3264,6 +3444,9 @@ var DataVaultService = class extends BaseService {
|
|
|
3264
3444
|
*/
|
|
3265
3445
|
async get(key, options) {
|
|
3266
3446
|
return this.withTelemetry("get", key, async () => {
|
|
3447
|
+
if (this.usesNetworkEncryption) {
|
|
3448
|
+
return this.getNetworkEncrypted(key, options);
|
|
3449
|
+
}
|
|
3267
3450
|
if (!this._isUnlocked || !this.masterKey) {
|
|
3268
3451
|
return vaultError({
|
|
3269
3452
|
code: "VAULT_LOCKED",
|
|
@@ -3331,7 +3514,7 @@ var DataVaultService = class extends BaseService {
|
|
|
3331
3514
|
*/
|
|
3332
3515
|
async delete(key) {
|
|
3333
3516
|
return this.withTelemetry("delete", key, async () => {
|
|
3334
|
-
if (!this.
|
|
3517
|
+
if (!this.isUnlocked) {
|
|
3335
3518
|
return vaultError({
|
|
3336
3519
|
code: "VAULT_LOCKED",
|
|
3337
3520
|
message: "Vault must be unlocked before deleting data"
|
|
@@ -3344,6 +3527,13 @@ var DataVaultService = class extends BaseService {
|
|
|
3344
3527
|
});
|
|
3345
3528
|
}
|
|
3346
3529
|
try {
|
|
3530
|
+
if (this.usesNetworkEncryption) {
|
|
3531
|
+
const valueDelResult2 = await this.tc.kv.delete(`vault/${key}`);
|
|
3532
|
+
if (!valueDelResult2.ok) {
|
|
3533
|
+
return vaultError({ code: "KEY_NOT_FOUND", key });
|
|
3534
|
+
}
|
|
3535
|
+
return ok(void 0);
|
|
3536
|
+
}
|
|
3347
3537
|
const [keyDelResult, valueDelResult] = await Promise.all([
|
|
3348
3538
|
this.tc.kv.delete(`keys/${key}`),
|
|
3349
3539
|
this.tc.kv.delete(`vault/${key}`)
|
|
@@ -3368,7 +3558,7 @@ var DataVaultService = class extends BaseService {
|
|
|
3368
3558
|
*/
|
|
3369
3559
|
async list(options) {
|
|
3370
3560
|
return this.withTelemetry("list", options?.prefix, async () => {
|
|
3371
|
-
if (!this.
|
|
3561
|
+
if (!this.isUnlocked) {
|
|
3372
3562
|
return vaultError({
|
|
3373
3563
|
code: "VAULT_LOCKED",
|
|
3374
3564
|
message: "Vault must be unlocked before listing data"
|
|
@@ -3418,6 +3608,9 @@ var DataVaultService = class extends BaseService {
|
|
|
3418
3608
|
*/
|
|
3419
3609
|
async head(key) {
|
|
3420
3610
|
return this.withTelemetry("head", key, async () => {
|
|
3611
|
+
if (this.usesNetworkEncryption) {
|
|
3612
|
+
return this.headNetworkEncrypted(key);
|
|
3613
|
+
}
|
|
3421
3614
|
if (!this._isUnlocked) {
|
|
3422
3615
|
return vaultError({
|
|
3423
3616
|
code: "VAULT_LOCKED",
|
|
@@ -3485,6 +3678,16 @@ var DataVaultService = class extends BaseService {
|
|
|
3485
3678
|
*/
|
|
3486
3679
|
async reencrypt(key, recipientDID, options) {
|
|
3487
3680
|
return this.withTelemetry("reencrypt", key, async () => {
|
|
3681
|
+
if (this.usesNetworkEncryption) {
|
|
3682
|
+
void recipientDID;
|
|
3683
|
+
void options;
|
|
3684
|
+
return vaultError({
|
|
3685
|
+
code: "STORAGE_ERROR",
|
|
3686
|
+
cause: new Error(
|
|
3687
|
+
"Vault key grants are deprecated for network-encrypted vaults; grant tinycloud.encryption/decrypt on the network plus KV access to vault data."
|
|
3688
|
+
)
|
|
3689
|
+
});
|
|
3690
|
+
}
|
|
3488
3691
|
if (!this._isUnlocked || !this.masterKey) {
|
|
3489
3692
|
return vaultError({
|
|
3490
3693
|
code: "VAULT_LOCKED",
|
|
@@ -3574,6 +3777,66 @@ var DataVaultService = class extends BaseService {
|
|
|
3574
3777
|
*/
|
|
3575
3778
|
async getShared(grantorDID, key, options) {
|
|
3576
3779
|
return this.withTelemetry("getShared", key, async () => {
|
|
3780
|
+
if (this.usesNetworkEncryption) {
|
|
3781
|
+
const grantorKV = options?.kv;
|
|
3782
|
+
if (!grantorKV) {
|
|
3783
|
+
return vaultError({
|
|
3784
|
+
code: "STORAGE_ERROR",
|
|
3785
|
+
cause: new Error(
|
|
3786
|
+
"getShared requires a delegated KV service via options.kv."
|
|
3787
|
+
)
|
|
3788
|
+
});
|
|
3789
|
+
}
|
|
3790
|
+
const config = this.networkEncryption;
|
|
3791
|
+
if (!config) {
|
|
3792
|
+
return vaultError({
|
|
3793
|
+
code: "VAULT_LOCKED",
|
|
3794
|
+
message: "Network encryption is not configured"
|
|
3795
|
+
});
|
|
3796
|
+
}
|
|
3797
|
+
if (!this.requireAuth()) {
|
|
3798
|
+
return vaultError({
|
|
3799
|
+
code: "VAULT_LOCKED",
|
|
3800
|
+
message: "Authentication required"
|
|
3801
|
+
});
|
|
3802
|
+
}
|
|
3803
|
+
try {
|
|
3804
|
+
const valueResult = await grantorKV.get(`vault/${key}`, {
|
|
3805
|
+
raw: true
|
|
3806
|
+
});
|
|
3807
|
+
if (!valueResult.ok) {
|
|
3808
|
+
return vaultError({ code: "KEY_NOT_FOUND", key });
|
|
3809
|
+
}
|
|
3810
|
+
const rawEnvelope = unwrapKVData(valueResult.data);
|
|
3811
|
+
const envelope = typeof rawEnvelope === "string" ? JSON.parse(rawEnvelope) : rawEnvelope;
|
|
3812
|
+
const proof = await this.decryptCapabilityProof();
|
|
3813
|
+
const plaintextResult = await config.service.decryptEnvelope(
|
|
3814
|
+
envelope,
|
|
3815
|
+
proof
|
|
3816
|
+
);
|
|
3817
|
+
if (!plaintextResult.ok) {
|
|
3818
|
+
return vaultError({
|
|
3819
|
+
code: "DECRYPTION_FAILED",
|
|
3820
|
+
message: plaintextResult.error.message
|
|
3821
|
+
});
|
|
3822
|
+
}
|
|
3823
|
+
const metadata = envelope.metadata ?? {};
|
|
3824
|
+
const contentType = metadata[VaultHeaders.CONTENT_TYPE] ?? "application/json";
|
|
3825
|
+
const keyId = metadata[VaultHeaders.KEY_ID] ?? envelope.encryptedSymmetricKeyHash.slice(0, 16);
|
|
3826
|
+
const value = this.deserializeValue(
|
|
3827
|
+
plaintextResult.data,
|
|
3828
|
+
contentType,
|
|
3829
|
+
options
|
|
3830
|
+
);
|
|
3831
|
+
void grantorDID;
|
|
3832
|
+
return { ok: true, data: { value, metadata, keyId } };
|
|
3833
|
+
} catch (error) {
|
|
3834
|
+
return vaultError({
|
|
3835
|
+
code: "STORAGE_ERROR",
|
|
3836
|
+
cause: toError(error)
|
|
3837
|
+
});
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3577
3840
|
if (!this._isUnlocked || !this.masterKey || !this.encryptionIdentity) {
|
|
3578
3841
|
return vaultError({
|
|
3579
3842
|
code: "VAULT_LOCKED",
|
|
@@ -3699,6 +3962,10 @@ var DataVaultService = class extends BaseService {
|
|
|
3699
3962
|
*/
|
|
3700
3963
|
async listGrants(key) {
|
|
3701
3964
|
return this.withTelemetry("listGrants", key, async () => {
|
|
3965
|
+
if (this.usesNetworkEncryption) {
|
|
3966
|
+
void key;
|
|
3967
|
+
return { ok: true, data: [] };
|
|
3968
|
+
}
|
|
3702
3969
|
if (!this._isUnlocked) {
|
|
3703
3970
|
return vaultError({
|
|
3704
3971
|
code: "VAULT_LOCKED",
|
|
@@ -3762,6 +4029,15 @@ var DataVaultService = class extends BaseService {
|
|
|
3762
4029
|
*/
|
|
3763
4030
|
async revoke(key, recipientDID) {
|
|
3764
4031
|
return this.withTelemetry("revoke", key, async () => {
|
|
4032
|
+
if (this.usesNetworkEncryption) {
|
|
4033
|
+
void recipientDID;
|
|
4034
|
+
return vaultError({
|
|
4035
|
+
code: "STORAGE_ERROR",
|
|
4036
|
+
cause: new Error(
|
|
4037
|
+
"Vault key grants are deprecated for network-encrypted vaults; revoke KV and tinycloud.encryption/decrypt grants instead."
|
|
4038
|
+
)
|
|
4039
|
+
});
|
|
4040
|
+
}
|
|
3765
4041
|
if (!this._isUnlocked || !this.masterKey) {
|
|
3766
4042
|
return vaultError({
|
|
3767
4043
|
code: "VAULT_LOCKED",
|
|
@@ -3935,11 +4211,14 @@ function resolveSecretPath(name, options = {}) {
|
|
|
3935
4211
|
...scope !== void 0 ? { scope } : {},
|
|
3936
4212
|
vaultKey,
|
|
3937
4213
|
permissionPaths: {
|
|
3938
|
-
keys: `keys/${vaultKey}`,
|
|
3939
4214
|
vault: `vault/${vaultKey}`
|
|
3940
4215
|
}
|
|
3941
4216
|
};
|
|
3942
4217
|
}
|
|
4218
|
+
function resolveSecretListPrefix(options = {}) {
|
|
4219
|
+
const scope = canonicalizeSecretScope(options.scope);
|
|
4220
|
+
return scope === void 0 ? "vault/secrets/" : `vault/secrets/scoped/${scope}/`;
|
|
4221
|
+
}
|
|
3943
4222
|
|
|
3944
4223
|
// src/secrets/SecretsService.ts
|
|
3945
4224
|
function invalidSecretInput(message) {
|
|
@@ -4017,14 +4296,980 @@ var SecretsService = class {
|
|
|
4017
4296
|
};
|
|
4018
4297
|
}
|
|
4019
4298
|
};
|
|
4299
|
+
|
|
4300
|
+
// src/encryption/canonical.ts
|
|
4301
|
+
function canonicalize(value) {
|
|
4302
|
+
if (value === void 0) {
|
|
4303
|
+
return "";
|
|
4304
|
+
}
|
|
4305
|
+
return stringify(value);
|
|
4306
|
+
}
|
|
4307
|
+
function stringify(value) {
|
|
4308
|
+
if (value === null) return "null";
|
|
4309
|
+
switch (typeof value) {
|
|
4310
|
+
case "boolean":
|
|
4311
|
+
case "number":
|
|
4312
|
+
return JSON.stringify(value);
|
|
4313
|
+
case "string":
|
|
4314
|
+
return JSON.stringify(value);
|
|
4315
|
+
case "object": {
|
|
4316
|
+
if (Array.isArray(value)) {
|
|
4317
|
+
return `[${value.map(stringify).join(",")}]`;
|
|
4318
|
+
}
|
|
4319
|
+
const keys = Object.keys(value).sort();
|
|
4320
|
+
const parts = [];
|
|
4321
|
+
for (const k of keys) {
|
|
4322
|
+
const v = value[k];
|
|
4323
|
+
if (v === void 0) continue;
|
|
4324
|
+
parts.push(`${JSON.stringify(k)}:${stringify(v)}`);
|
|
4325
|
+
}
|
|
4326
|
+
return `{${parts.join(",")}}`;
|
|
4327
|
+
}
|
|
4328
|
+
default:
|
|
4329
|
+
throw new TypeError(
|
|
4330
|
+
`canonicalize: unsupported value type ${typeof value}`
|
|
4331
|
+
);
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
var HEX = "0123456789abcdef";
|
|
4335
|
+
function hexEncode2(bytes) {
|
|
4336
|
+
let out = "";
|
|
4337
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
4338
|
+
const b = bytes[i];
|
|
4339
|
+
out += HEX[b >> 4 & 15] + HEX[b & 15];
|
|
4340
|
+
}
|
|
4341
|
+
return out;
|
|
4342
|
+
}
|
|
4343
|
+
function hexDecode(hex) {
|
|
4344
|
+
if (hex.length % 2 !== 0) {
|
|
4345
|
+
throw new Error("hex string must have even length");
|
|
4346
|
+
}
|
|
4347
|
+
const out = new Uint8Array(hex.length / 2);
|
|
4348
|
+
for (let i = 0; i < out.length; i++) {
|
|
4349
|
+
const hi = parseInt(hex[i * 2], 16);
|
|
4350
|
+
const lo = parseInt(hex[i * 2 + 1], 16);
|
|
4351
|
+
if (Number.isNaN(hi) || Number.isNaN(lo)) {
|
|
4352
|
+
throw new Error("invalid hex character");
|
|
4353
|
+
}
|
|
4354
|
+
out[i] = hi << 4 | lo;
|
|
4355
|
+
}
|
|
4356
|
+
return out;
|
|
4357
|
+
}
|
|
4358
|
+
function base64Encode2(bytes) {
|
|
4359
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
4360
|
+
let out = "";
|
|
4361
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
4362
|
+
const b0 = bytes[i];
|
|
4363
|
+
const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;
|
|
4364
|
+
const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;
|
|
4365
|
+
out += chars[b0 >> 2 & 63];
|
|
4366
|
+
out += chars[(b0 << 4 | b1 >> 4) & 63];
|
|
4367
|
+
out += i + 1 < bytes.length ? chars[(b1 << 2 | b2 >> 6) & 63] : "=";
|
|
4368
|
+
out += i + 2 < bytes.length ? chars[b2 & 63] : "=";
|
|
4369
|
+
}
|
|
4370
|
+
return out;
|
|
4371
|
+
}
|
|
4372
|
+
function base64Decode2(s) {
|
|
4373
|
+
const clean = s.replace(/[^A-Za-z0-9+/=]/g, "");
|
|
4374
|
+
const len = clean.length;
|
|
4375
|
+
if (len % 4 !== 0) {
|
|
4376
|
+
throw new Error("invalid base64 input");
|
|
4377
|
+
}
|
|
4378
|
+
const padding = clean.endsWith("==") ? 2 : clean.endsWith("=") ? 1 : 0;
|
|
4379
|
+
const outLen = len / 4 * 3 - padding;
|
|
4380
|
+
const out = new Uint8Array(outLen);
|
|
4381
|
+
const lookup = {};
|
|
4382
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
4383
|
+
for (let i = 0; i < chars.length; i++) lookup[chars[i]] = i;
|
|
4384
|
+
let outIdx = 0;
|
|
4385
|
+
for (let i = 0; i < len; i += 4) {
|
|
4386
|
+
const v0 = lookup[clean[i]] ?? 0;
|
|
4387
|
+
const v1 = lookup[clean[i + 1]] ?? 0;
|
|
4388
|
+
const v2 = clean[i + 2] === "=" ? 0 : lookup[clean[i + 2]] ?? 0;
|
|
4389
|
+
const v3 = clean[i + 3] === "=" ? 0 : lookup[clean[i + 3]] ?? 0;
|
|
4390
|
+
const b0 = v0 << 2 | v1 >> 4;
|
|
4391
|
+
const b1 = (v1 & 15) << 4 | v2 >> 2;
|
|
4392
|
+
const b2 = (v2 & 3) << 6 | v3;
|
|
4393
|
+
if (outIdx < outLen) out[outIdx++] = b0;
|
|
4394
|
+
if (outIdx < outLen) out[outIdx++] = b1;
|
|
4395
|
+
if (outIdx < outLen) out[outIdx++] = b2;
|
|
4396
|
+
}
|
|
4397
|
+
return out;
|
|
4398
|
+
}
|
|
4399
|
+
function utf8Encode(s) {
|
|
4400
|
+
return new TextEncoder().encode(s);
|
|
4401
|
+
}
|
|
4402
|
+
function utf8Decode(b) {
|
|
4403
|
+
return new TextDecoder().decode(b);
|
|
4404
|
+
}
|
|
4405
|
+
function canonicalHashHex(sha256, value) {
|
|
4406
|
+
const canonical = canonicalize(value);
|
|
4407
|
+
return hexEncode2(sha256(utf8Encode(canonical)));
|
|
4408
|
+
}
|
|
4409
|
+
|
|
4410
|
+
// src/encryption/networkId.ts
|
|
4411
|
+
var URN_PREFIX = "urn:tinycloud:encryption:";
|
|
4412
|
+
var NETWORK_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
4413
|
+
var NetworkIdError = class extends Error {
|
|
4414
|
+
constructor(message) {
|
|
4415
|
+
super(message);
|
|
4416
|
+
this.name = "NetworkIdError";
|
|
4417
|
+
}
|
|
4418
|
+
};
|
|
4419
|
+
function parseNetworkId(networkId) {
|
|
4420
|
+
if (typeof networkId !== "string" || networkId.length === 0) {
|
|
4421
|
+
throw new NetworkIdError("networkId must be a non-empty string");
|
|
4422
|
+
}
|
|
4423
|
+
if (!networkId.startsWith(URN_PREFIX)) {
|
|
4424
|
+
throw new NetworkIdError(
|
|
4425
|
+
`networkId must start with ${URN_PREFIX} (got ${JSON.stringify(networkId)})`
|
|
4426
|
+
);
|
|
4427
|
+
}
|
|
4428
|
+
const body = networkId.slice(URN_PREFIX.length);
|
|
4429
|
+
const lastColon = body.lastIndexOf(":");
|
|
4430
|
+
if (lastColon <= 0 || lastColon === body.length - 1) {
|
|
4431
|
+
throw new NetworkIdError(
|
|
4432
|
+
`networkId missing principal or name segment (got ${JSON.stringify(networkId)})`
|
|
4433
|
+
);
|
|
4434
|
+
}
|
|
4435
|
+
const principal = body.slice(0, lastColon);
|
|
4436
|
+
const name = body.slice(lastColon + 1);
|
|
4437
|
+
if (!principal.startsWith("did:")) {
|
|
4438
|
+
throw new NetworkIdError(
|
|
4439
|
+
`networkId principal must be a DID (got ${JSON.stringify(principal)})`
|
|
4440
|
+
);
|
|
4441
|
+
}
|
|
4442
|
+
const didParts = principal.split(":");
|
|
4443
|
+
if (didParts.length < 3 || didParts.some((p) => p.length === 0)) {
|
|
4444
|
+
throw new NetworkIdError(
|
|
4445
|
+
`networkId principal is not a well-formed DID (got ${JSON.stringify(principal)})`
|
|
4446
|
+
);
|
|
4447
|
+
}
|
|
4448
|
+
if (!NETWORK_NAME_RE.test(name)) {
|
|
4449
|
+
throw new NetworkIdError(
|
|
4450
|
+
`networkId name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
|
|
4451
|
+
);
|
|
4452
|
+
}
|
|
4453
|
+
return { networkId, principal, name };
|
|
4454
|
+
}
|
|
4455
|
+
function buildNetworkId(principal, name) {
|
|
4456
|
+
if (typeof principal !== "string" || !principal.startsWith("did:")) {
|
|
4457
|
+
throw new NetworkIdError("principal must be a DID");
|
|
4458
|
+
}
|
|
4459
|
+
if (typeof name !== "string" || !NETWORK_NAME_RE.test(name)) {
|
|
4460
|
+
throw new NetworkIdError(
|
|
4461
|
+
`network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
|
|
4462
|
+
);
|
|
4463
|
+
}
|
|
4464
|
+
const networkId = `${URN_PREFIX}${principal}:${name}`;
|
|
4465
|
+
parseNetworkId(networkId);
|
|
4466
|
+
return networkId;
|
|
4467
|
+
}
|
|
4468
|
+
function isNetworkId(networkId) {
|
|
4469
|
+
if (typeof networkId !== "string") {
|
|
4470
|
+
return false;
|
|
4471
|
+
}
|
|
4472
|
+
try {
|
|
4473
|
+
parseNetworkId(networkId);
|
|
4474
|
+
return true;
|
|
4475
|
+
} catch {
|
|
4476
|
+
return false;
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
function networkDiscoveryKey(name) {
|
|
4480
|
+
if (!NETWORK_NAME_RE.test(name)) {
|
|
4481
|
+
throw new NetworkIdError(
|
|
4482
|
+
`network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
|
|
4483
|
+
);
|
|
4484
|
+
}
|
|
4485
|
+
return `.well-known/encryption/network/${name}`;
|
|
4486
|
+
}
|
|
4487
|
+
var ENCRYPTION_NETWORK_URN_PREFIX = URN_PREFIX;
|
|
4488
|
+
var NETWORK_NAME_PATTERN = NETWORK_NAME_RE;
|
|
4489
|
+
|
|
4490
|
+
// src/encryption/types.ts
|
|
4491
|
+
var DEFAULT_ENCRYPTION_ALG = "x25519-aes256gcm/v1";
|
|
4492
|
+
var ENVELOPE_VERSION = 1;
|
|
4493
|
+
var DEFAULT_KEY_VERSION = 1;
|
|
4494
|
+
var DECRYPT_FACT_TYPE = "tinycloud.encryption.decrypt/v1";
|
|
4495
|
+
var DECRYPT_RESULT_TYPE = "tinycloud.encryption.decrypt-result/v1";
|
|
4496
|
+
var ENCRYPTION_SERVICE = "tinycloud.encryption";
|
|
4497
|
+
var ENCRYPTION_SERVICE_SHORT = "encryption";
|
|
4498
|
+
var DECRYPT_ACTION = "tinycloud.encryption/decrypt";
|
|
4499
|
+
function defaultEncryptionMessage(input) {
|
|
4500
|
+
switch (input.code) {
|
|
4501
|
+
case "NETWORK_NOT_FOUND":
|
|
4502
|
+
return input.message ?? `Network not found: ${input.networkId ?? input.name ?? "<unknown>"}`;
|
|
4503
|
+
case "NETWORK_NOT_ACTIVE":
|
|
4504
|
+
return input.message ?? `Network not active (state=${input.state})`;
|
|
4505
|
+
case "INVALID_NETWORK_ID":
|
|
4506
|
+
return input.message;
|
|
4507
|
+
case "INVALID_ENVELOPE":
|
|
4508
|
+
return input.message;
|
|
4509
|
+
case "DECRYPT_DENIED":
|
|
4510
|
+
return input.message;
|
|
4511
|
+
case "INVALID_RESPONSE":
|
|
4512
|
+
return input.message;
|
|
4513
|
+
case "RESPONSE_SIGNATURE_INVALID":
|
|
4514
|
+
return input.message ?? "Node response signature failed to verify";
|
|
4515
|
+
case "RESPONSE_BINDING_MISMATCH":
|
|
4516
|
+
return input.message ?? `Node response binding mismatch on field ${JSON.stringify(input.field)}`;
|
|
4517
|
+
case "TRANSPORT_ERROR":
|
|
4518
|
+
return input.message ?? input.cause.message;
|
|
4519
|
+
case "INVALID_INPUT":
|
|
4520
|
+
return input.message;
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4523
|
+
function encryptionError(input) {
|
|
4524
|
+
return {
|
|
4525
|
+
...input,
|
|
4526
|
+
service: "encryption",
|
|
4527
|
+
message: defaultEncryptionMessage(input)
|
|
4528
|
+
};
|
|
4529
|
+
}
|
|
4530
|
+
function toError2(error) {
|
|
4531
|
+
if (error instanceof Error) return error;
|
|
4532
|
+
if (typeof error === "object" && error !== null) {
|
|
4533
|
+
return new Error(JSON.stringify(error));
|
|
4534
|
+
}
|
|
4535
|
+
return new Error(String(error));
|
|
4536
|
+
}
|
|
4537
|
+
|
|
4538
|
+
// src/encryption/discovery.ts
|
|
4539
|
+
async function discoverNetwork(input) {
|
|
4540
|
+
let networkId;
|
|
4541
|
+
let principal;
|
|
4542
|
+
let name;
|
|
4543
|
+
try {
|
|
4544
|
+
if (input.identifier.startsWith("urn:tinycloud:encryption:")) {
|
|
4545
|
+
const parsed = parseNetworkId(input.identifier);
|
|
4546
|
+
networkId = parsed.networkId;
|
|
4547
|
+
principal = parsed.principal;
|
|
4548
|
+
name = parsed.name;
|
|
4549
|
+
} else {
|
|
4550
|
+
if (input.principal === void 0) {
|
|
4551
|
+
return {
|
|
4552
|
+
ok: false,
|
|
4553
|
+
error: encryptionError({
|
|
4554
|
+
code: "INVALID_INPUT",
|
|
4555
|
+
message: "discoverNetwork requires `principal` when identifier is a bare network name"
|
|
4556
|
+
})
|
|
4557
|
+
};
|
|
4558
|
+
}
|
|
4559
|
+
networkId = `urn:tinycloud:encryption:${input.principal}:${input.identifier}`;
|
|
4560
|
+
const parsed = parseNetworkId(networkId);
|
|
4561
|
+
principal = parsed.principal;
|
|
4562
|
+
name = parsed.name;
|
|
4563
|
+
}
|
|
4564
|
+
} catch (err3) {
|
|
4565
|
+
if (err3 instanceof NetworkIdError) {
|
|
4566
|
+
return {
|
|
4567
|
+
ok: false,
|
|
4568
|
+
error: encryptionError({
|
|
4569
|
+
code: "INVALID_NETWORK_ID",
|
|
4570
|
+
message: err3.message
|
|
4571
|
+
})
|
|
4572
|
+
};
|
|
4573
|
+
}
|
|
4574
|
+
throw err3;
|
|
4575
|
+
}
|
|
4576
|
+
if (input.node !== void 0) {
|
|
4577
|
+
try {
|
|
4578
|
+
const descriptor = await input.node.fetchByNetworkId(networkId);
|
|
4579
|
+
if (descriptor !== null) {
|
|
4580
|
+
const validated = validateDescriptor(descriptor, networkId, principal, name);
|
|
4581
|
+
if (!validated.ok) return validated;
|
|
4582
|
+
return { ok: true, data: { descriptor: validated.data, source: "node" } };
|
|
4583
|
+
}
|
|
4584
|
+
} catch (err3) {
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
if (input.wellKnown !== void 0) {
|
|
4588
|
+
try {
|
|
4589
|
+
const descriptor = await input.wellKnown.fetchWellKnown(
|
|
4590
|
+
principal,
|
|
4591
|
+
networkDiscoveryKey(name)
|
|
4592
|
+
);
|
|
4593
|
+
if (descriptor !== null) {
|
|
4594
|
+
const validated = validateDescriptor(descriptor, networkId, principal, name);
|
|
4595
|
+
if (!validated.ok) return validated;
|
|
4596
|
+
return {
|
|
4597
|
+
ok: true,
|
|
4598
|
+
data: { descriptor: validated.data, source: "well-known" }
|
|
4599
|
+
};
|
|
4600
|
+
}
|
|
4601
|
+
} catch (err3) {
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4604
|
+
return {
|
|
4605
|
+
ok: false,
|
|
4606
|
+
error: encryptionError({
|
|
4607
|
+
code: "NETWORK_NOT_FOUND",
|
|
4608
|
+
networkId,
|
|
4609
|
+
name
|
|
4610
|
+
})
|
|
4611
|
+
};
|
|
4612
|
+
}
|
|
4613
|
+
function validateDescriptor(descriptor, networkId, principal, name) {
|
|
4614
|
+
if (descriptor.networkId !== networkId) {
|
|
4615
|
+
return {
|
|
4616
|
+
ok: false,
|
|
4617
|
+
error: encryptionError({
|
|
4618
|
+
code: "INVALID_NETWORK_ID",
|
|
4619
|
+
message: `descriptor networkId ${JSON.stringify(descriptor.networkId)} does not match expected ${JSON.stringify(networkId)}`
|
|
4620
|
+
})
|
|
4621
|
+
};
|
|
4622
|
+
}
|
|
4623
|
+
if (descriptor.principal !== principal) {
|
|
4624
|
+
return {
|
|
4625
|
+
ok: false,
|
|
4626
|
+
error: encryptionError({
|
|
4627
|
+
code: "INVALID_NETWORK_ID",
|
|
4628
|
+
message: "descriptor principal does not match networkId principal"
|
|
4629
|
+
})
|
|
4630
|
+
};
|
|
4631
|
+
}
|
|
4632
|
+
if (descriptor.name !== name) {
|
|
4633
|
+
return {
|
|
4634
|
+
ok: false,
|
|
4635
|
+
error: encryptionError({
|
|
4636
|
+
code: "INVALID_NETWORK_ID",
|
|
4637
|
+
message: "descriptor name does not match networkId name"
|
|
4638
|
+
})
|
|
4639
|
+
};
|
|
4640
|
+
}
|
|
4641
|
+
if (typeof descriptor.publicEncryptionKey !== "string" || descriptor.publicEncryptionKey.length === 0) {
|
|
4642
|
+
return {
|
|
4643
|
+
ok: false,
|
|
4644
|
+
error: encryptionError({
|
|
4645
|
+
code: "INVALID_NETWORK_ID",
|
|
4646
|
+
message: "descriptor publicEncryptionKey must be a non-empty string"
|
|
4647
|
+
})
|
|
4648
|
+
};
|
|
4649
|
+
}
|
|
4650
|
+
return { ok: true, data: descriptor };
|
|
4651
|
+
}
|
|
4652
|
+
function ensureNetworkUsableForDecrypt(descriptor) {
|
|
4653
|
+
if (descriptor.state === "active" || descriptor.state === "rotating") {
|
|
4654
|
+
return { ok: true, data: descriptor };
|
|
4655
|
+
}
|
|
4656
|
+
return {
|
|
4657
|
+
ok: false,
|
|
4658
|
+
error: encryptionError({
|
|
4659
|
+
code: "NETWORK_NOT_ACTIVE",
|
|
4660
|
+
state: descriptor.state
|
|
4661
|
+
})
|
|
4662
|
+
};
|
|
4663
|
+
}
|
|
4664
|
+
|
|
4665
|
+
// src/encryption/envelope.ts
|
|
4666
|
+
function encryptToNetwork(crypto2, input) {
|
|
4667
|
+
parseNetworkId(input.networkId);
|
|
4668
|
+
const alg = input.alg ?? DEFAULT_ENCRYPTION_ALG;
|
|
4669
|
+
const keyVersion = input.keyVersion ?? DEFAULT_KEY_VERSION;
|
|
4670
|
+
const symmetricKey = crypto2.randomBytes(32);
|
|
4671
|
+
const ciphertext = crypto2.authEncrypt(symmetricKey, input.plaintext, input.aad);
|
|
4672
|
+
const wrapped = crypto2.sealToNetworkKey(input.networkPublicKey, symmetricKey);
|
|
4673
|
+
const encryptedSymmetricKey = base64Encode2(wrapped);
|
|
4674
|
+
const encryptedSymmetricKeyHash = canonicalHashHex(
|
|
4675
|
+
crypto2.sha256,
|
|
4676
|
+
encryptedSymmetricKey
|
|
4677
|
+
);
|
|
4678
|
+
const envelope = {
|
|
4679
|
+
v: ENVELOPE_VERSION,
|
|
4680
|
+
networkId: input.networkId,
|
|
4681
|
+
alg,
|
|
4682
|
+
keyVersion,
|
|
4683
|
+
encryptedSymmetricKey,
|
|
4684
|
+
encryptedSymmetricKeyHash,
|
|
4685
|
+
ciphertext: base64Encode2(ciphertext),
|
|
4686
|
+
...input.aad !== void 0 ? { aad: base64Encode2(input.aad) } : {},
|
|
4687
|
+
...input.metadata !== void 0 ? { metadata: input.metadata } : {}
|
|
4688
|
+
};
|
|
4689
|
+
return { envelope, symmetricKey };
|
|
4690
|
+
}
|
|
4691
|
+
function validateEnvelope(crypto2, envelope) {
|
|
4692
|
+
if (envelope === null || typeof envelope !== "object") {
|
|
4693
|
+
return {
|
|
4694
|
+
ok: false,
|
|
4695
|
+
error: encryptionError({
|
|
4696
|
+
code: "INVALID_ENVELOPE",
|
|
4697
|
+
message: "envelope must be an object"
|
|
4698
|
+
})
|
|
4699
|
+
};
|
|
4700
|
+
}
|
|
4701
|
+
const e = envelope;
|
|
4702
|
+
if (e.v !== ENVELOPE_VERSION) {
|
|
4703
|
+
return {
|
|
4704
|
+
ok: false,
|
|
4705
|
+
error: encryptionError({
|
|
4706
|
+
code: "INVALID_ENVELOPE",
|
|
4707
|
+
message: `envelope.v must be ${ENVELOPE_VERSION} (got ${e.v})`
|
|
4708
|
+
})
|
|
4709
|
+
};
|
|
4710
|
+
}
|
|
4711
|
+
try {
|
|
4712
|
+
parseNetworkId(e.networkId);
|
|
4713
|
+
} catch (err3) {
|
|
4714
|
+
return {
|
|
4715
|
+
ok: false,
|
|
4716
|
+
error: encryptionError({
|
|
4717
|
+
code: "INVALID_ENVELOPE",
|
|
4718
|
+
message: `envelope.networkId is malformed: ${err3 instanceof Error ? err3.message : String(err3)}`
|
|
4719
|
+
})
|
|
4720
|
+
};
|
|
4721
|
+
}
|
|
4722
|
+
for (const field of [
|
|
4723
|
+
"alg",
|
|
4724
|
+
"encryptedSymmetricKey",
|
|
4725
|
+
"encryptedSymmetricKeyHash",
|
|
4726
|
+
"ciphertext"
|
|
4727
|
+
]) {
|
|
4728
|
+
if (typeof e[field] !== "string" || e[field].length === 0) {
|
|
4729
|
+
return {
|
|
4730
|
+
ok: false,
|
|
4731
|
+
error: encryptionError({
|
|
4732
|
+
code: "INVALID_ENVELOPE",
|
|
4733
|
+
message: `envelope.${field} must be a non-empty string`
|
|
4734
|
+
})
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
if (typeof e.keyVersion !== "number" || !Number.isInteger(e.keyVersion)) {
|
|
4739
|
+
return {
|
|
4740
|
+
ok: false,
|
|
4741
|
+
error: encryptionError({
|
|
4742
|
+
code: "INVALID_ENVELOPE",
|
|
4743
|
+
message: "envelope.keyVersion must be an integer"
|
|
4744
|
+
})
|
|
4745
|
+
};
|
|
4746
|
+
}
|
|
4747
|
+
const expectedHash = canonicalHashHex(crypto2.sha256, e.encryptedSymmetricKey);
|
|
4748
|
+
if (expectedHash !== e.encryptedSymmetricKeyHash) {
|
|
4749
|
+
return {
|
|
4750
|
+
ok: false,
|
|
4751
|
+
error: encryptionError({
|
|
4752
|
+
code: "INVALID_ENVELOPE",
|
|
4753
|
+
message: "envelope.encryptedSymmetricKeyHash does not match canonical hash of encryptedSymmetricKey"
|
|
4754
|
+
})
|
|
4755
|
+
};
|
|
4756
|
+
}
|
|
4757
|
+
return { ok: true, data: e };
|
|
4758
|
+
}
|
|
4759
|
+
function decryptEnvelopeWithKey(crypto2, envelope, symmetricKey) {
|
|
4760
|
+
const ciphertext = base64Decode2(envelope.ciphertext);
|
|
4761
|
+
const aad = envelope.aad !== void 0 ? base64Decode2(envelope.aad) : void 0;
|
|
4762
|
+
return crypto2.authDecrypt(symmetricKey, ciphertext, aad);
|
|
4763
|
+
}
|
|
4764
|
+
|
|
4765
|
+
// src/encryption/invocation.ts
|
|
4766
|
+
function buildCanonicalDecryptRequest(input) {
|
|
4767
|
+
const canonicalBody = canonicalize(input.body);
|
|
4768
|
+
const bodyHash = canonicalHashHex(
|
|
4769
|
+
input.crypto.sha256,
|
|
4770
|
+
input.body
|
|
4771
|
+
);
|
|
4772
|
+
const receiverPublicKeyHash = canonicalHashHex(
|
|
4773
|
+
input.crypto.sha256,
|
|
4774
|
+
input.body.receiverPublicKey
|
|
4775
|
+
);
|
|
4776
|
+
return { canonicalBody, bodyHash, receiverPublicKeyHash };
|
|
4777
|
+
}
|
|
4778
|
+
function buildDecryptFacts(input) {
|
|
4779
|
+
const bodyHash = input.canonicalBody !== void 0 ? hexEncode2(input.crypto.sha256(utf8Encode(input.canonicalBody))) : canonicalHashHex(
|
|
4780
|
+
input.crypto.sha256,
|
|
4781
|
+
input.body
|
|
4782
|
+
);
|
|
4783
|
+
const receiverPublicKeyHash = canonicalHashHex(
|
|
4784
|
+
input.crypto.sha256,
|
|
4785
|
+
input.body.receiverPublicKey
|
|
4786
|
+
);
|
|
4787
|
+
return {
|
|
4788
|
+
type: DECRYPT_FACT_TYPE,
|
|
4789
|
+
targetNode: input.body.targetNode,
|
|
4790
|
+
networkId: input.body.networkId,
|
|
4791
|
+
bodyHash,
|
|
4792
|
+
encryptedSymmetricKeyHash: input.encryptedSymmetricKeyHash,
|
|
4793
|
+
receiverPublicKeyHash,
|
|
4794
|
+
alg: input.body.alg,
|
|
4795
|
+
keyVersion: input.body.keyVersion
|
|
4796
|
+
};
|
|
4797
|
+
}
|
|
4798
|
+
function buildDecryptAttenuation(networkId) {
|
|
4799
|
+
parseNetworkId(networkId);
|
|
4800
|
+
return {
|
|
4801
|
+
[networkId]: {
|
|
4802
|
+
[DECRYPT_ACTION]: {}
|
|
4803
|
+
}
|
|
4804
|
+
};
|
|
4805
|
+
}
|
|
4806
|
+
function checkDecryptInvocationInput(crypto2, input) {
|
|
4807
|
+
if (input.body.type !== DECRYPT_FACT_TYPE) {
|
|
4808
|
+
return {
|
|
4809
|
+
ok: false,
|
|
4810
|
+
error: encryptionError({
|
|
4811
|
+
code: "INVALID_INPUT",
|
|
4812
|
+
message: `body.type must be ${DECRYPT_FACT_TYPE}`
|
|
4813
|
+
})
|
|
4814
|
+
};
|
|
4815
|
+
}
|
|
4816
|
+
if (input.facts.type !== DECRYPT_FACT_TYPE) {
|
|
4817
|
+
return {
|
|
4818
|
+
ok: false,
|
|
4819
|
+
error: encryptionError({
|
|
4820
|
+
code: "INVALID_INPUT",
|
|
4821
|
+
message: `facts.type must be ${DECRYPT_FACT_TYPE}`
|
|
4822
|
+
})
|
|
4823
|
+
};
|
|
4824
|
+
}
|
|
4825
|
+
if (input.facts.targetNode !== input.targetNode) {
|
|
4826
|
+
return {
|
|
4827
|
+
ok: false,
|
|
4828
|
+
error: encryptionError({
|
|
4829
|
+
code: "INVALID_INPUT",
|
|
4830
|
+
message: "facts.targetNode must equal targetNode \u2014 the UCAN audience binds the request to a single node"
|
|
4831
|
+
})
|
|
4832
|
+
};
|
|
4833
|
+
}
|
|
4834
|
+
if (input.body.targetNode !== input.targetNode) {
|
|
4835
|
+
return {
|
|
4836
|
+
ok: false,
|
|
4837
|
+
error: encryptionError({
|
|
4838
|
+
code: "INVALID_INPUT",
|
|
4839
|
+
message: "body.targetNode must equal targetNode"
|
|
4840
|
+
})
|
|
4841
|
+
};
|
|
4842
|
+
}
|
|
4843
|
+
if (input.facts.networkId !== input.networkId) {
|
|
4844
|
+
return {
|
|
4845
|
+
ok: false,
|
|
4846
|
+
error: encryptionError({
|
|
4847
|
+
code: "INVALID_INPUT",
|
|
4848
|
+
message: "facts.networkId must equal networkId"
|
|
4849
|
+
})
|
|
4850
|
+
};
|
|
4851
|
+
}
|
|
4852
|
+
if (input.body.networkId !== input.networkId) {
|
|
4853
|
+
return {
|
|
4854
|
+
ok: false,
|
|
4855
|
+
error: encryptionError({
|
|
4856
|
+
code: "INVALID_INPUT",
|
|
4857
|
+
message: "body.networkId must equal networkId"
|
|
4858
|
+
})
|
|
4859
|
+
};
|
|
4860
|
+
}
|
|
4861
|
+
if (input.facts.alg !== input.body.alg) {
|
|
4862
|
+
return {
|
|
4863
|
+
ok: false,
|
|
4864
|
+
error: encryptionError({
|
|
4865
|
+
code: "INVALID_INPUT",
|
|
4866
|
+
message: "facts.alg must equal body.alg"
|
|
4867
|
+
})
|
|
4868
|
+
};
|
|
4869
|
+
}
|
|
4870
|
+
if (input.facts.keyVersion !== input.body.keyVersion) {
|
|
4871
|
+
return {
|
|
4872
|
+
ok: false,
|
|
4873
|
+
error: encryptionError({
|
|
4874
|
+
code: "INVALID_INPUT",
|
|
4875
|
+
message: "facts.keyVersion must equal body.keyVersion"
|
|
4876
|
+
})
|
|
4877
|
+
};
|
|
4878
|
+
}
|
|
4879
|
+
if (input.facts.encryptedSymmetricKeyHash !== input.body.encryptedSymmetricKeyHash) {
|
|
4880
|
+
return {
|
|
4881
|
+
ok: false,
|
|
4882
|
+
error: encryptionError({
|
|
4883
|
+
code: "INVALID_INPUT",
|
|
4884
|
+
message: "facts.encryptedSymmetricKeyHash must equal body.encryptedSymmetricKeyHash"
|
|
4885
|
+
})
|
|
4886
|
+
};
|
|
4887
|
+
}
|
|
4888
|
+
if (input.facts.receiverPublicKeyHash !== input.body.receiverPublicKeyHash) {
|
|
4889
|
+
return {
|
|
4890
|
+
ok: false,
|
|
4891
|
+
error: encryptionError({
|
|
4892
|
+
code: "INVALID_INPUT",
|
|
4893
|
+
message: "facts.receiverPublicKeyHash must equal body.receiverPublicKeyHash"
|
|
4894
|
+
})
|
|
4895
|
+
};
|
|
4896
|
+
}
|
|
4897
|
+
try {
|
|
4898
|
+
parseNetworkId(input.networkId);
|
|
4899
|
+
} catch (err3) {
|
|
4900
|
+
return {
|
|
4901
|
+
ok: false,
|
|
4902
|
+
error: encryptionError({
|
|
4903
|
+
code: "INVALID_NETWORK_ID",
|
|
4904
|
+
message: err3 instanceof Error ? err3.message : String(err3)
|
|
4905
|
+
})
|
|
4906
|
+
};
|
|
4907
|
+
}
|
|
4908
|
+
const canonicalBody = canonicalize(
|
|
4909
|
+
input.body
|
|
4910
|
+
);
|
|
4911
|
+
const expectedBodyHash = canonicalHashHex(crypto2.sha256, input.body);
|
|
4912
|
+
if (expectedBodyHash !== input.facts.bodyHash) {
|
|
4913
|
+
return {
|
|
4914
|
+
ok: false,
|
|
4915
|
+
error: encryptionError({
|
|
4916
|
+
code: "INVALID_INPUT",
|
|
4917
|
+
message: "facts.bodyHash does not match the canonical body hash"
|
|
4918
|
+
})
|
|
4919
|
+
};
|
|
4920
|
+
}
|
|
4921
|
+
return { ok: true, data: input, canonicalBody };
|
|
4922
|
+
}
|
|
4923
|
+
async function buildDecryptInvocation(crypto2, signer, input) {
|
|
4924
|
+
const checked = checkDecryptInvocationInput(crypto2, input);
|
|
4925
|
+
if (!checked.ok) {
|
|
4926
|
+
return checked;
|
|
4927
|
+
}
|
|
4928
|
+
try {
|
|
4929
|
+
const built = await signer.signDecryptInvocation(checked.data);
|
|
4930
|
+
if (!built.authorization || !built.invocationCid) {
|
|
4931
|
+
return {
|
|
4932
|
+
ok: false,
|
|
4933
|
+
error: encryptionError({
|
|
4934
|
+
code: "INVALID_INPUT",
|
|
4935
|
+
message: "decrypt-invocation signer returned an empty authorization or invocationCid"
|
|
4936
|
+
})
|
|
4937
|
+
};
|
|
4938
|
+
}
|
|
4939
|
+
if (built.canonicalBody !== checked.canonicalBody) {
|
|
4940
|
+
return {
|
|
4941
|
+
ok: false,
|
|
4942
|
+
error: encryptionError({
|
|
4943
|
+
code: "INVALID_INPUT",
|
|
4944
|
+
message: "decrypt-invocation signer returned a canonicalBody that does not match the SDK's canonicalization \u2014 signer must use the SDK-provided body"
|
|
4945
|
+
})
|
|
4946
|
+
};
|
|
4947
|
+
}
|
|
4948
|
+
return { ok: true, data: built };
|
|
4949
|
+
} catch (err3) {
|
|
4950
|
+
return {
|
|
4951
|
+
ok: false,
|
|
4952
|
+
error: encryptionError({
|
|
4953
|
+
code: "TRANSPORT_ERROR",
|
|
4954
|
+
cause: err3 instanceof Error ? err3 : new Error(String(err3)),
|
|
4955
|
+
message: `failed to sign decrypt invocation: ${err3 instanceof Error ? err3.message : String(err3)}`
|
|
4956
|
+
})
|
|
4957
|
+
};
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
|
|
4961
|
+
// src/encryption/receiverKey.ts
|
|
4962
|
+
function generateRandomReceiverKey(input) {
|
|
4963
|
+
const seed = input.crypto.randomBytes(32);
|
|
4964
|
+
return input.crypto.x25519FromSeed(seed);
|
|
4965
|
+
}
|
|
4966
|
+
async function deriveSignedReceiverKey(input) {
|
|
4967
|
+
const message = `tinycloud.encryption.receiver-key/v1:${input.networkId}:${input.context ?? ""}`;
|
|
4968
|
+
const sig = await input.signer.signMessage(message);
|
|
4969
|
+
const sigBytes = utf8Encode(sig);
|
|
4970
|
+
const seed = input.crypto.sha256(sigBytes);
|
|
4971
|
+
return input.crypto.x25519FromSeed(seed);
|
|
4972
|
+
}
|
|
4973
|
+
|
|
4974
|
+
// src/encryption/response.ts
|
|
4975
|
+
function canonicalSignedResponse(response) {
|
|
4976
|
+
const { nodeSignature: _drop, ...rest } = response;
|
|
4977
|
+
return canonicalize(rest);
|
|
4978
|
+
}
|
|
4979
|
+
function verifyDecryptResponse(input) {
|
|
4980
|
+
const { crypto: crypto2, request, facts, invocationCid, requestBodyHash, response } = input;
|
|
4981
|
+
if (response.type !== DECRYPT_RESULT_TYPE) {
|
|
4982
|
+
return {
|
|
4983
|
+
ok: false,
|
|
4984
|
+
error: encryptionError({
|
|
4985
|
+
code: "INVALID_RESPONSE",
|
|
4986
|
+
message: `response.type must be ${DECRYPT_RESULT_TYPE}`
|
|
4987
|
+
})
|
|
4988
|
+
};
|
|
4989
|
+
}
|
|
4990
|
+
if (response.targetNode !== request.targetNode) {
|
|
4991
|
+
return {
|
|
4992
|
+
ok: false,
|
|
4993
|
+
error: encryptionError({
|
|
4994
|
+
code: "RESPONSE_BINDING_MISMATCH",
|
|
4995
|
+
field: "targetNode"
|
|
4996
|
+
})
|
|
4997
|
+
};
|
|
4998
|
+
}
|
|
4999
|
+
if (response.networkId !== request.networkId) {
|
|
5000
|
+
return {
|
|
5001
|
+
ok: false,
|
|
5002
|
+
error: encryptionError({
|
|
5003
|
+
code: "RESPONSE_BINDING_MISMATCH",
|
|
5004
|
+
field: "networkId"
|
|
5005
|
+
})
|
|
5006
|
+
};
|
|
5007
|
+
}
|
|
5008
|
+
if (response.alg !== request.alg) {
|
|
5009
|
+
return {
|
|
5010
|
+
ok: false,
|
|
5011
|
+
error: encryptionError({
|
|
5012
|
+
code: "RESPONSE_BINDING_MISMATCH",
|
|
5013
|
+
field: "alg"
|
|
5014
|
+
})
|
|
5015
|
+
};
|
|
5016
|
+
}
|
|
5017
|
+
if (response.keyVersion !== request.keyVersion) {
|
|
5018
|
+
return {
|
|
5019
|
+
ok: false,
|
|
5020
|
+
error: encryptionError({
|
|
5021
|
+
code: "RESPONSE_BINDING_MISMATCH",
|
|
5022
|
+
field: "keyVersion"
|
|
5023
|
+
})
|
|
5024
|
+
};
|
|
5025
|
+
}
|
|
5026
|
+
if (response.encryptedSymmetricKeyHash !== request.encryptedSymmetricKeyHash) {
|
|
5027
|
+
return {
|
|
5028
|
+
ok: false,
|
|
5029
|
+
error: encryptionError({
|
|
5030
|
+
code: "RESPONSE_BINDING_MISMATCH",
|
|
5031
|
+
field: "encryptedSymmetricKeyHash"
|
|
5032
|
+
})
|
|
5033
|
+
};
|
|
5034
|
+
}
|
|
5035
|
+
if (response.receiverPublicKeyHash !== request.receiverPublicKeyHash) {
|
|
5036
|
+
return {
|
|
5037
|
+
ok: false,
|
|
5038
|
+
error: encryptionError({
|
|
5039
|
+
code: "RESPONSE_BINDING_MISMATCH",
|
|
5040
|
+
field: "receiverPublicKeyHash"
|
|
5041
|
+
})
|
|
5042
|
+
};
|
|
5043
|
+
}
|
|
5044
|
+
if (response.invocationCid !== invocationCid) {
|
|
5045
|
+
return {
|
|
5046
|
+
ok: false,
|
|
5047
|
+
error: encryptionError({
|
|
5048
|
+
code: "RESPONSE_BINDING_MISMATCH",
|
|
5049
|
+
field: "invocationCid"
|
|
5050
|
+
})
|
|
5051
|
+
};
|
|
5052
|
+
}
|
|
5053
|
+
const expectedRequestHash = hexEncode2(
|
|
5054
|
+
crypto2.sha256(utf8Encode(`${invocationCid}${requestBodyHash}`))
|
|
5055
|
+
);
|
|
5056
|
+
if (response.requestHash !== expectedRequestHash) {
|
|
5057
|
+
return {
|
|
5058
|
+
ok: false,
|
|
5059
|
+
error: encryptionError({
|
|
5060
|
+
code: "RESPONSE_BINDING_MISMATCH",
|
|
5061
|
+
field: "requestHash"
|
|
5062
|
+
})
|
|
5063
|
+
};
|
|
5064
|
+
}
|
|
5065
|
+
if (facts.encryptedSymmetricKeyHash !== response.encryptedSymmetricKeyHash || facts.receiverPublicKeyHash !== response.receiverPublicKeyHash || facts.networkId !== response.networkId || facts.targetNode !== response.targetNode || facts.alg !== response.alg || facts.keyVersion !== response.keyVersion) {
|
|
5066
|
+
return {
|
|
5067
|
+
ok: false,
|
|
5068
|
+
error: encryptionError({
|
|
5069
|
+
code: "RESPONSE_BINDING_MISMATCH",
|
|
5070
|
+
field: "facts"
|
|
5071
|
+
})
|
|
5072
|
+
};
|
|
5073
|
+
}
|
|
5074
|
+
const signedBytes = new TextEncoder().encode(
|
|
5075
|
+
canonicalSignedResponse(response)
|
|
5076
|
+
);
|
|
5077
|
+
const signatureBytes = base64Decode2(response.nodeSignature);
|
|
5078
|
+
if (!crypto2.verifyNodeSignature(response.nodeId, signedBytes, signatureBytes)) {
|
|
5079
|
+
return {
|
|
5080
|
+
ok: false,
|
|
5081
|
+
error: encryptionError({
|
|
5082
|
+
code: "RESPONSE_SIGNATURE_INVALID"
|
|
5083
|
+
})
|
|
5084
|
+
};
|
|
5085
|
+
}
|
|
5086
|
+
return { ok: true, data: response };
|
|
5087
|
+
}
|
|
5088
|
+
function openWrappedKey(crypto2, receiverPrivateKey, response) {
|
|
5089
|
+
const wrapped = base64Decode2(response.wrappedKey);
|
|
5090
|
+
return crypto2.openWithReceiverKey(receiverPrivateKey, wrapped);
|
|
5091
|
+
}
|
|
5092
|
+
|
|
5093
|
+
// src/encryption/EncryptionService.ts
|
|
5094
|
+
function encOk(data) {
|
|
5095
|
+
return { ok: true, data };
|
|
5096
|
+
}
|
|
5097
|
+
function encErr(error) {
|
|
5098
|
+
return { ok: false, error };
|
|
5099
|
+
}
|
|
5100
|
+
var EncryptionService = class extends BaseService {
|
|
5101
|
+
constructor(config) {
|
|
5102
|
+
super();
|
|
5103
|
+
this._config = config;
|
|
5104
|
+
}
|
|
5105
|
+
get config() {
|
|
5106
|
+
return this._config;
|
|
5107
|
+
}
|
|
5108
|
+
get crypto() {
|
|
5109
|
+
return this._config.crypto;
|
|
5110
|
+
}
|
|
5111
|
+
async discoverNetwork(identifier, principal) {
|
|
5112
|
+
const result = await discoverNetwork({
|
|
5113
|
+
identifier,
|
|
5114
|
+
...principal !== void 0 ? { principal } : {},
|
|
5115
|
+
...this._config.node !== void 0 ? { node: this._config.node } : {},
|
|
5116
|
+
...this._config.wellKnown !== void 0 ? { wellKnown: this._config.wellKnown } : {}
|
|
5117
|
+
});
|
|
5118
|
+
if (!result.ok) return result;
|
|
5119
|
+
return encOk(result.data.descriptor);
|
|
5120
|
+
}
|
|
5121
|
+
async encryptToNetwork(networkId, plaintext, options) {
|
|
5122
|
+
try {
|
|
5123
|
+
const discovered = await this.discoverNetwork(networkId);
|
|
5124
|
+
if (!discovered.ok) return discovered;
|
|
5125
|
+
const usable = ensureNetworkUsableForDecrypt(discovered.data);
|
|
5126
|
+
if (!usable.ok) return usable;
|
|
5127
|
+
const descriptor = usable.data;
|
|
5128
|
+
const networkPublicKey = base64Decode2(descriptor.publicEncryptionKey);
|
|
5129
|
+
const result = encryptToNetwork(this.crypto, {
|
|
5130
|
+
networkId,
|
|
5131
|
+
networkPublicKey,
|
|
5132
|
+
plaintext,
|
|
5133
|
+
...options?.aad !== void 0 ? { aad: options.aad } : {},
|
|
5134
|
+
alg: options?.alg ?? descriptor.alg,
|
|
5135
|
+
keyVersion: options?.keyVersion ?? descriptor.keyVersion,
|
|
5136
|
+
...options?.metadata !== void 0 ? { metadata: options.metadata } : {}
|
|
5137
|
+
});
|
|
5138
|
+
return encOk(result.envelope);
|
|
5139
|
+
} catch (error) {
|
|
5140
|
+
return encErr(
|
|
5141
|
+
encryptionError({
|
|
5142
|
+
code: "TRANSPORT_ERROR",
|
|
5143
|
+
cause: toError2(error)
|
|
5144
|
+
})
|
|
5145
|
+
);
|
|
5146
|
+
}
|
|
5147
|
+
}
|
|
5148
|
+
async decryptEnvelope(envelope, capabilityProof, options) {
|
|
5149
|
+
try {
|
|
5150
|
+
const validated = validateEnvelope(this.crypto, envelope);
|
|
5151
|
+
if (!validated.ok) return validated;
|
|
5152
|
+
let descriptor;
|
|
5153
|
+
if (options?.descriptor !== void 0) {
|
|
5154
|
+
descriptor = options.descriptor;
|
|
5155
|
+
} else {
|
|
5156
|
+
const discovered = await this.discoverNetwork(envelope.networkId);
|
|
5157
|
+
if (!discovered.ok) return discovered;
|
|
5158
|
+
descriptor = discovered.data;
|
|
5159
|
+
}
|
|
5160
|
+
const usable = ensureNetworkUsableForDecrypt(descriptor);
|
|
5161
|
+
if (!usable.ok) return usable;
|
|
5162
|
+
const targetNode = options?.targetNode ?? descriptor.members[0]?.nodeId;
|
|
5163
|
+
if (targetNode === void 0) {
|
|
5164
|
+
return encErr(
|
|
5165
|
+
encryptionError({
|
|
5166
|
+
code: "INVALID_INPUT",
|
|
5167
|
+
message: "no target node available from descriptor"
|
|
5168
|
+
})
|
|
5169
|
+
);
|
|
5170
|
+
}
|
|
5171
|
+
const receiverKey = generateRandomReceiverKey({ crypto: this.crypto });
|
|
5172
|
+
const receiverPublicKey = base64Encode2(receiverKey.publicKey);
|
|
5173
|
+
const receiverPublicKeyHash = canonicalHashHex(
|
|
5174
|
+
this.crypto.sha256,
|
|
5175
|
+
receiverPublicKey
|
|
5176
|
+
);
|
|
5177
|
+
const body = {
|
|
5178
|
+
type: DECRYPT_FACT_TYPE,
|
|
5179
|
+
targetNode,
|
|
5180
|
+
networkId: envelope.networkId,
|
|
5181
|
+
alg: envelope.alg,
|
|
5182
|
+
keyVersion: envelope.keyVersion,
|
|
5183
|
+
encryptedSymmetricKey: envelope.encryptedSymmetricKey,
|
|
5184
|
+
encryptedSymmetricKeyHash: envelope.encryptedSymmetricKeyHash,
|
|
5185
|
+
receiverPublicKey,
|
|
5186
|
+
receiverPublicKeyHash
|
|
5187
|
+
};
|
|
5188
|
+
const canonicalRequest = buildCanonicalDecryptRequest({
|
|
5189
|
+
crypto: this.crypto,
|
|
5190
|
+
body,
|
|
5191
|
+
receiverPublicKey: receiverKey.publicKey
|
|
5192
|
+
});
|
|
5193
|
+
const facts = buildDecryptFacts({
|
|
5194
|
+
crypto: this.crypto,
|
|
5195
|
+
body,
|
|
5196
|
+
encryptedSymmetricKeyHash: envelope.encryptedSymmetricKeyHash,
|
|
5197
|
+
receiverPublicKey: receiverKey.publicKey,
|
|
5198
|
+
canonicalBody: canonicalRequest.canonicalBody
|
|
5199
|
+
});
|
|
5200
|
+
const built = await buildDecryptInvocation(this.crypto, this._config.signer, {
|
|
5201
|
+
targetNode,
|
|
5202
|
+
networkId: envelope.networkId,
|
|
5203
|
+
body,
|
|
5204
|
+
facts,
|
|
5205
|
+
proof: capabilityProof
|
|
5206
|
+
});
|
|
5207
|
+
if (!built.ok) return built;
|
|
5208
|
+
let response;
|
|
5209
|
+
try {
|
|
5210
|
+
response = await this._config.transport.postDecrypt({
|
|
5211
|
+
targetNode,
|
|
5212
|
+
networkId: envelope.networkId,
|
|
5213
|
+
authorization: built.data.authorization,
|
|
5214
|
+
canonicalBody: built.data.canonicalBody
|
|
5215
|
+
});
|
|
5216
|
+
} catch (error) {
|
|
5217
|
+
return encErr(
|
|
5218
|
+
encryptionError({
|
|
5219
|
+
code: "TRANSPORT_ERROR",
|
|
5220
|
+
cause: toError2(error)
|
|
5221
|
+
})
|
|
5222
|
+
);
|
|
5223
|
+
}
|
|
5224
|
+
const verified = verifyDecryptResponse({
|
|
5225
|
+
crypto: this.crypto,
|
|
5226
|
+
request: body,
|
|
5227
|
+
facts,
|
|
5228
|
+
invocationCid: built.data.invocationCid,
|
|
5229
|
+
requestBodyHash: facts.bodyHash,
|
|
5230
|
+
response
|
|
5231
|
+
});
|
|
5232
|
+
if (!verified.ok) return verified;
|
|
5233
|
+
const symmetricKey = openWrappedKey(
|
|
5234
|
+
this.crypto,
|
|
5235
|
+
receiverKey.privateKey,
|
|
5236
|
+
verified.data
|
|
5237
|
+
);
|
|
5238
|
+
const plaintext = decryptEnvelopeWithKey(
|
|
5239
|
+
this.crypto,
|
|
5240
|
+
envelope,
|
|
5241
|
+
symmetricKey
|
|
5242
|
+
);
|
|
5243
|
+
return encOk(plaintext);
|
|
5244
|
+
} catch (error) {
|
|
5245
|
+
return encErr(
|
|
5246
|
+
encryptionError({
|
|
5247
|
+
code: "TRANSPORT_ERROR",
|
|
5248
|
+
cause: toError2(error)
|
|
5249
|
+
})
|
|
5250
|
+
);
|
|
5251
|
+
}
|
|
5252
|
+
}
|
|
5253
|
+
};
|
|
5254
|
+
EncryptionService.serviceName = "encryption";
|
|
4020
5255
|
export {
|
|
4021
5256
|
BaseService,
|
|
5257
|
+
DECRYPT_ACTION,
|
|
5258
|
+
DECRYPT_FACT_TYPE,
|
|
5259
|
+
DECRYPT_RESULT_TYPE,
|
|
5260
|
+
DEFAULT_ENCRYPTION_ALG,
|
|
5261
|
+
DEFAULT_KEY_VERSION,
|
|
4022
5262
|
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
4023
5263
|
DataVaultService,
|
|
4024
5264
|
DatabaseHandle,
|
|
4025
5265
|
DuckDbAction,
|
|
4026
5266
|
DuckDbDatabaseHandle,
|
|
4027
5267
|
DuckDbService,
|
|
5268
|
+
ENCRYPTION_NETWORK_URN_PREFIX,
|
|
5269
|
+
ENCRYPTION_SERVICE,
|
|
5270
|
+
ENCRYPTION_SERVICE_SHORT,
|
|
5271
|
+
ENVELOPE_VERSION,
|
|
5272
|
+
EncryptionService,
|
|
4028
5273
|
ErrorCodes,
|
|
4029
5274
|
GenericKVResponseSchema,
|
|
4030
5275
|
GenericResultSchema,
|
|
@@ -4034,6 +5279,8 @@ export {
|
|
|
4034
5279
|
KVListResultSchema,
|
|
4035
5280
|
KVResponseHeadersSchema,
|
|
4036
5281
|
KVService,
|
|
5282
|
+
NETWORK_NAME_PATTERN,
|
|
5283
|
+
NetworkIdError,
|
|
4037
5284
|
PrefixedKVService,
|
|
4038
5285
|
RetryPolicySchema,
|
|
4039
5286
|
SECRET_NAME_RE,
|
|
@@ -4055,23 +5302,51 @@ export {
|
|
|
4055
5302
|
authExpiredError,
|
|
4056
5303
|
authRequiredError,
|
|
4057
5304
|
authUnauthorizedError,
|
|
5305
|
+
base64Decode2 as base64Decode,
|
|
5306
|
+
base64Encode2 as base64Encode,
|
|
5307
|
+
buildCanonicalDecryptRequest,
|
|
5308
|
+
buildDecryptAttenuation,
|
|
5309
|
+
buildDecryptFacts,
|
|
5310
|
+
buildDecryptInvocation,
|
|
5311
|
+
buildNetworkId,
|
|
5312
|
+
canonicalHashHex,
|
|
5313
|
+
canonicalSignedResponse,
|
|
5314
|
+
canonicalize as canonicalizeEncryptionJson,
|
|
4058
5315
|
canonicalizeSecretScope,
|
|
5316
|
+
checkDecryptInvocationInput,
|
|
4059
5317
|
createKVResponseSchema,
|
|
4060
5318
|
createResultSchema,
|
|
4061
5319
|
createVaultCrypto,
|
|
5320
|
+
decryptEnvelopeWithKey,
|
|
4062
5321
|
defaultRetryPolicy,
|
|
5322
|
+
deriveSignedReceiverKey,
|
|
5323
|
+
discoverNetwork,
|
|
5324
|
+
encryptToNetwork,
|
|
5325
|
+
encryptionError,
|
|
5326
|
+
ensureNetworkUsableForDecrypt,
|
|
4063
5327
|
err,
|
|
4064
5328
|
errorResult,
|
|
5329
|
+
generateRandomReceiverKey,
|
|
5330
|
+
hexDecode,
|
|
5331
|
+
hexEncode2 as hexEncode,
|
|
5332
|
+
isNetworkId,
|
|
5333
|
+
networkDiscoveryKey,
|
|
4065
5334
|
networkError,
|
|
4066
5335
|
notFoundError,
|
|
4067
5336
|
ok,
|
|
5337
|
+
openWrappedKey,
|
|
4068
5338
|
parseAuthError,
|
|
5339
|
+
parseNetworkId,
|
|
4069
5340
|
permissionDeniedError,
|
|
5341
|
+
resolveSecretListPrefix,
|
|
4070
5342
|
resolveSecretPath,
|
|
4071
5343
|
serviceError,
|
|
4072
5344
|
storageLimitReachedError,
|
|
4073
5345
|
storageQuotaExceededError,
|
|
4074
5346
|
timeoutError,
|
|
5347
|
+
utf8Decode,
|
|
5348
|
+
utf8Encode,
|
|
5349
|
+
validateEnvelope,
|
|
4075
5350
|
validateKVListResponse,
|
|
4076
5351
|
validateKVResponseHeaders,
|
|
4077
5352
|
validateRetryPolicy,
|
|
@@ -4079,6 +5354,7 @@ export {
|
|
|
4079
5354
|
validateServiceRequestEvent,
|
|
4080
5355
|
validateServiceResponseEvent,
|
|
4081
5356
|
validateServiceSession,
|
|
5357
|
+
verifyDecryptResponse,
|
|
4082
5358
|
wrapError
|
|
4083
5359
|
};
|
|
4084
5360
|
//# sourceMappingURL=index.js.map
|