@opentdf/sdk 0.4.0-beta.4 → 0.4.0-beta.40

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 (189) hide show
  1. package/dist/cjs/src/access.js +16 -1
  2. package/dist/cjs/src/auth/oidc-clientcredentials-provider.js +4 -2
  3. package/dist/cjs/src/auth/oidc-externaljwt-provider.js +5 -3
  4. package/dist/cjs/src/auth/oidc-refreshtoken-provider.js +19 -3
  5. package/dist/cjs/src/auth/oidc.js +9 -8
  6. package/dist/cjs/src/auth/providers.js +7 -1
  7. package/dist/cjs/src/index.js +4 -2
  8. package/dist/cjs/src/opentdf.js +66 -12
  9. package/dist/cjs/src/platform/authorization/v2/authorization_pb.js +112 -0
  10. package/dist/cjs/src/platform/buf/validate/validate_pb.js +114 -170
  11. package/dist/cjs/src/platform/common/common_pb.js +16 -5
  12. package/dist/cjs/src/platform/entity/entity_pb.js +51 -0
  13. package/dist/cjs/src/platform/entityresolution/entity_resolution_pb.js +1 -1
  14. package/dist/cjs/src/platform/entityresolution/v2/entity_resolution_pb.js +49 -0
  15. package/dist/cjs/src/platform/google/api/annotations_pb.js +1 -1
  16. package/dist/cjs/src/platform/google/api/http_pb.js +3 -3
  17. package/dist/cjs/src/platform/kas/kas_pb.js +2 -2
  18. package/dist/cjs/src/platform/policy/attributes/attributes_pb.js +12 -2
  19. package/dist/cjs/src/platform/policy/kasregistry/key_access_server_registry_pb.js +57 -4
  20. package/dist/cjs/src/platform/policy/keymanagement/key_management_pb.js +2 -2
  21. package/dist/cjs/src/platform/policy/namespaces/namespaces_pb.js +31 -4
  22. package/dist/cjs/src/platform/policy/objects_pb.js +116 -42
  23. package/dist/cjs/src/platform/policy/obligations/obligations_pb.js +159 -0
  24. package/dist/cjs/src/platform/policy/registeredresources/registered_resources_pb.js +20 -15
  25. package/dist/cjs/src/platform/policy/resourcemapping/resource_mapping_pb.js +2 -3
  26. package/dist/cjs/src/platform/policy/selectors_pb.js +1 -1
  27. package/dist/cjs/src/platform/policy/subjectmapping/subject_mapping_pb.js +2 -3
  28. package/dist/cjs/src/platform/policy/unsafe/unsafe_pb.js +2 -4
  29. package/dist/cjs/src/platform.js +16 -3
  30. package/dist/cjs/src/policy/api.js +27 -7
  31. package/dist/cjs/src/policy/granter.js +75 -48
  32. package/dist/cjs/src/seekable.js +32 -1
  33. package/dist/cjs/src/utils.js +57 -3
  34. package/dist/cjs/tdf3/src/assertions.js +39 -2
  35. package/dist/cjs/tdf3/src/client/builders.js +13 -1
  36. package/dist/cjs/tdf3/src/client/index.js +200 -53
  37. package/dist/cjs/tdf3/src/client/validation.js +3 -3
  38. package/dist/cjs/tdf3/src/tdf.js +20 -2
  39. package/dist/cjs/tdf3/src/utils/unwrap.js +2 -2
  40. package/dist/types/src/access.d.ts +15 -0
  41. package/dist/types/src/access.d.ts.map +1 -1
  42. package/dist/types/src/auth/oidc-clientcredentials-provider.d.ts +1 -1
  43. package/dist/types/src/auth/oidc-clientcredentials-provider.d.ts.map +1 -1
  44. package/dist/types/src/auth/oidc-externaljwt-provider.d.ts +1 -1
  45. package/dist/types/src/auth/oidc-externaljwt-provider.d.ts.map +1 -1
  46. package/dist/types/src/auth/oidc-refreshtoken-provider.d.ts +15 -1
  47. package/dist/types/src/auth/oidc-refreshtoken-provider.d.ts.map +1 -1
  48. package/dist/types/src/auth/oidc.d.ts +4 -0
  49. package/dist/types/src/auth/oidc.d.ts.map +1 -1
  50. package/dist/types/src/auth/providers.d.ts.map +1 -1
  51. package/dist/types/src/index.d.ts +1 -0
  52. package/dist/types/src/index.d.ts.map +1 -1
  53. package/dist/types/src/opentdf.d.ts +126 -6
  54. package/dist/types/src/opentdf.d.ts.map +1 -1
  55. package/dist/types/src/platform/authorization/v2/authorization_pb.d.ts +439 -0
  56. package/dist/types/src/platform/authorization/v2/authorization_pb.d.ts.map +1 -0
  57. package/dist/types/src/platform/buf/validate/validate_pb.d.ts +495 -370
  58. package/dist/types/src/platform/buf/validate/validate_pb.d.ts.map +1 -1
  59. package/dist/types/src/platform/common/common_pb.d.ts +36 -0
  60. package/dist/types/src/platform/common/common_pb.d.ts.map +1 -1
  61. package/dist/types/src/platform/entity/entity_pb.d.ts +130 -0
  62. package/dist/types/src/platform/entity/entity_pb.d.ts.map +1 -0
  63. package/dist/types/src/platform/entityresolution/entity_resolution_pb.d.ts +4 -0
  64. package/dist/types/src/platform/entityresolution/entity_resolution_pb.d.ts.map +1 -1
  65. package/dist/types/src/platform/entityresolution/v2/entity_resolution_pb.d.ts +136 -0
  66. package/dist/types/src/platform/entityresolution/v2/entity_resolution_pb.d.ts.map +1 -0
  67. package/dist/types/src/platform/google/api/http_pb.d.ts.map +1 -1
  68. package/dist/types/src/platform/kas/kas_pb.d.ts +5 -0
  69. package/dist/types/src/platform/kas/kas_pb.d.ts.map +1 -1
  70. package/dist/types/src/platform/policy/attributes/attributes_pb.d.ts +44 -13
  71. package/dist/types/src/platform/policy/attributes/attributes_pb.d.ts.map +1 -1
  72. package/dist/types/src/platform/policy/kasregistry/key_access_server_registry_pb.d.ts +329 -24
  73. package/dist/types/src/platform/policy/kasregistry/key_access_server_registry_pb.d.ts.map +1 -1
  74. package/dist/types/src/platform/policy/keymanagement/key_management_pb.d.ts +20 -1
  75. package/dist/types/src/platform/policy/keymanagement/key_management_pb.d.ts.map +1 -1
  76. package/dist/types/src/platform/policy/namespaces/namespaces_pb.d.ts +143 -5
  77. package/dist/types/src/platform/policy/namespaces/namespaces_pb.d.ts.map +1 -1
  78. package/dist/types/src/platform/policy/objects_pb.d.ts +382 -33
  79. package/dist/types/src/platform/policy/objects_pb.d.ts.map +1 -1
  80. package/dist/types/src/platform/policy/obligations/obligations_pb.d.ts +670 -0
  81. package/dist/types/src/platform/policy/obligations/obligations_pb.d.ts.map +1 -0
  82. package/dist/types/src/platform/policy/registeredresources/registered_resources_pb.d.ts +67 -0
  83. package/dist/types/src/platform/policy/registeredresources/registered_resources_pb.d.ts.map +1 -1
  84. package/dist/types/src/platform/policy/resourcemapping/resource_mapping_pb.d.ts.map +1 -1
  85. package/dist/types/src/platform/policy/selectors_pb.d.ts +18 -0
  86. package/dist/types/src/platform/policy/selectors_pb.d.ts.map +1 -1
  87. package/dist/types/src/platform/policy/subjectmapping/subject_mapping_pb.d.ts.map +1 -1
  88. package/dist/types/src/platform/policy/unsafe/unsafe_pb.d.ts +18 -4
  89. package/dist/types/src/platform/policy/unsafe/unsafe_pb.d.ts.map +1 -1
  90. package/dist/types/src/platform.d.ts +16 -0
  91. package/dist/types/src/platform.d.ts.map +1 -1
  92. package/dist/types/src/policy/api.d.ts +2 -0
  93. package/dist/types/src/policy/api.d.ts.map +1 -1
  94. package/dist/types/src/policy/granter.d.ts +11 -6
  95. package/dist/types/src/policy/granter.d.ts.map +1 -1
  96. package/dist/types/src/seekable.d.ts +31 -0
  97. package/dist/types/src/seekable.d.ts.map +1 -1
  98. package/dist/types/src/utils.d.ts +56 -2
  99. package/dist/types/src/utils.d.ts.map +1 -1
  100. package/dist/types/tdf3/src/assertions.d.ts +4 -0
  101. package/dist/types/tdf3/src/assertions.d.ts.map +1 -1
  102. package/dist/types/tdf3/src/client/builders.d.ts +12 -0
  103. package/dist/types/tdf3/src/client/builders.d.ts.map +1 -1
  104. package/dist/types/tdf3/src/client/index.d.ts +11 -3
  105. package/dist/types/tdf3/src/client/index.d.ts.map +1 -1
  106. package/dist/types/tdf3/src/client/validation.d.ts +3 -3
  107. package/dist/types/tdf3/src/client/validation.d.ts.map +1 -1
  108. package/dist/types/tdf3/src/tdf.d.ts +2 -1
  109. package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
  110. package/dist/types/tdf3/src/utils/unwrap.d.ts.map +1 -1
  111. package/dist/web/src/access.js +16 -1
  112. package/dist/web/src/auth/oidc-clientcredentials-provider.js +4 -2
  113. package/dist/web/src/auth/oidc-externaljwt-provider.js +5 -3
  114. package/dist/web/src/auth/oidc-refreshtoken-provider.js +19 -3
  115. package/dist/web/src/auth/oidc.js +9 -8
  116. package/dist/web/src/auth/providers.js +7 -1
  117. package/dist/web/src/index.js +2 -1
  118. package/dist/web/src/opentdf.js +66 -12
  119. package/dist/web/src/platform/authorization/v2/authorization_pb.js +109 -0
  120. package/dist/web/src/platform/buf/validate/validate_pb.js +113 -169
  121. package/dist/web/src/platform/common/common_pb.js +15 -4
  122. package/dist/web/src/platform/entity/entity_pb.js +48 -0
  123. package/dist/web/src/platform/entityresolution/entity_resolution_pb.js +1 -1
  124. package/dist/web/src/platform/entityresolution/v2/entity_resolution_pb.js +46 -0
  125. package/dist/web/src/platform/google/api/annotations_pb.js +1 -1
  126. package/dist/web/src/platform/google/api/http_pb.js +3 -3
  127. package/dist/web/src/platform/kas/kas_pb.js +2 -2
  128. package/dist/web/src/platform/policy/attributes/attributes_pb.js +12 -2
  129. package/dist/web/src/platform/policy/kasregistry/key_access_server_registry_pb.js +55 -3
  130. package/dist/web/src/platform/policy/keymanagement/key_management_pb.js +2 -2
  131. package/dist/web/src/platform/policy/namespaces/namespaces_pb.js +30 -3
  132. package/dist/web/src/platform/policy/objects_pb.js +114 -41
  133. package/dist/web/src/platform/policy/obligations/obligations_pb.js +156 -0
  134. package/dist/web/src/platform/policy/registeredresources/registered_resources_pb.js +19 -14
  135. package/dist/web/src/platform/policy/resourcemapping/resource_mapping_pb.js +2 -3
  136. package/dist/web/src/platform/policy/selectors_pb.js +1 -1
  137. package/dist/web/src/platform/policy/subjectmapping/subject_mapping_pb.js +2 -3
  138. package/dist/web/src/platform/policy/unsafe/unsafe_pb.js +2 -4
  139. package/dist/web/src/platform.js +16 -3
  140. package/dist/web/src/policy/api.js +26 -7
  141. package/dist/web/src/policy/granter.js +75 -48
  142. package/dist/web/src/seekable.js +32 -1
  143. package/dist/web/src/utils.js +57 -3
  144. package/dist/web/tdf3/src/assertions.js +38 -2
  145. package/dist/web/tdf3/src/client/builders.js +13 -1
  146. package/dist/web/tdf3/src/client/index.js +202 -56
  147. package/dist/web/tdf3/src/client/validation.js +3 -3
  148. package/dist/web/tdf3/src/tdf.js +20 -2
  149. package/dist/web/tdf3/src/utils/unwrap.js +2 -2
  150. package/package.json +7 -5
  151. package/src/access.ts +15 -0
  152. package/src/auth/oidc-clientcredentials-provider.ts +4 -0
  153. package/src/auth/oidc-externaljwt-provider.ts +5 -1
  154. package/src/auth/oidc-refreshtoken-provider.ts +19 -1
  155. package/src/auth/oidc.ts +12 -7
  156. package/src/auth/providers.ts +6 -0
  157. package/src/index.ts +1 -0
  158. package/src/opentdf.ts +149 -72
  159. package/src/platform/authorization/v2/authorization_pb.ts +503 -0
  160. package/src/platform/buf/validate/validate_pb.ts +529 -401
  161. package/src/platform/common/common_pb.ts +48 -3
  162. package/src/platform/entity/entity_pb.ts +154 -0
  163. package/src/platform/entityresolution/entity_resolution_pb.ts +4 -0
  164. package/src/platform/entityresolution/v2/entity_resolution_pb.ts +170 -0
  165. package/src/platform/google/api/annotations_pb.ts +1 -1
  166. package/src/platform/google/api/http_pb.ts +2 -2
  167. package/src/platform/kas/kas_pb.ts +6 -1
  168. package/src/platform/policy/attributes/attributes_pb.ts +46 -16
  169. package/src/platform/policy/kasregistry/key_access_server_registry_pb.ts +371 -27
  170. package/src/platform/policy/keymanagement/key_management_pb.ts +24 -2
  171. package/src/platform/policy/namespaces/namespaces_pb.ts +163 -7
  172. package/src/platform/policy/objects_pb.ts +474 -59
  173. package/src/platform/policy/obligations/obligations_pb.ts +788 -0
  174. package/src/platform/policy/registeredresources/registered_resources_pb.ts +80 -13
  175. package/src/platform/policy/resourcemapping/resource_mapping_pb.ts +1 -2
  176. package/src/platform/policy/selectors_pb.ts +18 -0
  177. package/src/platform/policy/subjectmapping/subject_mapping_pb.ts +1 -2
  178. package/src/platform/policy/unsafe/unsafe_pb.ts +21 -6
  179. package/src/platform.ts +19 -5
  180. package/src/policy/api.ts +37 -6
  181. package/src/policy/granter.ts +82 -56
  182. package/src/seekable.ts +31 -0
  183. package/src/utils.ts +56 -2
  184. package/tdf3/src/assertions.ts +52 -1
  185. package/tdf3/src/client/builders.ts +15 -0
  186. package/tdf3/src/client/index.ts +279 -73
  187. package/tdf3/src/client/validation.ts +2 -2
  188. package/tdf3/src/tdf.ts +26 -2
  189. package/tdf3/src/utils/unwrap.ts +2 -1
