@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.
- package/dist/cjs/src/access/access-fetch.js +2 -1
- package/dist/cjs/src/access/access-rpc.js +11 -5
- package/dist/cjs/src/access/constants.js +6 -0
- package/dist/cjs/src/access.js +39 -4
- package/dist/cjs/src/auth/oidc-clientcredentials-provider.js +4 -2
- package/dist/cjs/src/auth/oidc-externaljwt-provider.js +5 -3
- package/dist/cjs/src/auth/oidc-refreshtoken-provider.js +19 -3
- package/dist/cjs/src/auth/oidc.js +9 -8
- package/dist/cjs/src/auth/providers.js +7 -1
- package/dist/cjs/src/index.js +4 -2
- package/dist/cjs/src/nanoclients.js +4 -4
- package/dist/cjs/src/nanotdf/Client.js +10 -6
- package/dist/cjs/src/opentdf.js +103 -13
- package/dist/cjs/src/platform/authorization/v2/authorization_pb.js +112 -0
- package/dist/cjs/src/platform/buf/validate/validate_pb.js +114 -170
- package/dist/cjs/src/platform/common/common_pb.js +16 -5
- package/dist/cjs/src/platform/entity/entity_pb.js +51 -0
- package/dist/cjs/src/platform/entityresolution/entity_resolution_pb.js +1 -1
- package/dist/cjs/src/platform/entityresolution/v2/entity_resolution_pb.js +49 -0
- package/dist/cjs/src/platform/google/api/annotations_pb.js +1 -1
- package/dist/cjs/src/platform/google/api/http_pb.js +3 -3
- package/dist/cjs/src/platform/kas/kas_pb.js +2 -2
- package/dist/cjs/src/platform/policy/attributes/attributes_pb.js +12 -2
- package/dist/cjs/src/platform/policy/kasregistry/key_access_server_registry_pb.js +57 -4
- package/dist/cjs/src/platform/policy/keymanagement/key_management_pb.js +2 -2
- package/dist/cjs/src/platform/policy/namespaces/namespaces_pb.js +31 -4
- package/dist/cjs/src/platform/policy/objects_pb.js +116 -42
- package/dist/cjs/src/platform/policy/obligations/obligations_pb.js +159 -0
- package/dist/cjs/src/platform/policy/registeredresources/registered_resources_pb.js +20 -15
- package/dist/cjs/src/platform/policy/resourcemapping/resource_mapping_pb.js +2 -3
- package/dist/cjs/src/platform/policy/selectors_pb.js +1 -1
- package/dist/cjs/src/platform/policy/subjectmapping/subject_mapping_pb.js +2 -3
- package/dist/cjs/src/platform/policy/unsafe/unsafe_pb.js +2 -4
- package/dist/cjs/src/platform.js +20 -3
- package/dist/cjs/src/policy/api.js +27 -7
- package/dist/cjs/src/policy/granter.js +75 -48
- package/dist/cjs/src/seekable.js +32 -1
- package/dist/cjs/src/utils.js +85 -3
- package/dist/cjs/tdf3/src/assertions.js +39 -2
- package/dist/cjs/tdf3/src/client/DecoratedReadableStream.js +8 -1
- package/dist/cjs/tdf3/src/client/builders.js +13 -1
- package/dist/cjs/tdf3/src/client/index.js +213 -54
- package/dist/cjs/tdf3/src/client/validation.js +3 -3
- package/dist/cjs/tdf3/src/tdf.js +42 -9
- package/dist/cjs/tdf3/src/utils/unwrap.js +2 -2
- package/dist/types/src/access/access-fetch.d.ts +1 -0
- package/dist/types/src/access/access-fetch.d.ts.map +1 -1
- package/dist/types/src/access/access-rpc.d.ts +2 -1
- package/dist/types/src/access/access-rpc.d.ts.map +1 -1
- package/dist/types/src/access/constants.d.ts +3 -0
- package/dist/types/src/access/constants.d.ts.map +1 -0
- package/dist/types/src/access.d.ts +30 -1
- package/dist/types/src/access.d.ts.map +1 -1
- package/dist/types/src/auth/oidc-clientcredentials-provider.d.ts +1 -1
- package/dist/types/src/auth/oidc-clientcredentials-provider.d.ts.map +1 -1
- package/dist/types/src/auth/oidc-externaljwt-provider.d.ts +1 -1
- package/dist/types/src/auth/oidc-externaljwt-provider.d.ts.map +1 -1
- package/dist/types/src/auth/oidc-refreshtoken-provider.d.ts +15 -1
- package/dist/types/src/auth/oidc-refreshtoken-provider.d.ts.map +1 -1
- package/dist/types/src/auth/oidc.d.ts +4 -0
- package/dist/types/src/auth/oidc.d.ts.map +1 -1
- package/dist/types/src/auth/providers.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/nanotdf/Client.d.ts +8 -1
- package/dist/types/src/nanotdf/Client.d.ts.map +1 -1
- package/dist/types/src/opentdf.d.ts +137 -6
- package/dist/types/src/opentdf.d.ts.map +1 -1
- package/dist/types/src/platform/authorization/v2/authorization_pb.d.ts +439 -0
- package/dist/types/src/platform/authorization/v2/authorization_pb.d.ts.map +1 -0
- package/dist/types/src/platform/buf/validate/validate_pb.d.ts +495 -370
- package/dist/types/src/platform/buf/validate/validate_pb.d.ts.map +1 -1
- package/dist/types/src/platform/common/common_pb.d.ts +36 -0
- package/dist/types/src/platform/common/common_pb.d.ts.map +1 -1
- package/dist/types/src/platform/entity/entity_pb.d.ts +130 -0
- package/dist/types/src/platform/entity/entity_pb.d.ts.map +1 -0
- package/dist/types/src/platform/entityresolution/entity_resolution_pb.d.ts +4 -0
- package/dist/types/src/platform/entityresolution/entity_resolution_pb.d.ts.map +1 -1
- package/dist/types/src/platform/entityresolution/v2/entity_resolution_pb.d.ts +136 -0
- package/dist/types/src/platform/entityresolution/v2/entity_resolution_pb.d.ts.map +1 -0
- package/dist/types/src/platform/google/api/http_pb.d.ts.map +1 -1
- package/dist/types/src/platform/kas/kas_pb.d.ts +5 -0
- package/dist/types/src/platform/kas/kas_pb.d.ts.map +1 -1
- package/dist/types/src/platform/policy/attributes/attributes_pb.d.ts +44 -13
- package/dist/types/src/platform/policy/attributes/attributes_pb.d.ts.map +1 -1
- package/dist/types/src/platform/policy/kasregistry/key_access_server_registry_pb.d.ts +329 -24
- package/dist/types/src/platform/policy/kasregistry/key_access_server_registry_pb.d.ts.map +1 -1
- package/dist/types/src/platform/policy/keymanagement/key_management_pb.d.ts +20 -1
- package/dist/types/src/platform/policy/keymanagement/key_management_pb.d.ts.map +1 -1
- package/dist/types/src/platform/policy/namespaces/namespaces_pb.d.ts +143 -5
- package/dist/types/src/platform/policy/namespaces/namespaces_pb.d.ts.map +1 -1
- package/dist/types/src/platform/policy/objects_pb.d.ts +382 -33
- package/dist/types/src/platform/policy/objects_pb.d.ts.map +1 -1
- package/dist/types/src/platform/policy/obligations/obligations_pb.d.ts +670 -0
- package/dist/types/src/platform/policy/obligations/obligations_pb.d.ts.map +1 -0
- package/dist/types/src/platform/policy/registeredresources/registered_resources_pb.d.ts +67 -0
- package/dist/types/src/platform/policy/registeredresources/registered_resources_pb.d.ts.map +1 -1
- package/dist/types/src/platform/policy/resourcemapping/resource_mapping_pb.d.ts.map +1 -1
- package/dist/types/src/platform/policy/selectors_pb.d.ts +18 -0
- package/dist/types/src/platform/policy/selectors_pb.d.ts.map +1 -1
- package/dist/types/src/platform/policy/subjectmapping/subject_mapping_pb.d.ts.map +1 -1
- package/dist/types/src/platform/policy/unsafe/unsafe_pb.d.ts +18 -4
- package/dist/types/src/platform/policy/unsafe/unsafe_pb.d.ts.map +1 -1
- package/dist/types/src/platform.d.ts +21 -0
- package/dist/types/src/platform.d.ts.map +1 -1
- package/dist/types/src/policy/api.d.ts +2 -0
- package/dist/types/src/policy/api.d.ts.map +1 -1
- package/dist/types/src/policy/granter.d.ts +11 -6
- package/dist/types/src/policy/granter.d.ts.map +1 -1
- package/dist/types/src/seekable.d.ts +31 -0
- package/dist/types/src/seekable.d.ts.map +1 -1
- package/dist/types/src/utils.d.ts +61 -2
- package/dist/types/src/utils.d.ts.map +1 -1
- package/dist/types/tdf3/src/assertions.d.ts +4 -0
- package/dist/types/tdf3/src/assertions.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/DecoratedReadableStream.d.ts +6 -0
- package/dist/types/tdf3/src/client/DecoratedReadableStream.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/builders.d.ts +14 -0
- package/dist/types/tdf3/src/client/builders.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/index.d.ts +25 -4
- package/dist/types/tdf3/src/client/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/validation.d.ts +3 -3
- package/dist/types/tdf3/src/client/validation.d.ts.map +1 -1
- package/dist/types/tdf3/src/tdf.d.ts +3 -1
- package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
- package/dist/types/tdf3/src/utils/unwrap.d.ts.map +1 -1
- package/dist/web/src/access/access-fetch.js +2 -1
- package/dist/web/src/access/access-rpc.js +11 -5
- package/dist/web/src/access/constants.js +3 -0
- package/dist/web/src/access.js +37 -3
- package/dist/web/src/auth/oidc-clientcredentials-provider.js +4 -2
- package/dist/web/src/auth/oidc-externaljwt-provider.js +5 -3
- package/dist/web/src/auth/oidc-refreshtoken-provider.js +19 -3
- package/dist/web/src/auth/oidc.js +9 -8
- package/dist/web/src/auth/providers.js +7 -1
- package/dist/web/src/index.js +2 -1
- package/dist/web/src/nanoclients.js +4 -4
- package/dist/web/src/nanotdf/Client.js +11 -7
- package/dist/web/src/opentdf.js +103 -13
- package/dist/web/src/platform/authorization/v2/authorization_pb.js +109 -0
- package/dist/web/src/platform/buf/validate/validate_pb.js +113 -169
- package/dist/web/src/platform/common/common_pb.js +15 -4
- package/dist/web/src/platform/entity/entity_pb.js +48 -0
- package/dist/web/src/platform/entityresolution/entity_resolution_pb.js +1 -1
- package/dist/web/src/platform/entityresolution/v2/entity_resolution_pb.js +46 -0
- package/dist/web/src/platform/google/api/annotations_pb.js +1 -1
- package/dist/web/src/platform/google/api/http_pb.js +3 -3
- package/dist/web/src/platform/kas/kas_pb.js +2 -2
- package/dist/web/src/platform/policy/attributes/attributes_pb.js +12 -2
- package/dist/web/src/platform/policy/kasregistry/key_access_server_registry_pb.js +55 -3
- package/dist/web/src/platform/policy/keymanagement/key_management_pb.js +2 -2
- package/dist/web/src/platform/policy/namespaces/namespaces_pb.js +30 -3
- package/dist/web/src/platform/policy/objects_pb.js +114 -41
- package/dist/web/src/platform/policy/obligations/obligations_pb.js +156 -0
- package/dist/web/src/platform/policy/registeredresources/registered_resources_pb.js +19 -14
- package/dist/web/src/platform/policy/resourcemapping/resource_mapping_pb.js +2 -3
- package/dist/web/src/platform/policy/selectors_pb.js +1 -1
- package/dist/web/src/platform/policy/subjectmapping/subject_mapping_pb.js +2 -3
- package/dist/web/src/platform/policy/unsafe/unsafe_pb.js +2 -4
- package/dist/web/src/platform.js +20 -3
- package/dist/web/src/policy/api.js +26 -7
- package/dist/web/src/policy/granter.js +75 -48
- package/dist/web/src/seekable.js +32 -1
- package/dist/web/src/utils.js +84 -3
- package/dist/web/tdf3/src/assertions.js +38 -2
- package/dist/web/tdf3/src/client/DecoratedReadableStream.js +8 -1
- package/dist/web/tdf3/src/client/builders.js +13 -1
- package/dist/web/tdf3/src/client/index.js +215 -57
- package/dist/web/tdf3/src/client/validation.js +3 -3
- package/dist/web/tdf3/src/tdf.js +42 -9
- package/dist/web/tdf3/src/utils/unwrap.js +2 -2
- package/package.json +7 -5
- package/src/access/access-fetch.ts +1 -0
- package/src/access/access-rpc.ts +13 -4
- package/src/access/constants.ts +2 -0
- package/src/access.ts +54 -2
- package/src/auth/oidc-clientcredentials-provider.ts +4 -0
- package/src/auth/oidc-externaljwt-provider.ts +5 -1
- package/src/auth/oidc-refreshtoken-provider.ts +19 -1
- package/src/auth/oidc.ts +12 -7
- package/src/auth/providers.ts +6 -0
- package/src/index.ts +1 -0
- package/src/nanoclients.ts +3 -3
- package/src/nanotdf/Client.ts +28 -6
- package/src/opentdf.ts +206 -73
- package/src/platform/authorization/v2/authorization_pb.ts +503 -0
- package/src/platform/buf/validate/validate_pb.ts +529 -401
- package/src/platform/common/common_pb.ts +48 -3
- package/src/platform/entity/entity_pb.ts +154 -0
- package/src/platform/entityresolution/entity_resolution_pb.ts +4 -0
- package/src/platform/entityresolution/v2/entity_resolution_pb.ts +170 -0
- package/src/platform/google/api/annotations_pb.ts +1 -1
- package/src/platform/google/api/http_pb.ts +2 -2
- package/src/platform/kas/kas_pb.ts +6 -1
- package/src/platform/policy/attributes/attributes_pb.ts +46 -16
- package/src/platform/policy/kasregistry/key_access_server_registry_pb.ts +371 -27
- package/src/platform/policy/keymanagement/key_management_pb.ts +24 -2
- package/src/platform/policy/namespaces/namespaces_pb.ts +163 -7
- package/src/platform/policy/objects_pb.ts +474 -59
- package/src/platform/policy/obligations/obligations_pb.ts +788 -0
- package/src/platform/policy/registeredresources/registered_resources_pb.ts +80 -13
- package/src/platform/policy/resourcemapping/resource_mapping_pb.ts +1 -2
- package/src/platform/policy/selectors_pb.ts +18 -0
- package/src/platform/policy/subjectmapping/subject_mapping_pb.ts +1 -2
- package/src/platform/policy/unsafe/unsafe_pb.ts +21 -6
- package/src/platform.ts +29 -5
- package/src/policy/api.ts +37 -6
- package/src/policy/granter.ts +82 -56
- package/src/seekable.ts +31 -0
- package/src/utils.ts +88 -2
- package/tdf3/src/assertions.ts +52 -1
- package/tdf3/src/client/DecoratedReadableStream.ts +9 -0
- package/tdf3/src/client/builders.ts +16 -0
- package/tdf3/src/client/index.ts +309 -73
- package/tdf3/src/client/validation.ts +2 -2
- package/tdf3/src/tdf.ts +53 -9
- package/tdf3/src/utils/unwrap.ts +2 -1
package/tdf3/src/client/index.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
423
|
-
this.
|
|
572
|
+
const fetchedFQNValues = await attributeFQNsAsValues(
|
|
573
|
+
this.platformUrl,
|
|
424
574
|
this.authProvider as AuthProvider,
|
|
425
|
-
...
|
|
575
|
+
...fqnsWithoutValues
|
|
426
576
|
);
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
scope.attributes = avs.map(({ fqn }) => fqn);
|
|
431
|
-
}
|
|
577
|
+
fetchedFQNValues.forEach((fetchedValue) => {
|
|
578
|
+
attributeValues.push(fetchedValue);
|
|
579
|
+
});
|
|
432
580
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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 [${
|
|
439
|
-
|
|
587
|
+
`Attribute mismatch between [${attributeFQNs}] and explicit values ${JSON.stringify(
|
|
588
|
+
attributeValues.map(({ fqn }) => fqn)
|
|
440
589
|
)}`
|
|
441
590
|
);
|
|
442
591
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
|
|
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
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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: [${
|
|
699
|
+
`Mismatched wrapping key algorithm: [${algorithm}] is not requested type, [${wrappingKeyAlgorithm}]`
|
|
477
700
|
);
|
|
478
701
|
}
|
|
479
702
|
let type: KeyAccessType;
|
|
480
|
-
switch (
|
|
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 ${
|
|
714
|
+
throw new ConfigurationError(`Unsupported algorithm ${algorithm}`);
|
|
489
715
|
}
|
|
490
716
|
return buildKeyAccess({
|
|
491
|
-
alg:
|
|
717
|
+
alg: algorithm,
|
|
492
718
|
type,
|
|
493
|
-
url:
|
|
494
|
-
kid:
|
|
495
|
-
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-]
|
|
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-]
|
|
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})`;
|