@opentdf/sdk 0.5.0-beta.48 → 0.5.0-beta.50
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-rpc.js +55 -3
- package/dist/cjs/src/nanotdf/Client.js +48 -43
- package/dist/cjs/src/utils.js +31 -1
- package/dist/cjs/tdf3/src/tdf.js +88 -28
- package/dist/types/src/access/access-rpc.d.ts +2 -0
- package/dist/types/src/access/access-rpc.d.ts.map +1 -1
- package/dist/types/src/nanotdf/Client.d.ts.map +1 -1
- package/dist/types/src/utils.d.ts +5 -0
- package/dist/types/src/utils.d.ts.map +1 -1
- package/dist/types/tdf3/src/tdf.d.ts +0 -4
- package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
- package/dist/web/src/access/access-rpc.js +54 -4
- package/dist/web/src/nanotdf/Client.js +49 -11
- package/dist/web/src/utils.js +30 -1
- package/dist/web/tdf3/src/tdf.js +89 -29
- package/package.json +1 -1
- package/src/access/access-rpc.ts +66 -3
- package/src/nanotdf/Client.ts +58 -9
- package/src/utils.ts +35 -1
- package/tdf3/src/tdf.ts +108 -39
package/src/access/access-rpc.ts
CHANGED
|
@@ -8,7 +8,14 @@ import {
|
|
|
8
8
|
} from '../access.js';
|
|
9
9
|
|
|
10
10
|
import { type AuthProvider } from '../auth/auth.js';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
ConfigurationError,
|
|
13
|
+
InvalidFileError,
|
|
14
|
+
NetworkError,
|
|
15
|
+
PermissionDeniedError,
|
|
16
|
+
ServiceError,
|
|
17
|
+
UnauthenticatedError,
|
|
18
|
+
} from '../errors.js';
|
|
12
19
|
import { PlatformClient } from '../platform.js';
|
|
13
20
|
import { RewrapResponse } from '../platform/kas/kas_pb.js';
|
|
14
21
|
import { ListKeyAccessServersResponse } from '../platform/policy/kasregistry/key_access_server_registry_pb.js';
|
|
@@ -19,6 +26,7 @@ import {
|
|
|
19
26
|
validateSecureUrl,
|
|
20
27
|
} from '../utils.js';
|
|
21
28
|
import { X_REWRAP_ADDITIONAL_CONTEXT } from './constants.js';
|
|
29
|
+
import { ConnectError, Code } from '@connectrpc/connect';
|
|
22
30
|
|
|
23
31
|
/**
|
|
24
32
|
* Get a rewrapped access key to the document, if possible
|
|
@@ -42,11 +50,66 @@ export async function fetchWrappedKey(
|
|
|
42
50
|
[X_REWRAP_ADDITIONAL_CONTEXT]: rewrapAdditionalContextHeader,
|
|
43
51
|
};
|
|
44
52
|
}
|
|
53
|
+
let response: RewrapResponse;
|
|
45
54
|
try {
|
|
46
|
-
|
|
55
|
+
response = await platform.v1.access.rewrap({ signedRequestToken }, options);
|
|
47
56
|
} catch (e) {
|
|
48
|
-
|
|
57
|
+
handleRpcRewrapError(e, platformUrl);
|
|
58
|
+
}
|
|
59
|
+
return response;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function handleRpcRewrapError(e: unknown, platformUrl: string): never {
|
|
63
|
+
if (e instanceof ConnectError) {
|
|
64
|
+
console.log('Error is a ConnectError with code:', e.code);
|
|
65
|
+
switch (e.code) {
|
|
66
|
+
case Code.InvalidArgument: // 400 Bad Request
|
|
67
|
+
throw new InvalidFileError(`400 for [${platformUrl}]: rewrap bad request [${e.message}]`);
|
|
68
|
+
case Code.PermissionDenied: // 403 Forbidden
|
|
69
|
+
throw new PermissionDeniedError(`403 for [${platformUrl}]; rewrap permission denied`);
|
|
70
|
+
case Code.Unauthenticated: // 401 Unauthorized
|
|
71
|
+
throw new UnauthenticatedError(`401 for [${platformUrl}]; rewrap auth failure`);
|
|
72
|
+
case Code.Internal:
|
|
73
|
+
case Code.Unimplemented:
|
|
74
|
+
case Code.DataLoss:
|
|
75
|
+
case Code.Unknown:
|
|
76
|
+
case Code.DeadlineExceeded:
|
|
77
|
+
case Code.Unavailable: // >=500 Server Error
|
|
78
|
+
throw new ServiceError(
|
|
79
|
+
`${e.code} for [${platformUrl}]: rewrap failure due to service error [${e.message}]`
|
|
80
|
+
);
|
|
81
|
+
default:
|
|
82
|
+
throw new NetworkError(`[${platformUrl}] [Rewrap] ${e.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
throw new NetworkError(`[${platformUrl}] [Rewrap] ${extractRpcErrorMessage(e)}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function handleRpcRewrapErrorString(e: string, platformUrl: string): never {
|
|
89
|
+
if (e.includes(Code[Code.InvalidArgument])) {
|
|
90
|
+
// 400 Bad Request
|
|
91
|
+
throw new InvalidFileError(`400 for [${platformUrl}]: rewrap bad request [${e}]`);
|
|
92
|
+
}
|
|
93
|
+
if (e.includes(Code[Code.PermissionDenied])) {
|
|
94
|
+
// 403 Forbidden
|
|
95
|
+
throw new PermissionDeniedError(`403 for [${platformUrl}]; rewrap permission denied`);
|
|
96
|
+
}
|
|
97
|
+
if (e.includes(Code[Code.Unauthenticated])) {
|
|
98
|
+
// 401 Unauthorized
|
|
99
|
+
throw new UnauthenticatedError(`401 for [${platformUrl}]; rewrap auth failure`);
|
|
100
|
+
}
|
|
101
|
+
if (
|
|
102
|
+
e.includes(Code[Code.Internal]) ||
|
|
103
|
+
e.includes(Code[Code.Unimplemented]) ||
|
|
104
|
+
e.includes(Code[Code.DataLoss]) ||
|
|
105
|
+
e.includes(Code[Code.Unknown]) ||
|
|
106
|
+
e.includes(Code[Code.DeadlineExceeded]) ||
|
|
107
|
+
e.includes(Code[Code.Unavailable])
|
|
108
|
+
) {
|
|
109
|
+
// >=500
|
|
110
|
+
throw new ServiceError(`500+ [${platformUrl}]: rewrap failure due to service error [${e}]`);
|
|
49
111
|
}
|
|
112
|
+
throw new NetworkError(`[${platformUrl}] [Rewrap] ${e}`);
|
|
50
113
|
}
|
|
51
114
|
|
|
52
115
|
export async function fetchKeyAccessServers(
|
package/src/nanotdf/Client.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { create, toJsonString } from '@bufbuild/protobuf';
|
|
2
|
+
import {
|
|
3
|
+
UnsignedRewrapRequest_WithPolicyRequestSchema,
|
|
4
|
+
UnsignedRewrapRequestSchema,
|
|
5
|
+
} from '../platform/kas/kas_pb.js';
|
|
2
6
|
import { generateKeyPair, keyAgreement } from '../nanotdf-crypto/index.js';
|
|
3
7
|
import getHkdfSalt from './helpers/getHkdfSalt.js';
|
|
4
8
|
import DefaultParams from './models/DefaultParams.js';
|
|
@@ -8,13 +12,16 @@ import {
|
|
|
8
12
|
KasPublicKeyInfo,
|
|
9
13
|
OriginAllowList,
|
|
10
14
|
} from '../access.js';
|
|
15
|
+
import { handleRpcRewrapErrorString } from '../../src/access/access-rpc.js';
|
|
11
16
|
import { AuthProvider, isAuthProvider, reqSignature } from '../auth/providers.js';
|
|
12
17
|
import { ConfigurationError, DecryptError, TdfError, UnsafeUrlError } from '../errors.js';
|
|
13
18
|
import {
|
|
14
19
|
cryptoPublicToPem,
|
|
15
20
|
getRequiredObligationFQNs,
|
|
16
21
|
pemToCryptoPublicKey,
|
|
22
|
+
upgradeRewrapResponseV1,
|
|
17
23
|
validateSecureUrl,
|
|
24
|
+
getPlatformUrlFromKasEndpoint,
|
|
18
25
|
} from '../utils.js';
|
|
19
26
|
|
|
20
27
|
export interface ClientConfig {
|
|
@@ -260,18 +267,35 @@ export default class Client {
|
|
|
260
267
|
throw new ConfigurationError('Signer key has not been set or generated');
|
|
261
268
|
}
|
|
262
269
|
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
270
|
+
const unsignedRequest = create(UnsignedRewrapRequestSchema, {
|
|
271
|
+
clientPublicKey: await cryptoPublicToPem(ephemeralKeyPair.publicKey),
|
|
272
|
+
requests: [
|
|
273
|
+
create(UnsignedRewrapRequest_WithPolicyRequestSchema, {
|
|
274
|
+
keyAccessObjects: [
|
|
275
|
+
{
|
|
276
|
+
keyAccessObjectId: 'kao-0', // only one kao, no bulk
|
|
277
|
+
keyAccessObject: {
|
|
278
|
+
header: new Uint8Array(nanoTdfHeader),
|
|
279
|
+
kasUrl: '',
|
|
280
|
+
protocol: Client.KAS_PROTOCOL,
|
|
281
|
+
keyType: Client.KEY_ACCESS_REMOTE,
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
algorithm: DefaultParams.defaultECAlgorithm,
|
|
286
|
+
}),
|
|
287
|
+
],
|
|
266
288
|
keyAccess: {
|
|
267
|
-
|
|
268
|
-
|
|
289
|
+
header: new Uint8Array(nanoTdfHeader),
|
|
290
|
+
kasUrl: '',
|
|
269
291
|
protocol: Client.KAS_PROTOCOL,
|
|
270
|
-
|
|
292
|
+
keyType: Client.KEY_ACCESS_REMOTE,
|
|
271
293
|
},
|
|
272
|
-
|
|
294
|
+
algorithm: DefaultParams.defaultECAlgorithm,
|
|
273
295
|
});
|
|
274
296
|
|
|
297
|
+
const requestBodyStr = toJsonString(UnsignedRewrapRequestSchema, unsignedRequest);
|
|
298
|
+
|
|
275
299
|
const jwtPayload = { requestBody: requestBodyStr };
|
|
276
300
|
|
|
277
301
|
const signedRequestToken = await reqSignature(jwtPayload, requestSignerKeyPair.privateKey, {
|
|
@@ -285,9 +309,34 @@ export default class Client {
|
|
|
285
309
|
this.authProvider,
|
|
286
310
|
this.fulfillableObligationFQNs
|
|
287
311
|
);
|
|
312
|
+
// Upgrade any V1 responses to V2
|
|
313
|
+
upgradeRewrapResponseV1(rewrapResp);
|
|
314
|
+
|
|
315
|
+
const result = rewrapResp.responses?.[0]?.results?.[0];
|
|
316
|
+
if (!result) {
|
|
317
|
+
// This should not happen - KAS should always return at least one response and one result
|
|
318
|
+
// or the upgradeRewrapResponseV1 should have created them
|
|
319
|
+
throw new DecryptError('KAS rewrap response missing expected response or result');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
let entityWrappedKey: Uint8Array<ArrayBufferLike>;
|
|
323
|
+
switch (result.result.case) {
|
|
324
|
+
case 'kasWrappedKey': {
|
|
325
|
+
entityWrappedKey = result.result.value;
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
case 'error': {
|
|
329
|
+
handleRpcRewrapErrorString(
|
|
330
|
+
result.result.value,
|
|
331
|
+
getPlatformUrlFromKasEndpoint(kasRewrapUrl)
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
default: {
|
|
335
|
+
throw new DecryptError('KAS rewrap response missing wrapped key');
|
|
336
|
+
}
|
|
337
|
+
}
|
|
288
338
|
|
|
289
339
|
// Extract the iv and ciphertext
|
|
290
|
-
const entityWrappedKey = rewrapResp.entityWrappedKey;
|
|
291
340
|
const ivLength =
|
|
292
341
|
clientVersion == Client.SDK_INITIAL_RELEASE ? Client.INITIAL_RELEASE_IV_SIZE : Client.IV_SIZE;
|
|
293
342
|
const iv = entityWrappedKey.subarray(0, ivLength);
|
package/src/utils.ts
CHANGED
|
@@ -3,7 +3,12 @@ import { exportSPKI, importX509 } from 'jose';
|
|
|
3
3
|
import { base64 } from './encodings/index.js';
|
|
4
4
|
import { pemCertToCrypto, pemPublicToCrypto } from './nanotdf-crypto/pemPublicToCrypto.js';
|
|
5
5
|
import { ConfigurationError } from './errors.js';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
RewrapResponse,
|
|
8
|
+
PolicyRewrapResultSchema,
|
|
9
|
+
KeyAccessRewrapResultSchema,
|
|
10
|
+
} from './platform/kas/kas_pb.js';
|
|
11
|
+
import { create } from '@bufbuild/protobuf';
|
|
7
12
|
import { ConnectError } from '@connectrpc/connect';
|
|
8
13
|
|
|
9
14
|
const REQUIRED_OBLIGATIONS_METADATA_KEY = 'X-Required-Obligations';
|
|
@@ -255,3 +260,32 @@ export function getRequiredObligationFQNs(response: RewrapResponse) {
|
|
|
255
260
|
|
|
256
261
|
return [...requiredObligations.values()];
|
|
257
262
|
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Upgrades a RewrapResponse from v1 format to v2.
|
|
266
|
+
* Note: This mutates the response in place.
|
|
267
|
+
*/
|
|
268
|
+
export function upgradeRewrapResponseV1(response: RewrapResponse) {
|
|
269
|
+
if (response.responses.length > 0) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (response.entityWrappedKey.length === 0) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
response.responses = [
|
|
277
|
+
create(PolicyRewrapResultSchema, {
|
|
278
|
+
policyId: 'policy',
|
|
279
|
+
results: [
|
|
280
|
+
create(KeyAccessRewrapResultSchema, {
|
|
281
|
+
keyAccessObjectId: 'kao-0',
|
|
282
|
+
status: 'permit',
|
|
283
|
+
result: {
|
|
284
|
+
case: 'kasWrappedKey',
|
|
285
|
+
value: response.entityWrappedKey,
|
|
286
|
+
},
|
|
287
|
+
}),
|
|
288
|
+
],
|
|
289
|
+
}),
|
|
290
|
+
];
|
|
291
|
+
}
|
package/tdf3/src/tdf.ts
CHANGED
|
@@ -8,7 +8,16 @@ import {
|
|
|
8
8
|
fetchWrappedKey,
|
|
9
9
|
publicKeyAlgorithmToJwa,
|
|
10
10
|
} from '../../src/access.js';
|
|
11
|
+
import { create, toJsonString } from '@bufbuild/protobuf';
|
|
12
|
+
import {
|
|
13
|
+
KeyAccessSchema,
|
|
14
|
+
UnsignedRewrapRequestSchema,
|
|
15
|
+
UnsignedRewrapRequest_WithPolicyRequestSchema,
|
|
16
|
+
UnsignedRewrapRequest_WithPolicySchema,
|
|
17
|
+
UnsignedRewrapRequest_WithKeyAccessObjectSchema,
|
|
18
|
+
} from '../../src/platform/kas/kas_pb.js';
|
|
11
19
|
import { type AuthProvider, reqSignature } from '../../src/auth/auth.js';
|
|
20
|
+
import { handleRpcRewrapErrorString } from '../../src/access/access-rpc.js';
|
|
12
21
|
import { allPool, anyPool } from '../../src/concurrency.js';
|
|
13
22
|
import { base64, hex } from '../../src/encodings/index.js';
|
|
14
23
|
import {
|
|
@@ -55,7 +64,11 @@ import { ZipReader, ZipWriter, keyMerge, concatUint8, buffToString } from './uti
|
|
|
55
64
|
import { CentralDirectory } from './utils/zip-reader.js';
|
|
56
65
|
import { ztdfSalt } from './crypto/salt.js';
|
|
57
66
|
import { Payload } from './models/payload.js';
|
|
58
|
-
import {
|
|
67
|
+
import {
|
|
68
|
+
getRequiredObligationFQNs,
|
|
69
|
+
upgradeRewrapResponseV1,
|
|
70
|
+
getPlatformUrlFromKasEndpoint,
|
|
71
|
+
} from '../../src/utils.js';
|
|
59
72
|
|
|
60
73
|
// TODO: input validation on manifest JSON
|
|
61
74
|
const DEFAULT_SEGMENT_SIZE = 1024 * 1024;
|
|
@@ -189,11 +202,6 @@ export type RewrapRequest = {
|
|
|
189
202
|
|
|
190
203
|
export type KasPublicKeyFormat = 'pkcs8' | 'jwks';
|
|
191
204
|
|
|
192
|
-
export type RewrapResponse = {
|
|
193
|
-
entityWrappedKey: string;
|
|
194
|
-
sessionPublicKey: string;
|
|
195
|
-
};
|
|
196
|
-
|
|
197
205
|
/**
|
|
198
206
|
* If we have KAS url but not public key we can fetch it from KAS, fetching
|
|
199
207
|
* the value from `${kas}/kas_public_key`.
|
|
@@ -783,13 +791,50 @@ async function unwrapKey({
|
|
|
783
791
|
|
|
784
792
|
const clientPublicKey = ephemeralEncryptionKeys.publicKey;
|
|
785
793
|
|
|
786
|
-
|
|
794
|
+
// Convert keySplitInfo to protobuf KeyAccess
|
|
795
|
+
const keyAccessProto = create(KeyAccessSchema, {
|
|
796
|
+
...(keySplitInfo.type && { keyType: keySplitInfo.type }),
|
|
797
|
+
...(keySplitInfo.url && { kasUrl: keySplitInfo.url }),
|
|
798
|
+
...(keySplitInfo.protocol && { protocol: keySplitInfo.protocol }),
|
|
799
|
+
...(keySplitInfo.wrappedKey && {
|
|
800
|
+
wrappedKey: new Uint8Array(base64.decodeArrayBuffer(keySplitInfo.wrappedKey)),
|
|
801
|
+
}),
|
|
802
|
+
...(keySplitInfo.policyBinding && { policyBinding: keySplitInfo.policyBinding }),
|
|
803
|
+
...(keySplitInfo.kid && { kid: keySplitInfo.kid }),
|
|
804
|
+
...(keySplitInfo.sid && { splitId: keySplitInfo.sid }),
|
|
805
|
+
...(keySplitInfo.encryptedMetadata && { encryptedMetadata: keySplitInfo.encryptedMetadata }),
|
|
806
|
+
...(keySplitInfo.ephemeralPublicKey && {
|
|
807
|
+
ephemeralPublicKey: keySplitInfo.ephemeralPublicKey,
|
|
808
|
+
}),
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
// Create the protobuf request
|
|
812
|
+
const unsignedRequest = create(UnsignedRewrapRequestSchema, {
|
|
813
|
+
clientPublicKey,
|
|
814
|
+
requests: [
|
|
815
|
+
create(UnsignedRewrapRequest_WithPolicyRequestSchema, {
|
|
816
|
+
keyAccessObjects: [
|
|
817
|
+
create(UnsignedRewrapRequest_WithKeyAccessObjectSchema, {
|
|
818
|
+
keyAccessObjectId: 'kao-0',
|
|
819
|
+
keyAccessObject: keyAccessProto,
|
|
820
|
+
}),
|
|
821
|
+
],
|
|
822
|
+
...(manifest.encryptionInformation.policy && {
|
|
823
|
+
policy: create(UnsignedRewrapRequest_WithPolicySchema, {
|
|
824
|
+
id: 'policy',
|
|
825
|
+
body: manifest.encryptionInformation.policy,
|
|
826
|
+
}),
|
|
827
|
+
}),
|
|
828
|
+
}),
|
|
829
|
+
],
|
|
830
|
+
// include deprecated fields for backward compatibility
|
|
787
831
|
algorithm: 'RS256',
|
|
788
|
-
keyAccess:
|
|
832
|
+
keyAccess: keyAccessProto,
|
|
789
833
|
policy: manifest.encryptionInformation.policy,
|
|
790
|
-
clientPublicKey,
|
|
791
834
|
});
|
|
792
835
|
|
|
836
|
+
const requestBodyStr = toJsonString(UnsignedRewrapRequestSchema, unsignedRequest);
|
|
837
|
+
|
|
793
838
|
const jwtPayload = { requestBody: requestBodyStr };
|
|
794
839
|
const signedRequestToken = await reqSignature(jwtPayload, dpopKeys.privateKey);
|
|
795
840
|
|
|
@@ -799,39 +844,63 @@ async function unwrapKey({
|
|
|
799
844
|
authProvider,
|
|
800
845
|
fulfillableObligations
|
|
801
846
|
);
|
|
802
|
-
|
|
847
|
+
// Upgrade V1 response to V2 format if needed
|
|
848
|
+
upgradeRewrapResponseV1(rewrapResp);
|
|
849
|
+
const { sessionPublicKey } = rewrapResp;
|
|
803
850
|
const requiredObligations = getRequiredObligationFQNs(rewrapResp);
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
hkdfHash: 'SHA-256',
|
|
811
|
-
});
|
|
812
|
-
const wrappedKeyAndNonce = entityWrappedKey;
|
|
813
|
-
const iv = wrappedKeyAndNonce.slice(0, 12);
|
|
814
|
-
const wrappedKey = wrappedKeyAndNonce.slice(12);
|
|
815
|
-
|
|
816
|
-
const dek = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, kek, wrappedKey);
|
|
817
|
-
|
|
818
|
-
return {
|
|
819
|
-
key: new Uint8Array(dek),
|
|
820
|
-
metadata,
|
|
821
|
-
requiredObligations,
|
|
822
|
-
};
|
|
851
|
+
// Assume only one response and one result for now (V1 style)
|
|
852
|
+
const result = rewrapResp.responses?.[0]?.results?.[0];
|
|
853
|
+
if (!result) {
|
|
854
|
+
// This should not happen - KAS should always return at least one response and one result
|
|
855
|
+
// or the upgradeRewrapResponseV1 should have created them
|
|
856
|
+
throw new DecryptError('KAS rewrap response missing expected response or result');
|
|
823
857
|
}
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
858
|
+
const metadata = result.metadata;
|
|
859
|
+
// Handle the different cases of result.result
|
|
860
|
+
switch (result.result.case) {
|
|
861
|
+
case 'kasWrappedKey': {
|
|
862
|
+
const entityWrappedKey = result.result.value;
|
|
863
|
+
|
|
864
|
+
if (wrappingKeyAlgorithm === 'ec:secp256r1') {
|
|
865
|
+
const serverEphemeralKey: CryptoKey = await pemPublicToCrypto(sessionPublicKey);
|
|
866
|
+
const ekr = ephemeralEncryptionKeysRaw as CryptoKeyPair;
|
|
867
|
+
const kek = await keyAgreement(ekr.privateKey, serverEphemeralKey, {
|
|
868
|
+
hkdfSalt: await ztdfSalt,
|
|
869
|
+
hkdfHash: 'SHA-256',
|
|
870
|
+
});
|
|
871
|
+
const wrappedKeyAndNonce = entityWrappedKey;
|
|
872
|
+
const iv = wrappedKeyAndNonce.slice(0, 12);
|
|
873
|
+
const wrappedKey = wrappedKeyAndNonce.slice(12);
|
|
829
874
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
875
|
+
const dek = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, kek, wrappedKey);
|
|
876
|
+
|
|
877
|
+
return {
|
|
878
|
+
key: new Uint8Array(dek),
|
|
879
|
+
metadata,
|
|
880
|
+
requiredObligations,
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
const key = Binary.fromArrayBuffer(entityWrappedKey);
|
|
884
|
+
const decryptedKeyBinary = await cryptoService.decryptWithPrivateKey(
|
|
885
|
+
key,
|
|
886
|
+
ephemeralEncryptionKeys.privateKey
|
|
887
|
+
);
|
|
888
|
+
|
|
889
|
+
return {
|
|
890
|
+
key: new Uint8Array(decryptedKeyBinary.asByteArray()),
|
|
891
|
+
metadata,
|
|
892
|
+
requiredObligations,
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
case 'error': {
|
|
897
|
+
handleRpcRewrapErrorString(result.result.value, getPlatformUrlFromKasEndpoint(url));
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
default: {
|
|
901
|
+
throw new DecryptError('KAS rewrap response missing wrapped key');
|
|
902
|
+
}
|
|
903
|
+
}
|
|
835
904
|
}
|
|
836
905
|
|
|
837
906
|
let poolSize = 1;
|