@@ -1,8 +1,8 @@
1
1
  import { v4 } from 'uuid';
2
2
  import {
3
- ZipReader,
4
- streamToBuffer,
5
3
  keyMiddleware as defaultKeyMiddleware,
4
+ streamToBuffer,
5
+ ZipReader,
6
6
  } from '../utils/index.js';
7
7
  import { base64 } from '../../../src/encodings/index.js';
8
8
  import {
@@ -10,8 +10,8 @@ import {
10
10
  type EncryptConfiguration,
11
11
  fetchKasPublicKey,
12
12
  loadTDFStream,
13
- validatePolicyObject,
14
13
  readStream,
14
+ validatePolicyObject,
15
15
  writeStream,
16
16
  } from '../tdf.js';
17
17
  import { unwrapHtml } from '../utils/unwrap.js';
@@ -27,22 +27,18 @@ import {
27
27
  } from '../../../src/utils.js';
28
28
 
29
29
  import {
30
- type EncryptParams,
31
30
  type DecryptParams,
32
- type Scope,
31
+ DecryptParamsBuilder,
32
+ type DecryptSource,
33
33
  type DecryptStreamMiddleware,
34
+ DEFAULT_SEGMENT_SIZE,
34
35
  type EncryptKeyMiddleware,
36
+ type EncryptParams,
37
+ EncryptParamsBuilder,
35
38
  type EncryptStreamMiddleware,
36
- type SplitStep,
39
+ type Scope,
37
40
  } from './builders.js';
38
41
  import { DecoratedReadableStream } from './DecoratedReadableStream.js';
39
-
40
- import {
41
- DEFAULT_SEGMENT_SIZE,
42
- DecryptParamsBuilder,
43
- type DecryptSource,
44
- EncryptParamsBuilder,
45
- } from './builders.js';
46
42
  import {
47
43
  fetchKeyAccessServers,
48
44
  type KasPublicKeyInfo,
@@ -62,8 +58,8 @@ import {
62
58
  } from '../models/index.js';
63
59
  import { plan } from '../../../src/policy/granter.js';
64
60
  import { attributeFQNsAsValues } from '../../../src/policy/api.js';
65
- import { type Value } from '../../../src/policy/attributes.js';
66
61
  import { type Chunker, fromBuffer, fromSource } from '../../../src/seekable.js';
62
+ import { Algorithm, SimpleKasKey } from '../../../src/platform/policy/objects_pb.js';
67
63
 
68
64
  const GLOBAL_BYTE_LIMIT = 64 * 1000 * 1000 * 1000; // 64 GB, see WS-9363.
69
65
 
@@ -72,6 +68,11 @@ const defaultClientConfig = { oidcOrigin: '', cryptoService: defaultCryptoServic
72
68
 
73
69
  const getFirstTwoBytes = async (chunker: Chunker) => new TextDecoder().decode(await chunker(0, 2));
74
70
 
71
+ async function algorithmFromPEM(pem: string) {
72
+ const k: CryptoKey = await pemToCryptoPublicKey(pem);
73
+ return keyAlgorithmToPublicKeyAlgorithm(k);
74
+ }
75
+
75
76
  // Convert a PEM string to a CryptoKey
76
77
  export const resolveKasInfo = async (
77
78
  pem: string,
@@ -227,6 +228,95 @@ function asPolicy(scope: Scope): Policy {
227
228
  };
228
229
  }
229
230
 
231
+ type KasKeyInfoCache = [
232
+ ...Parameters<typeof fetchKasPublicKey>,
233
+ keyInfoPromise: ReturnType<typeof fetchKasPublicKey>,
234
+ ][];
235
+
236
+ export function findEntryInCache(
237
+ cache: KasKeyInfoCache,
238
+ ...params: Parameters<typeof fetchKasPublicKey>
239
+ ) {
240
+ const [wantedKas, wantedAlgorithm, wantedKid] = params;
241
+ for (const item of cache) {
242
+ const [itemKas, itemAlgorithm, itemKid, itemKeyInfoPromise] = item;
243
+ if (itemKas !== wantedKas) {
244
+ continue;
245
+ }
246
+ // This makes undefined only match with undefined (base key).
247
+ // We could potentially consider any key a match if undefined algorithm?
248
+ if (itemAlgorithm !== wantedAlgorithm) {
249
+ continue;
250
+ }
251
+ if (wantedKid && itemKid !== wantedKid) {
252
+ continue;
253
+ }
254
+ return itemKeyInfoPromise;
255
+ }
256
+ return null;
257
+ }
258
+
259
+ const fetchKasKeyWithCache = (
260
+ cache: KasKeyInfoCache,
261
+ ...params: Parameters<typeof fetchKasPublicKey>
262
+ ): ReturnType<typeof fetchKasPublicKey> => {
263
+ const cachedEntry = findEntryInCache(cache, ...params);
264
+ if (cachedEntry !== null) {
265
+ return cachedEntry;
266
+ }
267
+ const keyInfoPromise = fetchKasPublicKey(...params);
268
+ cache.push([...params, keyInfoPromise]);
269
+ return keyInfoPromise;
270
+ };
271
+
272
+ function algorithmEnumValueToString(algorithmEnumValue: Algorithm) {
273
+ switch (algorithmEnumValue) {
274
+ case Algorithm.RSA_2048:
275
+ return 'rsa:2048';
276
+ case Algorithm.RSA_4096:
277
+ return 'rsa:4096';
278
+ case Algorithm.EC_P256:
279
+ return 'ec:secp256r1';
280
+ case Algorithm.EC_P384:
281
+ return 'ec:secp384r1';
282
+ case Algorithm.EC_P521:
283
+ return 'ec:secp521r1';
284
+ case Algorithm.UNSPECIFIED:
285
+ // Not entirely sure undefined is correct here, but since we need to generate a key for our cache
286
+ // synchonously, it seems to be the best approach for now.
287
+ return undefined;
288
+ default:
289
+ return undefined;
290
+ }
291
+ }
292
+
293
+ const putKasKeyIntoCache = (
294
+ cache: KasKeyInfoCache,
295
+ kasKey: Omit<SimpleKasKey, 'publicKey'> & {
296
+ publicKey: Exclude<SimpleKasKey['publicKey'], undefined>;
297
+ }
298
+ ): ReturnType<typeof fetchKasPublicKey> => {
299
+ const algorithmString = algorithmEnumValueToString(kasKey.publicKey.algorithm);
300
+ const cachedEntry = findEntryInCache(cache, kasKey.kasUri, algorithmString, kasKey.publicKey.kid);
301
+ if (cachedEntry) {
302
+ return cachedEntry;
303
+ }
304
+ const keyInfoPromise = (async function () {
305
+ const keyPromise = pemToCryptoPublicKey(kasKey.publicKey.pem);
306
+ const key = await keyPromise;
307
+ const algorithm = keyAlgorithmToPublicKeyAlgorithm(key);
308
+ return {
309
+ algorithm: algorithm,
310
+ key: keyPromise,
311
+ kid: kasKey.publicKey.kid,
312
+ publicKey: kasKey.publicKey.pem,
313
+ url: kasKey.kasUri,
314
+ };
315
+ })();
316
+ cache.push([kasKey.kasUri, algorithmString, kasKey.publicKey.kid, keyInfoPromise]);
317
+ return keyInfoPromise;
318
+ };
319
+
230
320
  export class Client {
231
321
  readonly cryptoService: CryptoService;
232
322
 
@@ -252,7 +342,7 @@ export class Client {
252
342
  */
253
343
  readonly platformUrl?: string;
254
344
 
255
- readonly kasKeys: Record<string, Promise<KasPublicKeyInfo>[]> = {};
345
+ readonly kasKeyInfoCache: KasKeyInfoCache = [];
256
346
 
257
347
  readonly easEndpoint?: string;
258
348
 
@@ -360,11 +450,13 @@ export class Client {
360
450
  cryptoService: this.cryptoService,
361
451
  dpopKeys: clientConfig.dpopKeys,
362
452
  });
363
- if (clientConfig.kasPublicKey) {
364
- this.kasKeys[this.kasEndpoint] = [
365
- resolveKasInfo(clientConfig.kasPublicKey, this.kasEndpoint),
366
- ];
367
- }
453
+ }
454
+
455
+ /** Necessary only for testing. A dependency-injection approach should be preferred, but that is difficult currently */
456
+ _doFetchKasKeyWithCache(
457
+ ...params: Parameters<typeof fetchKasKeyWithCache>
458
+ ): ReturnType<typeof fetchKasKeyWithCache> {
459
+ return fetchKasKeyWithCache(...params);
368
460
  }
369
461
 
370
462
  /**
@@ -396,62 +488,165 @@ export class Client {
396
488
  mimeType = 'unknown',
397
489
  windowSize = DEFAULT_SEGMENT_SIZE,
398
490
  keyMiddleware = defaultKeyMiddleware,
491
+ splitPlan: preconfiguredSplitPlan,
399
492
  streamMiddleware = async (stream: DecoratedReadableStream) => stream,
400
493
  tdfSpecVersion,
401
494
  wrappingKeyAlgorithm,
402
495
  } = opts;
403
496
  const scope = opts.scope ?? { attributes: [], dissem: [] };
404
497
 
498
+ for (const attributeValue of scope.attributeValues || []) {
499
+ for (const kasKey of attributeValue.kasKeys) {
500
+ if (kasKey.publicKey !== undefined) {
501
+ await putKasKeyIntoCache(this.kasKeyInfoCache, {
502
+ // TypeScript is silly and cannot infer that publicKey is not undefined, without re-referencing it like this, even though we checked already.
503
+ ...kasKey,
504
+ publicKey: kasKey.publicKey,
505
+ });
506
+ }
507
+ }
508
+ }
509
+
405
510
  const policyObject = asPolicy(scope);
406
511
  validatePolicyObject(policyObject);
407
512
 
408
- let splitPlan = opts.splitPlan;
409
- if (!splitPlan && autoconfigure) {
410
- let avs: Value[] = scope.attributeValues ?? [];
411
- const fqns: string[] = scope.attributes
412
- ? scope.attributes.map((attribute) =>
413
- typeof attribute === 'string' ? attribute : attribute.attribute
414
- )
415
- : [];
416
-
417
- if (!avs.length && fqns.length) {
418
- // Hydrate avs from policy endpoint givnen the fqns
419
- if (!this.policyEndpoint) {
420
- throw new ConfigurationError('policyEndpoint not set in TDF3 Client constructor');
513
+ const splitPlan: {
514
+ kas: string;
515
+ kid?: string;
516
+ pem: string;
517
+ sid?: string;
518
+ }[] = [];
519
+ if (preconfiguredSplitPlan) {
520
+ for (const preconfiguredSplit of preconfiguredSplitPlan) {
521
+ const kasPublicKeyInfo = await this._doFetchKasKeyWithCache(
522
+ this.kasKeyInfoCache,
523
+ preconfiguredSplit.kas,
524
+ wrappingKeyAlgorithm,
525
+ preconfiguredSplit.kid
526
+ );
527
+ splitPlan.push({
528
+ kas: kasPublicKeyInfo.url,
529
+ kid: kasPublicKeyInfo.kid,
530
+ pem: kasPublicKeyInfo.publicKey,
531
+ sid: preconfiguredSplit.sid,
532
+ });
533
+ }
534
+ } else if (autoconfigure) {
535
+ const attributeValues = scope.attributeValues ?? [];
536
+ if (!scope.attributes) {
537
+ scope.attributes = attributeValues.map(({ fqn }) => fqn);
538
+ }
539
+ const attributeFQNs = (scope.attributes ?? []).map((attribute) =>
540
+ typeof attribute === 'string' ? attribute : attribute.attribute
541
+ );
542
+ const fqnsWithoutValues = attributeFQNs.filter((fqn) =>
543
+ attributeValues.every((av) => av.fqn !== fqn)
544
+ );
545
+
546
+ if (fqnsWithoutValues.length) {
547
+ // Hydrate missing avs from policy endpoint given the fqns
548
+ if (!this.platformUrl) {
549
+ throw new ConfigurationError('platformUrl not set in TDF3 Client constructor');
421
550
  }
422
- avs = await attributeFQNsAsValues(
423
- this.policyEndpoint,
551
+ const fetchedFQNValues = await attributeFQNsAsValues(
552
+ this.platformUrl,
424
553
  this.authProvider as AuthProvider,
425
- ...fqns
554
+ ...fqnsWithoutValues
426
555
  );
427
- } else if (scope.attributeValues) {
428
- avs = scope.attributeValues;
429
- if (!scope.attributes) {
430
- scope.attributes = avs.map(({ fqn }) => fqn);
431
- }
556
+ fetchedFQNValues.forEach((fetchedValue) => {
557
+ attributeValues.push(fetchedValue);
558
+ });
432
559
  }
433
- if (
434
- avs.length != (scope.attributes?.length || 0) ||
435
- !avs.map(({ fqn }) => fqn).every((a) => fqns.indexOf(a) >= 0)
436
- ) {
560
+
561
+ const hasAllFQNs = attributeFQNs.every((fqn) =>
562
+ attributeValues.some((attributeValue) => attributeValue.fqn === fqn)
563
+ );
564
+ if (attributeFQNs.length != attributeValues.length || !hasAllFQNs) {
437
565
  throw new ConfigurationError(
438
- `Attribute mismatch between [${fqns}] and explicit values ${JSON.stringify(
439
- avs.map(({ fqn }) => fqn)
566
+ `Attribute mismatch between [${attributeFQNs}] and explicit values ${JSON.stringify(
567
+ attributeValues.map(({ fqn }) => fqn)
440
568
  )}`
441
569
  );
442
570
  }
443
- const detailedPlan = plan(avs);
444
- splitPlan = detailedPlan.map((kat) => {
445
- const { kas, sid } = kat;
446
- const pubKey = kas.publicKey?.publicKey;
447
- if (pubKey?.case === 'cached' && pubKey.value.keys && !(kas.uri in this.kasKeys)) {
448
- const keys = pubKey.value.keys;
449
- if (keys?.length) {
450
- this.kasKeys[kas.uri] = keys.map((key) => resolveKasInfo(key.pem, kas.uri, key.kid));
571
+
572
+ for (const attributeValue of attributeValues) {
573
+ for (const kasKey of attributeValue.kasKeys) {
574
+ if (kasKey.publicKey !== undefined) {
575
+ await putKasKeyIntoCache(this.kasKeyInfoCache, {
576
+ // TypeScript is silly and cannot infer that publicKey is not undefined, without re-referencing it like this, even though we checked already.
577
+ ...kasKey,
578
+ publicKey: kasKey.publicKey,
579
+ });
451
580
  }
452
581
  }
453
- return { kas: kas.uri, sid };
454
- });
582
+ }
583
+
584
+ const detailedPlan = plan(attributeValues);
585
+ for (const item of detailedPlan) {
586
+ if ('kid' in item.kas) {
587
+ const pemAlgorithm = await algorithmFromPEM(item.kas.pem);
588
+ const kasPublicKeyInfo = await this._doFetchKasKeyWithCache(
589
+ this.kasKeyInfoCache,
590
+ item.kas.kasUri,
591
+ pemAlgorithm,
592
+ item.kas.kid
593
+ );
594
+ splitPlan.push({
595
+ kas: kasPublicKeyInfo.url,
596
+ kid: kasPublicKeyInfo.kid,
597
+ pem: kasPublicKeyInfo.publicKey,
598
+ sid: item.sid,
599
+ });
600
+ continue;
601
+ }
602
+
603
+ if (!item.kas.publicKey) {
604
+ const kasPublicKeyInfo = await this._doFetchKasKeyWithCache(
605
+ this.kasKeyInfoCache,
606
+ item.kas.uri,
607
+ wrappingKeyAlgorithm,
608
+ undefined
609
+ );
610
+ splitPlan.push({
611
+ kas: kasPublicKeyInfo.url,
612
+ kid: kasPublicKeyInfo.kid,
613
+ pem: kasPublicKeyInfo.publicKey,
614
+ sid: item.sid,
615
+ });
616
+ continue;
617
+ }
618
+
619
+ switch (item.kas.publicKey.publicKey.case) {
620
+ case 'remote':
621
+ const kasPublicKeyInfo = await this._doFetchKasKeyWithCache(
622
+ this.kasKeyInfoCache,
623
+ item.kas.publicKey.publicKey.value,
624
+ wrappingKeyAlgorithm,
625
+ undefined
626
+ );
627
+ splitPlan.push({
628
+ kas: kasPublicKeyInfo.url,
629
+ kid: kasPublicKeyInfo.kid,
630
+ pem: kasPublicKeyInfo.publicKey,
631
+ sid: item.sid,
632
+ });
633
+ break;
634
+
635
+ case 'cached':
636
+ for (const cachedPublicKey of item.kas.publicKey.publicKey.value.keys) {
637
+ splitPlan.push({
638
+ kas: item.kas.uri,
639
+ kid: cachedPublicKey.kid,
640
+ pem: cachedPublicKey.pem,
641
+ sid: item.sid,
642
+ });
643
+ }
644
+ break;
645
+
646
+ default:
647
+ throw new Error(`Unknown public key type: ${item.kas.publicKey.publicKey.case}`);
648
+ }
649
+ }
455
650
  }
456
651
 
457
652
  // TODO: Refactor underlying builder to remove some of this unnecessary config.
@@ -462,37 +657,47 @@ export class Client {
462
657
  ? maxByteLimit
463
658
  : opts.byteLimit;
464
659
  const encryptionInformation = new SplitKey(new AesGcmCipher(this.cryptoService));
465
- const splits: SplitStep[] = splitPlan?.length
466
- ? splitPlan
467
- : [{ kas: opts.defaultKASEndpoint ?? this.kasEndpoint }];
660
+ if (splitPlan.length === 0) {
661
+ const kasPublicKeyInfo = await this._doFetchKasKeyWithCache(
662
+ this.kasKeyInfoCache,
663
+ opts.defaultKASEndpoint ?? this.kasEndpoint,
664
+ wrappingKeyAlgorithm,
665
+ undefined
666
+ );
667
+ splitPlan.push({
668
+ kas: kasPublicKeyInfo.url,
669
+ kid: kasPublicKeyInfo.kid,
670
+ pem: kasPublicKeyInfo.publicKey,
671
+ });
672
+ }
468
673
  encryptionInformation.keyAccess = await Promise.all(
469
- splits.map(async ({ kas, sid }) => {
470
- if (!(kas in this.kasKeys)) {
471
- this.kasKeys[kas] = [fetchKasPublicKey(kas, wrappingKeyAlgorithm)];
472
- }
473
- const kasPublicKey = await Promise.any(this.kasKeys[kas]);
474
- if (kasPublicKey.algorithm !== wrappingKeyAlgorithm) {
674
+ splitPlan.map(async ({ kas, kid, pem, sid }) => {
675
+ const algorithm = await algorithmFromPEM(pem);
676
+ if (algorithm !== wrappingKeyAlgorithm) {
475
677
  console.warn(
476
- `Mismatched wrapping key algorithm: [${kasPublicKey.algorithm}] is not requested type, [${wrappingKeyAlgorithm}]`
678
+ `Mismatched wrapping key algorithm: [${algorithm}] is not requested type, [${wrappingKeyAlgorithm}]`
477
679
  );
478
680
  }
479
681
  let type: KeyAccessType;
480
- switch (kasPublicKey.algorithm) {
682
+ switch (algorithm) {
481
683
  case 'rsa:2048':
684
+ case 'rsa:4096':
482
685
  type = 'wrapped';
483
686
  break;
687
+ case 'ec:secp384r1':
688
+ case 'ec:secp521r1':
484
689
  case 'ec:secp256r1':
485
690
  type = 'ec-wrapped';
486
691
  break;
487
692
  default:
488
- throw new ConfigurationError(`Unsupported algorithm ${kasPublicKey.algorithm}`);
693
+ throw new ConfigurationError(`Unsupported algorithm ${algorithm}`);
489
694
  }
490
695
  return buildKeyAccess({
491
- alg: kasPublicKey.algorithm,
696
+ alg: algorithm,
492
697
  type,
493
- url: kasPublicKey.url,
494
- kid: kasPublicKey.kid,
495
- publicKey: kasPublicKey.publicKey,
698
+ url: kas,
699
+ kid: kid,
700
+ publicKey: pem,
496
701
  metadata,
497
702
  sid,
498
703
  });
@@ -516,6 +721,7 @@ export class Client {
516
721
  keyForEncryption,
517
722
  keyForManifest,
518
723
  assertionConfigs: opts.assertionConfigs,
724
+ systemMetadataAssertion: opts.systemMetadataAssertion,
519
725
  tdfSpecVersion,
520
726
  };
521
727
 
@@ -21,10 +21,10 @@ const IP_HOST_PORT = '([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}:[0-9]{1
21
21
  const HOST = `(${HOST_PORT}|${WWW_HOST}|${IP_HOST_PORT})`;
22
22
 
23
23
  // validate attr name be like `/attr/<attr_name>`
24
- export const ATTR_NAME = `(/${ATTR_NAME_PROP_NAME}/[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]?)`;
24
+ export const ATTR_NAME = `(/${ATTR_NAME_PROP_NAME}/(%[0-9a-fA-F][0-9a-fA-F]|[a-zA-Z0-9])+((%[0-9a-fA-F][0-9a-fA-F]|[a-zA-Z0-9-])+[a-zA-Z0-9])?)`;
25
25
 
26
26
  // validate value pattern
27
- export const ATTR_VALUE = `(/${ATTR_VALUE_PROP_NAME}/[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]?)`;
27
+ export const ATTR_VALUE = `(/${ATTR_VALUE_PROP_NAME}/(%[0-9a-fA-F][0-9a-fA-F]|[a-zA-Z0-9])+((%[0-9a-fA-F][0-9a-fA-F]|[a-zA-Z0-9-])+[a-zA-Z0-9])?)`;
28
28
 
29
29
  // validate attribute authority e.g. https://example.com
30
30
  const ATTR_AUTHORITY_PATTERN = `(${SCHEME}${HOST})`;
package/tdf3/src/tdf.ts CHANGED
@@ -147,6 +147,7 @@ export type EncryptConfiguration = {
147
147
  keyForEncryption: KeyInfo;
148
148
  keyForManifest: KeyInfo;
149
149
  assertionConfigs?: AssertionConfig[];
150
+ systemMetadataAssertion?: boolean;
150
151
  tdfSpecVersion?: string;
151
152
  };
152
153
 
@@ -197,8 +198,15 @@ export type RewrapResponse = {
197
198
  */
198
199
  export async function fetchKasPublicKey(
199
200
  kas: string,
200
- algorithm?: KasPublicKeyAlgorithm
201
+ algorithm?: KasPublicKeyAlgorithm,
202
+ kid?: string
201
203
  ): Promise<KasPublicKeyInfo> {
204
+ if (kid) {
205
+ // Some specific thing for fetching a key by kid?
206
+ // Currently this is just "using" `kid` so TypeScript doesn't complain and
207
+ // we can use the type for our cache parameters.
208
+ // So this empty `if` is actually doing something.
209
+ }
202
210
  return fetchKasPubKeyV2(kas, algorithm);
203
211
  }
204
212
 
@@ -527,8 +535,24 @@ export async function writeStream(cfg: EncryptConfiguration): Promise<DecoratedR
527
535
  manifest.encryptionInformation.integrityInformation.segments = segmentInfos;
528
536
 
529
537
  manifest.encryptionInformation.method.isStreamable = true;
530
-
531
538
  const signedAssertions: assertions.Assertion[] = [];
539
+ if (cfg.systemMetadataAssertion) {
540
+ const systemMetadataConfigBase = assertions.getSystemMetadataAssertionConfig();
541
+ const signingKeyForSystemMetadata: AssertionKey = {
542
+ alg: 'HS256', // Default algorithm, can be configured if needed
543
+ key: new Uint8Array(cfg.keyForEncryption.unwrappedKeyBinary.asArrayBuffer()),
544
+ };
545
+ signedAssertions.push(
546
+ await assertions.CreateAssertion(
547
+ aggregateHash,
548
+ {
549
+ ...systemMetadataConfigBase, // Spread the properties from the base config
550
+ signingKey: signingKeyForSystemMetadata, // Add the signing key
551
+ },
552
+ cfg.tdfSpecVersion // Pass the TDF spec version
553
+ )
554
+ );
555
+ }
532
556
  if (cfg.assertionConfigs && cfg.assertionConfigs.length > 0) {
533
557
  await Promise.all(
534
558
  cfg.assertionConfigs.map(async (assertionConfig) => {
@@ -3,7 +3,8 @@ import { InvalidFileError } from '../../../src/errors.js';
3
3
 
4
4
  export function unwrapHtml(htmlPayload: Uint8Array): Uint8Array {
5
5
  const html = new TextDecoder().decode(htmlPayload);
6
- const payloadRe = /<input id=['"]?data-input['"]?[^>]*?value=['"]?([a-zA-Z0-9+/=]+)['"]?/;
6
+ const payloadRe =
7
+ /<input\s+[^>]*id=(?:['"]?)data-input(?:['"]?)[^>]*value=(?:['"]?)([a-zA-Z0-9+/=\-_]+)(?:['"]?)/;
7
8
  const reResult = payloadRe.exec(html);
8
9
  if (!reResult) {
9
10
  throw new InvalidFileError('Payload is missing');