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

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 (217) hide show
  1. package/dist/cjs/src/access/access-fetch.js +2 -1
  2. package/dist/cjs/src/access/access-rpc.js +11 -5
  3. package/dist/cjs/src/access/constants.js +6 -0
  4. package/dist/cjs/src/access.js +39 -4
  5. package/dist/cjs/src/auth/oidc-clientcredentials-provider.js +4 -2
  6. package/dist/cjs/src/auth/oidc-externaljwt-provider.js +5 -3
  7. package/dist/cjs/src/auth/oidc-refreshtoken-provider.js +19 -3
  8. package/dist/cjs/src/auth/oidc.js +9 -8
  9. package/dist/cjs/src/auth/providers.js +7 -1
  10. package/dist/cjs/src/index.js +4 -2
  11. package/dist/cjs/src/nanoclients.js +4 -4
  12. package/dist/cjs/src/nanotdf/Client.js +10 -6
  13. package/dist/cjs/src/opentdf.js +103 -13
  14. package/dist/cjs/src/platform/authorization/v2/authorization_pb.js +112 -0
  15. package/dist/cjs/src/platform/buf/validate/validate_pb.js +114 -170
  16. package/dist/cjs/src/platform/common/common_pb.js +16 -5
  17. package/dist/cjs/src/platform/entity/entity_pb.js +51 -0
  18. package/dist/cjs/src/platform/entityresolution/entity_resolution_pb.js +1 -1
  19. package/dist/cjs/src/platform/entityresolution/v2/entity_resolution_pb.js +49 -0
  20. package/dist/cjs/src/platform/google/api/annotations_pb.js +1 -1
  21. package/dist/cjs/src/platform/google/api/http_pb.js +3 -3
  22. package/dist/cjs/src/platform/kas/kas_pb.js +2 -2
  23. package/dist/cjs/src/platform/policy/attributes/attributes_pb.js +12 -2
  24. package/dist/cjs/src/platform/policy/kasregistry/key_access_server_registry_pb.js +57 -4
  25. package/dist/cjs/src/platform/policy/keymanagement/key_management_pb.js +2 -2
  26. package/dist/cjs/src/platform/policy/namespaces/namespaces_pb.js +31 -4
  27. package/dist/cjs/src/platform/policy/objects_pb.js +116 -42
  28. package/dist/cjs/src/platform/policy/obligations/obligations_pb.js +159 -0
  29. package/dist/cjs/src/platform/policy/registeredresources/registered_resources_pb.js +20 -15
  30. package/dist/cjs/src/platform/policy/resourcemapping/resource_mapping_pb.js +2 -3
  31. package/dist/cjs/src/platform/policy/selectors_pb.js +1 -1
  32. package/dist/cjs/src/platform/policy/subjectmapping/subject_mapping_pb.js +2 -3
  33. package/dist/cjs/src/platform/policy/unsafe/unsafe_pb.js +2 -4
  34. package/dist/cjs/src/platform.js +20 -3
  35. package/dist/cjs/src/policy/api.js +27 -7
  36. package/dist/cjs/src/policy/granter.js +75 -48
  37. package/dist/cjs/src/seekable.js +32 -1
  38. package/dist/cjs/src/utils.js +85 -3
  39. package/dist/cjs/tdf3/src/assertions.js +39 -2
  40. package/dist/cjs/tdf3/src/client/DecoratedReadableStream.js +8 -1
  41. package/dist/cjs/tdf3/src/client/builders.js +13 -1
  42. package/dist/cjs/tdf3/src/client/index.js +213 -54
  43. package/dist/cjs/tdf3/src/client/validation.js +3 -3
  44. package/dist/cjs/tdf3/src/tdf.js +42 -9
  45. package/dist/cjs/tdf3/src/utils/unwrap.js +2 -2
  46. package/dist/types/src/access/access-fetch.d.ts +1 -0
  47. package/dist/types/src/access/access-fetch.d.ts.map +1 -1
  48. package/dist/types/src/access/access-rpc.d.ts +2 -1
  49. package/dist/types/src/access/access-rpc.d.ts.map +1 -1
  50. package/dist/types/src/access/constants.d.ts +3 -0
  51. package/dist/types/src/access/constants.d.ts.map +1 -0
  52. package/dist/types/src/access.d.ts +30 -1
  53. package/dist/types/src/access.d.ts.map +1 -1
  54. package/dist/types/src/auth/oidc-clientcredentials-provider.d.ts +1 -1
  55. package/dist/types/src/auth/oidc-clientcredentials-provider.d.ts.map +1 -1
  56. package/dist/types/src/auth/oidc-externaljwt-provider.d.ts +1 -1
  57. package/dist/types/src/auth/oidc-externaljwt-provider.d.ts.map +1 -1
  58. package/dist/types/src/auth/oidc-refreshtoken-provider.d.ts +15 -1
  59. package/dist/types/src/auth/oidc-refreshtoken-provider.d.ts.map +1 -1
  60. package/dist/types/src/auth/oidc.d.ts +4 -0
  61. package/dist/types/src/auth/oidc.d.ts.map +1 -1
  62. package/dist/types/src/auth/providers.d.ts.map +1 -1
  63. package/dist/types/src/index.d.ts +1 -0
  64. package/dist/types/src/index.d.ts.map +1 -1
  65. package/dist/types/src/nanotdf/Client.d.ts +8 -1
  66. package/dist/types/src/nanotdf/Client.d.ts.map +1 -1
  67. package/dist/types/src/opentdf.d.ts +137 -6
  68. package/dist/types/src/opentdf.d.ts.map +1 -1
  69. package/dist/types/src/platform/authorization/v2/authorization_pb.d.ts +439 -0
  70. package/dist/types/src/platform/authorization/v2/authorization_pb.d.ts.map +1 -0
  71. package/dist/types/src/platform/buf/validate/validate_pb.d.ts +495 -370
  72. package/dist/types/src/platform/buf/validate/validate_pb.d.ts.map +1 -1
  73. package/dist/types/src/platform/common/common_pb.d.ts +36 -0
  74. package/dist/types/src/platform/common/common_pb.d.ts.map +1 -1
  75. package/dist/types/src/platform/entity/entity_pb.d.ts +130 -0
  76. package/dist/types/src/platform/entity/entity_pb.d.ts.map +1 -0
  77. package/dist/types/src/platform/entityresolution/entity_resolution_pb.d.ts +4 -0
  78. package/dist/types/src/platform/entityresolution/entity_resolution_pb.d.ts.map +1 -1
  79. package/dist/types/src/platform/entityresolution/v2/entity_resolution_pb.d.ts +136 -0
  80. package/dist/types/src/platform/entityresolution/v2/entity_resolution_pb.d.ts.map +1 -0
  81. package/dist/types/src/platform/google/api/http_pb.d.ts.map +1 -1
  82. package/dist/types/src/platform/kas/kas_pb.d.ts +5 -0
  83. package/dist/types/src/platform/kas/kas_pb.d.ts.map +1 -1
  84. package/dist/types/src/platform/policy/attributes/attributes_pb.d.ts +44 -13
  85. package/dist/types/src/platform/policy/attributes/attributes_pb.d.ts.map +1 -1
  86. package/dist/types/src/platform/policy/kasregistry/key_access_server_registry_pb.d.ts +329 -24
  87. package/dist/types/src/platform/policy/kasregistry/key_access_server_registry_pb.d.ts.map +1 -1
  88. package/dist/types/src/platform/policy/keymanagement/key_management_pb.d.ts +20 -1
  89. package/dist/types/src/platform/policy/keymanagement/key_management_pb.d.ts.map +1 -1
  90. package/dist/types/src/platform/policy/namespaces/namespaces_pb.d.ts +143 -5
  91. package/dist/types/src/platform/policy/namespaces/namespaces_pb.d.ts.map +1 -1
  92. package/dist/types/src/platform/policy/objects_pb.d.ts +382 -33
  93. package/dist/types/src/platform/policy/objects_pb.d.ts.map +1 -1
  94. package/dist/types/src/platform/policy/obligations/obligations_pb.d.ts +670 -0
  95. package/dist/types/src/platform/policy/obligations/obligations_pb.d.ts.map +1 -0
  96. package/dist/types/src/platform/policy/registeredresources/registered_resources_pb.d.ts +67 -0
  97. package/dist/types/src/platform/policy/registeredresources/registered_resources_pb.d.ts.map +1 -1
  98. package/dist/types/src/platform/policy/resourcemapping/resource_mapping_pb.d.ts.map +1 -1
  99. package/dist/types/src/platform/policy/selectors_pb.d.ts +18 -0
  100. package/dist/types/src/platform/policy/selectors_pb.d.ts.map +1 -1
  101. package/dist/types/src/platform/policy/subjectmapping/subject_mapping_pb.d.ts.map +1 -1
  102. package/dist/types/src/platform/policy/unsafe/unsafe_pb.d.ts +18 -4
  103. package/dist/types/src/platform/policy/unsafe/unsafe_pb.d.ts.map +1 -1
  104. package/dist/types/src/platform.d.ts +21 -0
  105. package/dist/types/src/platform.d.ts.map +1 -1
  106. package/dist/types/src/policy/api.d.ts +2 -0
  107. package/dist/types/src/policy/api.d.ts.map +1 -1
  108. package/dist/types/src/policy/granter.d.ts +11 -6
  109. package/dist/types/src/policy/granter.d.ts.map +1 -1
  110. package/dist/types/src/seekable.d.ts +31 -0
  111. package/dist/types/src/seekable.d.ts.map +1 -1
  112. package/dist/types/src/utils.d.ts +61 -2
  113. package/dist/types/src/utils.d.ts.map +1 -1
  114. package/dist/types/tdf3/src/assertions.d.ts +4 -0
  115. package/dist/types/tdf3/src/assertions.d.ts.map +1 -1
  116. package/dist/types/tdf3/src/client/DecoratedReadableStream.d.ts +6 -0
  117. package/dist/types/tdf3/src/client/DecoratedReadableStream.d.ts.map +1 -1
  118. package/dist/types/tdf3/src/client/builders.d.ts +14 -0
  119. package/dist/types/tdf3/src/client/builders.d.ts.map +1 -1
  120. package/dist/types/tdf3/src/client/index.d.ts +25 -4
  121. package/dist/types/tdf3/src/client/index.d.ts.map +1 -1
  122. package/dist/types/tdf3/src/client/validation.d.ts +3 -3
  123. package/dist/types/tdf3/src/client/validation.d.ts.map +1 -1
  124. package/dist/types/tdf3/src/tdf.d.ts +3 -1
  125. package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
  126. package/dist/types/tdf3/src/utils/unwrap.d.ts.map +1 -1
  127. package/dist/web/src/access/access-fetch.js +2 -1
  128. package/dist/web/src/access/access-rpc.js +11 -5
  129. package/dist/web/src/access/constants.js +3 -0
  130. package/dist/web/src/access.js +37 -3
  131. package/dist/web/src/auth/oidc-clientcredentials-provider.js +4 -2
  132. package/dist/web/src/auth/oidc-externaljwt-provider.js +5 -3
  133. package/dist/web/src/auth/oidc-refreshtoken-provider.js +19 -3
  134. package/dist/web/src/auth/oidc.js +9 -8
  135. package/dist/web/src/auth/providers.js +7 -1
  136. package/dist/web/src/index.js +2 -1
  137. package/dist/web/src/nanoclients.js +4 -4
  138. package/dist/web/src/nanotdf/Client.js +11 -7
  139. package/dist/web/src/opentdf.js +103 -13
  140. package/dist/web/src/platform/authorization/v2/authorization_pb.js +109 -0
  141. package/dist/web/src/platform/buf/validate/validate_pb.js +113 -169
  142. package/dist/web/src/platform/common/common_pb.js +15 -4
  143. package/dist/web/src/platform/entity/entity_pb.js +48 -0
  144. package/dist/web/src/platform/entityresolution/entity_resolution_pb.js +1 -1
  145. package/dist/web/src/platform/entityresolution/v2/entity_resolution_pb.js +46 -0
  146. package/dist/web/src/platform/google/api/annotations_pb.js +1 -1
  147. package/dist/web/src/platform/google/api/http_pb.js +3 -3
  148. package/dist/web/src/platform/kas/kas_pb.js +2 -2
  149. package/dist/web/src/platform/policy/attributes/attributes_pb.js +12 -2
  150. package/dist/web/src/platform/policy/kasregistry/key_access_server_registry_pb.js +55 -3
  151. package/dist/web/src/platform/policy/keymanagement/key_management_pb.js +2 -2
  152. package/dist/web/src/platform/policy/namespaces/namespaces_pb.js +30 -3
  153. package/dist/web/src/platform/policy/objects_pb.js +114 -41
  154. package/dist/web/src/platform/policy/obligations/obligations_pb.js +156 -0
  155. package/dist/web/src/platform/policy/registeredresources/registered_resources_pb.js +19 -14
  156. package/dist/web/src/platform/policy/resourcemapping/resource_mapping_pb.js +2 -3
  157. package/dist/web/src/platform/policy/selectors_pb.js +1 -1
  158. package/dist/web/src/platform/policy/subjectmapping/subject_mapping_pb.js +2 -3
  159. package/dist/web/src/platform/policy/unsafe/unsafe_pb.js +2 -4
  160. package/dist/web/src/platform.js +20 -3
  161. package/dist/web/src/policy/api.js +26 -7
  162. package/dist/web/src/policy/granter.js +75 -48
  163. package/dist/web/src/seekable.js +32 -1
  164. package/dist/web/src/utils.js +84 -3
  165. package/dist/web/tdf3/src/assertions.js +38 -2
  166. package/dist/web/tdf3/src/client/DecoratedReadableStream.js +8 -1
  167. package/dist/web/tdf3/src/client/builders.js +13 -1
  168. package/dist/web/tdf3/src/client/index.js +215 -57
  169. package/dist/web/tdf3/src/client/validation.js +3 -3
  170. package/dist/web/tdf3/src/tdf.js +42 -9
  171. package/dist/web/tdf3/src/utils/unwrap.js +2 -2
  172. package/package.json +7 -5
  173. package/src/access/access-fetch.ts +1 -0
  174. package/src/access/access-rpc.ts +13 -4
  175. package/src/access/constants.ts +2 -0
  176. package/src/access.ts +54 -2
  177. package/src/auth/oidc-clientcredentials-provider.ts +4 -0
  178. package/src/auth/oidc-externaljwt-provider.ts +5 -1
  179. package/src/auth/oidc-refreshtoken-provider.ts +19 -1
  180. package/src/auth/oidc.ts +12 -7
  181. package/src/auth/providers.ts +6 -0
  182. package/src/index.ts +1 -0
  183. package/src/nanoclients.ts +3 -3
  184. package/src/nanotdf/Client.ts +28 -6
  185. package/src/opentdf.ts +206 -73
  186. package/src/platform/authorization/v2/authorization_pb.ts +503 -0
  187. package/src/platform/buf/validate/validate_pb.ts +529 -401
  188. package/src/platform/common/common_pb.ts +48 -3
  189. package/src/platform/entity/entity_pb.ts +154 -0
  190. package/src/platform/entityresolution/entity_resolution_pb.ts +4 -0
  191. package/src/platform/entityresolution/v2/entity_resolution_pb.ts +170 -0
  192. package/src/platform/google/api/annotations_pb.ts +1 -1
  193. package/src/platform/google/api/http_pb.ts +2 -2
  194. package/src/platform/kas/kas_pb.ts +6 -1
  195. package/src/platform/policy/attributes/attributes_pb.ts +46 -16
  196. package/src/platform/policy/kasregistry/key_access_server_registry_pb.ts +371 -27
  197. package/src/platform/policy/keymanagement/key_management_pb.ts +24 -2
  198. package/src/platform/policy/namespaces/namespaces_pb.ts +163 -7
  199. package/src/platform/policy/objects_pb.ts +474 -59
  200. package/src/platform/policy/obligations/obligations_pb.ts +788 -0
  201. package/src/platform/policy/registeredresources/registered_resources_pb.ts +80 -13
  202. package/src/platform/policy/resourcemapping/resource_mapping_pb.ts +1 -2
  203. package/src/platform/policy/selectors_pb.ts +18 -0
  204. package/src/platform/policy/subjectmapping/subject_mapping_pb.ts +1 -2
  205. package/src/platform/policy/unsafe/unsafe_pb.ts +21 -6
  206. package/src/platform.ts +29 -5
  207. package/src/policy/api.ts +37 -6
  208. package/src/policy/granter.ts +82 -56
  209. package/src/seekable.ts +31 -0
  210. package/src/utils.ts +88 -2
  211. package/tdf3/src/assertions.ts +52 -1
  212. package/tdf3/src/client/DecoratedReadableStream.ts +9 -0
  213. package/tdf3/src/client/builders.ts +16 -0
  214. package/tdf3/src/client/index.ts +309 -73
  215. package/tdf3/src/client/validation.ts +2 -2
  216. package/tdf3/src/tdf.ts +53 -9
  217. 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,
