@storacha/encrypt-upload-client 0.0.39 → 1.0.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.
Files changed (55) hide show
  1. package/dist/config/env.d.ts.map +1 -1
  2. package/dist/config/env.js +19 -6
  3. package/dist/core/client.d.ts +8 -12
  4. package/dist/core/client.d.ts.map +1 -1
  5. package/dist/core/client.js +12 -21
  6. package/dist/core/metadata/encrypted-metadata.d.ts +8 -0
  7. package/dist/core/metadata/encrypted-metadata.d.ts.map +1 -0
  8. package/dist/core/metadata/encrypted-metadata.js +69 -0
  9. package/dist/core/metadata/kms-metadata.d.ts +36 -0
  10. package/dist/core/metadata/kms-metadata.d.ts.map +1 -0
  11. package/dist/core/metadata/kms-metadata.js +156 -0
  12. package/dist/core/{encrypted-metadata.d.ts → metadata/lit-metadata.d.ts} +11 -11
  13. package/dist/core/metadata/lit-metadata.d.ts.map +1 -0
  14. package/dist/core/{encrypted-metadata.js → metadata/lit-metadata.js} +32 -42
  15. package/dist/crypto/adapters/kms-crypto-adapter.d.ts +148 -0
  16. package/dist/crypto/adapters/kms-crypto-adapter.d.ts.map +1 -0
  17. package/dist/crypto/adapters/kms-crypto-adapter.js +321 -0
  18. package/dist/crypto/adapters/lit-crypto-adapter.d.ts +96 -0
  19. package/dist/crypto/adapters/lit-crypto-adapter.d.ts.map +1 -0
  20. package/dist/crypto/adapters/lit-crypto-adapter.js +210 -0
  21. package/dist/crypto/factories.browser.d.ts +11 -0
  22. package/dist/crypto/factories.browser.d.ts.map +1 -0
  23. package/dist/crypto/factories.browser.js +16 -0
  24. package/dist/crypto/factories.node.d.ts +26 -0
  25. package/dist/crypto/factories.node.d.ts.map +1 -0
  26. package/dist/crypto/factories.node.js +38 -0
  27. package/dist/crypto/index.d.ts +5 -0
  28. package/dist/crypto/index.d.ts.map +1 -0
  29. package/dist/crypto/index.js +7 -0
  30. package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.d.ts +76 -0
  31. package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.d.ts.map +1 -0
  32. package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.js +177 -0
  33. package/dist/crypto/symmetric/node-aes-cbc-crypto.d.ts +43 -0
  34. package/dist/crypto/symmetric/node-aes-cbc-crypto.d.ts.map +1 -0
  35. package/dist/crypto/symmetric/node-aes-cbc-crypto.js +110 -0
  36. package/dist/handlers/decrypt-handler.d.ts +9 -4
  37. package/dist/handlers/decrypt-handler.d.ts.map +1 -1
  38. package/dist/handlers/decrypt-handler.js +62 -93
  39. package/dist/handlers/encrypt-handler.d.ts +1 -1
  40. package/dist/handlers/encrypt-handler.d.ts.map +1 -1
  41. package/dist/handlers/encrypt-handler.js +31 -41
  42. package/dist/index.d.ts +0 -1
  43. package/dist/index.js +0 -1
  44. package/dist/protocols/lit.d.ts +1 -3
  45. package/dist/protocols/lit.d.ts.map +1 -1
  46. package/dist/types.d.ts +135 -20
  47. package/dist/types.d.ts.map +1 -1
  48. package/package.json +27 -18
  49. package/dist/core/encrypted-metadata.d.ts.map +0 -1
  50. package/dist/crypto-adapters/browser-crypto-adapter.d.ts +0 -42
  51. package/dist/crypto-adapters/browser-crypto-adapter.d.ts.map +0 -1
  52. package/dist/crypto-adapters/browser-crypto-adapter.js +0 -109
  53. package/dist/crypto-adapters/node-crypto-adapter.d.ts +0 -17
  54. package/dist/crypto-adapters/node-crypto-adapter.d.ts.map +0 -1
  55. package/dist/crypto-adapters/node-crypto-adapter.js +0 -66
@@ -3,11 +3,11 @@ import * as dagCBOR from '@ipld/dag-cbor';
3
3
  import * as Link from 'multiformats/link';
4
4
  import { sha256 } from 'multiformats/hashes/sha2';
5
5
  import { CAR, ok, error, Schema } from '@ucanto/core';
6
- import * as Types from '../types.js';
7
- import { UnknownFormat } from './errors.js';
8
- import { stringToBytes, bytesToString } from '../utils.js';
6
+ import * as Types from '../../types.js';
7
+ import { UnknownFormat } from '../errors.js';
8
+ import { stringToBytes, bytesToString } from '../../utils.js';
9
9
  export const version = 'encrypted-metadata@0.1';
