@opentdf/sdk 0.3.1 → 0.3.2-beta.1
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 +155 -0
- package/dist/cjs/src/access/access-rpc.js +131 -0
- package/dist/cjs/src/access.js +81 -107
- package/dist/cjs/src/auth/oidc.js +2 -2
- package/dist/cjs/src/nanotdf/Client.js +17 -13
- package/dist/cjs/src/nanotdf/models/Header.js +2 -2
- package/dist/cjs/src/nanotdf-crypto/keyAgreement.js +2 -2
- package/dist/cjs/src/opentdf.js +42 -8
- package/dist/cjs/src/platform/authorization/authorization_pb.js +138 -0
- package/dist/cjs/src/platform/buf/validate/validate_pb.js +410 -0
- package/dist/cjs/src/platform/common/common_pb.js +79 -0
- package/dist/cjs/src/platform/entityresolution/entity_resolution_pb.js +49 -0
- package/dist/cjs/src/platform/google/api/annotations_pb.js +30 -0
- package/dist/cjs/src/platform/google/api/http_pb.js +37 -0
- package/dist/cjs/src/platform/kas/kas_pb.js +96 -0
- package/dist/cjs/src/platform/policy/actions/actions_pb.js +70 -0
- package/dist/cjs/src/platform/policy/attributes/attributes_pb.js +240 -0
- package/dist/cjs/src/platform/policy/kasregistry/key_access_server_registry_pb.js +236 -0
- package/dist/cjs/src/platform/policy/keymanagement/key_management_pb.js +70 -0
- package/dist/cjs/src/platform/policy/namespaces/namespaces_pb.js +121 -0
- package/dist/cjs/src/platform/policy/objects_pb.js +395 -0
- package/dist/cjs/src/platform/policy/registeredresources/registered_resources_pb.js +132 -0
- package/dist/cjs/src/platform/policy/resourcemapping/resource_mapping_pb.js +139 -0
- package/dist/cjs/src/platform/policy/selectors_pb.js +67 -0
- package/dist/cjs/src/platform/policy/subjectmapping/subject_mapping_pb.js +146 -0
- package/dist/cjs/src/platform/policy/unsafe/unsafe_pb.js +124 -0
- package/dist/cjs/src/platform/protoc-gen-openapiv2/options/annotations_pb.js +68 -0
- package/dist/cjs/src/platform/protoc-gen-openapiv2/options/openapiv2_pb.js +307 -0
- package/dist/cjs/src/platform/wellknownconfiguration/wellknown_configuration_pb.js +33 -0
- package/dist/cjs/src/platform.js +124 -0
- package/dist/cjs/src/policy/api.js +21 -38
- package/dist/cjs/src/policy/attributes.js +4 -1
- package/dist/cjs/src/policy/granter.js +9 -9
- package/dist/cjs/src/utils.js +31 -1
- package/dist/cjs/src/version.js +2 -2
- package/dist/cjs/tdf3/src/client/index.js +23 -18
- package/dist/cjs/tdf3/src/tdf.js +5 -5
- package/dist/types/src/access/access-fetch.d.ts +21 -0
- package/dist/types/src/access/access-fetch.d.ts.map +1 -0
- package/dist/types/src/access/access-rpc.d.ts +22 -0
- package/dist/types/src/access/access-rpc.d.ts.map +1 -0
- package/dist/types/src/access.d.ts +19 -11
- package/dist/types/src/access.d.ts.map +1 -1
- package/dist/types/src/nanotdf/Client.d.ts +3 -1
- package/dist/types/src/nanotdf/Client.d.ts.map +1 -1
- package/dist/types/src/opentdf.d.ts +5 -1
- package/dist/types/src/opentdf.d.ts.map +1 -1
- package/dist/types/src/platform/authorization/authorization_pb.d.ts +609 -0
- package/dist/types/src/platform/authorization/authorization_pb.d.ts.map +1 -0
- package/dist/types/src/platform/buf/validate/validate_pb.d.ts +4466 -0
- package/dist/types/src/platform/buf/validate/validate_pb.d.ts.map +1 -0
- package/dist/types/src/platform/common/common_pb.d.ts +112 -0
- package/dist/types/src/platform/common/common_pb.d.ts.map +1 -0
- package/dist/types/src/platform/entityresolution/entity_resolution_pb.d.ts +199 -0
- package/dist/types/src/platform/entityresolution/entity_resolution_pb.d.ts.map +1 -0
- package/dist/types/src/platform/google/api/annotations_pb.d.ts +14 -0
- package/dist/types/src/platform/google/api/annotations_pb.d.ts.map +1 -0
- package/dist/types/src/platform/google/api/http_pb.d.ts +441 -0
- package/dist/types/src/platform/google/api/http_pb.d.ts.map +1 -0
- package/dist/types/src/platform/kas/kas_pb.d.ts +404 -0
- package/dist/types/src/platform/kas/kas_pb.d.ts.map +1 -0
- package/dist/types/src/platform/policy/actions/actions_pb.d.ts +265 -0
- package/dist/types/src/platform/policy/actions/actions_pb.d.ts.map +1 -0
- package/dist/types/src/platform/policy/attributes/attributes_pb.d.ts +1022 -0
- package/dist/types/src/platform/policy/attributes/attributes_pb.d.ts.map +1 -0
- package/dist/types/src/platform/policy/kasregistry/key_access_server_registry_pb.d.ts +1306 -0
- package/dist/types/src/platform/policy/kasregistry/key_access_server_registry_pb.d.ts.map +1 -0
- package/dist/types/src/platform/policy/keymanagement/key_management_pb.d.ts +269 -0
- package/dist/types/src/platform/policy/keymanagement/key_management_pb.d.ts.map +1 -0
- package/dist/types/src/platform/policy/namespaces/namespaces_pb.d.ts +448 -0
- package/dist/types/src/platform/policy/namespaces/namespaces_pb.d.ts.map +1 -0
- package/dist/types/src/platform/policy/objects_pb.d.ts +1112 -0
- package/dist/types/src/platform/policy/objects_pb.d.ts.map +1 -0
- package/dist/types/src/platform/policy/registeredresources/registered_resources_pb.d.ts +539 -0
- package/dist/types/src/platform/policy/registeredresources/registered_resources_pb.d.ts.map +1 -0
- package/dist/types/src/platform/policy/resourcemapping/resource_mapping_pb.d.ts +558 -0
- package/dist/types/src/platform/policy/resourcemapping/resource_mapping_pb.d.ts.map +1 -0
- package/dist/types/src/platform/policy/selectors_pb.d.ts +221 -0
- package/dist/types/src/platform/policy/selectors_pb.d.ts.map +1 -0
- package/dist/types/src/platform/policy/subjectmapping/subject_mapping_pb.d.ts +582 -0
- package/dist/types/src/platform/policy/subjectmapping/subject_mapping_pb.d.ts.map +1 -0
- package/dist/types/src/platform/policy/unsafe/unsafe_pb.d.ts +513 -0
- package/dist/types/src/platform/policy/unsafe/unsafe_pb.d.ts.map +1 -0
- package/dist/types/src/platform/protoc-gen-openapiv2/options/annotations_pb.d.ts +62 -0
- package/dist/types/src/platform/protoc-gen-openapiv2/options/annotations_pb.d.ts.map +1 -0
- package/dist/types/src/platform/protoc-gen-openapiv2/options/openapiv2_pb.d.ts +1441 -0
- package/dist/types/src/platform/protoc-gen-openapiv2/options/openapiv2_pb.d.ts.map +1 -0
- package/dist/types/src/platform/wellknownconfiguration/wellknown_configuration_pb.d.ts +59 -0
- package/dist/types/src/platform/wellknownconfiguration/wellknown_configuration_pb.d.ts.map +1 -0
- package/dist/types/src/platform.d.ts +49 -0
- package/dist/types/src/platform.d.ts.map +1 -0
- package/dist/types/src/policy/api.d.ts +1 -1
- package/dist/types/src/policy/api.d.ts.map +1 -1
- package/dist/types/src/policy/attributes.d.ts +10 -87
- package/dist/types/src/policy/attributes.d.ts.map +1 -1
- package/dist/types/src/policy/granter.d.ts.map +1 -1
- package/dist/types/src/utils.d.ts +10 -0
- package/dist/types/src/utils.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/dist/types/tdf3/src/client/index.d.ts +8 -3
- package/dist/types/tdf3/src/client/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
- package/dist/web/src/access/access-fetch.js +150 -0
- package/dist/web/src/access/access-rpc.js +125 -0
- package/dist/web/src/access.js +82 -110
- package/dist/web/src/auth/oidc.js +2 -2
- package/dist/web/src/nanotdf/Client.js +18 -14
- package/dist/web/src/nanotdf/models/Header.js +2 -2
- package/dist/web/src/nanotdf-crypto/keyAgreement.js +2 -2
- package/dist/web/src/opentdf.js +43 -9
- package/dist/web/src/platform/authorization/authorization_pb.js +135 -0
- package/dist/web/src/platform/buf/validate/validate_pb.js +407 -0
- package/dist/web/src/platform/common/common_pb.js +76 -0
- package/dist/web/src/platform/entityresolution/entity_resolution_pb.js +46 -0
- package/dist/web/src/platform/google/api/annotations_pb.js +27 -0
- package/dist/web/src/platform/google/api/http_pb.js +34 -0
- package/dist/web/src/platform/kas/kas_pb.js +93 -0
- package/dist/web/src/platform/policy/actions/actions_pb.js +67 -0
- package/dist/web/src/platform/policy/attributes/attributes_pb.js +237 -0
- package/dist/web/src/platform/policy/kasregistry/key_access_server_registry_pb.js +233 -0
- package/dist/web/src/platform/policy/keymanagement/key_management_pb.js +67 -0
- package/dist/web/src/platform/policy/namespaces/namespaces_pb.js +118 -0
- package/dist/web/src/platform/policy/objects_pb.js +392 -0
- package/dist/web/src/platform/policy/registeredresources/registered_resources_pb.js +129 -0
- package/dist/web/src/platform/policy/resourcemapping/resource_mapping_pb.js +136 -0
- package/dist/web/src/platform/policy/selectors_pb.js +64 -0
- package/dist/web/src/platform/policy/subjectmapping/subject_mapping_pb.js +143 -0
- package/dist/web/src/platform/policy/unsafe/unsafe_pb.js +121 -0
- package/dist/web/src/platform/protoc-gen-openapiv2/options/annotations_pb.js +65 -0
- package/dist/web/src/platform/protoc-gen-openapiv2/options/openapiv2_pb.js +304 -0
- package/dist/web/src/platform/wellknownconfiguration/wellknown_configuration_pb.js +30 -0
- package/dist/web/src/platform.js +87 -0
- package/dist/web/src/policy/api.js +23 -40
- package/dist/web/src/policy/attributes.js +3 -2
- package/dist/web/src/policy/granter.js +9 -9
- package/dist/web/src/utils.js +29 -1
- package/dist/web/src/version.js +2 -2
- package/dist/web/tdf3/src/client/index.js +25 -20
- package/dist/web/tdf3/src/tdf.js +5 -5
- package/package.json +17 -1
- package/src/access/access-fetch.ts +202 -0
- package/src/access/access-rpc.ts +175 -0
- package/src/access.ts +113 -138
- package/src/auth/oidc.ts +1 -1
- package/src/nanotdf/Client.ts +28 -18
- package/src/nanotdf/models/Header.ts +1 -1
- package/src/nanotdf-crypto/keyAgreement.ts +1 -1
- package/src/opentdf.ts +66 -10
- package/src/platform/authorization/authorization_pb.ts +689 -0
- package/src/platform/buf/validate/validate_pb.ts +4626 -0
- package/src/platform/common/common_pb.ts +135 -0
- package/src/platform/entityresolution/entity_resolution_pb.ts +233 -0
- package/src/platform/google/api/annotations_pb.ts +39 -0
- package/src/platform/google/api/http_pb.ts +474 -0
- package/src/platform/kas/kas_pb.ts +484 -0
- package/src/platform/policy/actions/actions_pb.ts +312 -0
- package/src/platform/policy/attributes/attributes_pb.ts +1181 -0
- package/src/platform/policy/kasregistry/key_access_server_registry_pb.ts +1482 -0
- package/src/platform/policy/keymanagement/key_management_pb.ts +316 -0
- package/src/platform/policy/namespaces/namespaces_pb.ts +528 -0
- package/src/platform/policy/objects_pb.ts +1319 -0
- package/src/platform/policy/registeredresources/registered_resources_pb.ts +623 -0
- package/src/platform/policy/resourcemapping/resource_mapping_pb.ts +658 -0
- package/src/platform/policy/selectors_pb.ts +277 -0
- package/src/platform/policy/subjectmapping/subject_mapping_pb.ts +687 -0
- package/src/platform/policy/unsafe/unsafe_pb.ts +593 -0
- package/src/platform/protoc-gen-openapiv2/options/annotations_pb.ts +83 -0
- package/src/platform/protoc-gen-openapiv2/options/openapiv2_pb.ts +1615 -0
- package/src/platform/wellknownconfiguration/wellknown_configuration_pb.ts +78 -0
- package/src/platform.ts +122 -0
- package/src/policy/api.ts +29 -42
- package/src/policy/attributes.ts +12 -108
- package/src/policy/granter.ts +7 -8
- package/src/utils.ts +30 -0
- package/src/version.ts +1 -1
- package/tdf3/src/client/index.ts +40 -19
- package/tdf3/src/tdf.ts +5 -6
- package/src/platform/authorization/authorization_connect.d.ts +0 -44
- package/src/platform/authorization/authorization_connect.js +0 -44
- package/src/platform/authorization/authorization_pb.d.ts +0 -707
- package/src/platform/authorization/authorization_pb.js +0 -372
- package/src/platform/common/common_pb.d.ts +0 -129
- package/src/platform/common/common_pb.js +0 -58
- package/src/platform/entityresolution/entity_resolution_connect.d.ts +0 -35
- package/src/platform/entityresolution/entity_resolution_connect.js +0 -35
- package/src/platform/entityresolution/entity_resolution_pb.d.ts +0 -242
- package/src/platform/entityresolution/entity_resolution_pb.js +0 -139
- package/src/platform/kas/kas_connect.d.ts +0 -59
- package/src/platform/kas/kas_connect.js +0 -59
- package/src/platform/kas/kas_pb.d.ts +0 -200
- package/src/platform/kas/kas_pb.js +0 -84
- package/src/platform/policy/attributes/attributes_connect.d.ts +0 -168
- package/src/platform/policy/attributes/attributes_connect.js +0 -168
- package/src/platform/policy/attributes/attributes_pb.d.ts +0 -929
- package/src/platform/policy/attributes/attributes_pb.js +0 -363
- package/src/platform/policy/kasregistry/key_access_server_registry_connect.d.ts +0 -62
- package/src/platform/policy/kasregistry/key_access_server_registry_connect.js +0 -62
- package/src/platform/policy/kasregistry/key_access_server_registry_pb.d.ts +0 -283
- package/src/platform/policy/kasregistry/key_access_server_registry_pb.js +0 -113
- package/src/platform/policy/namespaces/namespaces_connect.d.ts +0 -62
- package/src/platform/policy/namespaces/namespaces_connect.js +0 -62
- package/src/platform/policy/namespaces/namespaces_pb.d.ts +0 -270
- package/src/platform/policy/namespaces/namespaces_pb.js +0 -110
- package/src/platform/policy/objects_pb.d.ts +0 -725
- package/src/platform/policy/objects_pb.js +0 -288
- package/src/platform/policy/resourcemapping/resource_mapping_connect.d.ts +0 -259
- package/src/platform/policy/resourcemapping/resource_mapping_connect.js +0 -259
- package/src/platform/policy/resourcemapping/resource_mapping_pb.d.ts +0 -314
- package/src/platform/policy/resourcemapping/resource_mapping_pb.js +0 -142
- package/src/platform/policy/selectors_pb.d.ts +0 -269
- package/src/platform/policy/selectors_pb.js +0 -110
- package/src/platform/policy/subjectmapping/subject_mapping_connect.d.ts +0 -118
- package/src/platform/policy/subjectmapping/subject_mapping_connect.js +0 -118
- package/src/platform/policy/subjectmapping/subject_mapping_pb.d.ts +0 -672
- package/src/platform/policy/subjectmapping/subject_mapping_pb.js +0 -260
- package/src/platform/wellknownconfiguration/wellknown_configuration_connect.d.ts +0 -26
- package/src/platform/wellknownconfiguration/wellknown_configuration_connect.js +0 -26
- package/src/platform/wellknownconfiguration/wellknown_configuration_pb.d.ts +0 -75
- package/src/platform/wellknownconfiguration/wellknown_configuration_pb.js +0 -35
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {
|
|
2
|
+
KasPublicKeyAlgorithm,
|
|
3
|
+
KasPublicKeyInfo,
|
|
4
|
+
noteInvalidPublicKey,
|
|
5
|
+
OriginAllowList,
|
|
6
|
+
} from '../access.js';
|
|
7
|
+
import { type AuthProvider } from '../auth/auth.js';
|
|
8
|
+
import {
|
|
9
|
+
ConfigurationError,
|
|
10
|
+
InvalidFileError,
|
|
11
|
+
NetworkError,
|
|
12
|
+
PermissionDeniedError,
|
|
13
|
+
ServiceError,
|
|
14
|
+
UnauthenticatedError,
|
|
15
|
+
} from '../errors.js';
|
|
16
|
+
import { pemToCryptoPublicKey, validateSecureUrl } from '../utils.js';
|
|
17
|
+
|
|
18
|
+
export type RewrapRequest = {
|
|
19
|
+
signedRequestToken: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type RewrapResponseLegacy = {
|
|
23
|
+
metadata: Record<string, unknown>;
|
|
24
|
+
entityWrappedKey: string;
|
|
25
|
+
sessionPublicKey: string;
|
|
26
|
+
schemaVersion: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get a rewrapped access key to the document, if possible
|
|
31
|
+
* @param url Key access server rewrap endpoint
|
|
32
|
+
* @param requestBody a signed request with an encrypted document key
|
|
33
|
+
* @param authProvider Authorization middleware
|
|
34
|
+
*/
|
|
35
|
+
export async function fetchWrappedKey(
|
|
36
|
+
url: string,
|
|
37
|
+
requestBody: RewrapRequest,
|
|
38
|
+
authProvider: AuthProvider
|
|
39
|
+
): Promise<RewrapResponseLegacy> {
|
|
40
|
+
const req = await authProvider.withCreds({
|
|
41
|
+
url,
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
},
|
|
46
|
+
body: JSON.stringify(requestBody),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
let response: Response;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
response = await fetch(req.url, {
|
|
53
|
+
method: req.method,
|
|
54
|
+
mode: 'cors', // no-cors, *cors, same-origin
|
|
55
|
+
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
|
|
56
|
+
credentials: 'same-origin', // include, *same-origin, omit
|
|
57
|
+
headers: req.headers,
|
|
58
|
+
redirect: 'follow', // manual, *follow, error
|
|
59
|
+
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
|
|
60
|
+
body: req.body as BodyInit,
|
|
61
|
+
});
|
|
62
|
+
} catch (e) {
|
|
63
|
+
throw new NetworkError(`unable to fetch wrapped key from [${url}]`, e);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
switch (response.status) {
|
|
68
|
+
case 400:
|
|
69
|
+
throw new InvalidFileError(
|
|
70
|
+
`400 for [${req.url}]: rewrap bad request [${await response.text()}]`
|
|
71
|
+
);
|
|
72
|
+
case 401:
|
|
73
|
+
throw new UnauthenticatedError(`401 for [${req.url}]; rewrap auth failure`);
|
|
74
|
+
case 403:
|
|
75
|
+
throw new PermissionDeniedError(`403 for [${req.url}]; rewrap permission denied`);
|
|
76
|
+
default:
|
|
77
|
+
if (response.status >= 500) {
|
|
78
|
+
throw new ServiceError(
|
|
79
|
+
`${response.status} for [${req.url}]: rewrap failure due to service error [${await response.text()}]`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
throw new NetworkError(
|
|
83
|
+
`${req.method} ${req.url} => ${response.status} ${response.statusText}`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return response.json();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function fetchKeyAccessServers(
|
|
92
|
+
platformUrl: string,
|
|
93
|
+
authProvider: AuthProvider
|
|
94
|
+
): Promise<OriginAllowList> {
|
|
95
|
+
let nextOffset = 0;
|
|
96
|
+
const allServers = [];
|
|
97
|
+
do {
|
|
98
|
+
const req = await authProvider.withCreds({
|
|
99
|
+
url: `${platformUrl}/key-access-servers?pagination.offset=${nextOffset}`,
|
|
100
|
+
method: 'GET',
|
|
101
|
+
headers: {
|
|
102
|
+
'Content-Type': 'application/json',
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
let response: Response;
|
|
106
|
+
try {
|
|
107
|
+
response = await fetch(req.url, {
|
|
108
|
+
method: req.method,
|
|
109
|
+
headers: req.headers,
|
|
110
|
+
body: req.body as BodyInit,
|
|
111
|
+
mode: 'cors',
|
|
112
|
+
cache: 'no-cache',
|
|
113
|
+
credentials: 'same-origin',
|
|
114
|
+
redirect: 'follow',
|
|
115
|
+
referrerPolicy: 'no-referrer',
|
|
116
|
+
});
|
|
117
|
+
} catch (e) {
|
|
118
|
+
throw new NetworkError(`unable to fetch kas list from [${req.url}]`, e);
|
|
119
|
+
}
|
|
120
|
+
// if we get an error from the kas registry, throw an error
|
|
121
|
+
if (!response.ok) {
|
|
122
|
+
throw new ServiceError(
|
|
123
|
+
`unable to fetch kas list from [${req.url}], status: ${response.status}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
const { keyAccessServers = [], pagination = {} } = await response.json();
|
|
127
|
+
allServers.push(...keyAccessServers);
|
|
128
|
+
nextOffset = pagination.nextOffset || 0;
|
|
129
|
+
} while (nextOffset > 0);
|
|
130
|
+
|
|
131
|
+
const serverUrls = allServers.map((server) => server.uri);
|
|
132
|
+
// add base platform kas
|
|
133
|
+
if (!serverUrls.includes(`${platformUrl}/kas`)) {
|
|
134
|
+
serverUrls.push(`${platformUrl}/kas`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return new OriginAllowList(serverUrls, false);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export async function fetchKasPubKey(
|
|
141
|
+
kasEndpoint: string,
|
|
142
|
+
algorithm?: KasPublicKeyAlgorithm
|
|
143
|
+
): Promise<KasPublicKeyInfo> {
|
|
144
|
+
if (!kasEndpoint) {
|
|
145
|
+
throw new ConfigurationError('KAS definition not found');
|
|
146
|
+
}
|
|
147
|
+
// Logs insecure KAS. Secure is enforced in constructor
|
|
148
|
+
validateSecureUrl(kasEndpoint);
|
|
149
|
+
|
|
150
|
+
// Parse kasEndpoint to URL, then append to its path and update its query parameters
|
|
151
|
+
let pkUrlV2: URL;
|
|
152
|
+
try {
|
|
153
|
+
pkUrlV2 = new URL(kasEndpoint);
|
|
154
|
+
} catch (e) {
|
|
155
|
+
throw new ConfigurationError(`KAS definition invalid: [${kasEndpoint}]`, e);
|
|
156
|
+
}
|
|
157
|
+
if (!pkUrlV2.pathname.endsWith('kas_public_key')) {
|
|
158
|
+
if (!pkUrlV2.pathname.endsWith('/')) {
|
|
159
|
+
pkUrlV2.pathname += '/';
|
|
160
|
+
}
|
|
161
|
+
pkUrlV2.pathname += 'v2/kas_public_key';
|
|
162
|
+
}
|
|
163
|
+
pkUrlV2.searchParams.set('algorithm', algorithm || 'rsa:2048');
|
|
164
|
+
if (!pkUrlV2.searchParams.get('v')) {
|
|
165
|
+
pkUrlV2.searchParams.set('v', '2');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let kasPubKeyResponseV2: Response;
|
|
169
|
+
try {
|
|
170
|
+
kasPubKeyResponseV2 = await fetch(pkUrlV2);
|
|
171
|
+
} catch (e) {
|
|
172
|
+
throw new NetworkError(`unable to fetch public key from [${pkUrlV2}]`, e);
|
|
173
|
+
}
|
|
174
|
+
if (!kasPubKeyResponseV2.ok) {
|
|
175
|
+
switch (kasPubKeyResponseV2.status) {
|
|
176
|
+
case 404:
|
|
177
|
+
throw new ConfigurationError(`404 for [${pkUrlV2}]`);
|
|
178
|
+
case 401:
|
|
179
|
+
throw new UnauthenticatedError(`401 for [${pkUrlV2}]`);
|
|
180
|
+
case 403:
|
|
181
|
+
throw new PermissionDeniedError(`403 for [${pkUrlV2}]`);
|
|
182
|
+
default:
|
|
183
|
+
throw new NetworkError(
|
|
184
|
+
`${pkUrlV2} => ${kasPubKeyResponseV2.status} ${kasPubKeyResponseV2.statusText}`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const jsonContent = await kasPubKeyResponseV2.json();
|
|
189
|
+
const { publicKey, kid }: KasPublicKeyInfo = jsonContent;
|
|
190
|
+
if (!publicKey) {
|
|
191
|
+
throw new NetworkError(
|
|
192
|
+
`invalid response from public key endpoint [${JSON.stringify(jsonContent)}]`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
key: noteInvalidPublicKey(pkUrlV2, pemToCryptoPublicKey(publicKey)),
|
|
197
|
+
publicKey,
|
|
198
|
+
url: kasEndpoint,
|
|
199
|
+
algorithm: algorithm || 'rsa:2048',
|
|
200
|
+
...(kid && { kid }),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isPublicKeyAlgorithm,
|
|
3
|
+
KasPublicKeyAlgorithm,
|
|
4
|
+
KasPublicKeyInfo,
|
|
5
|
+
noteInvalidPublicKey,
|
|
6
|
+
OriginAllowList,
|
|
7
|
+
} from '../access.js';
|
|
8
|
+
import { type AuthProvider } from '../auth/auth.js';
|
|
9
|
+
import { ConfigurationError, NetworkError } from '../errors.js';
|
|
10
|
+
import { PlatformClient } from '../platform.js';
|
|
11
|
+
import { RewrapResponse } from '../platform/kas/kas_pb.js';
|
|
12
|
+
import { ListKeyAccessServersResponse } from '../platform/policy/kasregistry/key_access_server_registry_pb.js';
|
|
13
|
+
import {
|
|
14
|
+
extractRpcErrorMessage,
|
|
15
|
+
getPlatformUrlFromKasEndpoint,
|
|
16
|
+
pemToCryptoPublicKey,
|
|
17
|
+
validateSecureUrl,
|
|
18
|
+
} from '../utils.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get a rewrapped access key to the document, if possible
|
|
22
|
+
* @param url Key access server rewrap endpoint
|
|
23
|
+
* @param requestBody a signed request with an encrypted document key
|
|
24
|
+
* @param authProvider Authorization middleware
|
|
25
|
+
* @param clientVersion
|
|
26
|
+
*/
|
|
27
|
+
export async function fetchWrappedKey(
|
|
28
|
+
url: string,
|
|
29
|
+
signedRequestToken: string,
|
|
30
|
+
authProvider: AuthProvider
|
|
31
|
+
): Promise<RewrapResponse> {
|
|
32
|
+
const platformUrl = getPlatformUrlFromKasEndpoint(url);
|
|
33
|
+
const platform = new PlatformClient({ authProvider, platformUrl });
|
|
34
|
+
try {
|
|
35
|
+
return await platform.v1.access.rewrap({
|
|
36
|
+
signedRequestToken,
|
|
37
|
+
});
|
|
38
|
+
} catch (e) {
|
|
39
|
+
throw new NetworkError(`[${platformUrl}] [Rewrap] ${extractRpcErrorMessage(e)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function fetchKeyAccessServers(
|
|
44
|
+
platformUrl: string,
|
|
45
|
+
authProvider: AuthProvider
|
|
46
|
+
): Promise<OriginAllowList> {
|
|
47
|
+
let nextOffset = 0;
|
|
48
|
+
const allServers = [];
|
|
49
|
+
const platform = new PlatformClient({ authProvider, platformUrl });
|
|
50
|
+
|
|
51
|
+
do {
|
|
52
|
+
let response: ListKeyAccessServersResponse;
|
|
53
|
+
try {
|
|
54
|
+
response = await platform.v1.keyAccessServerRegistry.listKeyAccessServers({
|
|
55
|
+
pagination: {
|
|
56
|
+
offset: nextOffset,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
} catch (e) {
|
|
60
|
+
throw new NetworkError(
|
|
61
|
+
`[${platformUrl}] [ListKeyAccessServers] ${extractRpcErrorMessage(e)}`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
allServers.push(...response.keyAccessServers);
|
|
66
|
+
nextOffset = response?.pagination?.nextOffset || 0;
|
|
67
|
+
} while (nextOffset > 0);
|
|
68
|
+
|
|
69
|
+
const serverUrls = allServers.map((server) => server.uri);
|
|
70
|
+
// add base platform kas
|
|
71
|
+
if (!serverUrls.includes(`${platformUrl}/kas`)) {
|
|
72
|
+
serverUrls.push(`${platformUrl}/kas`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return new OriginAllowList(serverUrls, false);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface PlatformBaseKey {
|
|
79
|
+
kas_id?: string;
|
|
80
|
+
kas_uri: string;
|
|
81
|
+
public_key: {
|
|
82
|
+
algorithm: KasPublicKeyAlgorithm;
|
|
83
|
+
kid: string;
|
|
84
|
+
pem: string;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isBaseKey(baseKey?: unknown): baseKey is PlatformBaseKey {
|
|
89
|
+
if (!baseKey) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const bk = baseKey as PlatformBaseKey;
|
|
93
|
+
return (
|
|
94
|
+
!!bk.kas_uri &&
|
|
95
|
+
!!bk.public_key &&
|
|
96
|
+
typeof bk.public_key === 'object' &&
|
|
97
|
+
!!bk.public_key.pem &&
|
|
98
|
+
!!bk.public_key.algorithm &&
|
|
99
|
+
isPublicKeyAlgorithm(bk.public_key.algorithm)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function fetchKasPubKey(
|
|
104
|
+
kasEndpoint: string,
|
|
105
|
+
algorithm?: KasPublicKeyAlgorithm
|
|
106
|
+
): Promise<KasPublicKeyInfo> {
|
|
107
|
+
if (!kasEndpoint) {
|
|
108
|
+
throw new ConfigurationError('KAS definition not found');
|
|
109
|
+
}
|
|
110
|
+
// Logs insecure KAS. Secure is enforced in constructor
|
|
111
|
+
validateSecureUrl(kasEndpoint);
|
|
112
|
+
|
|
113
|
+
const platformUrl = getPlatformUrlFromKasEndpoint(kasEndpoint);
|
|
114
|
+
const platform = new PlatformClient({
|
|
115
|
+
platformUrl,
|
|
116
|
+
});
|
|
117
|
+
try {
|
|
118
|
+
const { kid, publicKey } = await platform.v1.access.publicKey({
|
|
119
|
+
algorithm: algorithm || 'rsa:2048',
|
|
120
|
+
v: '2',
|
|
121
|
+
});
|
|
122
|
+
const result: KasPublicKeyInfo = {
|
|
123
|
+
key: noteInvalidPublicKey(new URL(platformUrl), pemToCryptoPublicKey(publicKey)),
|
|
124
|
+
publicKey,
|
|
125
|
+
url: kasEndpoint,
|
|
126
|
+
algorithm: algorithm || 'rsa:2048',
|
|
127
|
+
...(kid && { kid }),
|
|
128
|
+
};
|
|
129
|
+
return result;
|
|
130
|
+
} catch (e) {
|
|
131
|
+
throw new NetworkError(`[${platformUrl}] [PublicKey] ${extractRpcErrorMessage(e)}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Fetch the base public key from WellKnownConfiguration of the platform.
|
|
137
|
+
* @param kasEndpoint The KAS endpoint URL.
|
|
138
|
+
* @throws {ConfigurationError} If the KAS endpoint is not defined.
|
|
139
|
+
* @throws {NetworkError} If there is an error fetching the public key from the KAS endpoint.
|
|
140
|
+
* @returns The base public key information for the KAS endpoint.
|
|
141
|
+
*/
|
|
142
|
+
export async function fetchKasBasePubKey(kasEndpoint: string): Promise<KasPublicKeyInfo> {
|
|
143
|
+
if (!kasEndpoint) {
|
|
144
|
+
throw new ConfigurationError('KAS definition not found');
|
|
145
|
+
}
|
|
146
|
+
validateSecureUrl(kasEndpoint);
|
|
147
|
+
|
|
148
|
+
const platformUrl = getPlatformUrlFromKasEndpoint(kasEndpoint);
|
|
149
|
+
const platform = new PlatformClient({
|
|
150
|
+
platformUrl,
|
|
151
|
+
});
|
|
152
|
+
try {
|
|
153
|
+
const { configuration } = await platform.v1.wellknown.getWellKnownConfiguration({});
|
|
154
|
+
const baseKey = configuration?.base_key as unknown as PlatformBaseKey;
|
|
155
|
+
if (!isBaseKey(baseKey)) {
|
|
156
|
+
throw new NetworkError(
|
|
157
|
+
`Invalid Platform Configuration: [${kasEndpoint}] is missing BaseKey in WellKnownConfiguration`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const result: KasPublicKeyInfo = {
|
|
162
|
+
key: noteInvalidPublicKey(
|
|
163
|
+
new URL(baseKey.kas_uri),
|
|
164
|
+
pemToCryptoPublicKey(baseKey.public_key.pem)
|
|
165
|
+
),
|
|
166
|
+
publicKey: baseKey.public_key.pem,
|
|
167
|
+
url: baseKey.kas_uri,
|
|
168
|
+
algorithm: baseKey.public_key.algorithm,
|
|
169
|
+
kid: baseKey.public_key.kid,
|
|
170
|
+
};
|
|
171
|
+
return result;
|
|
172
|
+
} catch (e) {
|
|
173
|
+
throw new NetworkError(`[${platformUrl}] [PublicKey] ${extractRpcErrorMessage(e)}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
package/src/access.ts
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import { type AuthProvider } from './auth/auth.js';
|
|
2
|
+
import { ServiceError } from './errors.js';
|
|
3
|
+
import { RewrapResponse } from './platform/kas/kas_pb.js';
|
|
4
|
+
import { getPlatformUrlFromKasEndpoint, validateSecureUrl } from './utils.js';
|
|
5
|
+
|
|
2
6
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from './
|
|
10
|
-
import {
|
|
7
|
+
fetchKasBasePubKey,
|
|
8
|
+
fetchKeyAccessServers as fetchKeyAccessServersRpc,
|
|
9
|
+
} from './access/access-rpc.js';
|
|
10
|
+
import { fetchKeyAccessServers as fetchKeyAccessServersLegacy } from './access/access-fetch.js';
|
|
11
|
+
import { fetchWrappedKey as fetchWrappedKeysRpc } from './access/access-rpc.js';
|
|
12
|
+
import { fetchWrappedKey as fetchWrappedKeysLegacy } from './access/access-fetch.js';
|
|
13
|
+
import { fetchKasPubKey as fetchKasPubKeyRpc } from './access/access-rpc.js';
|
|
14
|
+
import { fetchKasPubKey as fetchKasPubKeyLegacy } from './access/access-fetch.js';
|
|
11
15
|
|
|
12
16
|
export type RewrapRequest = {
|
|
13
17
|
signedRequestToken: string;
|
|
14
18
|
};
|
|
15
19
|
|
|
16
|
-
export type RewrapResponse = {
|
|
17
|
-
metadata: Record<string, unknown>;
|
|
18
|
-
entityWrappedKey: string;
|
|
19
|
-
sessionPublicKey: string;
|
|
20
|
-
schemaVersion: string;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
20
|
/**
|
|
24
21
|
* Get a rewrapped access key to the document, if possible
|
|
25
22
|
* @param url Key access server rewrap endpoint
|
|
@@ -29,85 +26,60 @@ export type RewrapResponse = {
|
|
|
29
26
|
*/
|
|
30
27
|
export async function fetchWrappedKey(
|
|
31
28
|
url: string,
|
|
32
|
-
|
|
33
|
-
authProvider: AuthProvider
|
|
34
|
-
clientVersion: string
|
|
29
|
+
signedRequestToken: string,
|
|
30
|
+
authProvider: AuthProvider
|
|
35
31
|
): Promise<RewrapResponse> {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
response = await fetch(req.url, {
|
|
49
|
-
method: req.method,
|
|
50
|
-
mode: 'cors', // no-cors, *cors, same-origin
|
|
51
|
-
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
|
|
52
|
-
credentials: 'same-origin', // include, *same-origin, omit
|
|
53
|
-
headers: req.headers,
|
|
54
|
-
redirect: 'follow', // manual, *follow, error
|
|
55
|
-
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
|
|
56
|
-
body: req.body as BodyInit,
|
|
57
|
-
});
|
|
58
|
-
} catch (e) {
|
|
59
|
-
throw new NetworkError(`unable to fetch wrapped key from [${url}]`, e);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (!response.ok) {
|
|
63
|
-
switch (response.status) {
|
|
64
|
-
case 400:
|
|
65
|
-
throw new InvalidFileError(
|
|
66
|
-
`400 for [${req.url}]: rewrap bad request [${await response.text()}]`
|
|
67
|
-
);
|
|
68
|
-
case 401:
|
|
69
|
-
throw new UnauthenticatedError(`401 for [${req.url}]; rewrap auth failure`);
|
|
70
|
-
case 403:
|
|
71
|
-
throw new PermissionDeniedError(`403 for [${req.url}]; rewrap permission denied`);
|
|
72
|
-
default:
|
|
73
|
-
if (response.status >= 500) {
|
|
74
|
-
throw new ServiceError(
|
|
75
|
-
`${response.status} for [${req.url}]: rewrap failure due to service error [${await response.text()}]`
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
throw new NetworkError(
|
|
79
|
-
`${req.method} ${req.url} => ${response.status} ${response.statusText}`
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return response.json();
|
|
32
|
+
const platformUrl = getPlatformUrlFromKasEndpoint(url);
|
|
33
|
+
|
|
34
|
+
return await tryPromisesUntilFirstSuccess(
|
|
35
|
+
() => fetchWrappedKeysRpc(platformUrl, signedRequestToken, authProvider),
|
|
36
|
+
() =>
|
|
37
|
+
fetchWrappedKeysLegacy(
|
|
38
|
+
url,
|
|
39
|
+
{ signedRequestToken },
|
|
40
|
+
authProvider
|
|
41
|
+
) as unknown as Promise<RewrapResponse>
|
|
42
|
+
);
|
|
85
43
|
}
|
|
86
44
|
|
|
87
|
-
export type KasPublicKeyAlgorithm =
|
|
45
|
+
export type KasPublicKeyAlgorithm =
|
|
46
|
+
| 'ec:secp256r1'
|
|
47
|
+
| 'ec:secp384r1'
|
|
48
|
+
| 'ec:secp521r1'
|
|
49
|
+
| 'rsa:2048'
|
|
50
|
+
| 'rsa:4096';
|
|
88
51
|
|
|
89
52
|
export const isPublicKeyAlgorithm = (a: string): a is KasPublicKeyAlgorithm => {
|
|
90
53
|
return a === 'ec:secp256r1' || a === 'rsa:2048';
|
|
91
54
|
};
|
|
92
55
|
|
|
93
|
-
export const keyAlgorithmToPublicKeyAlgorithm = (
|
|
56
|
+
export const keyAlgorithmToPublicKeyAlgorithm = (k: CryptoKey): KasPublicKeyAlgorithm => {
|
|
57
|
+
const a = k.algorithm;
|
|
94
58
|
if (a.name === 'ECDSA' || a.name === 'ECDH') {
|
|
95
59
|
const eca = a as EcKeyAlgorithm;
|
|
96
|
-
|
|
97
|
-
|
|
60
|
+
switch (eca.namedCurve) {
|
|
61
|
+
case 'P-256':
|
|
62
|
+
return 'ec:secp256r1';
|
|
63
|
+
case 'P-384':
|
|
64
|
+
return 'ec:secp384r1';
|
|
65
|
+
case 'P-521':
|
|
66
|
+
return 'ec:secp521r1';
|
|
67
|
+
default:
|
|
68
|
+
throw new Error(`unsupported EC curve: ${eca.namedCurve}`);
|
|
98
69
|
}
|
|
99
|
-
throw new Error(`unsupported EC curve: ${eca.namedCurve}`);
|
|
100
70
|
}
|
|
101
|
-
if (a.name === 'RSA-OAEP') {
|
|
71
|
+
if (a.name === 'RSA-OAEP' || a.name === 'RSASSA-PKCS1-v1_5') {
|
|
102
72
|
const rsaa = a as RsaHashedKeyAlgorithm;
|
|
103
|
-
if (rsaa.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
73
|
+
if (rsaa.publicExponent.toString() !== '1,0,1') {
|
|
74
|
+
throw new Error(`unsupported RSA public exponent: ${rsaa.publicExponent}`);
|
|
75
|
+
}
|
|
76
|
+
switch (rsaa.modulusLength) {
|
|
77
|
+
case 2048:
|
|
78
|
+
return 'rsa:2048';
|
|
79
|
+
case 4096:
|
|
80
|
+
return 'rsa:4096';
|
|
81
|
+
default:
|
|
82
|
+
throw new Error(`unsupported RSA modulus length: ${rsaa.modulusLength}`);
|
|
111
83
|
}
|
|
112
84
|
}
|
|
113
85
|
throw new Error(`unsupported key algorithm: ${a.name}`);
|
|
@@ -119,6 +91,14 @@ export const publicKeyAlgorithmToJwa = (a: KasPublicKeyAlgorithm): string => {
|
|
|
119
91
|
return 'ES256';
|
|
120
92
|
case 'rsa:2048':
|
|
121
93
|
return 'RS256';
|
|
94
|
+
case 'rsa:4096':
|
|
95
|
+
return 'RS512';
|
|
96
|
+
case 'ec:secp384r1':
|
|
97
|
+
return 'ES384';
|
|
98
|
+
case 'ec:secp521r1':
|
|
99
|
+
return 'ES512';
|
|
100
|
+
default:
|
|
101
|
+
throw new Error(`unsupported public key algorithm: ${a}`);
|
|
122
102
|
}
|
|
123
103
|
};
|
|
124
104
|
|
|
@@ -145,7 +125,7 @@ export type KasPublicKeyInfo = {
|
|
|
145
125
|
key: Promise<CryptoKey>;
|
|
146
126
|
};
|
|
147
127
|
|
|
148
|
-
async function noteInvalidPublicKey(url: URL, r: Promise<CryptoKey>): Promise<CryptoKey> {
|
|
128
|
+
export async function noteInvalidPublicKey(url: URL, r: Promise<CryptoKey>): Promise<CryptoKey> {
|
|
149
129
|
try {
|
|
150
130
|
return await r;
|
|
151
131
|
} catch (e) {
|
|
@@ -156,76 +136,49 @@ async function noteInvalidPublicKey(url: URL, r: Promise<CryptoKey>): Promise<Cr
|
|
|
156
136
|
}
|
|
157
137
|
}
|
|
158
138
|
|
|
139
|
+
export async function fetchKeyAccessServers(
|
|
140
|
+
platformUrl: string,
|
|
141
|
+
authProvider: AuthProvider
|
|
142
|
+
): Promise<OriginAllowList> {
|
|
143
|
+
return await tryPromisesUntilFirstSuccess(
|
|
144
|
+
() => fetchKeyAccessServersRpc(platformUrl, authProvider),
|
|
145
|
+
() => fetchKeyAccessServersLegacy(platformUrl, authProvider)
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
159
149
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
150
|
+
* Fetch the EC (secp256r1) public key for a KAS endpoint.
|
|
151
|
+
* @param kasEndpoint The KAS endpoint URL.
|
|
152
|
+
* @returns The public key information for the KAS endpoint.
|
|
162
153
|
*/
|
|
163
154
|
export async function fetchECKasPubKey(kasEndpoint: string): Promise<KasPublicKeyInfo> {
|
|
164
155
|
return fetchKasPubKey(kasEndpoint, 'ec:secp256r1');
|
|
165
156
|
}
|
|
166
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Fetch the public key for a KAS endpoint.
|
|
160
|
+
* This function will first try to fetch the base public key,
|
|
161
|
+
* then it will try to fetch the public key using the RPC method,
|
|
162
|
+
* and finally it will try to fetch the public key using the legacy method.
|
|
163
|
+
* If all attempts fail, it will return the error from RPC Public Key fetch.
|
|
164
|
+
* @param kasEndpoint The KAS endpoint URL.
|
|
165
|
+
* @param algorithm Optional algorithm to fetch the public key for.
|
|
166
|
+
* @returns The public key information.
|
|
167
|
+
*/
|
|
167
168
|
export async function fetchKasPubKey(
|
|
168
169
|
kasEndpoint: string,
|
|
169
170
|
algorithm?: KasPublicKeyAlgorithm
|
|
170
171
|
): Promise<KasPublicKeyInfo> {
|
|
171
|
-
if (!kasEndpoint) {
|
|
172
|
-
throw new ConfigurationError('KAS definition not found');
|
|
173
|
-
}
|
|
174
|
-
// Logs insecure KAS. Secure is enforced in constructor
|
|
175
|
-
validateSecureUrl(kasEndpoint);
|
|
176
|
-
|
|
177
|
-
// Parse kasEndpoint to URL, then append to its path and update its query parameters
|
|
178
|
-
let pkUrlV2: URL;
|
|
179
172
|
try {
|
|
180
|
-
|
|
173
|
+
return await fetchKasBasePubKey(kasEndpoint);
|
|
181
174
|
} catch (e) {
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
if (!pkUrlV2.pathname.endsWith('kas_public_key')) {
|
|
185
|
-
if (!pkUrlV2.pathname.endsWith('/')) {
|
|
186
|
-
pkUrlV2.pathname += '/';
|
|
187
|
-
}
|
|
188
|
-
pkUrlV2.pathname += 'v2/kas_public_key';
|
|
189
|
-
}
|
|
190
|
-
pkUrlV2.searchParams.set('algorithm', algorithm || 'rsa:2048');
|
|
191
|
-
if (!pkUrlV2.searchParams.get('v')) {
|
|
192
|
-
pkUrlV2.searchParams.set('v', '2');
|
|
175
|
+
console.log(e);
|
|
193
176
|
}
|
|
194
177
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
throw new NetworkError(`unable to fetch public key from [${pkUrlV2}]`, e);
|
|
200
|
-
}
|
|
201
|
-
if (!kasPubKeyResponseV2.ok) {
|
|
202
|
-
switch (kasPubKeyResponseV2.status) {
|
|
203
|
-
case 404:
|
|
204
|
-
throw new ConfigurationError(`404 for [${pkUrlV2}]`);
|
|
205
|
-
case 401:
|
|
206
|
-
throw new UnauthenticatedError(`401 for [${pkUrlV2}]`);
|
|
207
|
-
case 403:
|
|
208
|
-
throw new PermissionDeniedError(`403 for [${pkUrlV2}]`);
|
|
209
|
-
default:
|
|
210
|
-
throw new NetworkError(
|
|
211
|
-
`${pkUrlV2} => ${kasPubKeyResponseV2.status} ${kasPubKeyResponseV2.statusText}`
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
const jsonContent = await kasPubKeyResponseV2.json();
|
|
216
|
-
const { publicKey, kid }: KasPublicKeyInfo = jsonContent;
|
|
217
|
-
if (!publicKey) {
|
|
218
|
-
throw new NetworkError(
|
|
219
|
-
`invalid response from public key endpoint [${JSON.stringify(jsonContent)}]`
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
return {
|
|
223
|
-
key: noteInvalidPublicKey(pkUrlV2, pemToCryptoPublicKey(publicKey)),
|
|
224
|
-
publicKey,
|
|
225
|
-
url: kasEndpoint,
|
|
226
|
-
algorithm: algorithm || 'rsa:2048',
|
|
227
|
-
...(kid && { kid }),
|
|
228
|
-
};
|
|
178
|
+
return await tryPromisesUntilFirstSuccess(
|
|
179
|
+
() => fetchKasPubKeyRpc(kasEndpoint, algorithm),
|
|
180
|
+
() => fetchKasPubKeyLegacy(kasEndpoint, algorithm)
|
|
181
|
+
);
|
|
229
182
|
}
|
|
230
183
|
|
|
231
184
|
const origin = (u: string): string => {
|
|
@@ -252,3 +205,25 @@ export class OriginAllowList {
|
|
|
252
205
|
return this.origins.includes(origin(url));
|
|
253
206
|
}
|
|
254
207
|
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Tries two promise-returning functions in order and returns the first successful result.
|
|
211
|
+
* If both fail, throws the error from the second.
|
|
212
|
+
* @param first First function returning a promise to try.
|
|
213
|
+
* @param second Second function returning a promise to try if the first fails.
|
|
214
|
+
*/
|
|
215
|
+
async function tryPromisesUntilFirstSuccess<T>(
|
|
216
|
+
first: () => Promise<T>,
|
|
217
|
+
second: () => Promise<T>
|
|
218
|
+
): Promise<T> {
|
|
219
|
+
try {
|
|
220
|
+
return await first();
|
|
221
|
+
} catch (e1) {
|
|
222
|
+
console.info('v2 request error', e1);
|
|
223
|
+
try {
|
|
224
|
+
return await second();
|
|
225
|
+
} catch (err) {
|
|
226
|
+
throw err;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|