@@ -142,6 +143,12 @@ export interface ClientConfig {
142
143
  * Defaults to `[]`.
143
144
  */
144
145
  allowedKases?: string[];
146
+ /**
147
+ * List of obligation value FQNs in platform policy that can be fulfilled
148
+ * by the PEP handling this client (i.e. 'https://example.com/obl/drm/value/mask').
149
+ * Defaults to '[]'.
150
+ */
151
+ fulfillableObligationFQNs?: string[];
145
152
  // Platform URL to use to lookup allowed KASes when allowedKases is empty
146
153
  platformUrl?: string;
147
154
  ignoreAllowList?: boolean;
@@ -227,6 +234,95 @@ function asPolicy(scope: Scope): Policy {
227
234
  };
228
235
  }
229
236
 
237
+ type KasKeyInfoCache = [
238
+ ...Parameters<typeof fetchKasPublicKey>,
239
+ keyInfoPromise: ReturnType<typeof fetchKasPublicKey>,
240
+ ][];
241
+
242
+ export function findEntryInCache(
243
+ cache: KasKeyInfoCache,
244
+ ...params: Parameters<typeof fetchKasPublicKey>
245
+ ) {
246
+ const [wantedKas, wantedAlgorithm, wantedKid] = params;
247
+ for (const item of cache) {
248
+ const [itemKas, itemAlgorithm, itemKid, itemKeyInfoPromise] = item;
249
+ if (itemKas !== wantedKas) {
250
+ continue;
251
+ }
252
+ // This makes undefined only match with undefined (base key).
253
+ // We could potentially consider any key a match if undefined algorithm?
254
+ if (itemAlgorithm !== wantedAlgorithm) {
255
+ continue;
256
+ }
257
+ if (wantedKid && itemKid !== wantedKid) {
258
+ continue;
259
+ }
260
+ return itemKeyInfoPromise;
261
+ }
262
+ return null;
263
+ }
264
+
265
+ const fetchKasKeyWithCache = (
266
+ cache: KasKeyInfoCache,
267
+ ...params: Parameters<typeof fetchKasPublicKey>
268
+ ): ReturnType<typeof fetchKasPublicKey> => {
269
+ const cachedEntry = findEntryInCache(cache, ...params);
270
+ if (cachedEntry !== null) {
271
+ return cachedEntry;
272
+ }
273
+ const keyInfoPromise = fetchKasPublicKey(...params);
274
+ cache.push([...params, keyInfoPromise]);
275
+ return keyInfoPromise;
276
+ };
277
+
278
+ function algorithmEnumValueToString(algorithmEnumValue: Algorithm) {
279
+ switch (algorithmEnumValue) {
280
+ case Algorithm.RSA_2048:
281
+ return 'rsa:2048';
282
+ case Algorithm.RSA_4096:
283
+ return 'rsa:4096';
284
+ case Algorithm.EC_P256:
285
+ return 'ec:secp256r1';
286
+ case Algorithm.EC_P384:
287
+ return 'ec:secp384r1';
288
+ case Algorithm.EC_P521:
289
+ return 'ec:secp521r1';
290
+ case Algorithm.UNSPECIFIED:
291
+ // Not entirely sure undefined is correct here, but since we need to generate a key for our cache
292
+ // synchonously, it seems to be the best approach for now.
293
+ return undefined;
294
+ default:
295
+ return undefined;
296
+ }
297
+ }
298
+
299
+ const putKasKeyIntoCache = (
300
+ cache: KasKeyInfoCache,
301
+ kasKey: Omit<SimpleKasKey, 'publicKey'> & {
302
+ publicKey: Exclude<SimpleKasKey['publicKey'], undefined>;
303
+ }
304
+ ): ReturnType<typeof fetchKasPublicKey> => {
305
+ const algorithmString = algorithmEnumValueToString(kasKey.publicKey.algorithm);
306
+ const cachedEntry = findEntryInCache(cache, kasKey.kasUri, algorithmString, kasKey.publicKey.kid);
307
+ if (cachedEntry) {
308
+ return cachedEntry;
309
+ }
310
+ const keyInfoPromise = (async function () {
311
+ const keyPromise = pemToCryptoPublicKey(kasKey.publicKey.pem);
312
+ const key = await keyPromise;
313
+ const algorithm = keyAlgorithmToPublicKeyAlgorithm(key);
314
+ return {
315
+ algorithm: algorithm,
316
+ key: keyPromise,
317
+ kid: kasKey.publicKey.kid,
318
+ publicKey: kasKey.publicKey.pem,
319
+ url: kasKey.kasUri,
320
+ };
321
+ })();
322
+ cache.push([kasKey.kasUri, algorithmString, kasKey.publicKey.kid, keyInfoPromise]);
323
+ return keyInfoPromise;
324
+ };
325
+
230
326
  export class Client {
231
327
  readonly cryptoService: CryptoService;
232
328
 
@@ -247,12 +343,19 @@ export class Client {
247
343
  */
248
344
  readonly allowedKases?: OriginAllowList;
249
345
 
346
+ /**
347
+ * List of obligation value FQNs in platform policy that can be fulfilled
348
+ * by the PEP utilizing this client (i.e. 'https://example.com/obl/drm/value/mask').
349
+ * Defaults to '[]'. Currently set per Client and not per TDF.
350
+ */
351
+ readonly fulfillableObligationFQNs: string[];
352
+
250
353
  /**
251
354
  * URL of the platform, required to fetch list of allowed KASes when allowedKases is empty
252
355
  */
253
356
  readonly platformUrl?: string;
254
357
 
255
- readonly kasKeys: Record<string, Promise<KasPublicKeyInfo>[]> = {};
358
+ readonly kasKeyInfoCache: KasKeyInfoCache = [];
256
359
 
257
360
  readonly easEndpoint?: string;
258
361
 
@@ -327,6 +430,14 @@ export class Client {
327
430
  }
328
431
  }
329
432
 
433
+ this.fulfillableObligationFQNs = config.fulfillableObligationFQNs?.length
434
+ ? config.fulfillableObligationFQNs
435
+ : [];
436
+
437
+ if (clientConfig.easEndpoint) {
438
+ this.easEndpoint = clientConfig.easEndpoint;
439
+ }
440
+
330
441
  this.authProvider = config.authProvider;
331
442
  this.clientConfig = clientConfig;
332
443
 
@@ -360,11 +471,13 @@ export class Client {
360
471
  cryptoService: this.cryptoService,
361
472
  dpopKeys: clientConfig.dpopKeys,
362
473
  });