10
- export const EncryptedMetadataSchema = Schema.variant({
10
+ export const LitMetadataSchema = Schema.variant({
11
11
  [version]: Schema.struct({
12
12
  encryptedDataCID: Schema.link(),
13
13
  identityBoundCiphertext: Schema.bytes(),
@@ -18,7 +18,7 @@ export const EncryptedMetadataSchema = Schema.variant({
18
18
  }).array(),
19
19
  }),
20
20
  });
21
- export const EncryptedMetadataInputSchema = Schema.struct({
21
+ export const LitMetadataInputSchema = Schema.struct({
22
22
  encryptedDataCID: Schema.string(),
23
23
  identityBoundCiphertext: Schema.string(),
24
24
  plaintextKeyHash: Schema.string(),
@@ -27,22 +27,22 @@ export const EncryptedMetadataInputSchema = Schema.struct({
27
27
  value: Schema.unknown(),
28
28
  }).array(),
29
29
  });
30
- /** @implements {Types.EncryptedMetadataView} */
31
- class EncryptedMetadata {
30
+ /** @implements {Types.LitMetadataView} */
31
+ class LitEncryptedMetadata {
32
32
  #encryptedDataCID;
33
33
  #identityBoundCiphertext;
34
34
  #plaintextKeyHash;
35
35
  #accessControlConditions;
36
- /** @param {Types.EncryptedMetadata|Types.EncryptedMetadataInput} encryptedMetadataInput */
36
+ /** @param {Types.LitMetadata|Types.LitMetadataInput} encryptedMetadataInput */
37
37
  constructor(encryptedMetadataInput) {
38
- /** @type {Types.EncryptedMetadata} */
38
+ /** @type {Types.LitMetadata} */
39
39
  let encryptedMetadata;
40
- if (EncryptedMetadataInputSchema.is(encryptedMetadataInput)) {
40
+ if (LitMetadataInputSchema.is(encryptedMetadataInput)) {
41
41
  encryptedMetadata = parse(
42
- /** @type {Types.EncryptedMetadataInput} */ (encryptedMetadataInput));
42
+ /** @type {Types.LitMetadataInput} */ (encryptedMetadataInput));
43
43
  }
44
44
  else {
45
- encryptedMetadata = /** @type {Types.EncryptedMetadata} */ (encryptedMetadataInput);
45
+ encryptedMetadata = /** @type {Types.LitMetadata} */ (encryptedMetadataInput);
46
46
  }
47
47
  this.#encryptedDataCID = encryptedMetadata.encryptedDataCID;
48
48
  this.#identityBoundCiphertext = encryptedMetadata.identityBoundCiphertext;
@@ -62,18 +62,8 @@ class EncryptedMetadata {
62
62
  get accessControlConditions() {
63
63
  return this.#accessControlConditions;
64
64
  }
65
- archive() {
66
- /** @type {Types.EncryptedMetadata} */
67
- const input = {
68
- encryptedDataCID: this.encryptedDataCID,
69
- identityBoundCiphertext: this.identityBoundCiphertext,
70
- plaintextKeyHash: this.plaintextKeyHash,
71
- accessControlConditions: this.accessControlConditions,
72
- };
73
- return archive(input);
74
- }
75
65
  archiveBlock() {
76
- /** @type {Types.EncryptedMetadata} */
66
+ /** @type {Types.LitMetadata} */
77
67
  const input = {
78
68
  encryptedDataCID: this.encryptedDataCID,
79
69
  identityBoundCiphertext: this.identityBoundCiphertext,
@@ -82,19 +72,19 @@ class EncryptedMetadata {
82
72
  };
83
73
  return archiveBlock(input);
84
74
  }
85
- /** @returns {Types.EncryptedMetadataInput} */
75
+ /** @returns {Types.LitMetadataInput} */
86
76
  toJSON() {
87
77
  return toJSON(this);
88
78
  }
89
79
  }
90
80
  /**
91
- * @param {Types.EncryptedMetadata|Types.EncryptedMetadataInput} encryptedMetadataInput
92
- * @returns {Types.EncryptedMetadataView}
81
+ * @param {Types.LitMetadata|Types.LitMetadataInput} encryptedMetadataInput
82
+ * @returns {Types.LitMetadataView}
93
83
  */
94
- export const create = (encryptedMetadataInput) => new EncryptedMetadata(encryptedMetadataInput);
84
+ export const create = (encryptedMetadataInput) => new LitEncryptedMetadata(encryptedMetadataInput);
95
85
  /**
96
- * @param {Types.EncryptedMetadataView} encryptedMetadata
97
- * @returns {Types.EncryptedMetadataInput}
86
+ * @param {Types.LitMetadataView} encryptedMetadata
87
+ * @returns {Types.LitMetadataInput}
98
88
  */
99
89
  export const toJSON = (encryptedMetadata) => ({
100
90
  encryptedDataCID: encryptedMetadata.encryptedDataCID.toString(),
@@ -103,8 +93,8 @@ export const toJSON = (encryptedMetadata) => ({
103
93
  accessControlConditions: encryptedMetadata.accessControlConditions,
104
94
  });
105
95
  /**
106
- * @param {Types.EncryptedMetadataInput} encryptedMetadataInput
107
- * @returns {Types.EncryptedMetadata}
96
+ * @param {Types.LitMetadataInput} encryptedMetadataInput
97
+ * @returns {Types.LitMetadata}
108
98
  */
109
99
  export const parse = (encryptedMetadataInput) => ({
110
100
  encryptedDataCID: CID.parse(encryptedMetadataInput.encryptedDataCID),
@@ -113,7 +103,7 @@ export const parse = (encryptedMetadataInput) => ({
113
103
  accessControlConditions: encryptedMetadataInput.accessControlConditions,
114
104
  });
115
105
  /**
116
- * @param {Types.EncryptedMetadata} encryptedMetadataInput
106
+ * @param {Types.LitMetadata} encryptedMetadataInput
117
107
  * @returns {Promise<import('@ucanto/interface').Block>}
118
108
  */
119
109
  export const archiveBlock = async (encryptedMetadataInput) => {
@@ -123,16 +113,16 @@ export const archiveBlock = async (encryptedMetadataInput) => {
123
113
  return { cid, bytes };
124
114
  };
125
115
  /**
126
- * @param {Types.EncryptedMetadata} encryptedMetadataInput
116
+ * @param {Types.LitMetadata} encryptedMetadata
127
117
  * @returns {Promise<Types.Result<Uint8Array>>}
128
118
  */
129
- export const archive = async (encryptedMetadataInput) => {
130
- const block = await archiveBlock(encryptedMetadataInput);
119
+ export const archive = async (encryptedMetadata) => {
120
+ const block = await archiveBlock(encryptedMetadata);
131
121
  return ok(CAR.encode({ roots: [block] }));
132
122
  };
133
123
  /**
134
124
  * @param {Uint8Array} archive
135
- * @returns {Types.Result<Types.EncryptedMetadataView, Types.UnknownFormat>}
125
+ * @returns {Types.Result<Types.LitMetadataView, Types.UnknownFormat>}
136
126
  */
137
127
  export const extract = (archive) => {
138
128
  const { roots } = CAR.decode(archive);
@@ -148,19 +138,19 @@ export const extract = (archive) => {
148
138
  /**
149
139
  * @param {object} source
150
140
  * @param {Types.IPLDBlock} source.root
151
- * @returns {Types.Result<Types.EncryptedMetadataView, Types.UnknownFormat>}
141
+ * @returns {Types.Result<Types.LitMetadataView, Types.UnknownFormat>}
152
142
  */
153
143
  export const view = ({ root }) => {
154
144
  const value = dagCBOR.decode(root.bytes);
155
- const [version, encryptedMetadataData] = EncryptedMetadataSchema.match(value);
156
- switch (version) {
145
+ const [matchedVersion, encryptedMetadataData] = LitMetadataSchema.match(value);
146
+ switch (matchedVersion) {
157
147
  case version: {
158
148
  const encryptedMetadata = create(
159
- /** @type {Types.EncryptedMetadata}*/ (encryptedMetadataData));
149
+ /** @type {Types.LitMetadata}*/ (encryptedMetadataData));
160
150
  return ok(encryptedMetadata);
161
151
  }
162
152
  default:
163
- return error(new UnknownFormat(`unknown index version: ${version}`));
153
+ return error(new UnknownFormat(`unknown Lit metadata version: ${matchedVersion}`));
164
154
  }
165
155
  };
166
- //# sourceMappingURL=encrypted-metadata.js.map
156
+ //# sourceMappingURL=lit-metadata.js.map
@@ -0,0 +1,148 @@
1
+ /**
2
+ * KMSCryptoAdapter implements the complete CryptoAdapter interface using KMS.
3
+ * It uses composition with a SymmetricCrypto implementation for file encryption/decryption
4
+ * and KMS via private gateway for key management operations.
5
+ *
6
+ * @class
7
+ * @implements {Type.CryptoAdapter}
8
+ */
9
+ export class KMSCryptoAdapter implements Type.CryptoAdapter {
10
+ /**
11
+ * Create a new KMS crypto adapter
12
+ *
13
+ * @param {Type.SymmetricCrypto} symmetricCrypto - The symmetric crypto implementation (browser or node)
14
+ * @param {URL|string} keyManagerServiceURL - The key manager service URL
15
+ * @param {`did:${string}:${string}`} keyManagerServiceDID - The key manager service DID
16
+ * @param {object} [options] - Optional configuration
17
+ * @param {boolean} [options.allowInsecureHttp] - Allow HTTP for testing (NOT for production)
18
+ */
19
+ constructor(symmetricCrypto: Type.SymmetricCrypto, keyManagerServiceURL: URL | string, keyManagerServiceDID: `did:${string}:${string}`, options?: {
20
+ allowInsecureHttp?: boolean | undefined;
21
+ });
22
+ symmetricCrypto: Type.SymmetricCrypto;
23
+ keyManagerServiceURL: URL;
24
+ keyManagerServiceDID: import("@ucanto/client").PrincipalView<`did:${string}:${string}`>;
25
+ /**
26
+ * Encrypt a stream of data using the symmetric crypto
27
+ *
28
+ * @param {Type.BlobLike} data
29
+ */
30
+ encryptStream(data: Type.BlobLike): Promise<Type.EncryptOutput>;
31
+ /**
32
+ * Decrypt a stream of data using the symmetric crypto
33
+ *
34
+ * @param {ReadableStream} encryptedData
35
+ * @param {Uint8Array} key
36
+ * @param {Uint8Array} iv
37
+ */
38
+ decryptStream(encryptedData: ReadableStream, key: Uint8Array, iv: Uint8Array): Promise<ReadableStream<any>>;
39
+ /**
40
+ * Encrypt a symmetric key using the KMS
41
+ *
42
+ * @param {Uint8Array} key
43
+ * @param {Uint8Array} iv
44
+ * @param {Type.EncryptionConfig} encryptionConfig
45
+ * @returns {Promise<Type.EncryptedKeyResult>}
46
+ */
47
+ encryptSymmetricKey(key: Uint8Array, iv: Uint8Array, encryptionConfig: Type.EncryptionConfig): Promise<Type.EncryptedKeyResult>;
48
+ /**
49
+ * @param {string} encryptedKey
50
+ * @param {object} configs
51
+ * @param {Type.DecryptionOptions} configs.decryptionOptions
52
+ * @param {Type.ExtractedMetadata} configs.metadata
53
+ * @param {Uint8Array} configs.delegationCAR
54
+ * @param {Type.AnyLink} configs.resourceCID
55
+ * @param {import('@storacha/client/types').Signer<import('@storacha/client/types').DID, import('@storacha/client/types').SigAlg>} configs.issuer
56
+ * @param {import('@storacha/client/types').DID} configs.audience
57
+ */
58
+ decryptSymmetricKey(encryptedKey: string, configs: {
59
+ decryptionOptions: Type.DecryptionOptions;
60
+ metadata: Type.ExtractedMetadata;
61
+ delegationCAR: Uint8Array;
62
+ resourceCID: Type.AnyLink;
63
+ issuer: import("@storacha/client/types").Signer<import("@storacha/client/types").DID, import("@storacha/client/types").SigAlg>;
64
+ audience: import("@storacha/client/types").DID;
65
+ }): Promise<{
66
+ key: Uint8Array;
67
+ iv: Uint8Array;
68
+ }>;
69
+ /**
70
+ * Get decrypted symmetric key in base64 string from KMS via private gateway
71
+ *
72
+ * @param {string} encryptedSymmetricKey - The encrypted symmetric key (base64-encoded)
73
+ * @param {Type.SpaceDID} spaceDID - The space DID
74
+ * @param {import('@ucanto/interface').Proof} delegationProof - The delegation proof
75
+ * @param {import('@storacha/client/types').Signer<import('@storacha/client/types').DID, import('@storacha/client/types').SigAlg>} issuer - The issuer
76
+ * @returns {Promise<{decryptedSymmetricKey: string}>} - The decrypted symmetric key (base64-encoded)
77
+ */
78
+ getDecryptedSymmetricKey(encryptedSymmetricKey: string, spaceDID: Type.SpaceDID, delegationProof: import("@ucanto/interface").Proof, issuer: import("@storacha/client/types").Signer<import("@storacha/client/types").DID, import("@storacha/client/types").SigAlg>): Promise<{
79
+ decryptedSymmetricKey: string;
80
+ }>;
81
+ /**
82
+ * Extract the encrypted metadata from the CAR file
83
+ * KMS adapter only handles KMS format (encrypted-metadata@0.2)
84
+ *
85
+ * @param {Uint8Array} car
86
+ * @returns {Type.ExtractedMetadata}
87
+ */
88
+ extractEncryptedMetadata(car: Uint8Array): Type.ExtractedMetadata;
89
+ /**
90
+ * @param {Type.ExtractedMetadata} metadata
91
+ * @returns {string}
92
+ */
93
+ getEncryptedKey(metadata: Type.ExtractedMetadata): string;
94
+ /**
95
+ * Encode metadata for upload
96
+ *
97
+ * @param {string} encryptedDataCID - The CID of the encrypted data
98
+ * @param {string} encryptedKey - The encrypted key
99
+ * @param {Type.KMSKeyMetadata} metadata - The metadata to encode
100
+ * @returns {Promise<{ cid: import('@storacha/upload-client/types').AnyLink, bytes: Uint8Array }>} - The encoded metadata
101
+ */
102
+ encodeMetadata(encryptedDataCID: string, encryptedKey: string, metadata: Type.KMSKeyMetadata): Promise<{
103
+ cid: import("@storacha/upload-client/types").AnyLink;
104
+ bytes: Uint8Array;
105
+ }>;
106
+ /**
107
+ * Get the RSA public key from the space/encryption/setup
108
+ *
109
+ * @param {Type.EncryptionConfig} encryptionConfig
110
+ * @returns {Promise<{ publicKey: string, provider: string, algorithm: string }>}
111
+ */
112
+ getSpacePublicKey(encryptionConfig: Type.EncryptionConfig): Promise<{
113
+ publicKey: string;
114
+ provider: string;
115
+ algorithm: string;
116
+ }>;
117
+ /**
118
+ * Get the Web Crypto API SubtleCrypto interface (universal compatibility)
119
+ *
120
+ * @returns {SubtleCrypto} - The SubtleCrypto interface
121
+ */
122
+ getSubtleCrypto(): SubtleCrypto;
123
+ /**
124
+ * Encrypt data with RSA-OAEP using the public key
125
+ *
126
+ * @param {Uint8Array} dataToEncrypt
127
+ * @param {string} publicKeyPem
128
+ * @returns {Promise<Uint8Array>}
129
+ */
130
+ encryptWithRSA(dataToEncrypt: Uint8Array, publicKeyPem: string): Promise<Uint8Array>;
131
+ /**
132
+ * Convert PEM-encoded public key to ArrayBuffer for Web Crypto API
133
+ *
134
+ * @param {string} pem - PEM-encoded public key string
135
+ * @returns {ArrayBuffer} - DER-encoded key data for crypto.subtle.importKey()
136
+ */
137
+ pemToArrayBuffer(pem: string): ArrayBuffer;
138
+ newKeyManagerServiceConnection(): import("@ucanto/interface").ConnectionView<any>;
139
+ /**
140
+ * Sanitize the space DID for the KMS key ID
141
+ *
142
+ * @param {Type.SpaceDID} spaceDID
143
+ * @returns {string}
144
+ */
145
+ sanitizeSpaceDIDForKMSKeyId(spaceDID: Type.SpaceDID): string;
146
+ }
147
+ import * as Type from '../../types.js';
148
+ //# sourceMappingURL=kms-crypto-adapter.d.ts.map
@@ -0,0 +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;;;;;;;;;OASG;IACH,kCATW,MAAM,WAEd;QAAwC,iBAAiB,EAAjD,IAAI,CAAC,iBAAiB;QACU,QAAQ,EAAxC,IAAI,CAAC,iBAAiB;QACF,aAAa,EAAjC,UAAU;QACY,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;;;OA4BA;IAED;;;;;;;;OAQG;IACH,gDANW,MAAM,YACN,IAAI,CAAC,QAAQ,mBACb,OAAO,mBAAmB,EAAE,KAAK,UACjC,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,CA4BpD;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,CAyB/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;sBAhZqB,gBAAgB"}
@@ -0,0 +1,321 @@
1
+ import { connect } from '@ucanto/client';
2
+ import { CAR, HTTP } from '@ucanto/transport';
3
+ import { base64 } from 'multiformats/bases/base64';
4
+ import * as Type from '../../types.js';
5
+ import { EncryptionSetup, EncryptionKeyDecrypt, } from '@storacha/capabilities/space';
6
+ import { KMSMetadata } from '../../core/metadata/encrypted-metadata.js';
7
+ import * as DID from '@ipld/dag-ucan/did';
8
+ /**
9
+ * KMSCryptoAdapter implements the complete CryptoAdapter interface using KMS.
10
+ * It uses composition with a SymmetricCrypto implementation for file encryption/decryption
11
+ * and KMS via private gateway for key management operations.
12
+ *
13
+ * @class
14
+ * @implements {Type.CryptoAdapter}
15
+ */
16
+ export class KMSCryptoAdapter {
17
+ /**
18
+ * Create a new KMS crypto adapter
19
+ *
20
+ * @param {Type.SymmetricCrypto} symmetricCrypto - The symmetric crypto implementation (browser or node)
21
+ * @param {URL|string} keyManagerServiceURL - The key manager service URL
22
+ * @param {`did:${string}:${string}`} keyManagerServiceDID - The key manager service DID
23
+ * @param {object} [options] - Optional configuration
24
+ * @param {boolean} [options.allowInsecureHttp] - Allow HTTP for testing (NOT for production)
25
+ */
26
+ constructor(symmetricCrypto, keyManagerServiceURL, keyManagerServiceDID, options = {}) {
27
+ this.symmetricCrypto = symmetricCrypto;
28
+ // SECURITY: Enforce HTTPS protocol for key manager service communications (P1.1)
29
+ const url = keyManagerServiceURL instanceof URL
30
+ ? keyManagerServiceURL
31
+ : new URL(keyManagerServiceURL);
32
+ const { allowInsecureHttp = false } = options;
33
+ if (url.protocol !== 'https:' && !allowInsecureHttp) {
34
+ throw new Error(`Key manager service must use HTTPS protocol for security. Received: ${url.protocol}. ` +
35
+ `Please update the service URL to use HTTPS (e.g., https://your-key-manager-service.com). ` +
36
+ `For testing only, you can pass { allowInsecureHttp: true } as the fourth parameter.`);
37
+ }
38
+ this.keyManagerServiceURL = url;
39
+ this.keyManagerServiceDID = DID.parse(keyManagerServiceDID);
40
+ }
41
+ /**
42
+ * Encrypt a stream of data using the symmetric crypto
43
+ *
44
+ * @param {Type.BlobLike} data
45
+ */
46
+ async encryptStream(data) {
47
+ return this.symmetricCrypto.encryptStream(data);
48
+ }
49
+ /**
50
+ * Decrypt a stream of data using the symmetric crypto
51
+ *
52
+ * @param {ReadableStream} encryptedData
53
+ * @param {Uint8Array} key
54
+ * @param {Uint8Array} iv
55
+ */
56
+ async decryptStream(encryptedData, key, iv) {
57
+ return this.symmetricCrypto.decryptStream(encryptedData, key, iv);
58
+ }
59
+ /**
60
+ * Encrypt a symmetric key using the KMS
61
+ *
62
+ * @param {Uint8Array} key
63
+ * @param {Uint8Array} iv
64
+ * @param {Type.EncryptionConfig} encryptionConfig
65
+ * @returns {Promise<Type.EncryptedKeyResult>}
66
+ */
67
+ async encryptSymmetricKey(key, iv, encryptionConfig) {
68
+ // Step 1: Combine key and IV to encrypt a single string
69
+ const combinedKeyAndIV = this.symmetricCrypto.combineKeyAndIV(key, iv);
70
+ // Step 2: Get RSA public key from space/encryption/setup
71
+ const setupResponse = await this.getSpacePublicKey(encryptionConfig);
72
+ // Step 3: Encrypt with RSA-OAEP
73
+ const encryptedKey = await this.encryptWithRSA(combinedKeyAndIV, setupResponse.publicKey);
74
+ // Step 4: Return the encrypted key and metadata
75
+ return {
76
+ strategy: 'kms',
77
+ encryptedKey: base64.encode(encryptedKey), // base64-encoded for transport/storage
78
+ metadata: {
79
+ space: encryptionConfig.spaceDID,
80
+ kms: {
81
+ provider: setupResponse.provider,
82
+ keyId: this.sanitizeSpaceDIDForKMSKeyId(encryptionConfig.spaceDID),
83
+ algorithm: setupResponse.algorithm,
84
+ },
85
+ },
86
+ };
87
+ }
88
+ /**
89
+ * @param {string} encryptedKey
90
+ * @param {object} configs
91
+ * @param {Type.DecryptionOptions} configs.decryptionOptions
92
+ * @param {Type.ExtractedMetadata} configs.metadata
93
+ * @param {Uint8Array} configs.delegationCAR
94
+ * @param {Type.AnyLink} configs.resourceCID
95
+ * @param {import('@storacha/client/types').Signer<import('@storacha/client/types').DID, import('@storacha/client/types').SigAlg>} configs.issuer
96
+ * @param {import('@storacha/client/types').DID} configs.audience
97
+ */
98
+ async decryptSymmetricKey(encryptedKey, configs) {
99
+ // Step 1: Validate configs
100
+ const { decryptionOptions, metadata, issuer } = configs;
101
+ if (metadata.strategy !== 'kms') {
102
+ throw new Error('KMSCryptoAdapter can only handle KMS metadata');
103
+ }
104
+ const { spaceDID, delegationProof } = decryptionOptions;
105
+ if (!spaceDID || !delegationProof) {
106
+ throw new Error('SpaceDID and delegationProof are required');
107
+ }
108
+ if (!issuer) {
109
+ throw new Error('Issuer is required');
110
+ }
111
+ // Step 2: Get the decrypted key from KMS via gateway
112
+ const { decryptedSymmetricKey } = await this.getDecryptedSymmetricKey(encryptedKey, spaceDID, delegationProof, issuer);
113
+ // Step 3: Decode and split the combined key and IV
114
+ const combinedKeyAndIV = base64.decode(decryptedSymmetricKey);
115
+ return this.symmetricCrypto.splitKeyAndIV(combinedKeyAndIV);
116
+ }
117
+ /**
118
+ * Get decrypted symmetric key in base64 string from KMS via private gateway
119
+ *
120
+ * @param {string} encryptedSymmetricKey - The encrypted symmetric key (base64-encoded)
121
+ * @param {Type.SpaceDID} spaceDID - The space DID
122
+ * @param {import('@ucanto/interface').Proof} delegationProof - The delegation proof
123
+ * @param {import('@storacha/client/types').Signer<import('@storacha/client/types').DID, import('@storacha/client/types').SigAlg>} issuer - The issuer
124
+ * @returns {Promise<{decryptedSymmetricKey: string}>} - The decrypted symmetric key (base64-encoded)
125
+ */
126
+ async getDecryptedSymmetricKey(encryptedSymmetricKey, spaceDID, delegationProof, issuer) {
127
+ // Step 1: Invoke the KeyDecrypt capability passing the decryption proof
128
+ const result = await EncryptionKeyDecrypt.invoke({
129
+ issuer,
130
+ audience: this.keyManagerServiceDID,
131
+ with: spaceDID,
132
+ nb: {
133
+ key: base64.decode(encryptedSymmetricKey), // Convert base64 string to bytes
134
+ },
135
+ proofs: [delegationProof],
136
+ }).execute(this.newKeyManagerServiceConnection());
137
+ // Step 2: Handle the result
138
+ if (result.out.error) {
139
+ throw new Error(`KMS decryption failed: ${JSON.stringify(result.out.error)}`);
140
+ }
141
+ // Step 3: Return the base64-encoded decrypted key from the gateway response
142
+ return /** @type {{decryptedSymmetricKey: string}} */ (result.out.ok);
143
+ }
144
+ /**
145
+ * Extract the encrypted metadata from the CAR file
146
+ * KMS adapter only handles KMS format (encrypted-metadata@0.2)
147
+ *
148
+ * @param {Uint8Array} car
149
+ * @returns {Type.ExtractedMetadata}
150
+ */
151
+ extractEncryptedMetadata(car) {
152
+ const kmsContentResult = KMSMetadata.extract(car);
153
+ if (kmsContentResult.error) {
154
+ throw kmsContentResult.error;
155
+ }
156
+ const kmsContent = kmsContentResult.ok.toJSON();
157
+ // Validate it's KMS format
158
+ if (!kmsContent.encryptedSymmetricKey ||
159
+ !kmsContent.space ||
160
+ !kmsContent.kms) {
161
+ throw new Error('Invalid KMS metadata format - missing encryptedSymmetricKey, space, or kms fields');
162
+ }
163
+ // Return with strategy identifier
164
+ return {
165
+ strategy: 'kms',
166
+ encryptedDataCID: kmsContent.encryptedDataCID,
167
+ encryptedSymmetricKey: kmsContent.encryptedSymmetricKey,
168
+ space: /** @type {Type.SpaceDID} */ (kmsContent.space),
169
+ kms: {
170
+ provider: kmsContent.kms.provider,
171
+ keyId: kmsContent.kms.keyId,
172
+ algorithm: kmsContent.kms.algorithm,
173
+ },
174
+ };
175
+ }
176
+ /**
177
+ * @param {Type.ExtractedMetadata} metadata
178
+ * @returns {string}
179
+ */
180
+ getEncryptedKey(metadata) {
181
+ // For KMS metadata, we need to handle the different structure
182
+ if (metadata.strategy === 'kms') {
183
+ return /** @type {Type.KMSExtractedMetadata} */ (metadata)
184
+ .encryptedSymmetricKey;
185
+ }
186
+ throw new Error('KMSCryptoAdapter can only handle KMS metadata');
187
+ }
188
+ /**
189
+ * Encode metadata for upload
190
+ *
191
+ * @param {string} encryptedDataCID - The CID of the encrypted data
192
+ * @param {string} encryptedKey - The encrypted key
193
+ * @param {Type.KMSKeyMetadata} metadata - The metadata to encode
194
+ * @returns {Promise<{ cid: import('@storacha/upload-client/types').AnyLink, bytes: Uint8Array }>} - The encoded metadata
195
+ */
196
+ async encodeMetadata(encryptedDataCID, encryptedKey, metadata) {
197
+ const kmsKeyMetadata =
198
+ /** @type {import('../../types.js').KMSKeyMetadata} */ (metadata);
199
+ /** @type {Type.KMSMetadataInput} */
200
+ const uploadData = {
201
+ encryptedDataCID,
202
+ encryptedSymmetricKey: encryptedKey,
203
+ space: kmsKeyMetadata.space,
204
+ kms: {
205
+ provider: kmsKeyMetadata.kms.provider,
206
+ keyId: kmsKeyMetadata.kms.keyId,
207
+ algorithm: kmsKeyMetadata.kms.algorithm,
208
+ },
209
+ };
210
+ const kmsMetadata = KMSMetadata.create(uploadData);
211
+ return await kmsMetadata.archiveBlock();
212
+ }
213
+ /**
214
+ * Get the RSA public key from the space/encryption/setup
215
+ *
216
+ * @param {Type.EncryptionConfig} encryptionConfig
217
+ * @returns {Promise<{ publicKey: string, provider: string, algorithm: string }>}
218
+ */
219
+ async getSpacePublicKey(encryptionConfig) {
220
+ // Step 1: Invoke the EncryptionSetup capability
221
+ const setupResult = await EncryptionSetup.invoke({
222
+ issuer: encryptionConfig.issuer,
223
+ audience: this.keyManagerServiceDID,
224
+ with: encryptionConfig.spaceDID,
225
+ nb: {
226
+ location: encryptionConfig.location,
227
+ keyring: encryptionConfig.keyring,
228
+ },
229
+ }).execute(this.newKeyManagerServiceConnection());
230
+ // Step 2: Handle the result
231
+ if (setupResult.out.error) {
232
+ throw new Error(`Failed to get public key: ${JSON.stringify(setupResult.out.error)}`);
233
+ }
234
+ // Step 3: Return the public key and key reference
235
+ return /** @type {{ publicKey: string, provider: string, algorithm: string }} */ (setupResult.out.ok);
236
+ }
237
+ /**
238
+ * Get the Web Crypto API SubtleCrypto interface (universal compatibility)
239
+ *
240
+ * @returns {SubtleCrypto} - The SubtleCrypto interface
241
+ */
242
+ getSubtleCrypto() {
243
+ // Web Crypto API is available in modern browsers (secure contexts) and Node.js 16+
244
+ if (globalThis.crypto?.subtle) {
245
+ return globalThis.crypto.subtle;
246
+ }
247
+ throw new Error('Web Crypto API (SubtleCrypto) not available. Ensure you are running in a secure context (HTTPS) or Node.js 16+.');
248
+ }
249
+ /**
250
+ * Encrypt data with RSA-OAEP using the public key
251
+ *
252
+ * @param {Uint8Array} dataToEncrypt
253
+ * @param {string} publicKeyPem
254
+ * @returns {Promise<Uint8Array>}
255
+ */
256
+ async encryptWithRSA(dataToEncrypt, publicKeyPem) {
257
+ const subtle = this.getSubtleCrypto();
258
+ // Step 1. Import the PEM public key
259
+ const publicKey = await subtle.importKey('spki', this.pemToArrayBuffer(publicKeyPem), {
260
+ name: 'RSA-OAEP',
261
+ hash: 'SHA-256',
262
+ }, false, ['encrypt']);
263
+ // Step 2. Encrypt the raw data directly
264
+ const encrypted = await subtle.encrypt({ name: 'RSA-OAEP' }, publicKey, dataToEncrypt);
265
+ return new Uint8Array(encrypted);
266
+ }
267
+ /**
268
+ * Convert PEM-encoded public key to ArrayBuffer for Web Crypto API
269
+ *
270
+ * @param {string} pem - PEM-encoded public key string
271
+ * @returns {ArrayBuffer} - DER-encoded key data for crypto.subtle.importKey()
272
+ */
273
+ pemToArrayBuffer(pem) {
274
+ // Strip PEM headers, footers, and whitespace to get base64 DER data
275
+ const base64String = pem
276
+ .replace('-----BEGIN PUBLIC KEY-----', '')
277
+ .replace('-----END PUBLIC KEY-----', '')
278
+ .replace(/\s/g, '');
279
+ // For Node.js environment, use Buffer for standard base64 decoding
280
+ if (typeof Buffer !== 'undefined') {
281
+ return Buffer.from(base64String, 'base64');
282
+ }
283
+ // For browser environment, use atob for standard base64 decoding
284
+ let binaryString;
285
+ if (typeof globalThis !== 'undefined' && globalThis.atob) {
286
+ binaryString = globalThis.atob(base64String);
287
+ }
288
+ else if (typeof Buffer !== 'undefined') {
289
+ // @ts-ignore - Buffer exists in Node.js environment
290
+ binaryString = Buffer.from(base64String, 'base64').toString('binary');
291
+ }
292
+ else {
293
+ throw new Error('Neither atob nor Buffer available for base64 decoding');
294
+ }
295
+ const bytes = new Uint8Array(binaryString.length);
296
+ for (let i = 0; i < binaryString.length; i++) {
297
+ bytes[i] = binaryString.charCodeAt(i);
298
+ }
299
+ return bytes;
300
+ }
301
+ newKeyManagerServiceConnection() {
302
+ return connect({
303
+ id: this.keyManagerServiceDID,
304
+ codec: CAR.outbound,
305
+ channel: HTTP.open({
306
+ url: this.keyManagerServiceURL,
307
+ method: 'POST',
308
+ }),
309
+ });
310
+ }
311
+ /**
312
+ * Sanitize the space DID for the KMS key ID
313
+ *
314
+ * @param {Type.SpaceDID} spaceDID
315
+ * @returns {string}
316
+ */
317
+ sanitizeSpaceDIDForKMSKeyId(spaceDID) {
318
+ return spaceDID.replace(/^did:key:/, '');
319
+ }
320
+ }
321
+ //# sourceMappingURL=kms-crypto-adapter.js.map