@storacha/encrypt-upload-client 1.0.1 → 1.1.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/core/client.d.ts +3 -4
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +4 -5
- package/dist/crypto/adapters/kms-crypto-adapter.d.ts +5 -6
- package/dist/crypto/adapters/kms-crypto-adapter.d.ts.map +1 -1
- package/dist/crypto/adapters/kms-crypto-adapter.js +22 -13
- package/dist/crypto/adapters/lit-crypto-adapter.d.ts +4 -4
- package/dist/crypto/adapters/lit-crypto-adapter.d.ts.map +1 -1
- package/dist/crypto/adapters/lit-crypto-adapter.js +10 -10
- package/dist/crypto/factories.browser.d.ts +5 -1
- package/dist/crypto/factories.browser.d.ts.map +1 -1
- package/dist/crypto/factories.browser.js +4 -2
- package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.d.ts.map +1 -1
- package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.js +0 -1
- package/dist/handlers/decrypt-handler.d.ts +3 -3
- package/dist/handlers/decrypt-handler.d.ts.map +1 -1
- package/dist/handlers/decrypt-handler.js +56 -15
- package/dist/handlers/encrypt-handler.d.ts.map +1 -1
- package/dist/handlers/encrypt-handler.js +8 -5
- package/dist/types.d.ts +28 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/file-metadata.d.ts +14 -0
- package/dist/utils/file-metadata.d.ts.map +1 -0
- package/dist/utils/file-metadata.js +231 -0
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +4 -6
- package/package.json +4 -4
package/dist/core/client.d.ts
CHANGED
|
@@ -34,11 +34,10 @@ export class EncryptedClient implements Type.EncryptedClient {
|
|
|
34
34
|
* Retrieve and decrypt a file from the Storacha network
|
|
35
35
|
*
|
|
36
36
|
* @param {Type.AnyLink} cid - The link to the file to retrieve
|
|
37
|
-
* @param {
|
|
38
|
-
* @
|
|
39
|
-
* @returns {Promise<ReadableStream>} - The decrypted file
|
|
37
|
+
* @param {Type.DecryptionConfig} decryptionConfig - User-provided decryption config
|
|
38
|
+
* @returns {Promise<Type.DecryptionResult>} - The decrypted file with metadata
|
|
40
39
|
*/
|
|
41
|
-
retrieveAndDecryptFile(cid: Type.AnyLink,
|
|
40
|
+
retrieveAndDecryptFile(cid: Type.AnyLink, decryptionConfig: Type.DecryptionConfig): Promise<Type.DecryptionResult>;
|
|
42
41
|
}
|
|
43
42
|
export function create(options: Type.EncryptedClientOptions): Promise<EncryptedClient>;
|
|
44
43
|
import * as Type from '../types.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.js"],"names":[],"mappings":"AAKA,yCAAyC;AACzC,wCADiB,IAAI,CAAC,eAAe;IAoBnC;;;;OAIG;IACH,4BAJW,OAAO,kBAAkB,EAAE,MAAM,iBACjC,IAAI,CAAC,aAAa,cAClB,GAAG,EAMb;IA3BD;;;OAGG;IACH,0BAHU,IAAI,CAAC,aAAa,CAGd;IAEd;;;OAGG;IACH,2BAHU,OAAO,kBAAkB,EAAE,MAAM,CAG5B;IAEf;;;OAGG;IACH,uBAHU,GAAG,CAGF;IAaX;;;;;;;OAOG;IACH,2BALW,IAAI,CAAC,QAAQ,oBACb,IAAI,CAAC,gBAAgB,kBACrB,IAAI,CAAC,aAAa,GAChB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAUjC;IAED
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.js"],"names":[],"mappings":"AAKA,yCAAyC;AACzC,wCADiB,IAAI,CAAC,eAAe;IAoBnC;;;;OAIG;IACH,4BAJW,OAAO,kBAAkB,EAAE,MAAM,iBACjC,IAAI,CAAC,aAAa,cAClB,GAAG,EAMb;IA3BD;;;OAGG;IACH,0BAHU,IAAI,CAAC,aAAa,CAGd;IAEd;;;OAGG;IACH,2BAHU,OAAO,kBAAkB,EAAE,MAAM,CAG5B;IAEf;;;OAGG;IACH,uBAHU,GAAG,CAGF;IAaX;;;;;;;OAOG;IACH,2BALW,IAAI,CAAC,QAAQ,oBACb,IAAI,CAAC,gBAAgB,kBACrB,IAAI,CAAC,aAAa,GAChB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAUjC;IAED;;;;;;OAMG;IACH,4BAJW,IAAI,CAAC,OAAO,oBACZ,IAAI,CAAC,gBAAgB,GACnB,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAU1C;CACF;AASM,gCAFI,IAAI,CAAC,sBAAsB,4BAQrC;sBArFqB,aAAa"}
|
package/dist/core/client.js
CHANGED
|
@@ -44,12 +44,11 @@ export class EncryptedClient {
|
|
|
44
44
|
* Retrieve and decrypt a file from the Storacha network
|
|
45
45
|
*
|
|
46
46
|
* @param {Type.AnyLink} cid - The link to the file to retrieve
|
|
47
|
-
* @param {
|
|
48
|
-
* @
|
|
49
|
-
* @returns {Promise<ReadableStream>} - The decrypted file
|
|
47
|
+
* @param {Type.DecryptionConfig} decryptionConfig - User-provided decryption config
|
|
48
|
+
* @returns {Promise<Type.DecryptionResult>} - The decrypted file with metadata
|
|
50
49
|
*/
|
|
51
|
-
async retrieveAndDecryptFile(cid,
|
|
52
|
-
return retrieveAndDecrypt(this._storachaClient, this._cryptoAdapter, this._gatewayURL, cid,
|
|
50
|
+
async retrieveAndDecryptFile(cid, decryptionConfig) {
|
|
51
|
+
return retrieveAndDecrypt(this._storachaClient, this._cryptoAdapter, this._gatewayURL, cid, decryptionConfig);
|
|
53
52
|
}
|
|
54
53
|
}
|
|
55
54
|
/**
|
|
@@ -48,17 +48,15 @@ export class KMSCryptoAdapter implements Type.CryptoAdapter {
|
|
|
48
48
|
/**
|
|
49
49
|
* @param {string} encryptedKey
|
|
50
50
|
* @param {object} configs
|
|
51
|
-
* @param {Type.
|
|
51
|
+
* @param {Type.DecryptionConfig} configs.decryptionConfig
|
|
52
52
|
* @param {Type.ExtractedMetadata} configs.metadata
|
|
53
|
-
* @param {Uint8Array} configs.delegationCAR
|
|
54
53
|
* @param {Type.AnyLink} configs.resourceCID
|
|
55
54
|
* @param {import('@storacha/client/types').Signer<import('@storacha/client/types').DID, import('@storacha/client/types').SigAlg>} configs.issuer
|
|
56
55
|
* @param {import('@storacha/client/types').DID} configs.audience
|
|
57
56
|
*/
|
|
58
57
|
decryptSymmetricKey(encryptedKey: string, configs: {
|
|
59
|
-
|
|
58
|
+
decryptionConfig: Type.DecryptionConfig;
|
|
60
59
|
metadata: Type.ExtractedMetadata;
|
|
61
|
-
delegationCAR: Uint8Array;
|
|
62
60
|
resourceCID: Type.AnyLink;
|
|
63
61
|
issuer: import("@storacha/client/types").Signer<import("@storacha/client/types").DID, import("@storacha/client/types").SigAlg>;
|
|
64
62
|
audience: import("@storacha/client/types").DID;
|
|
@@ -71,11 +69,12 @@ export class KMSCryptoAdapter implements Type.CryptoAdapter {
|
|
|
71
69
|
*
|
|
72
70
|
* @param {string} encryptedSymmetricKey - The encrypted symmetric key (base64-encoded)
|
|
73
71
|
* @param {Type.SpaceDID} spaceDID - The space DID
|
|
74
|
-
* @param {import('@ucanto/interface').Proof}
|
|
72
|
+
* @param {import('@ucanto/interface').Proof} decryptionProof - The decryption delegation proof
|
|
73
|
+
* @param {import('@ucanto/interface').Proof[]} proofs - The proofs to access the space
|
|
75
74
|
* @param {import('@storacha/client/types').Signer<import('@storacha/client/types').DID, import('@storacha/client/types').SigAlg>} issuer - The issuer
|
|
76
75
|
* @returns {Promise<{decryptedSymmetricKey: string}>} - The decrypted symmetric key (base64-encoded)
|
|
77
76
|
*/
|
|
78
|
-
getDecryptedSymmetricKey(encryptedSymmetricKey: string, spaceDID: Type.SpaceDID,
|
|
77
|
+
getDecryptedSymmetricKey(encryptedSymmetricKey: string, spaceDID: Type.SpaceDID, decryptionProof: import("@ucanto/interface").Proof, proofs: import("@ucanto/interface").Proof[], issuer: import("@storacha/client/types").Signer<import("@storacha/client/types").DID, import("@storacha/client/types").SigAlg>): Promise<{
|
|
79
78
|
decryptedSymmetricKey: string;
|
|
80
79
|
}>;
|
|
81
80
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kms-crypto-adapter.d.ts","sourceRoot":"","sources":["../../../src/crypto/adapters/kms-crypto-adapter.js"],"names":[],"mappings":"AAWA;;;;;;;GAOG;AACH,yCAFgB,IAAI,CAAC,aAAa;IAGhC;;;;;;;;OAQG;IACH,6BANW,IAAI,CAAC,eAAe,wBACpB,GAAG,GAAC,MAAM,wBACV,OAAO,MAAM,IAAI,MAAM,EAAE,YAEjC;QAA0B,iBAAiB;KAC7C,EA0BA;IAnBC,sCAAsC;IAiBtC,0BAA+B;IAC/B,wFAA2D;IAG7D;;;;OAIG;IACH,oBAFW,IAAI,CAAC,QAAQ,+BAIvB;IAED;;;;;;OAMG;IACH,6BAJW,cAAc,OACd,UAAU,MACV,UAAU,gCAIpB;IAED;;;;;;;OAOG;IACH,yBALW,UAAU,MACV,UAAU,oBACV,IAAI,CAAC,gBAAgB,GACnB,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,CA4B5C;IAED
|
|
1
|
+
{"version":3,"file":"kms-crypto-adapter.d.ts","sourceRoot":"","sources":["../../../src/crypto/adapters/kms-crypto-adapter.js"],"names":[],"mappings":"AAWA;;;;;;;GAOG;AACH,yCAFgB,IAAI,CAAC,aAAa;IAGhC;;;;;;;;OAQG;IACH,6BANW,IAAI,CAAC,eAAe,wBACpB,GAAG,GAAC,MAAM,wBACV,OAAO,MAAM,IAAI,MAAM,EAAE,YAEjC;QAA0B,iBAAiB;KAC7C,EA0BA;IAnBC,sCAAsC;IAiBtC,0BAA+B;IAC/B,wFAA2D;IAG7D;;;;OAIG;IACH,oBAFW,IAAI,CAAC,QAAQ,+BAIvB;IAED;;;;;;OAMG;IACH,6BAJW,cAAc,OACd,UAAU,MACV,UAAU,gCAIpB;IAED;;;;;;;OAOG;IACH,yBALW,UAAU,MACV,UAAU,oBACV,IAAI,CAAC,gBAAgB,GACnB,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,CA4B5C;IAED;;;;;;;;OAQG;IACH,kCARW,MAAM,WAEd;QAAuC,gBAAgB,EAA/C,IAAI,CAAC,gBAAgB;QACW,QAAQ,EAAxC,IAAI,CAAC,iBAAiB;QACA,WAAW,EAAjC,IAAI,CAAC,OAAO;QACoH,MAAM,EAAtI,OAAO,wBAAwB,EAAE,MAAM,CAAC,OAAO,wBAAwB,EAAE,GAAG,EAAE,OAAO,wBAAwB,EAAE,MAAM,CAAC;QACxE,QAAQ,EAAtD,OAAO,wBAAwB,EAAE,GAAG;KAC9C;;;OA6BA;IAED;;;;;;;;;OASG;IACH,gDAPW,MAAM,YACN,IAAI,CAAC,QAAQ,mBACb,OAAO,mBAAmB,EAAE,KAAK,UACjC,OAAO,mBAAmB,EAAE,KAAK,EAAE,UACnC,OAAO,wBAAwB,EAAE,MAAM,CAAC,OAAO,wBAAwB,EAAE,GAAG,EAAE,OAAO,wBAAwB,EAAE,MAAM,CAAC,GACpH,OAAO,CAAC;QAAC,qBAAqB,EAAE,MAAM,CAAA;KAAC,CAAC,CAgCpD;IAED;;;;;;OAMG;IACH,8BAHW,UAAU,GACR,IAAI,CAAC,iBAAiB,CAiClC;IAED;;;OAGG;IACH,0BAHW,IAAI,CAAC,iBAAiB,GACpB,MAAM,CASlB;IAED;;;;;;;OAOG;IACH,iCALW,MAAM,gBACN,MAAM,YACN,IAAI,CAAC,cAAc,GACjB,OAAO,CAAC;QAAE,GAAG,EAAE,OAAO,+BAA+B,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC,CAoBhG;IAED;;;;;OAKG;IACH,oCAHW,IAAI,CAAC,gBAAgB,GACnB,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CA6B/E;IAED;;;;OAIG;IACH,mBAFa,YAAY,CAWxB;IAED;;;;;;OAMG;IACH,8BAJW,UAAU,gBACV,MAAM,GACJ,OAAO,CAAC,UAAU,CAAC,CAyB/B;IAED;;;;;OAKG;IACH,sBAHW,MAAM,GACJ,WAAW,CA6BvB;IAED,kFASC;IAED;;;;;OAKG;IACH,sCAHW,IAAI,CAAC,QAAQ,GACX,MAAM,CAIlB;CACF;sBAzZqB,gBAAgB"}
|
|
@@ -88,28 +88,27 @@ export class KMSCryptoAdapter {
|
|
|
88
88
|
/**
|
|
89
89
|
* @param {string} encryptedKey
|
|
90
90
|
* @param {object} configs
|
|
91
|
-
* @param {Type.
|
|
91
|
+
* @param {Type.DecryptionConfig} configs.decryptionConfig
|
|
92
92
|
* @param {Type.ExtractedMetadata} configs.metadata
|
|
93
|
-
* @param {Uint8Array} configs.delegationCAR
|
|
94
93
|
* @param {Type.AnyLink} configs.resourceCID
|
|
95
94
|
* @param {import('@storacha/client/types').Signer<import('@storacha/client/types').DID, import('@storacha/client/types').SigAlg>} configs.issuer
|
|
96
95
|
* @param {import('@storacha/client/types').DID} configs.audience
|
|
97
96
|
*/
|
|
98
97
|
async decryptSymmetricKey(encryptedKey, configs) {
|
|
99
98
|
// Step 1: Validate configs
|
|
100
|
-
const {
|
|
99
|
+
const { decryptionConfig, metadata, issuer } = configs;
|
|
101
100
|
if (metadata.strategy !== 'kms') {
|
|
102
101
|
throw new Error('KMSCryptoAdapter can only handle KMS metadata');
|
|
103
102
|
}
|
|
104
|
-
const { spaceDID,
|
|
105
|
-
if (!spaceDID || !
|
|
106
|
-
throw new Error('SpaceDID and
|
|
103
|
+
const { spaceDID, decryptDelegation } = decryptionConfig;
|
|
104
|
+
if (!spaceDID || !decryptDelegation) {
|
|
105
|
+
throw new Error('SpaceDID and decryptDelegation are required');
|
|
107
106
|
}
|
|
108
107
|
if (!issuer) {
|
|
109
108
|
throw new Error('Issuer is required');
|
|
110
109
|
}
|
|
111
110
|
// Step 2: Get the decrypted key from KMS via gateway
|
|
112
|
-
const { decryptedSymmetricKey } = await this.getDecryptedSymmetricKey(encryptedKey, spaceDID,
|
|
111
|
+
const { decryptedSymmetricKey } = await this.getDecryptedSymmetricKey(encryptedKey, spaceDID, decryptDelegation, configs.decryptionConfig.proofs || [], issuer);
|
|
113
112
|
// Step 3: Decode and split the combined key and IV
|
|
114
113
|
const combinedKeyAndIV = base64.decode(decryptedSymmetricKey);
|
|
115
114
|
return this.symmetricCrypto.splitKeyAndIV(combinedKeyAndIV);
|
|
@@ -119,11 +118,12 @@ export class KMSCryptoAdapter {
|
|
|
119
118
|
*
|
|
120
119
|
* @param {string} encryptedSymmetricKey - The encrypted symmetric key (base64-encoded)
|
|
121
120
|
* @param {Type.SpaceDID} spaceDID - The space DID
|
|
122
|
-
* @param {import('@ucanto/interface').Proof}
|
|
121
|
+
* @param {import('@ucanto/interface').Proof} decryptionProof - The decryption delegation proof
|
|
122
|
+
* @param {import('@ucanto/interface').Proof[]} proofs - The proofs to access the space
|
|
123
123
|
* @param {import('@storacha/client/types').Signer<import('@storacha/client/types').DID, import('@storacha/client/types').SigAlg>} issuer - The issuer
|
|
124
124
|
* @returns {Promise<{decryptedSymmetricKey: string}>} - The decrypted symmetric key (base64-encoded)
|
|
125
125
|
*/
|
|
126
|
-
async getDecryptedSymmetricKey(encryptedSymmetricKey, spaceDID,
|
|
126
|
+
async getDecryptedSymmetricKey(encryptedSymmetricKey, spaceDID, decryptionProof, proofs, issuer) {
|
|
127
127
|
// Step 1: Invoke the KeyDecrypt capability passing the decryption proof
|
|
128
128
|
const result = await EncryptionKeyDecrypt.invoke({
|
|
129
129
|
issuer,
|
|
@@ -132,13 +132,17 @@ export class KMSCryptoAdapter {
|
|
|
132
132
|
nb: {
|
|
133
133
|
key: base64.decode(encryptedSymmetricKey), // Convert base64 string to bytes
|
|
134
134
|
},
|
|
135
|
-
proofs: [
|
|
135
|
+
proofs: proofs ? [...proofs, decryptionProof] : [decryptionProof],
|
|
136
136
|
}).execute(this.newKeyManagerServiceConnection());
|
|
137
137
|
// Step 2: Handle the result
|
|
138
138
|
if (result.out.error) {
|
|
139
|
-
|
|
139
|
+
// Only show the error message, not the full error object with stack trace
|
|
140
|
+
const errorMessage = result.out.error.message ||
|
|
141
|
+
result.out.error.name ||
|
|
142
|
+
'KMS decryption failed';
|
|
143
|
+
throw new Error(errorMessage);
|
|
140
144
|
}
|
|
141
|
-
// Step 3: Return the
|
|
145
|
+
// Step 3: Return the multibase-encoded decrypted key from the gateway response
|
|
142
146
|
return /** @type {{decryptedSymmetricKey: string}} */ (result.out.ok);
|
|
143
147
|
}
|
|
144
148
|
/**
|
|
@@ -226,10 +230,15 @@ export class KMSCryptoAdapter {
|
|
|
226
230
|
location: encryptionConfig.location,
|
|
227
231
|
keyring: encryptionConfig.keyring,
|
|
228
232
|
},
|
|
233
|
+
proofs: encryptionConfig.proofs,
|
|
229
234
|
}).execute(this.newKeyManagerServiceConnection());
|
|
230
235
|
// Step 2: Handle the result
|
|
231
236
|
if (setupResult.out.error) {
|
|
232
|
-
|
|
237
|
+
// Only show the error message, not the full error object with stack trace to avoid leaking information
|
|
238
|
+
const errorMessage = setupResult.out.error.message ||
|
|
239
|
+
setupResult.out.error.name ||
|
|
240
|
+
'Encryption setup failed';
|
|
241
|
+
throw new Error(errorMessage);
|
|
233
242
|
}
|
|
234
243
|
// Step 3: Return the public key and key reference
|
|
235
244
|
return /** @type {{ publicKey: string, provider: string, algorithm: string }} */ (setupResult.out.ok);
|
|
@@ -46,18 +46,18 @@ export class LitCryptoAdapter implements Type.CryptoAdapter {
|
|
|
46
46
|
*
|
|
47
47
|
* @param {string} encryptedKey - The encrypted key to decrypt
|
|
48
48
|
* @param {object} configs - The decryption configuration
|
|
49
|
-
* @param {Type.
|
|
49
|
+
* @param {Type.DecryptionConfig} configs.decryptionConfig - The decryption config
|
|
50
50
|
* @param {Type.ExtractedMetadata} configs.metadata - The extracted metadata
|
|
51
|
-
* @param {
|
|
51
|
+
* @param {import('@ucanto/interface').Proof} configs.decryptDelegation - The delegation that gives permission to decrypt (required for both strategies)
|
|
52
52
|
* @param {Type.AnyLink} configs.resourceCID - The resource CID
|
|
53
53
|
* @param {import('@storacha/client/types').Signer<import('@storacha/client/types').DID, import('@storacha/client/types').SigAlg>} configs.issuer - The issuer
|
|
54
54
|
* @param {import('@storacha/client/types').DID} configs.audience - The audience
|
|
55
55
|
* @returns {Promise<{ key: Uint8Array, iv: Uint8Array }>} - The decrypted key and IV
|
|
56
56
|
*/
|
|
57
57
|
decryptSymmetricKey(encryptedKey: string, configs: {
|
|
58
|
-
|
|
58
|
+
decryptionConfig: Type.DecryptionConfig;
|
|
59
59
|
metadata: Type.ExtractedMetadata;
|
|
60
|
-
|
|
60
|
+
decryptDelegation: import("@ucanto/interface").Proof;
|
|
61
61
|
resourceCID: Type.AnyLink;
|
|
62
62
|
issuer: import("@storacha/client/types").Signer<import("@storacha/client/types").DID, import("@storacha/client/types").SigAlg>;
|
|
63
63
|
audience: import("@storacha/client/types").DID;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lit-crypto-adapter.d.ts","sourceRoot":"","sources":["../../../src/crypto/adapters/lit-crypto-adapter.js"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,yCAFgB,IAAI,CAAC,aAAa;IAGhC;;;;;OAKG;IACH,6BAHW,IAAI,CAAC,eAAe,aACpB,OAAO,+BAA+B,EAAE,aAAa,EAK/D;IAFC,sCAAsC;IACtC,iEAA0B;IAG5B;;;;;OAKG;IACH,oBAHW,IAAI,CAAC,QAAQ,GACX,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAIvC;IAED;;;;;;;OAOG;IACH,6BALW,cAAc,OACd,UAAU,MACV,UAAU,GACR,OAAO,CAAC,cAAc,CAAC,CAInC;IAED;;;;;;;OAOG;IACH,yBALW,UAAU,MACV,UAAU,oBACV,IAAI,CAAC,gBAAgB,GACnB,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAgC5C;IAED;;;;;;;;;;;;OAYG;IACH,kCAVW,MAAM,WAEd;
|
|
1
|
+
{"version":3,"file":"lit-crypto-adapter.d.ts","sourceRoot":"","sources":["../../../src/crypto/adapters/lit-crypto-adapter.js"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,yCAFgB,IAAI,CAAC,aAAa;IAGhC;;;;;OAKG;IACH,6BAHW,IAAI,CAAC,eAAe,aACpB,OAAO,+BAA+B,EAAE,aAAa,EAK/D;IAFC,sCAAsC;IACtC,iEAA0B;IAG5B;;;;;OAKG;IACH,oBAHW,IAAI,CAAC,QAAQ,GACX,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAIvC;IAED;;;;;;;OAOG;IACH,6BALW,cAAc,OACd,UAAU,MACV,UAAU,GACR,OAAO,CAAC,cAAc,CAAC,CAInC;IAED;;;;;;;OAOG;IACH,yBALW,UAAU,MACV,UAAU,oBACV,IAAI,CAAC,gBAAgB,GACnB,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAgC5C;IAED;;;;;;;;;;;;OAYG;IACH,kCAVW,MAAM,WAEd;QAAuC,gBAAgB,EAA/C,IAAI,CAAC,gBAAgB;QACW,QAAQ,EAAxC,IAAI,CAAC,iBAAiB;QACqB,iBAAiB,EAA5D,OAAO,mBAAmB,EAAE,KAAK;QACX,WAAW,EAAjC,IAAI,CAAC,OAAO;QACoH,MAAM,EAAtI,OAAO,wBAAwB,EAAE,MAAM,CAAC,OAAO,wBAAwB,EAAE,GAAG,EAAE,OAAO,wBAAwB,EAAE,MAAM,CAAC;QACxE,QAAQ,EAAtD,OAAO,wBAAwB,EAAE,GAAG;KAC5C,GAAU,OAAO,CAAC;QAAE,GAAG,EAAE,UAAU,CAAC;QAAC,EAAE,EAAE,UAAU,CAAA;KAAE,CAAC,CAgFxD;IAED;;;;;OAKG;IACH,8BAHW,UAAU,GACR,IAAI,CAAC,iBAAiB,CA4BlC;IAED;;;;;OAKG;IACH,0BAHW,IAAI,CAAC,iBAAiB,GACpB,MAAM,CAOlB;IAED;;;;;;;OAOG;IACH,iCALW,MAAM,gBACN,MAAM,YACN,IAAI,CAAC,cAAc,GACjB,OAAO,CAAC;QAAE,GAAG,EAAE,OAAO,+BAA+B,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC,CAehG;CACF;sBAtPqB,gBAAgB"}
|
|
@@ -78,16 +78,16 @@ export class LitCryptoAdapter {
|
|
|
78
78
|
*
|
|
79
79
|
* @param {string} encryptedKey - The encrypted key to decrypt
|
|
80
80
|
* @param {object} configs - The decryption configuration
|
|
81
|
-
* @param {Type.
|
|
81
|
+
* @param {Type.DecryptionConfig} configs.decryptionConfig - The decryption config
|
|
82
82
|
* @param {Type.ExtractedMetadata} configs.metadata - The extracted metadata
|
|
83
|
-
* @param {
|
|
83
|
+
* @param {import('@ucanto/interface').Proof} configs.decryptDelegation - The delegation that gives permission to decrypt (required for both strategies)
|
|
84
84
|
* @param {Type.AnyLink} configs.resourceCID - The resource CID
|
|
85
85
|
* @param {import('@storacha/client/types').Signer<import('@storacha/client/types').DID, import('@storacha/client/types').SigAlg>} configs.issuer - The issuer
|
|
86
86
|
* @param {import('@storacha/client/types').DID} configs.audience - The audience
|
|
87
87
|
* @returns {Promise<{ key: Uint8Array, iv: Uint8Array }>} - The decrypted key and IV
|
|
88
88
|
*/
|
|
89
89
|
async decryptSymmetricKey(encryptedKey, configs) {
|
|
90
|
-
const {
|
|
90
|
+
const { decryptionConfig, metadata, resourceCID, issuer, audience } = configs;
|
|
91
91
|
// Validate Lit metadata
|
|
92
92
|
if (metadata.strategy !== 'lit') {
|
|
93
93
|
throw new Error('LitCryptoAdapter can only handle Lit metadata');
|
|
@@ -96,26 +96,26 @@ export class LitCryptoAdapter {
|
|
|
96
96
|
// Step 1. Extract spaceDID from access control conditions
|
|
97
97
|
const spaceDID = /** @type {Type.SpaceDID} */ (accessControlConditions[0].parameters[1]);
|
|
98
98
|
// Step 2. Create session signatures if not provided
|
|
99
|
-
let sessionSigs =
|
|
99
|
+
let sessionSigs = decryptionConfig.sessionSigs;
|
|
100
100
|
if (!sessionSigs) {
|
|
101
101
|
const acc =
|
|
102
102
|
/** @type import('@lit-protocol/types').AccessControlConditions */ (
|
|
103
103
|
/** @type {unknown} */ (accessControlConditions));
|
|
104
104
|
const expiration = new Date(Date.now() + 1000 * 60 * 5).toISOString(); // 5 min
|
|
105
105
|
// Step 2.1. Create session signatures for the wallet if provided
|
|
106
|
-
if (
|
|
106
|
+
if (decryptionConfig.wallet) {
|
|
107
107
|
sessionSigs = await Lit.getSessionSigs(this.litClient, {
|
|
108
|
-
wallet:
|
|
108
|
+
wallet: decryptionConfig.wallet,
|
|
109
109
|
dataToEncryptHash: plaintextKeyHash,
|
|
110
110
|
expiration,
|
|
111
111
|
accessControlConditions: acc,
|
|
112
112
|
});
|
|
113
113
|
}
|
|
114
114
|
// Step 2.2. Otherwise, create session signatures for the PKP if provided
|
|
115
|
-
else if (
|
|
115
|
+
else if (decryptionConfig.pkpPublicKey && decryptionConfig.authMethod) {
|
|
116
116
|
sessionSigs = await Lit.getPkpSessionSigs(this.litClient, {
|
|
117
|
-
pkpPublicKey:
|
|
118
|
-
authMethod:
|
|
117
|
+
pkpPublicKey: decryptionConfig.pkpPublicKey,
|
|
118
|
+
authMethod: decryptionConfig.authMethod,
|
|
119
119
|
dataToEncryptHash: plaintextKeyHash,
|
|
120
120
|
expiration,
|
|
121
121
|
accessControlConditions: acc,
|
|
@@ -127,7 +127,7 @@ export class LitCryptoAdapter {
|
|
|
127
127
|
}
|
|
128
128
|
// Step 3. Create wrapped UCAN invocation
|
|
129
129
|
const wrappedInvocationJSON = await createDecryptWrappedInvocation({
|
|
130
|
-
|
|
130
|
+
decryptDelegation: decryptionConfig.decryptDelegation,
|
|
131
131
|
spaceDID,
|
|
132
132
|
resourceCID,
|
|
133
133
|
issuer,
|
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @param {URL|string} keyManagerServiceURL
|
|
7
7
|
* @param {string} keyManagerServiceDID
|
|
8
|
+
* @param {object} [options] - Optional configuration
|
|
9
|
+
* @param {boolean} [options.allowInsecureHttp] - Allow HTTP for testing (NOT for production)
|
|
8
10
|
*/
|
|
9
|
-
export function createGenericKMSAdapter(keyManagerServiceURL: URL | string, keyManagerServiceDID: string
|
|
11
|
+
export function createGenericKMSAdapter(keyManagerServiceURL: URL | string, keyManagerServiceDID: string, options?: {
|
|
12
|
+
allowInsecureHttp?: boolean | undefined;
|
|
13
|
+
}): KMSCryptoAdapter;
|
|
10
14
|
import { KMSCryptoAdapter } from './adapters/kms-crypto-adapter.js';
|
|
11
15
|
//# sourceMappingURL=factories.browser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factories.browser.d.ts","sourceRoot":"","sources":["../../src/crypto/factories.browser.js"],"names":[],"mappings":"AAGA
|
|
1
|
+
{"version":3,"file":"factories.browser.d.ts","sourceRoot":"","sources":["../../src/crypto/factories.browser.js"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,8DALW,GAAG,GAAC,MAAM,wBACV,MAAM,YAEd;IAA0B,iBAAiB;CAC7C,oBAaA;iCAxBgC,kCAAkC"}
|
|
@@ -7,10 +7,12 @@ import { KMSCryptoAdapter } from './adapters/kms-crypto-adapter.js';
|
|
|
7
7
|
*
|
|
8
8
|
* @param {URL|string} keyManagerServiceURL
|
|
9
9
|
* @param {string} keyManagerServiceDID
|
|
10
|
+
* @param {object} [options] - Optional configuration
|
|
11
|
+
* @param {boolean} [options.allowInsecureHttp] - Allow HTTP for testing (NOT for production)
|
|
10
12
|
*/
|
|
11
|
-
export function createGenericKMSAdapter(keyManagerServiceURL, keyManagerServiceDID) {
|
|
13
|
+
export function createGenericKMSAdapter(keyManagerServiceURL, keyManagerServiceDID, options = {}) {
|
|
12
14
|
const symmetricCrypto = new GenericAesCtrStreamingCrypto();
|
|
13
15
|
return new KMSCryptoAdapter(symmetricCrypto, keyManagerServiceURL,
|
|
14
|
-
/** @type {`did:${string}:${string}`} */ (keyManagerServiceDID));
|
|
16
|
+
/** @type {`did:${string}:${string}`} */ (keyManagerServiceDID), options);
|
|
15
17
|
}
|
|
16
18
|
//# sourceMappingURL=factories.browser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generic-aes-ctr-streaming-crypto.d.ts","sourceRoot":"","sources":["../../../src/crypto/symmetric/generic-aes-ctr-streaming-crypto.js"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qDAFgB,IAAI,CAAC,eAAe;IASlC;;;;OAIG;IACH,eAFa,OAAO,CAAC,UAAU,CAAC,CAI/B;IAED;;;;;;OAMG;IACH,0BAJW,UAAU,aACV,MAAM,GACJ,UAAU,CAsBtB;IAED;;;;;OAKG;IACH,oBAHW,IAAI,GACF,OAAO,CAAC;QAAE,GAAG,EAAE,UAAU,CAAC;QAAC,EAAE,EAAE,UAAU,CAAC;QAAC,eAAe,EAAE,cAAc,CAAA;KAAE,CAAC,CAsDzF;IAED;;;;;;;OAOG;IACH,6BALW,cAAc,OACd,UAAU,MACV,UAAU,GACR,OAAO,CAAC,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"generic-aes-ctr-streaming-crypto.d.ts","sourceRoot":"","sources":["../../../src/crypto/symmetric/generic-aes-ctr-streaming-crypto.js"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qDAFgB,IAAI,CAAC,eAAe;IASlC;;;;OAIG;IACH,eAFa,OAAO,CAAC,UAAU,CAAC,CAI/B;IAED;;;;;;OAMG;IACH,0BAJW,UAAU,aACV,MAAM,GACJ,UAAU,CAsBtB;IAED;;;;;OAKG;IACH,oBAHW,IAAI,GACF,OAAO,CAAC;QAAE,GAAG,EAAE,UAAU,CAAC;QAAC,EAAE,EAAE,UAAU,CAAC;QAAC,eAAe,EAAE,cAAc,CAAA;KAAE,CAAC,CAsDzF;IAED;;;;;;;OAOG;IACH,6BALW,cAAc,OACd,UAAU,MACV,UAAU,GACR,OAAO,CAAC,cAAc,CAAC,CA+CnC;IAED;;;;;;OAMG;IACH,qBAJW,UAAU,MACV,UAAU,GACR,UAAU,CAatB;IAED;;;;;OAKG;IACH,wBAHW,UAAU,GACR;QAAE,GAAG,EAAE,UAAU,CAAC;QAAC,EAAE,EAAE,UAAU,CAAA;KAAE,CAc/C;CACF;sBAlOqB,gBAAgB"}
|
|
@@ -114,7 +114,6 @@ export class GenericAesCtrStreamingCrypto {
|
|
|
114
114
|
// State for AES-CTR counter management
|
|
115
115
|
let counter = new Uint8Array(iv);
|
|
116
116
|
let totalBlocks = 0; // Track total blocks processed (CRITICAL for security)
|
|
117
|
-
// Create TransformStream (inspired by Node.js approach)
|
|
118
117
|
const decryptTransform = new TransformStream({
|
|
119
118
|
transform: async (chunk, controller) => {
|
|
120
119
|
try {
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* encryption and decryption operations.
|
|
6
6
|
* @param {Uint8Array} key - The symmetric key
|
|
7
7
|
* @param {Uint8Array} iv - The initialization vector
|
|
8
|
-
* @param {Uint8Array} content - The encrypted file content
|
|
8
|
+
* @param {AsyncIterable<Uint8Array>|Uint8Array} content - The encrypted file content
|
|
9
9
|
* @returns {Promise<ReadableStream>} The decrypted file stream
|
|
10
10
|
*/
|
|
11
|
-
export function decryptFileWithKey(cryptoAdapter: Type.CryptoAdapter, key: Uint8Array, iv: Uint8Array, content: Uint8Array): Promise<ReadableStream>;
|
|
12
|
-
export function retrieveAndDecrypt(storachaClient: import("@storacha/client").Client, cryptoAdapter: Type.CryptoAdapter, gatewayURL: URL, cid: Type.AnyLink,
|
|
11
|
+
export function decryptFileWithKey(cryptoAdapter: Type.CryptoAdapter, key: Uint8Array, iv: Uint8Array, content: AsyncIterable<Uint8Array> | Uint8Array): Promise<ReadableStream>;
|
|
12
|
+
export function retrieveAndDecrypt(storachaClient: import("@storacha/client").Client, cryptoAdapter: Type.CryptoAdapter, gatewayURL: URL, cid: Type.AnyLink, decryptionConfig: Type.DecryptionConfig): Promise<Type.DecryptionResult>;
|
|
13
13
|
export function getCarFileFromPublicGateway(gatewayURL: URL, cid: string): Promise<Uint8Array>;
|
|
14
14
|
import * as Type from '../types.js';
|
|
15
15
|
//# sourceMappingURL=decrypt-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decrypt-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/decrypt-handler.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"decrypt-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/decrypt-handler.js"],"names":[],"mappings":"AAyEA;;;;;;;;;GASG;AACH,kDAPW,IAAI,CAAC,aAAa,OAElB,UAAU,MACV,UAAU,WACV,aAAa,CAAC,UAAU,CAAC,GAAC,UAAU,GAClC,OAAO,CAAC,cAAc,CAAC,CAiDnC;AA/GM,mDARI,OAAO,kBAAkB,EAAE,MAAM,iBACjC,IAAI,CAAC,aAAa,cAElB,GAAG,OACH,IAAI,CAAC,OAAO,oBACZ,IAAI,CAAC,gBAAgB,GACnB,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAsD1C;AAwEM,wDAJI,GAAG,OACH,MAAM,GACJ,OAAO,CAAC,UAAU,CAAC,CA+B/B;sBAvKqB,aAAa"}
|
|
@@ -3,6 +3,7 @@ import { CarIndexer, CarReader } from '@ipld/car';
|
|
|
3
3
|
import { exporter } from 'ipfs-unixfs-exporter';
|
|
4
4
|
import { MemoryBlockstore } from 'blockstore-core';
|
|
5
5
|
import * as Type from '../types.js';
|
|
6
|
+
import { extractFileMetadata } from '../utils/file-metadata.js';
|
|
6
7
|
/**
|
|
7
8
|
* Retrieve and decrypt a file from the IPFS gateway using any supported encryption strategy.
|
|
8
9
|
*
|
|
@@ -11,11 +12,10 @@ import * as Type from '../types.js';
|
|
|
11
12
|
* encryption and decryption operations.
|
|
12
13
|
* @param {URL} gatewayURL - The IPFS gateway URL
|
|
13
14
|
* @param {Type.AnyLink} cid - The link to the file to retrieve
|
|
14
|
-
* @param {
|
|
15
|
-
* @
|
|
16
|
-
* @returns {Promise<ReadableStream>} The decrypted file stream
|
|
15
|
+
* @param {Type.DecryptionConfig} decryptionConfig - User-provided decryption config
|
|
16
|
+
* @returns {Promise<Type.DecryptionResult>} The decrypted file stream with metadata
|
|
17
17
|
*/
|
|
18
|
-
export const retrieveAndDecrypt = async (storachaClient, cryptoAdapter, gatewayURL, cid,
|
|
18
|
+
export const retrieveAndDecrypt = async (storachaClient, cryptoAdapter, gatewayURL, cid, decryptionConfig) => {
|
|
19
19
|
// Step 1: Get the encrypted metadata from the public gateway
|
|
20
20
|
const encryptedMetadataCar = await getCarFileFromPublicGateway(gatewayURL, cid.toString());
|
|
21
21
|
// Step 2: Extract encrypted metadata from the CAR file
|
|
@@ -25,15 +25,20 @@ export const retrieveAndDecrypt = async (storachaClient, cryptoAdapter, gatewayU
|
|
|
25
25
|
// Step 4: Decrypt the encrypted symmetric key
|
|
26
26
|
const encryptedSymmetricKey = cryptoAdapter.getEncryptedKey(metadata);
|
|
27
27
|
const { key, iv } = await cryptoAdapter.decryptSymmetricKey(encryptedSymmetricKey, {
|
|
28
|
-
|
|
28
|
+
decryptionConfig,
|
|
29
29
|
metadata,
|
|
30
|
-
delegationCAR,
|
|
31
30
|
resourceCID: cid,
|
|
32
31
|
issuer: storachaClient.agent.issuer,
|
|
33
32
|
audience: storachaClient.defaultProvider(),
|
|
34
33
|
});
|
|
35
34
|
// Step 5: Decrypt the encrypted file content using the decrypted symmetric key and IV
|
|
36
|
-
|
|
35
|
+
const decryptedStreamWithMetadata = await decryptFileWithKey(cryptoAdapter, key, iv, encryptedData);
|
|
36
|
+
// Step 6: Extract file content and metadata
|
|
37
|
+
const { fileStream, fileMetadata } = await extractFileMetadata(decryptedStreamWithMetadata);
|
|
38
|
+
return {
|
|
39
|
+
stream: fileStream,
|
|
40
|
+
fileMetadata,
|
|
41
|
+
};
|
|
37
42
|
};
|
|
38
43
|
/**
|
|
39
44
|
* Decrypt file content using the decrypted symmetric key and IV.
|
|
@@ -42,17 +47,53 @@ export const retrieveAndDecrypt = async (storachaClient, cryptoAdapter, gatewayU
|
|
|
42
47
|
* encryption and decryption operations.
|
|
43
48
|
* @param {Uint8Array} key - The symmetric key
|
|
44
49
|
* @param {Uint8Array} iv - The initialization vector
|
|
45
|
-
* @param {Uint8Array} content - The encrypted file content
|
|
50
|
+
* @param {AsyncIterable<Uint8Array>|Uint8Array} content - The encrypted file content
|
|
46
51
|
* @returns {Promise<ReadableStream>} The decrypted file stream
|
|
47
52
|
*/
|
|
48
|
-
export function decryptFileWithKey(cryptoAdapter, key, iv, content) {
|
|
53
|
+
export async function decryptFileWithKey(cryptoAdapter, key, iv, content) {
|
|
54
|
+
// Convert content to ReadableStream with true on-demand streaming
|
|
55
|
+
/** @type {AsyncIterator<Uint8Array> | null} */
|
|
56
|
+
let iterator = null;
|
|
49
57
|
const contentStream = new ReadableStream({
|
|
50
|
-
start(
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
start() {
|
|
59
|
+
// Initialize iterator for async iterable (no memory loading here)
|
|
60
|
+
if (!(content instanceof Uint8Array)) {
|
|
61
|
+
iterator = content[Symbol.asyncIterator]();
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
async pull(controller) {
|
|
65
|
+
try {
|
|
66
|
+
if (content instanceof Uint8Array) {
|
|
67
|
+
// Handle single Uint8Array (legacy case)
|
|
68
|
+
controller.enqueue(content);
|
|
69
|
+
controller.close();
|
|
70
|
+
}
|
|
71
|
+
else if (iterator) {
|
|
72
|
+
// Handle async iterable - get next chunk on-demand
|
|
73
|
+
const { value, done } = await iterator.next();
|
|
74
|
+
if (done) {
|
|
75
|
+
controller.close();
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
controller.enqueue(value); // Only load one chunk at a time
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
controller.close();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
controller.error(error);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
cancel() {
|
|
90
|
+
// Clean up iterator if stream is cancelled
|
|
91
|
+
if (iterator && typeof iterator.return === 'function') {
|
|
92
|
+
void iterator.return();
|
|
93
|
+
}
|
|
53
94
|
},
|
|
54
95
|
});
|
|
55
|
-
const decryptedStream = cryptoAdapter.decryptStream(contentStream, key, iv);
|
|
96
|
+
const decryptedStream = await cryptoAdapter.decryptStream(contentStream, key, iv);
|
|
56
97
|
return decryptedStream;
|
|
57
98
|
}
|
|
58
99
|
/**
|
|
@@ -107,7 +148,7 @@ const getEncryptedDataFromCar = async (car, encryptedDataCID) => {
|
|
|
107
148
|
await blockstore.put(CID.parse(encryptedDataCID), blockBytes);
|
|
108
149
|
// Step 4: Get the encrypted data from the CAR file
|
|
109
150
|
const encryptedDataEntry = await exporter(CID.parse(encryptedDataCID), blockstore);
|
|
110
|
-
// Step 5: Return the async iterable
|
|
111
|
-
return encryptedDataEntry.content();
|
|
151
|
+
// Step 5: Return the async iterable for streaming
|
|
152
|
+
return encryptedDataEntry.content();
|
|
112
153
|
};
|
|
113
154
|
//# sourceMappingURL=decrypt-handler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encrypt-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/encrypt-handler.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"encrypt-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/encrypt-handler.js"],"names":[],"mappings":"AAiBO,iDARI,OAAO,kBAAkB,EAAE,MAAM,iBACjC,IAAI,CAAC,aAAa,QAElB,IAAI,CAAC,QAAQ,oBACb,IAAI,CAAC,gBAAgB,kBACrB,IAAI,CAAC,aAAa,GAChB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CA6BjC;sBAzCqB,aAAa"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CARWriterStream } from 'carstream';
|
|
2
2
|
import { createFileEncoderStream } from '@storacha/upload-client/unixfs';
|
|
3
3
|
import * as Type from '../types.js';
|
|
4
|
+
import { createFileWithMetadata } from '../utils/file-metadata.js';
|
|
4
5
|
/**
|
|
5
6
|
* Encrypt and upload a file to the Storacha network
|
|
6
7
|
*
|
|
@@ -60,7 +61,7 @@ const buildAndUploadEncryptedMetadata = async (storachaClient, encryptedPayload,
|
|
|
60
61
|
});
|
|
61
62
|
};
|
|
62
63
|
/**
|
|
63
|
-
* Encrypt a file using the crypto adapter and return the encrypted payload.
|
|
64
|
+
* Encrypt a file with embedded metadata using the crypto adapter and return the encrypted payload.
|
|
64
65
|
* The encrypted payload contains the encrypted file, the encrypted symmetric key, and the metadata.
|
|
65
66
|
*
|
|
66
67
|
* @param {Type.CryptoAdapter} cryptoAdapter - The crypto adapter responsible for performing
|
|
@@ -70,11 +71,13 @@ const buildAndUploadEncryptedMetadata = async (storachaClient, encryptedPayload,
|
|
|
70
71
|
* @returns {Promise<Type.EncryptionPayload>} - The encrypted file
|
|
71
72
|
*/
|
|
72
73
|
const encryptFile = async (cryptoAdapter, file, encryptionConfig) => {
|
|
73
|
-
// Step 1:
|
|
74
|
-
const
|
|
75
|
-
// Step 2:
|
|
74
|
+
// Step 1: Embed metadata in file content if provided
|
|
75
|
+
const fileWithMetadata = createFileWithMetadata(file, encryptionConfig.fileMetadata);
|
|
76
|
+
// Step 2: Encrypt the file (with embedded metadata) using the crypto adapter
|
|
77
|
+
const { key, iv, encryptedStream } = await cryptoAdapter.encryptStream(fileWithMetadata);
|
|
78
|
+
// Step 3: Use crypto adapter to encrypt the symmetric key
|
|
76
79
|
const keyResult = await cryptoAdapter.encryptSymmetricKey(key, iv, encryptionConfig);
|
|
77
|
-
// Step
|
|
80
|
+
// Step 4: Return the encrypted payload (no separate metadata needed)
|
|
78
81
|
return {
|
|
79
82
|
strategy: keyResult.strategy,
|
|
80
83
|
encryptedKey: keyResult.encryptedKey,
|
package/dist/types.d.ts
CHANGED
|
@@ -11,9 +11,19 @@ export type { Result, UnknownLink };
|
|
|
11
11
|
export type { BlobLike, AnyLink };
|
|
12
12
|
export type { UploadOptions } from '@storacha/client/types';
|
|
13
13
|
import type { SpaceDID } from '@storacha/capabilities/types';
|
|
14
|
+
export interface FileMetadata {
|
|
15
|
+
name: string;
|
|
16
|
+
type: string;
|
|
17
|
+
extension: string;
|
|
18
|
+
metadata?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
export interface DecryptionResult {
|
|
21
|
+
stream: ReadableStream;
|
|
22
|
+
fileMetadata?: FileMetadata;
|
|
23
|
+
}
|
|
14
24
|
export interface EncryptedClient {
|
|
15
25
|
encryptAndUploadFile(file: BlobLike, config: EncryptionConfig, uploadOptions?: UploadOptions): Promise<AnyLink>;
|
|
16
|
-
retrieveAndDecryptFile(cid: AnyLink,
|
|
26
|
+
retrieveAndDecryptFile(cid: AnyLink, decryptionConfig: DecryptionConfig): Promise<DecryptionResult>;
|
|
17
27
|
}
|
|
18
28
|
export type EncryptedClientOptions = {
|
|
19
29
|
storachaClient: StorachaClient;
|
|
@@ -39,9 +49,8 @@ export interface CryptoAdapter {
|
|
|
39
49
|
decryptStream(encryptedData: ReadableStream, key: Uint8Array, iv: Uint8Array): Promise<ReadableStream>;
|
|
40
50
|
encryptSymmetricKey(key: Uint8Array, iv: Uint8Array, encryptionConfig: EncryptionConfig): Promise<EncryptedKeyResult>;
|
|
41
51
|
decryptSymmetricKey(encryptedKey: string, configs: {
|
|
42
|
-
|
|
52
|
+
decryptionConfig: DecryptionConfig;
|
|
43
53
|
metadata: ExtractedMetadata;
|
|
44
|
-
delegationCAR: Uint8Array;
|
|
45
54
|
resourceCID: AnyLink;
|
|
46
55
|
issuer: Signer<DID, SigAlg>;
|
|
47
56
|
audience: DID;
|
|
@@ -65,6 +74,10 @@ export interface EncryptionConfig {
|
|
|
65
74
|
* The DID of the space to encrypt the file for
|
|
66
75
|
*/
|
|
67
76
|
spaceDID: SpaceDID;
|
|
77
|
+
/**
|
|
78
|
+
* Proofs to access the space
|
|
79
|
+
*/
|
|
80
|
+
proofs?: Proof[];
|
|
68
81
|
/**
|
|
69
82
|
* The location of the KMS key to use for encryption
|
|
70
83
|
*/
|
|
@@ -73,14 +86,22 @@ export interface EncryptionConfig {
|
|
|
73
86
|
* The keyring of the KMS key to use for encryption
|
|
74
87
|
*/
|
|
75
88
|
keyring?: string;
|
|
89
|
+
/**
|
|
90
|
+
* File metadata to embed in encrypted file
|
|
91
|
+
*/
|
|
92
|
+
fileMetadata?: FileMetadata;
|
|
76
93
|
}
|
|
77
|
-
export interface
|
|
94
|
+
export interface DecryptionConfig {
|
|
95
|
+
decryptDelegation: Proof;
|
|
96
|
+
spaceDID: SpaceDID;
|
|
97
|
+
/**
|
|
98
|
+
* Proofs to access the space
|
|
99
|
+
*/
|
|
100
|
+
proofs?: Proof[];
|
|
78
101
|
wallet?: Wallet;
|
|
79
102
|
sessionSigs?: SessionSigsMap;
|
|
80
103
|
pkpPublicKey?: string;
|
|
81
104
|
authMethod?: AuthMethod;
|
|
82
|
-
spaceDID?: SpaceDID;
|
|
83
|
-
delegationProof?: Proof;
|
|
84
105
|
}
|
|
85
106
|
export interface EncryptedKeyResult {
|
|
86
107
|
strategy: EncryptionStrategy;
|
|
@@ -175,7 +196,7 @@ export interface LitWalletSigner {
|
|
|
175
196
|
wallet: Wallet;
|
|
176
197
|
}
|
|
177
198
|
export interface CreateDecryptWrappedInvocationOptions {
|
|
178
|
-
|
|
199
|
+
decryptDelegation: Proof;
|
|
179
200
|
issuer: Signer<DID, SigAlg>;
|
|
180
201
|
audience: `did:${string}:${string}`;
|
|
181
202
|
spaceDID: `did:key:${string}`;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACjE,OAAO,EACL,uBAAuB,EACvB,UAAU,EACV,OAAO,EACP,cAAc,EACf,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EACV,QAAQ,EACR,OAAO,EACP,MAAM,EACN,GAAG,EACH,MAAM,EACN,aAAa,EACd,MAAM,wBAAwB,CAAA;AAE/B,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAClD,YAAY,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AAC5D,YAAY,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AACjE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,CAAA;AACnC,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAA;AACjC,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAG3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AAE5D,MAAM,WAAW,eAAe;IAC9B,oBAAoB,CAClB,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,gBAAgB,EACxB,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,OAAO,CAAC,CAAA;IACnB,sBAAsB,CACpB,GAAG,EAAE,OAAO,EACZ,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const FileMetadataSchema: Schema.StructSchema<{
|
|
2
|
+
name: Schema.StringSchema<string, unknown>;
|
|
3
|
+
type: Schema.StringSchema<string, unknown>;
|
|
4
|
+
extension: Schema.StringSchema<string, unknown>;
|
|
5
|
+
metadata: Schema.Schema<unknown, any>;
|
|
6
|
+
}, unknown>;
|
|
7
|
+
export function createFileWithMetadata(file: Type.BlobLike, metadata?: Type.FileMetadata): Blob;
|
|
8
|
+
export function extractFileMetadata(decryptedStream: ReadableStream): Promise<{
|
|
9
|
+
fileStream: ReadableStream;
|
|
10
|
+
fileMetadata?: Type.FileMetadata;
|
|
11
|
+
}>;
|
|
12
|
+
import { Schema } from '@ucanto/core';
|
|
13
|
+
import * as Type from '../types.js';
|
|
14
|
+
//# sourceMappingURL=file-metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-metadata.d.ts","sourceRoot":"","sources":["../../src/utils/file-metadata.js"],"names":[],"mappings":"AAUA;;;;;YAKE;AASK,6CAJI,IAAI,CAAC,QAAQ,aACb,IAAI,CAAC,YAAY,GACf,IAAI,CAmDhB;AAQM,qDAHI,cAAc,GACZ,OAAO,CAAC;IAAC,UAAU,EAAE,cAAc,CAAC;IAAC,YAAY,CAAC,EAAE,IAAI,CAAC,YAAY,CAAA;CAAC,CAAC,CA8DnF;uBA5IsB,cAAc;sBADf,aAAa"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import * as Type from '../types.js';
|
|
2
|
+
import { Schema } from '@ucanto/core';
|
|
3
|
+
const METADATA_HEADER_SIZE = 1024;
|
|
4
|
+
const MAX_JSON_DEPTH = 5;
|
|
5
|
+
const MAX_JSON_OBJECTS = 20;
|
|
6
|
+
const MAX_FIELD_LENGTH = 200;
|
|
7
|
+
const MAX_CUSTOM_METADATA_SIZE = 10000;
|
|
8
|
+
// Schema for file metadata validation
|
|
9
|
+
export const FileMetadataSchema = Schema.struct({
|
|
10
|
+
name: Schema.string(),
|
|
11
|
+
type: Schema.string(),
|
|
12
|
+
extension: Schema.string(),
|
|
13
|
+
metadata: Schema.optional(Schema.unknown()),
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Embed file metadata in a fixed-size header at the beginning of file content
|
|
17
|
+
*
|
|
18
|
+
* @param {Type.BlobLike} file - The file to embed metadata in
|
|
19
|
+
* @param {Type.FileMetadata} [metadata] - Optional file metadata
|
|
20
|
+
* @returns {Blob} File with embedded metadata header
|
|
21
|
+
*/
|
|
22
|
+
export const createFileWithMetadata = (file, metadata) => {
|
|
23
|
+
if (metadata === undefined) {
|
|
24
|
+
// No metadata - just return original file as Blob
|
|
25
|
+
if (file instanceof Blob) {
|
|
26
|
+
return file;
|
|
27
|
+
}
|
|
28
|
+
// Handle known BlobLike types that are valid BlobParts
|
|
29
|
+
if (file instanceof Uint8Array || file instanceof ArrayBuffer) {
|
|
30
|
+
return new Blob([file]);
|
|
31
|
+
}
|
|
32
|
+
throw new Error('Unsupported BlobLike type - must be Blob, Uint8Array, or ArrayBuffer');
|
|
33
|
+
}
|
|
34
|
+
// Validate and serialize metadata
|
|
35
|
+
validateMetadataStructure(metadata);
|
|
36
|
+
const metadataJson = JSON.stringify(metadata);
|
|
37
|
+
const metadataBytes = new TextEncoder().encode(metadataJson);
|
|
38
|
+
if (metadataBytes.length > METADATA_HEADER_SIZE - 4) {
|
|
39
|
+
throw new Error(`Metadata too large: ${metadataBytes.length} bytes (max ${METADATA_HEADER_SIZE - 4})`);
|
|
40
|
+
}
|
|
41
|
+
// Create fixed-size header: [4 bytes length][metadata][padding]
|
|
42
|
+
const header = new Uint8Array(METADATA_HEADER_SIZE);
|
|
43
|
+
const lengthBytes = new Uint8Array(new Uint32Array([metadataBytes.length]).buffer);
|
|
44
|
+
header.set(lengthBytes, 0); // First 4 bytes = length
|
|
45
|
+
header.set(metadataBytes, 4); // Metadata starts at byte 4
|
|
46
|
+
// Rest remains zero-filled
|
|
47
|
+
let originalFile;
|
|
48
|
+
if (file instanceof Blob) {
|
|
49
|
+
originalFile = file;
|
|
50
|
+
}
|
|
51
|
+
else if (file instanceof Uint8Array || file instanceof ArrayBuffer) {
|
|
52
|
+
originalFile = new Blob([file]);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
throw new Error('Unsupported BlobLike type - must be Blob, Uint8Array, or ArrayBuffer');
|
|
56
|
+
}
|
|
57
|
+
return new Blob([header, originalFile]);
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Extract file metadata from encrypted file stream
|
|
61
|
+
*
|
|
62
|
+
* @param {ReadableStream} decryptedStream - The decrypted file stream
|
|
63
|
+
* @returns {Promise<{fileStream: ReadableStream, fileMetadata?: Type.FileMetadata}>}
|
|
64
|
+
*/
|
|
65
|
+
export const extractFileMetadata = async (decryptedStream) => {
|
|
66
|
+
const reader = decryptedStream.getReader();
|
|
67
|
+
try {
|
|
68
|
+
// Read fixed-size header
|
|
69
|
+
const { bytes: header, remainder } = await readExactBytes(reader, METADATA_HEADER_SIZE);
|
|
70
|
+
if (!header) {
|
|
71
|
+
return { fileStream: createStreamFromReader(reader) };
|
|
72
|
+
}
|
|
73
|
+
// Read metadata length from first 4 bytes
|
|
74
|
+
const lengthView = new DataView(header.buffer, 0, 4);
|
|
75
|
+
const metadataLength = lengthView.getUint32(0, true);
|
|
76
|
+
// Validate length
|
|
77
|
+
if (metadataLength < 0 || metadataLength > METADATA_HEADER_SIZE - 4) {
|
|
78
|
+
throw new Error('Invalid metadata length');
|
|
79
|
+
}
|
|
80
|
+
let fileMetadata = undefined;
|
|
81
|
+
if (metadataLength > 0) {
|
|
82
|
+
// Extract and parse metadata
|
|
83
|
+
const metadataBytes = header.slice(4, 4 + metadataLength);
|
|
84
|
+
const metadataJson = new TextDecoder('utf-8', { fatal: true }).decode(metadataBytes);
|
|
85
|
+
fileMetadata = secureJsonParse(metadataJson);
|
|
86
|
+
validateMetadataStructure(fileMetadata);
|
|
87
|
+
}
|
|
88
|
+
// Create file stream (header is consumed, continue with rest)
|
|
89
|
+
// Include any remainder bytes from the header read first
|
|
90
|
+
let remainderEnqueued = false;
|
|
91
|
+
const fileStream = new ReadableStream({
|
|
92
|
+
async pull(controller) {
|
|
93
|
+
// First, enqueue any remainder bytes from header read
|
|
94
|
+
if (!remainderEnqueued && remainder) {
|
|
95
|
+
controller.enqueue(remainder);
|
|
96
|
+
remainderEnqueued = true;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// Then continue with the reader
|
|
100
|
+
const { done, value } = await reader.read();
|
|
101
|
+
if (done) {
|
|
102
|
+
controller.close();
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
controller.enqueue(value);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
return { fileStream, fileMetadata };
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.warn('Metadata extraction failed:', error);
|
|
113
|
+
return { fileStream: createStreamFromReader(reader) };
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Read exact number of bytes from a stream reader
|
|
118
|
+
*
|
|
119
|
+
* @param {ReadableStreamDefaultReader} reader - Stream reader
|
|
120
|
+
* @param {number} size - Number of bytes to read
|
|
121
|
+
* @returns {Promise<{bytes: Uint8Array|null, remainder: Uint8Array|null}>} The bytes and any remainder
|
|
122
|
+
*/
|
|
123
|
+
async function readExactBytes(reader, size) {
|
|
124
|
+
const chunks = [];
|
|
125
|
+
let totalRead = 0;
|
|
126
|
+
while (totalRead < size) {
|
|
127
|
+
const { done, value } = await reader.read();
|
|
128
|
+
if (done) {
|
|
129
|
+
return { bytes: null, remainder: null }; // Not enough data
|
|
130
|
+
}
|
|
131
|
+
chunks.push(value);
|
|
132
|
+
totalRead += value.length;
|
|
133
|
+
}
|
|
134
|
+
// Combine chunks and extract exact size
|
|
135
|
+
const combined = new Uint8Array(totalRead);
|
|
136
|
+
let offset = 0;
|
|
137
|
+
for (const chunk of chunks) {
|
|
138
|
+
combined.set(chunk, offset);
|
|
139
|
+
offset += chunk.length;
|
|
140
|
+
}
|
|
141
|
+
const bytes = combined.slice(0, size);
|
|
142
|
+
const remainder = totalRead > size ? combined.slice(size) : null;
|
|
143
|
+
return { bytes, remainder };
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Parse JSON with security validations
|
|
147
|
+
*
|
|
148
|
+
* @param {string} jsonString - JSON string to parse
|
|
149
|
+
* @returns {Type.FileMetadata} Parsed metadata
|
|
150
|
+
*/
|
|
151
|
+
function secureJsonParse(jsonString) {
|
|
152
|
+
// Validate JSON structure before parsing
|
|
153
|
+
if (jsonString.length > 800) {
|
|
154
|
+
// Leave room for encoding overhead
|
|
155
|
+
throw new Error('JSON too large');
|
|
156
|
+
}
|
|
157
|
+
// Check nesting depth
|
|
158
|
+
const depth = calculateJsonDepth(jsonString);
|
|
159
|
+
if (depth > MAX_JSON_DEPTH) {
|
|
160
|
+
throw new Error('JSON too deeply nested');
|
|
161
|
+
}
|
|
162
|
+
// Check object count
|
|
163
|
+
const objectCount = (jsonString.match(/[{[]/g) || []).length;
|
|
164
|
+
if (objectCount > MAX_JSON_OBJECTS) {
|
|
165
|
+
throw new Error('Too many JSON objects');
|
|
166
|
+
}
|
|
167
|
+
return JSON.parse(jsonString);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Calculate maximum nesting depth of JSON string
|
|
171
|
+
*
|
|
172
|
+
* @param {string} jsonString - JSON string
|
|
173
|
+
* @returns {number} Maximum depth
|
|
174
|
+
*/
|
|
175
|
+
function calculateJsonDepth(jsonString) {
|
|
176
|
+
let depth = 0;
|
|
177
|
+
let maxDepth = 0;
|
|
178
|
+
for (const char of jsonString) {
|
|
179
|
+
if (char === '{' || char === '[') {
|
|
180
|
+
depth++;
|
|
181
|
+
maxDepth = Math.max(maxDepth, depth);
|
|
182
|
+
}
|
|
183
|
+
else if (char === '}' || char === ']') {
|
|
184
|
+
depth--;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return maxDepth;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Validate file metadata structure using schema
|
|
191
|
+
*
|
|
192
|
+
* @param {Type.FileMetadata} metadata - Metadata to validate
|
|
193
|
+
*/
|
|
194
|
+
function validateMetadataStructure(metadata) {
|
|
195
|
+
if (!FileMetadataSchema.is(metadata)) {
|
|
196
|
+
throw new Error('Invalid metadata structure');
|
|
197
|
+
}
|
|
198
|
+
// Additional length validations
|
|
199
|
+
if (metadata.name.length > MAX_FIELD_LENGTH ||
|
|
200
|
+
metadata.type.length > MAX_FIELD_LENGTH ||
|
|
201
|
+
metadata.extension.length > MAX_FIELD_LENGTH) {
|
|
202
|
+
throw new Error('Metadata field too long');
|
|
203
|
+
}
|
|
204
|
+
// Validate optional metadata size
|
|
205
|
+
if (metadata.metadata !== undefined) {
|
|
206
|
+
const metadataStr = JSON.stringify(metadata.metadata);
|
|
207
|
+
if (metadataStr.length > MAX_CUSTOM_METADATA_SIZE) {
|
|
208
|
+
throw new Error('Custom metadata too large');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Create a readable stream from an existing reader
|
|
214
|
+
*
|
|
215
|
+
* @param {ReadableStreamDefaultReader} reader - Stream reader
|
|
216
|
+
* @returns {ReadableStream} New readable stream
|
|
217
|
+
*/
|
|
218
|
+
function createStreamFromReader(reader) {
|
|
219
|
+
return new ReadableStream({
|
|
220
|
+
async pull(controller) {
|
|
221
|
+
const { done, value } = await reader.read();
|
|
222
|
+
if (done) {
|
|
223
|
+
controller.close();
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
controller.enqueue(value);
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=file-metadata.js.map
|
package/dist/utils.d.ts
CHANGED
|
@@ -10,6 +10,6 @@ export function stringToBytes(str: string): Uint8Array;
|
|
|
10
10
|
* @returns {string}
|
|
11
11
|
*/
|
|
12
12
|
export function bytesToString(bytes: Uint8Array): string;
|
|
13
|
-
export function createDecryptWrappedInvocation({
|
|
13
|
+
export function createDecryptWrappedInvocation({ decryptDelegation, issuer, spaceDID, resourceCID, audience, expiration, }: Type.CreateDecryptWrappedInvocationOptions): Promise<import("@ucanto/server").ToString<Uint8Array<ArrayBufferLike>, string>>;
|
|
14
14
|
import * as Type from './types.js';
|
|
15
15
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AA4CA;;;;GAIG;AACH,mCAHW,MAAM,GACJ,UAAU,CAItB;AAED;;;;GAIG;AACH,qCAHW,UAAU,GACR,MAAM,CAIlB;AAnDM,4HAFI,IAAI,CAAC,qCAAqC,mFAmCpD;sBAvCqB,YAAY"}
|
package/dist/utils.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { DID } from '@ucanto/server';
|
|
2
2
|
import * as dagJSON from '@ipld/dag-json';
|
|
3
|
-
import { extract } from '@ucanto/core/delegation';
|
|
4
3
|
import * as Space from '@storacha/capabilities/space';
|
|
5
4
|
import * as Type from './types.js';
|
|
6
5
|
/**
|
|
7
6
|
*
|
|
8
7
|
* @param {Type.CreateDecryptWrappedInvocationOptions} param0
|
|
9
8
|
*/
|
|
10
|
-
export const createDecryptWrappedInvocation = async ({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
throw delegation.error;
|
|
9
|
+
export const createDecryptWrappedInvocation = async ({ decryptDelegation, issuer, spaceDID, resourceCID, audience, expiration, }) => {
|
|
10
|
+
if (!decryptDelegation) {
|
|
11
|
+
throw new Error('Decrypt delegation is required');
|
|
14
12
|
}
|
|
15
13
|
const invocationOptions = {
|
|
16
14
|
issuer,
|
|
@@ -20,7 +18,7 @@ export const createDecryptWrappedInvocation = async ({ delegationCAR, issuer, sp
|
|
|
20
18
|
resource: resourceCID,
|
|
21
19
|
},
|
|
22
20
|
expiration: expiration,
|
|
23
|
-
proofs: [
|
|
21
|
+
proofs: [decryptDelegation],
|
|
24
22
|
};
|
|
25
23
|
const decryptWrappedInvocation = await Space.decrypt
|
|
26
24
|
.invoke(invocationOptions)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storacha/encrypt-upload-client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"description": "Client for upload and download encrypted files",
|
|
7
7
|
"author": "Storacha",
|
|
@@ -80,9 +80,9 @@
|
|
|
80
80
|
"ethers": "5.7.1",
|
|
81
81
|
"ipfs-unixfs-exporter": "^10.0.0",
|
|
82
82
|
"multiformats": "^13.3.3",
|
|
83
|
-
"@storacha/
|
|
84
|
-
"@storacha/client": "^1.
|
|
85
|
-
"@storacha/
|
|
83
|
+
"@storacha/client": "^1.7.0",
|
|
84
|
+
"@storacha/upload-client": "^1.3.0",
|
|
85
|
+
"@storacha/capabilities": "^1.8.0"
|
|
86
86
|
},
|
|
87
87
|
"devDependencies": {
|
|
88
88
|
"@lit-protocol/types": "^7.0.8",
|