363
- if (clientConfig.kasPublicKey) {
364
- this.kasKeys[this.kasEndpoint] = [
365
- resolveKasInfo(clientConfig.kasPublicKey, this.kasEndpoint),
366
- ];
367
- }
474
+ }
475
+
476
+ /** Necessary only for testing. A dependency-injection approach should be preferred, but that is difficult currently */
477
+ _doFetchKasKeyWithCache(
478
+ ...params: Parameters<typeof fetchKasKeyWithCache>
479
+ ): ReturnType<typeof fetchKasKeyWithCache> {
480
+ return fetchKasKeyWithCache(...params);
368
481
  }
369
482
 
370
483
  /**
@@ -396,62 +509,165 @@ export class Client {
396
509
  mimeType = 'unknown',
397
510
  windowSize = DEFAULT_SEGMENT_SIZE,
398
511
  keyMiddleware = defaultKeyMiddleware,
512
+ splitPlan: preconfiguredSplitPlan,
399
513
  streamMiddleware = async (stream: DecoratedReadableStream) => stream,
400
514
  tdfSpecVersion,
401
515
  wrappingKeyAlgorithm,
402
516
  } = opts;
403
517
  const scope = opts.scope ?? { attributes: [], dissem: [] };
404
518
 
519
+ for (const attributeValue of scope.attributeValues || []) {
520
+ for (const kasKey of attributeValue.kasKeys) {
521
+ if (kasKey.publicKey !== undefined) {
522
+ await putKasKeyIntoCache(this.kasKeyInfoCache, {
523
+ // TypeScript is silly and cannot infer that publicKey is not undefined, without re-referencing it like this, even though we checked already.
524
+ ...kasKey,
525
+ publicKey: kasKey.publicKey,
526
+ });
527
+ }
528
+ }
529
+ }
530
+
405
531
  const policyObject = asPolicy(scope);
406
532
  validatePolicyObject(policyObject);
407
533
 
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');
534
+ const splitPlan: {
535
+ kas: string;
536
+ kid?: string;
537
+ pem: string;
538
+ sid?: string;
539
+ }[] = [];
540
+ if (preconfiguredSplitPlan) {
541
+ for (const preconfiguredSplit of preconfiguredSplitPlan) {
542
+ const kasPublicKeyInfo = await this._doFetchKasKeyWithCache(
543
+ this.kasKeyInfoCache,
544
+ preconfiguredSplit.kas,
545
+ wrappingKeyAlgorithm,
546
+ preconfiguredSplit.kid
547
+ );
548
+ splitPlan.push({
549
+ kas: kasPublicKeyInfo.url,
550
+ kid: kasPublicKeyInfo.kid,
551
+ pem: kasPublicKeyInfo.publicKey,
552
+ sid: preconfiguredSplit.sid,
553
+ });
554
+ }
555
+ } else if (autoconfigure) {
556
+ const attributeValues = scope.attributeValues ?? [];
557
+ if (!scope.attributes) {
558
+ scope.attributes = attributeValues.map(({ fqn }) => fqn);
559
+ }
560
+ const attributeFQNs = (scope.attributes ?? []).map((attribute) =>
561
+ typeof attribute === 'string' ? attribute : attribute.attribute
562
+ );
563
+ const fqnsWithoutValues = attributeFQNs.filter((fqn) =>
564
+ attributeValues.every((av) => av.fqn !== fqn)
565
+ );
566
+
567
+ if (fqnsWithoutValues.length) {
568
+ // Hydrate missing avs from policy endpoint given the fqns
569
+ if (!this.platformUrl) {
570
+ throw new ConfigurationError('platformUrl not set in TDF3 Client constructor');
421
571
  }
422
- avs = await attributeFQNsAsValues(
423
- this.policyEndpoint,
572
+ const fetchedFQNValues = await attributeFQNsAsValues(
573
+ this.platformUrl,
424
574
  this.authProvider as AuthProvider,
425
- ...fqns
575
+ ...fqnsWithoutValues
426
576
  );
427
- } else if (scope.attributeValues) {
428
- avs = scope.attributeValues;
429
- if (!scope.attributes) {
430
- scope.attributes = avs.map(({ fqn }) => fqn);
431
- }
577
+ fetchedFQNValues.forEach((fetchedValue) => {
578
+ attributeValues.push(fetchedValue);
579
+ });
432
580
  }
433
- if (
434
- avs.length != (scope.attributes?.length || 0) ||
435
- !avs.map(({ fqn }) => fqn).every((a) => fqns.indexOf(a) >= 0)
436
- ) {
581
+
582
+ const hasAllFQNs = attributeFQNs.every((fqn) =>
583
+ attributeValues.some((attributeValue) => attributeValue.fqn === fqn)
584
+ );
585
+ if (attributeFQNs.length != attributeValues.length || !hasAllFQNs) {
437
586
  throw new ConfigurationError(
438
- `Attribute mismatch between [${fqns}] and explicit values ${JSON.stringify(
439
- avs.map(({ fqn }) => fqn)
587
+ `Attribute mismatch between [${attributeFQNs}] and explicit values ${JSON.stringify(
588
+ attributeValues.map(({ fqn }) => fqn)
440
589
  )}`
441
590
  );
442
591
  }
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));
592
+
593
+ for (const attributeValue of attributeValues) {
594
+ for (const kasKey of attributeValue.kasKeys) {
595
+ if (kasKey.publicKey !== undefined) {
596
+ await putKasKeyIntoCache(this.kasKeyInfoCache, {
597
+ // TypeScript is silly and cannot infer that publicKey is not undefined, without re-referencing it like this, even though we checked already.
598
+ ...kasKey,
599
+ publicKey: kasKey.publicKey,
600
+ });
451
601
  }
452
602
  }
453
- return { kas: kas.uri, sid };
454
- });
603
+ }
604
+
605
+ const detailedPlan = plan(attributeValues);
606
+ for (const item of detailedPlan) {
607
+ if ('kid' in item.kas) {
608
+ const pemAlgorithm = await algorithmFromPEM(item.kas.pem);
609
+ const kasPublicKeyInfo = await this._doFetchKasKeyWithCache(
610
+ this.kasKeyInfoCache,
611
+ item.kas.kasUri,
612
+ pemAlgorithm,
613
+ item.kas.kid
614
+ );
615
+ splitPlan.push({
616
+ kas: kasPublicKeyInfo.url,
617
+ kid: kasPublicKeyInfo.kid,
618
+ pem: kasPublicKeyInfo.publicKey,
619
+ sid: item.sid,
620
+ });
621
+ continue;
622
+ }
623
+
624
+ if (!item.kas.publicKey) {
625
+ const kasPublicKeyInfo = await this._doFetchKasKeyWithCache(
626
+ this.kasKeyInfoCache,
627
+ item.kas.uri,
628
+ wrappingKeyAlgorithm,
629
+ undefined
630
+ );
631
+ splitPlan.push({
632
+ kas: kasPublicKeyInfo.url,
633
+ kid: kasPublicKeyInfo.kid,
634
+ pem: kasPublicKeyInfo.publicKey,
635
+ sid: item.sid,
636
+ });
637
+ continue;
638
+ }
639
+
640
+ switch (item.kas.publicKey.publicKey.case) {
641
+ case 'remote':
642
+ const kasPublicKeyInfo = await this._doFetchKasKeyWithCache(
643
+ this.kasKeyInfoCache,
644
+ item.kas.publicKey.publicKey.value,
645
+ wrappingKeyAlgorithm,
646
+ undefined
647
+ );
648
+ splitPlan.push({
649
+ kas: kasPublicKeyInfo.url,
650
+ kid: kasPublicKeyInfo.kid,
651
+ pem: kasPublicKeyInfo.publicKey,
652
+ sid: item.sid,
653
+ });
654
+ break;
655
+
656
+ case 'cached':
657
+ for (const cachedPublicKey of item.kas.publicKey.publicKey.value.keys) {
658
+ splitPlan.push({
659
+ kas: item.kas.uri,
660
+ kid: cachedPublicKey.kid,
661
+ pem: cachedPublicKey.pem,
662
+ sid: item.sid,
663
+ });
664
+ }
665
+ break;
666
+
667
+ default:
668
+ throw new Error(`Unknown public key type: ${item.kas.publicKey.publicKey.case}`);
669
+ }
670
+ }
455
671
  }
456
672
 
457
673
  // TODO: Refactor underlying builder to remove some of this unnecessary config.
@@ -462,37 +678,47 @@ export class Client {
462
678
  ? maxByteLimit
463
679
  : opts.byteLimit;
464
680
  const encryptionInformation = new SplitKey(new AesGcmCipher(this.cryptoService));
465
- const splits: SplitStep[] = splitPlan?.length
466
- ? splitPlan
467
- : [{ kas: opts.defaultKASEndpoint ?? this.kasEndpoint }];
681
+ if (splitPlan.length === 0) {
682
+ const kasPublicKeyInfo = await this._doFetchKasKeyWithCache(
683
+ this.kasKeyInfoCache,
684
+ opts.defaultKASEndpoint ?? this.kasEndpoint,
685
+ wrappingKeyAlgorithm,
686
+ undefined
687
+ );
688
+ splitPlan.push({
689
+ kas: kasPublicKeyInfo.url,
690
+ kid: kasPublicKeyInfo.kid,
691
+ pem: kasPublicKeyInfo.publicKey,
692
+ });
693
+ }
468
694
  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) {
695
+ splitPlan.map(async ({ kas, kid, pem, sid }) => {
696
+ const algorithm = await algorithmFromPEM(pem);
697
+ if (algorithm !== wrappingKeyAlgorithm) {
475
698
  console.warn(
476
- `Mismatched wrapping key algorithm: [${kasPublicKey.algorithm}] is not requested type, [${wrappingKeyAlgorithm}]`
699
+ `Mismatched wrapping key algorithm: [${algorithm}] is not requested type, [${wrappingKeyAlgorithm}]`
477
700
  );
478
701
  }
479
702
  let type: KeyAccessType;
480
- switch (kasPublicKey.algorithm) {
703
+ switch (algorithm) {
481
704
  case 'rsa:2048':
705
+ case 'rsa:4096':
482
706
  type = 'wrapped';
483
707
  break;
708
+ case 'ec:secp384r1':
709
+ case 'ec:secp521r1':
484
710
  case 'ec:secp256r1':
485
711
  type = 'ec-wrapped';
486
712
  break;
487
713
  default:
488
- throw new ConfigurationError(`Unsupported algorithm ${kasPublicKey.algorithm}`);
714
+ throw new ConfigurationError(`Unsupported algorithm ${algorithm}`);
489
715
  }
490
716
  return buildKeyAccess({
491
- alg: kasPublicKey.algorithm,
717
+ alg: algorithm,
492
718
  type,
493
- url: kasPublicKey.url,
494
- kid: kasPublicKey.kid,
495
- publicKey: kasPublicKey.publicKey,
719
+ url: kas,
720
+ kid: kid,
721
+ publicKey: pem,
496
722
  metadata,
497
723
  sid,
498
724
  });
@@ -516,6 +742,7 @@ export class Client {
516
742
  keyForEncryption,
517
743
  keyForManifest,
518
744
  assertionConfigs: opts.assertionConfigs,
745
+ systemMetadataAssertion: opts.systemMetadataAssertion,
519
746
  tdfSpecVersion,
520
747
  };
521
748
 
@@ -530,6 +757,7 @@ export class Client {
530
757
  * @param params.source A data stream object, one of remote, stream, buffer, etc. types.
531
758
  * @param params.eo Optional entity object (legacy AuthZ)
532
759
  * @param params.assertionVerificationKeys Optional verification keys for assertions.
760
+ * @param params.fulfillableObligationFQNs Optional fulfillable obligation value FQNs (overrides those on the Client)
533
761
  * @return a {@link https://nodejs.org/api/stream.html#stream_class_stream_readable|Readable} stream containing the decrypted plaintext.
534
762
  * @see DecryptParamsBuilder
535
763
  */
@@ -542,6 +770,7 @@ export class Client {
542
770
  noVerifyAssertions,
543
771
  concurrencyLimit = 1,
544
772
  wrappingKeyAlgorithm,
773
+ fulfillableObligationFQNs = [],
545
774
  }: DecryptParams): Promise<DecoratedReadableStream> {
546
775
  const dpopKeys = await this.dpopKeys;
547
776
  if (!this.authProvider) {
@@ -556,6 +785,12 @@ export class Client {
556
785
  throw new ConfigurationError('platformUrl is required when allowedKases is empty');
557
786
  }
558
787
 
788
+ const hasEmptyDecryptParamObligationsButGlobal =
789
+ !fulfillableObligationFQNs.length && this.fulfillableObligationFQNs.length;
790
+ if (hasEmptyDecryptParamObligationsButGlobal) {
791
+ fulfillableObligationFQNs = this.fulfillableObligationFQNs;
792
+ }
793
+
559
794
  // Await in order to catch any errors from this call.
560
795
  // TODO: Write error event to stream and don't await.
561
796
  return await (streamMiddleware as DecryptStreamMiddleware)(
@@ -572,6 +807,7 @@ export class Client {
572
807
  assertionVerificationKeys,
573
808
  noVerifyAssertions,
574
809
  wrappingKeyAlgorithm,
810
+ fulfillableObligations: fulfillableObligationFQNs,
575
811
  })
576
812
  );
577
813
  }
@@ -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})`;