@opentdf/sdk 0.9.0-beta.92 → 0.9.0-beta.94
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/README.md +2 -2
- package/dist/cjs/src/access/access-fetch.js +1 -2
- package/dist/cjs/src/access/access-rpc.js +1 -3
- package/dist/cjs/src/access.js +1 -14
- package/dist/cjs/src/auth/auth.js +13 -10
- package/dist/cjs/src/auth/dpop.js +121 -0
- package/dist/cjs/src/auth/oidc-clientcredentials-provider.js +37 -3
- package/dist/cjs/src/auth/oidc-externaljwt-provider.js +37 -3
- package/dist/cjs/src/auth/oidc-refreshtoken-provider.js +37 -3
- package/dist/cjs/src/auth/oidc.js +10 -8
- package/dist/cjs/src/auth/providers.js +35 -12
- package/dist/cjs/src/crypto/index.js +16 -2
- package/dist/cjs/src/crypto/pemPublicToCrypto.js +17 -11
- package/dist/cjs/src/opentdf.js +50 -13
- package/dist/cjs/src/policy/discovery.js +2 -2
- package/dist/cjs/tdf3/index.js +4 -2
- package/dist/cjs/tdf3/src/assertions.js +71 -31
- package/dist/cjs/tdf3/src/ciphers/aes-gcm-cipher.js +1 -1
- package/dist/cjs/tdf3/src/ciphers/symmetric-cipher-base.js +4 -2
- package/dist/cjs/tdf3/src/client/index.js +23 -33
- package/dist/cjs/tdf3/src/crypto/crypto-utils.js +12 -5
- package/dist/cjs/tdf3/src/crypto/declarations.js +1 -1
- package/dist/cjs/tdf3/src/crypto/index.js +849 -88
- package/dist/cjs/tdf3/src/crypto/jose/jwt-claims-set.js +11 -0
- package/dist/cjs/tdf3/src/crypto/jose/validate-crit.js +8 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/lib/buffer_utils.js +41 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/lib/epoch.js +6 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/lib/is_object.js +21 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/lib/jwt_claims_set.js +112 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/lib/secs.js +60 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/lib/validate_crit.js +38 -0
- package/dist/cjs/tdf3/src/crypto/jose/vendor/util/errors.js +135 -0
- package/dist/cjs/tdf3/src/crypto/jwt.js +183 -0
- package/dist/cjs/tdf3/src/crypto/salt.js +14 -8
- package/dist/cjs/tdf3/src/models/encryption-information.js +17 -20
- package/dist/cjs/tdf3/src/models/key-access.js +43 -63
- package/dist/cjs/tdf3/src/tdf.js +75 -75
- package/dist/cjs/tdf3/src/utils/index.js +5 -39
- package/dist/types/src/access/access-fetch.d.ts.map +1 -1
- package/dist/types/src/access/access-rpc.d.ts.map +1 -1
- package/dist/types/src/access.d.ts +0 -5
- package/dist/types/src/access.d.ts.map +1 -1
- package/dist/types/src/auth/auth.d.ts +9 -6
- package/dist/types/src/auth/auth.d.ts.map +1 -1
- package/dist/types/src/auth/dpop.d.ts +60 -0
- package/dist/types/src/auth/dpop.d.ts.map +1 -0
- package/dist/types/src/auth/oidc-clientcredentials-provider.d.ts +3 -2
- package/dist/types/src/auth/oidc-clientcredentials-provider.d.ts.map +1 -1
- package/dist/types/src/auth/oidc-externaljwt-provider.d.ts +3 -2
- package/dist/types/src/auth/oidc-externaljwt-provider.d.ts.map +1 -1
- package/dist/types/src/auth/oidc-refreshtoken-provider.d.ts +3 -2
- package/dist/types/src/auth/oidc-refreshtoken-provider.d.ts.map +1 -1
- package/dist/types/src/auth/oidc.d.ts +6 -4
- package/dist/types/src/auth/oidc.d.ts.map +1 -1
- package/dist/types/src/auth/providers.d.ts +5 -4
- package/dist/types/src/auth/providers.d.ts.map +1 -1
- package/dist/types/src/crypto/index.d.ts +2 -1
- package/dist/types/src/crypto/index.d.ts.map +1 -1
- package/dist/types/src/crypto/pemPublicToCrypto.d.ts +18 -0
- package/dist/types/src/crypto/pemPublicToCrypto.d.ts.map +1 -1
- package/dist/types/src/opentdf.d.ts +26 -7
- package/dist/types/src/opentdf.d.ts.map +1 -1
- package/dist/types/src/policy/discovery.d.ts +2 -2
- package/dist/types/tdf3/index.d.ts +3 -3
- package/dist/types/tdf3/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/assertions.d.ts +23 -8
- package/dist/types/tdf3/src/assertions.d.ts.map +1 -1
- package/dist/types/tdf3/src/ciphers/aes-gcm-cipher.d.ts +3 -3
- package/dist/types/tdf3/src/ciphers/aes-gcm-cipher.d.ts.map +1 -1
- package/dist/types/tdf3/src/ciphers/symmetric-cipher-base.d.ts +4 -4
- package/dist/types/tdf3/src/ciphers/symmetric-cipher-base.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/builders.d.ts +2 -2
- package/dist/types/tdf3/src/client/builders.d.ts.map +1 -1
- package/dist/types/tdf3/src/client/index.d.ts +6 -5
- package/dist/types/tdf3/src/client/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/crypto/crypto-utils.d.ts +14 -4
- package/dist/types/tdf3/src/crypto/crypto-utils.d.ts.map +1 -1
- package/dist/types/tdf3/src/crypto/declarations.d.ts +283 -18
- package/dist/types/tdf3/src/crypto/declarations.d.ts.map +1 -1
- package/dist/types/tdf3/src/crypto/index.d.ts +105 -28
- package/dist/types/tdf3/src/crypto/index.d.ts.map +1 -1
- package/dist/types/tdf3/src/crypto/jose/jwt-claims-set.d.ts +3 -0
- package/dist/types/tdf3/src/crypto/jose/jwt-claims-set.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/validate-crit.d.ts +5 -0
- package/dist/types/tdf3/src/crypto/jose/validate-crit.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/buffer_utils.d.ts +6 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/buffer_utils.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/epoch.d.ts +3 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/epoch.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/is_object.d.ts +3 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/is_object.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/jwt_claims_set.d.ts +3 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/jwt_claims_set.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/secs.d.ts +3 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/secs.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/validate_crit.d.ts +3 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/lib/validate_crit.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/util/errors.d.ts +76 -0
- package/dist/types/tdf3/src/crypto/jose/vendor/util/errors.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/jwt.d.ts +76 -0
- package/dist/types/tdf3/src/crypto/jwt.d.ts.map +1 -0
- package/dist/types/tdf3/src/crypto/salt.d.ts +6 -1
- package/dist/types/tdf3/src/crypto/salt.d.ts.map +1 -1
- package/dist/types/tdf3/src/models/encryption-information.d.ts +4 -4
- package/dist/types/tdf3/src/models/encryption-information.d.ts.map +1 -1
- package/dist/types/tdf3/src/models/key-access.d.ts +8 -5
- package/dist/types/tdf3/src/models/key-access.d.ts.map +1 -1
- package/dist/types/tdf3/src/tdf.d.ts +8 -8
- package/dist/types/tdf3/src/tdf.d.ts.map +1 -1
- package/dist/types/tdf3/src/utils/index.d.ts +4 -3
- package/dist/types/tdf3/src/utils/index.d.ts.map +1 -1
- package/dist/web/src/access/access-fetch.js +3 -4
- package/dist/web/src/access/access-rpc.js +3 -5
- package/dist/web/src/access.js +1 -13
- package/dist/web/src/auth/auth.js +13 -10
- package/dist/web/src/auth/dpop.js +118 -0
- package/dist/web/src/auth/oidc-clientcredentials-provider.js +4 -3
- package/dist/web/src/auth/oidc-externaljwt-provider.js +4 -3
- package/dist/web/src/auth/oidc-refreshtoken-provider.js +4 -3
- package/dist/web/src/auth/oidc.js +11 -9
- package/dist/web/src/auth/providers.js +13 -12
- package/dist/web/src/crypto/index.js +4 -2
- package/dist/web/src/crypto/pemPublicToCrypto.js +11 -9
- package/dist/web/src/opentdf.js +17 -13
- package/dist/web/src/policy/discovery.js +2 -2
- package/dist/web/tdf3/index.js +3 -2
- package/dist/web/tdf3/src/assertions.js +71 -31
- package/dist/web/tdf3/src/ciphers/aes-gcm-cipher.js +1 -1
- package/dist/web/tdf3/src/ciphers/symmetric-cipher-base.js +4 -2
- package/dist/web/tdf3/src/client/index.js +25 -35
- package/dist/web/tdf3/src/crypto/crypto-utils.js +12 -5
- package/dist/web/tdf3/src/crypto/declarations.js +1 -1
- package/dist/web/tdf3/src/crypto/index.js +830 -84
- package/dist/web/tdf3/src/crypto/jose/jwt-claims-set.js +5 -0
- package/dist/web/tdf3/src/crypto/jose/validate-crit.js +3 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/lib/buffer_utils.js +35 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/lib/epoch.js +4 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/lib/is_object.js +19 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/lib/jwt_claims_set.js +107 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/lib/secs.js +58 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/lib/validate_crit.js +36 -0
- package/dist/web/tdf3/src/crypto/jose/vendor/util/errors.js +117 -0
- package/dist/web/tdf3/src/crypto/jwt.js +174 -0
- package/dist/web/tdf3/src/crypto/salt.js +13 -7
- package/dist/web/tdf3/src/models/encryption-information.js +11 -14
- package/dist/web/tdf3/src/models/key-access.js +44 -31
- package/dist/web/tdf3/src/tdf.js +71 -71
- package/dist/web/tdf3/src/utils/index.js +5 -6
- package/package.json +11 -4
- package/src/access/access-fetch.ts +2 -8
- package/src/access/access-rpc.ts +0 -7
- package/src/access.ts +0 -17
- package/src/auth/auth.ts +21 -12
- package/src/auth/dpop.ts +222 -0
- package/src/auth/oidc-clientcredentials-provider.ts +23 -15
- package/src/auth/oidc-externaljwt-provider.ts +23 -15
- package/src/auth/oidc-refreshtoken-provider.ts +23 -15
- package/src/auth/oidc.ts +21 -10
- package/src/auth/providers.ts +46 -29
- package/src/crypto/index.ts +21 -1
- package/src/crypto/pemPublicToCrypto.ts +11 -9
- package/src/opentdf.ts +36 -17
- package/src/policy/discovery.ts +2 -2
- package/tdf3/index.ts +32 -5
- package/tdf3/src/assertions.ts +99 -30
- package/tdf3/src/ciphers/aes-gcm-cipher.ts +7 -2
- package/tdf3/src/ciphers/symmetric-cipher-base.ts +7 -4
- package/tdf3/src/client/builders.ts +2 -2
- package/tdf3/src/client/index.ts +60 -59
- package/tdf3/src/crypto/crypto-utils.ts +15 -8
- package/tdf3/src/crypto/declarations.ts +338 -22
- package/tdf3/src/crypto/index.ts +1021 -118
- package/tdf3/src/crypto/jose/jwt-claims-set.ts +10 -0
- package/tdf3/src/crypto/jose/validate-crit.ts +9 -0
- package/tdf3/src/crypto/jose/vendor/lib/buffer_utils.ts +34 -0
- package/tdf3/src/crypto/jose/vendor/lib/epoch.ts +3 -0
- package/tdf3/src/crypto/jose/vendor/lib/is_object.ts +18 -0
- package/tdf3/src/crypto/jose/vendor/lib/jwt_claims_set.ts +106 -0
- package/tdf3/src/crypto/jose/vendor/lib/secs.ts +57 -0
- package/tdf3/src/crypto/jose/vendor/lib/validate_crit.ts +35 -0
- package/tdf3/src/crypto/jose/vendor/util/errors.ts +101 -0
- package/tdf3/src/crypto/jwt.ts +256 -0
- package/tdf3/src/crypto/salt.ts +16 -8
- package/tdf3/src/models/encryption-information.ts +14 -21
- package/tdf3/src/models/key-access.ts +57 -41
- package/tdf3/src/tdf.ts +110 -93
- package/tdf3/src/utils/index.ts +5 -6
package/tdf3/src/assertions.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { canonicalizeEx } from 'json-canonicalize';
|
|
2
|
-
import { SignJWT, jwtVerify, importJWK, importX509 } from 'jose';
|
|
3
2
|
import { base64, hex } from '../../src/encodings/index.js';
|
|
4
3
|
import { ConfigurationError, IntegrityError, InvalidFileError } from '../../src/errors.js';
|
|
5
4
|
import { tdfSpecVersion, version as sdkVersion } from '../../src/version.js';
|
|
5
|
+
import {
|
|
6
|
+
type CryptoService,
|
|
7
|
+
type PrivateKey,
|
|
8
|
+
type PublicKey,
|
|
9
|
+
type SymmetricKey,
|
|
10
|
+
} from './crypto/declarations.js';
|
|
11
|
+
import { decodeProtectedHeader, signJwt, verifyJwt, type JwtHeader } from './crypto/jwt.js';
|
|
6
12
|
|
|
7
13
|
export type AssertionKeyAlg = 'ES256' | 'RS256' | 'HS256';
|
|
8
14
|
export type AssertionType = 'handling' | 'other';
|
|
@@ -41,39 +47,69 @@ export type AssertionPayload = {
|
|
|
41
47
|
/**
|
|
42
48
|
* Computes the SHA-256 hash of the assertion object, excluding the 'binding' and 'hash' properties.
|
|
43
49
|
*
|
|
50
|
+
* @param a - The assertion to hash
|
|
51
|
+
* @param cryptoService - The crypto service to use for hashing
|
|
44
52
|
* @returns the hexadecimal string representation of the hash
|
|
45
53
|
*/
|
|
46
|
-
export async function hash(a: Assertion): Promise<string> {
|
|
54
|
+
export async function hash(a: Assertion, cryptoService: CryptoService): Promise<string> {
|
|
47
55
|
const result = canonicalizeEx(a, {
|
|
48
56
|
exclude: ['binding', 'hash', 'sign', 'verify', 'signingKey'],
|
|
49
57
|
});
|
|
50
58
|
|
|
51
|
-
const
|
|
52
|
-
return hex.encodeArrayBuffer(
|
|
59
|
+
const hashBytes = await cryptoService.digest('SHA-256', new TextEncoder().encode(result));
|
|
60
|
+
return hex.encodeArrayBuffer(hashBytes.buffer);
|
|
53
61
|
}
|
|
54
62
|
|
|
55
63
|
/**
|
|
56
64
|
* Signs the given hash and signature using the provided key and sets the binding method and signature.
|
|
57
65
|
*
|
|
58
|
-
* @param
|
|
66
|
+
* @param thiz - The assertion to sign.
|
|
67
|
+
* @param assertionHash - The hash to be signed.
|
|
59
68
|
* @param sig - The signature to be signed.
|
|
60
|
-
* @param
|
|
61
|
-
* @
|
|
69
|
+
* @param key - The key used for signing.
|
|
70
|
+
* @param cryptoService - The crypto service to use for signing.
|
|
71
|
+
* @returns A promise that resolves to the signed assertion.
|
|
62
72
|
*/
|
|
63
73
|
async function sign(
|
|
64
74
|
thiz: Assertion,
|
|
65
75
|
assertionHash: string,
|
|
66
76
|
sig: string,
|
|
67
|
-
key: AssertionKey
|
|
77
|
+
key: AssertionKey,
|
|
78
|
+
cryptoService: CryptoService
|
|
68
79
|
): Promise<Assertion> {
|
|
69
80
|
const payload: AssertionPayload = {
|
|
70
81
|
assertionHash,
|
|
71
82
|
assertionSig: sig,
|
|
72
83
|
};
|
|
73
84
|
|
|
85
|
+
const header: JwtHeader = { alg: key.alg };
|
|
86
|
+
|
|
87
|
+
if (typeof key.key === 'object' && '_brand' in key.key && key.key._brand === 'PublicKey') {
|
|
88
|
+
throw new ConfigurationError(
|
|
89
|
+
'Cannot sign assertion with PublicKey. Use PrivateKey or SymmetricKey for signing.'
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let signingMaterial: PrivateKey | SymmetricKey;
|
|
94
|
+
if (typeof key.key === 'string') {
|
|
95
|
+
if (!cryptoService.importPrivateKey) {
|
|
96
|
+
throw new ConfigurationError(
|
|
97
|
+
'CryptoService does not support importing private keys. Cannot sign assertion with a PEM string. Use PrivateKey or SymmetricKey for signing.'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
signingMaterial = await cryptoService.importPrivateKey(key.key, {
|
|
101
|
+
usage: 'sign',
|
|
102
|
+
extractable: false,
|
|
103
|
+
});
|
|
104
|
+
} else if (key.key instanceof Uint8Array) {
|
|
105
|
+
signingMaterial = await cryptoService.importSymmetricKey(key.key);
|
|
106
|
+
} else {
|
|
107
|
+
signingMaterial = key.key as PrivateKey | SymmetricKey;
|
|
108
|
+
}
|
|
109
|
+
|
|
74
110
|
let token: string;
|
|
75
111
|
try {
|
|
76
|
-
token = await
|
|
112
|
+
token = await signJwt(cryptoService, payload, signingMaterial, header);
|
|
77
113
|
} catch (error) {
|
|
78
114
|
throw new ConfigurationError(`Signing assertion failed: ${error.message}`, error);
|
|
79
115
|
}
|
|
@@ -107,36 +143,54 @@ export function isAssertionConfig(obj: unknown): obj is AssertionConfig {
|
|
|
107
143
|
/**
|
|
108
144
|
* Verifies the signature of the assertion using the provided key.
|
|
109
145
|
*
|
|
110
|
-
* @param
|
|
111
|
-
* @
|
|
112
|
-
* @
|
|
146
|
+
* @param thiz - The assertion to verify.
|
|
147
|
+
* @param aggregateHash - The aggregate hash for integrity checking.
|
|
148
|
+
* @param key - The key used for verification.
|
|
149
|
+
* @param isLegacyTDF - Whether this is a legacy TDF format.
|
|
150
|
+
* @param cryptoService - The crypto service to use for verification.
|
|
151
|
+
* @throws {InvalidFileError} If the verification fails.
|
|
152
|
+
* @throws {IntegrityError} If the integrity check fails.
|
|
113
153
|
*/
|
|
114
154
|
export async function verify(
|
|
115
155
|
thiz: Assertion,
|
|
116
156
|
aggregateHash: Uint8Array,
|
|
117
157
|
key: AssertionKey,
|
|
118
|
-
isLegacyTDF: boolean
|
|
158
|
+
isLegacyTDF: boolean,
|
|
159
|
+
cryptoService: CryptoService
|
|
119
160
|
): Promise<void> {
|
|
120
161
|
let payload: AssertionPayload;
|
|
121
162
|
try {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
163
|
+
// Parse JWT header to check for embedded keys (jwk or x5c)
|
|
164
|
+
const header = decodeProtectedHeader(thiz.binding.signature);
|
|
165
|
+
|
|
166
|
+
// Runtime check: ensure we have a verification key, not a signing key
|
|
167
|
+
if (typeof key.key === 'object' && '_brand' in key.key && key.key._brand === 'PrivateKey') {
|
|
168
|
+
throw new ConfigurationError(
|
|
169
|
+
'Cannot verify assertion with PrivateKey. Use PublicKey or SymmetricKey for verification.'
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
let verificationKey: string | Uint8Array | PublicKey | SymmetricKey = key.key;
|
|
173
|
+
|
|
174
|
+
if (header.jwk) {
|
|
175
|
+
// Convert embedded JWK to PEM
|
|
176
|
+
verificationKey = await cryptoService.jwkToPublicKeyPem(header.jwk as JsonWebKey);
|
|
177
|
+
} else if (header.x5c && Array.isArray(header.x5c) && header.x5c.length > 0) {
|
|
178
|
+
// Extract public key from X.509 certificate
|
|
179
|
+
const cert = `-----BEGIN CERTIFICATE-----\n${header.x5c[0]}\n-----END CERTIFICATE-----`;
|
|
180
|
+
verificationKey = await cryptoService.extractPublicKeyPem(cert);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const result = await verifyJwt(cryptoService, thiz.binding.signature, verificationKey, {
|
|
184
|
+
algorithms: [key.alg],
|
|
131
185
|
});
|
|
132
|
-
payload =
|
|
186
|
+
payload = result.payload as AssertionPayload;
|
|
133
187
|
} catch (error) {
|
|
134
188
|
throw new InvalidFileError(`Verifying assertion failed: ${error.message}`, error);
|
|
135
189
|
}
|
|
136
190
|
const { assertionHash, assertionSig } = payload;
|
|
137
191
|
|
|
138
192
|
// Get the hash of the assertion
|
|
139
|
-
const hashOfAssertion = await hash(thiz);
|
|
193
|
+
const hashOfAssertion = await hash(thiz, cryptoService);
|
|
140
194
|
|
|
141
195
|
// check if assertionHash is same as hashOfAssertion
|
|
142
196
|
if (hashOfAssertion !== assertionHash) {
|
|
@@ -164,13 +218,17 @@ export async function verify(
|
|
|
164
218
|
|
|
165
219
|
/**
|
|
166
220
|
* Creates an Assertion object with the specified properties.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
*
|
|
221
|
+
*
|
|
222
|
+
* @param aggregateHash - The aggregate hash for the assertion.
|
|
223
|
+
* @param assertionConfig - The configuration for the assertion.
|
|
224
|
+
* @param cryptoService - The crypto service to use for signing.
|
|
225
|
+
* @param targetVersion - The target TDF spec version.
|
|
226
|
+
* @returns The created assertion.
|
|
170
227
|
*/
|
|
171
228
|
export async function CreateAssertion(
|
|
172
229
|
aggregateHash: Uint8Array | string,
|
|
173
230
|
assertionConfig: AssertionConfig,
|
|
231
|
+
cryptoService: CryptoService,
|
|
174
232
|
targetVersion?: string
|
|
175
233
|
): Promise<Assertion> {
|
|
176
234
|
if (!assertionConfig.signingKey) {
|
|
@@ -187,7 +245,7 @@ export async function CreateAssertion(
|
|
|
187
245
|
binding: { method: '', signature: '' },
|
|
188
246
|
};
|
|
189
247
|
|
|
190
|
-
const assertionHash = await hash(a);
|
|
248
|
+
const assertionHash = await hash(a, cryptoService);
|
|
191
249
|
let encodedHash: string;
|
|
192
250
|
switch (targetVersion || '4.3.0') {
|
|
193
251
|
case '4.2.2':
|
|
@@ -212,12 +270,23 @@ export async function CreateAssertion(
|
|
|
212
270
|
throw new ConfigurationError(`Unsupported TDF spec version: [${targetVersion}]`);
|
|
213
271
|
}
|
|
214
272
|
|
|
215
|
-
return await sign(a, assertionHash, encodedHash, assertionConfig.signingKey);
|
|
273
|
+
return await sign(a, assertionHash, encodedHash, assertionConfig.signingKey, cryptoService);
|
|
216
274
|
}
|
|
217
275
|
|
|
276
|
+
// TODO: Split AssertionKey into two separate types:
|
|
277
|
+
// - AssertionSigningKey: key restricted to PrivateKey | SymmetricKey (no strings, no raw bytes)
|
|
278
|
+
// - AssertionVerificationKey: key restricted to string | PublicKey | SymmetricKey
|
|
279
|
+
// This would make the signing/verification distinction type-safe rather than relying on runtime checks.
|
|
280
|
+
// AssertionConfig.signingKey would use AssertionSigningKey; verify() and AssertionVerificationKeys would use AssertionVerificationKey.
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Key used for signing or verifying assertions.
|
|
284
|
+
* For asymmetric algorithms (RS256, ES256): PEM string, PrivateKey (for signing), or PublicKey (for verification).
|
|
285
|
+
* For symmetric algorithms (HS256): Uint8Array or SymmetricKey (opaque).
|
|
286
|
+
*/
|
|
218
287
|
export type AssertionKey = {
|
|
219
288
|
alg: AssertionKeyAlg;
|
|
220
|
-
key:
|
|
289
|
+
key: string | Uint8Array | PrivateKey | PublicKey | SymmetricKey;
|
|
221
290
|
};
|
|
222
291
|
|
|
223
292
|
// AssertionConfig is a shadow of Assertion with the addition of the signing key.
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type CryptoService,
|
|
8
8
|
type DecryptResult,
|
|
9
9
|
type EncryptResult,
|
|
10
|
+
type SymmetricKey,
|
|
10
11
|
} from '../crypto/declarations.js';
|
|
11
12
|
|
|
12
13
|
const KEY_LENGTH = 32;
|
|
@@ -45,7 +46,7 @@ export class AesGcmCipher extends SymmetricCipher {
|
|
|
45
46
|
* result from the crypto service and construct the payload automatically from
|
|
46
47
|
* it's parts. There is no need to process the payload.
|
|
47
48
|
*/
|
|
48
|
-
override async encrypt(payload: Binary, key:
|
|
49
|
+
override async encrypt(payload: Binary, key: SymmetricKey, iv: Binary): Promise<EncryptResult> {
|
|
49
50
|
const toConcat: Uint8Array[] = [];
|
|
50
51
|
const result = await this.cryptoService.encrypt(payload, key, iv, Algorithms.AES_256_GCM);
|
|
51
52
|
toConcat.push(new Uint8Array(iv.asArrayBuffer()));
|
|
@@ -62,7 +63,11 @@ export class AesGcmCipher extends SymmetricCipher {
|
|
|
62
63
|
* @returns
|
|
63
64
|
*/
|
|
64
65
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
65
|
-
override async decrypt(
|
|
66
|
+
override async decrypt(
|
|
67
|
+
buffer: ArrayBuffer,
|
|
68
|
+
key: SymmetricKey,
|
|
69
|
+
iv?: Binary
|
|
70
|
+
): Promise<DecryptResult> {
|
|
66
71
|
const { payload, payloadIv, payloadAuthTag } = processGcmPayload(buffer);
|
|
67
72
|
|
|
68
73
|
return this.cryptoService.decrypt(
|
|
@@ -3,7 +3,9 @@ import {
|
|
|
3
3
|
type CryptoService,
|
|
4
4
|
type DecryptResult,
|
|
5
5
|
type EncryptResult,
|
|
6
|
+
type SymmetricKey,
|
|
6
7
|
} from '../crypto/declarations.js';
|
|
8
|
+
import { encodeArrayBuffer as hexEncode } from '../../../src/encodings/hex.js';
|
|
7
9
|
|
|
8
10
|
export abstract class SymmetricCipher {
|
|
9
11
|
cryptoService: CryptoService;
|
|
@@ -22,17 +24,18 @@ export abstract class SymmetricCipher {
|
|
|
22
24
|
if (!this.ivLength) {
|
|
23
25
|
throw Error('No iv length');
|
|
24
26
|
}
|
|
25
|
-
|
|
27
|
+
const bytes = await this.cryptoService.randomBytes(this.ivLength);
|
|
28
|
+
return hexEncode(bytes.buffer);
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
async generateKey(): Promise<
|
|
31
|
+
async generateKey(): Promise<SymmetricKey> {
|
|
29
32
|
if (!this.keyLength) {
|
|
30
33
|
throw Error('No key length');
|
|
31
34
|
}
|
|
32
35
|
return this.cryptoService.generateKey(this.keyLength);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
abstract encrypt(payload: Binary, key:
|
|
38
|
+
abstract encrypt(payload: Binary, key: SymmetricKey, iv: Binary): Promise<EncryptResult>;
|
|
36
39
|
|
|
37
|
-
abstract decrypt(payload: Uint8Array, key:
|
|
40
|
+
abstract decrypt(payload: Uint8Array, key: SymmetricKey, iv?: Binary): Promise<DecryptResult>;
|
|
38
41
|
}
|
|
@@ -4,7 +4,7 @@ import { type Metadata } from '../tdf.js';
|
|
|
4
4
|
import { Binary } from '../binary.js';
|
|
5
5
|
|
|
6
6
|
import { ConfigurationError } from '../../../src/errors.js';
|
|
7
|
-
import { PemKeyPair } from '../crypto/declarations.js';
|
|
7
|
+
import { PemKeyPair, type SymmetricKey } from '../crypto/declarations.js';
|
|
8
8
|
import { DecoratedReadableStream } from './DecoratedReadableStream.js';
|
|
9
9
|
import { type Chunker } from '../../../src/seekable.js';
|
|
10
10
|
import { AssertionConfig, AssertionVerificationKeys } from '../assertions.js';
|
|
@@ -516,7 +516,7 @@ class EncryptParamsBuilder {
|
|
|
516
516
|
}
|
|
517
517
|
}
|
|
518
518
|
|
|
519
|
-
export type DecryptKeyMiddleware = (key:
|
|
519
|
+
export type DecryptKeyMiddleware = (key: SymmetricKey) => Promise<SymmetricKey>;
|
|
520
520
|
|
|
521
521
|
export type DecryptStreamMiddleware = (
|
|
522
522
|
stream: DecoratedReadableStream
|
package/tdf3/src/client/index.ts
CHANGED
|
@@ -19,12 +19,7 @@ import { OIDCRefreshTokenProvider } from '../../../src/auth/oidc-refreshtoken-pr
|
|
|
19
19
|
import { OIDCExternalJwtProvider } from '../../../src/auth/oidc-externaljwt-provider.js';
|
|
20
20
|
import { CryptoService } from '../crypto/declarations.js';
|
|
21
21
|
import { type AuthProvider, HttpRequest, withHeaders } from '../../../src/auth/auth.js';
|
|
22
|
-
import {
|
|
23
|
-
getPlatformUrlFromKasEndpoint,
|
|
24
|
-
pemToCryptoPublicKey,
|
|
25
|
-
rstrip,
|
|
26
|
-
validateSecureUrl,
|
|
27
|
-
} from '../../../src/utils.js';
|
|
22
|
+
import { getPlatformUrlFromKasEndpoint, rstrip, validateSecureUrl } from '../../../src/utils.js';
|
|
28
23
|
|
|
29
24
|
import {
|
|
30
25
|
type DecryptParams,
|
|
@@ -42,13 +37,11 @@ import { DecoratedReadableStream } from './DecoratedReadableStream.js';
|
|
|
42
37
|
import {
|
|
43
38
|
fetchKeyAccessServers,
|
|
44
39
|
type KasPublicKeyInfo,
|
|
45
|
-
keyAlgorithmToPublicKeyAlgorithm,
|
|
46
40
|
OriginAllowList,
|
|
47
41
|
} from '../../../src/access.js';
|
|
48
42
|
import { ConfigurationError } from '../../../src/errors.js';
|
|
49
|
-
import { Binary } from '../binary.js';
|
|
50
43
|
import { AesGcmCipher } from '../ciphers/aes-gcm-cipher.js';
|
|
51
|
-
import {
|
|
44
|
+
import { type KeyPair, type SymmetricKey } from '../crypto/declarations.js';
|
|
52
45
|
import * as defaultCryptoService from '../crypto/index.js';
|
|
53
46
|
import {
|
|
54
47
|
type AttributeObject,
|
|
@@ -69,24 +62,23 @@ const defaultClientConfig = { oidcOrigin: '', cryptoService: defaultCryptoServic
|
|
|
69
62
|
|
|
70
63
|
const getFirstTwoBytes = async (chunker: Chunker) => new TextDecoder().decode(await chunker(0, 2));
|
|
71
64
|
|
|
72
|
-
async function algorithmFromPEM(pem: string) {
|
|
73
|
-
const
|
|
74
|
-
return
|
|
65
|
+
async function algorithmFromPEM(pem: string, cryptoService: CryptoService) {
|
|
66
|
+
const keyInfo = await cryptoService.parsePublicKeyPem(pem);
|
|
67
|
+
return keyInfo.algorithm;
|
|
75
68
|
}
|
|
76
69
|
|
|
77
|
-
// Convert a PEM string to
|
|
70
|
+
// Convert a PEM string to KasPublicKeyInfo
|
|
78
71
|
export const resolveKasInfo = async (
|
|
79
72
|
pem: string,
|
|
80
73
|
uri: string,
|
|
74
|
+
cryptoService: CryptoService,
|
|
81
75
|
kid?: string
|
|
82
76
|
): Promise<KasPublicKeyInfo> => {
|
|
83
|
-
const
|
|
84
|
-
const algorithm = keyAlgorithmToPublicKeyAlgorithm(k);
|
|
77
|
+
const keyInfo = await cryptoService.parsePublicKeyPem(pem);
|
|
85
78
|
return {
|
|
86
|
-
key: Promise.resolve(k),
|
|
87
79
|
publicKey: pem,
|
|
88
80
|
url: uri,
|
|
89
|
-
algorithm,
|
|
81
|
+
algorithm: keyInfo.algorithm,
|
|
90
82
|
kid: kid,
|
|
91
83
|
};
|
|
92
84
|
};
|
|
@@ -132,7 +124,7 @@ export interface ClientConfig {
|
|
|
132
124
|
/// oauth client id; used to generate oauth authProvider
|
|
133
125
|
clientId?: string;
|
|
134
126
|
dpopEnabled?: boolean;
|
|
135
|
-
dpopKeys?: Promise<
|
|
127
|
+
dpopKeys?: Promise<KeyPair>;
|
|
136
128
|
kasEndpoint: string;
|
|
137
129
|
/**
|
|
138
130
|
* Service to use to look up ABAC. Used during autoconfigure. Defaults to
|
|
@@ -177,28 +169,23 @@ export interface ClientConfig {
|
|
|
177
169
|
*/
|
|
178
170
|
export async function createSessionKeys({
|
|
179
171
|
authProvider,
|
|
180
|
-
// FIXME use cryptoservice to generate keys again
|
|
181
172
|
cryptoService,
|
|
182
173
|
dpopKeys,
|
|
183
174
|
}: {
|
|
184
175
|
authProvider?: AuthProvider;
|
|
185
176
|
cryptoService: CryptoService;
|
|
186
|
-
dpopKeys?: Promise<
|
|
187
|
-
}): Promise<
|
|
188
|
-
let signingKeys:
|
|
177
|
+
dpopKeys?: Promise<KeyPair>;
|
|
178
|
+
}): Promise<KeyPair> {
|
|
179
|
+
let signingKeys: KeyPair;
|
|
189
180
|
if (dpopKeys) {
|
|
190
181
|
signingKeys = await dpopKeys;
|
|
191
182
|
} else {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
signingKeys = await toCryptoKeyPair(keys);
|
|
183
|
+
// generateSigningKeyPair returns opaque KeyPair
|
|
184
|
+
signingKeys = await cryptoService.generateSigningKeyPair();
|
|
195
185
|
}
|
|
196
186
|
|
|
197
187
|
// This will contact the auth server and forcibly refresh the auth token claims,
|
|
198
188
|
// binding the token and the (new) pubkey together.
|
|
199
|
-
// Note that we base64 encode the PEM string here as a quick workaround, simply because
|
|
200
|
-
// a formatted raw PEM string isn't a valid header value and sending it raw makes keycloak's
|
|
201
|
-
// header parser barf. There are more subtle ways to solve this, but this works for now.
|
|
202
189
|
if (authProvider) {
|
|
203
190
|
await authProvider?.updateClientPublicKey(signingKeys);
|
|
204
191
|
}
|
|
@@ -301,7 +288,8 @@ const putKasKeyIntoCache = (
|
|
|
301
288
|
cache: KasKeyInfoCache,
|
|
302
289
|
kasKey: Omit<SimpleKasKey, 'publicKey'> & {
|
|
303
290
|
publicKey: Exclude<SimpleKasKey['publicKey'], undefined>;
|
|
304
|
-
}
|
|
291
|
+
},
|
|
292
|
+
cryptoService: CryptoService
|
|
305
293
|
): ReturnType<typeof fetchKasPublicKey> => {
|
|
306
294
|
const algorithmString = algorithmEnumValueToString(kasKey.publicKey.algorithm);
|
|
307
295
|
const cachedEntry = findEntryInCache(cache, kasKey.kasUri, algorithmString, kasKey.publicKey.kid);
|
|
@@ -309,12 +297,9 @@ const putKasKeyIntoCache = (
|
|
|
309
297
|
return cachedEntry;
|
|
310
298
|
}
|
|
311
299
|
const keyInfoPromise = (async function () {
|
|
312
|
-
const
|
|
313
|
-
const key = await keyPromise;
|
|
314
|
-
const algorithm = keyAlgorithmToPublicKeyAlgorithm(key);
|
|
300
|
+
const keyInfo = await cryptoService.parsePublicKeyPem(kasKey.publicKey.pem);
|
|
315
301
|
return {
|
|
316
|
-
algorithm: algorithm,
|
|
317
|
-
key: keyPromise,
|
|
302
|
+
algorithm: keyInfo.algorithm,
|
|
318
303
|
kid: kasKey.publicKey.kid,
|
|
319
304
|
publicKey: kasKey.publicKey.pem,
|
|
320
305
|
url: kasKey.kasUri,
|
|
@@ -371,7 +356,7 @@ export class Client {
|
|
|
371
356
|
/**
|
|
372
357
|
* Session binding keys. Used for DPoP and signed request bodies.
|
|
373
358
|
*/
|
|
374
|
-
readonly dpopKeys: Promise<
|
|
359
|
+
readonly dpopKeys: Promise<KeyPair>;
|
|
375
360
|
|
|
376
361
|
readonly dpopEnabled: boolean;
|
|
377
362
|
|
|
@@ -453,18 +438,24 @@ export class Client {
|
|
|
453
438
|
//browser-based OIDC login and authentication process against the OIDC endpoint using their chosen method,
|
|
454
439
|
//and provide us with a valid refresh token/clientId obtained from that process.
|
|
455
440
|
if (clientConfig.refreshToken) {
|
|
456
|
-
this.authProvider = new OIDCRefreshTokenProvider(
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
441
|
+
this.authProvider = new OIDCRefreshTokenProvider(
|
|
442
|
+
{
|
|
443
|
+
clientId: clientConfig.clientId,
|
|
444
|
+
refreshToken: clientConfig.refreshToken,
|
|
445
|
+
oidcOrigin: clientConfig.oidcOrigin,
|
|
446
|
+
},
|
|
447
|
+
this.cryptoService
|
|
448
|
+
);
|
|
461
449
|
} else if (clientConfig.externalJwt) {
|
|
462
450
|
//Are we exchanging a JWT previously issued by a trusted external entity (e.g. Google) for a bearer token?
|
|
463
|
-
this.authProvider = new OIDCExternalJwtProvider(
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
451
|
+
this.authProvider = new OIDCExternalJwtProvider(
|
|
452
|
+
{
|
|
453
|
+
clientId: clientConfig.clientId,
|
|
454
|
+
externalJwt: clientConfig.externalJwt,
|
|
455
|
+
oidcOrigin: clientConfig.oidcOrigin,
|
|
456
|
+
},
|
|
457
|
+
this.cryptoService
|
|
458
|
+
);
|
|
468
459
|
}
|
|
469
460
|
}
|
|
470
461
|
this.dpopKeys = createSessionKeys({
|
|
@@ -509,22 +500,27 @@ export class Client {
|
|
|
509
500
|
metadata,
|
|
510
501
|
mimeType = 'unknown',
|
|
511
502
|
windowSize = DEFAULT_SEGMENT_SIZE,
|
|
512
|
-
keyMiddleware
|
|
503
|
+
keyMiddleware: keyMiddlewareOpt,
|
|
513
504
|
splitPlan: preconfiguredSplitPlan,
|
|
514
505
|
streamMiddleware = async (stream: DecoratedReadableStream) => stream,
|
|
515
506
|
tdfSpecVersion,
|
|
516
507
|
wrappingKeyAlgorithm,
|
|
517
508
|
} = opts;
|
|
509
|
+
const keyMiddleware = keyMiddlewareOpt ?? (() => defaultKeyMiddleware(this.cryptoService));
|
|
518
510
|
const scope = opts.scope ?? { attributes: [], dissem: [] };
|
|
519
511
|
|
|
520
512
|
for (const attributeValue of scope.attributeValues || []) {
|
|
521
513
|
for (const kasKey of attributeValue.kasKeys) {
|
|
522
514
|
if (kasKey.publicKey !== undefined) {
|
|
523
|
-
await putKasKeyIntoCache(
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
515
|
+
await putKasKeyIntoCache(
|
|
516
|
+
this.kasKeyInfoCache,
|
|
517
|
+
{
|
|
518
|
+
// TypeScript is silly and cannot infer that publicKey is not undefined, without re-referencing it like this, even though we checked already.
|
|
519
|
+
...kasKey,
|
|
520
|
+
publicKey: kasKey.publicKey,
|
|
521
|
+
},
|
|
522
|
+
this.cryptoService
|
|
523
|
+
);
|
|
528
524
|
}
|
|
529
525
|
}
|
|
530
526
|
}
|
|
@@ -594,11 +590,15 @@ export class Client {
|
|
|
594
590
|
for (const attributeValue of attributeValues) {
|
|
595
591
|
for (const kasKey of effectiveKasKeys(attributeValue)) {
|
|
596
592
|
if (kasKey.publicKey !== undefined) {
|
|
597
|
-
await putKasKeyIntoCache(
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
593
|
+
await putKasKeyIntoCache(
|
|
594
|
+
this.kasKeyInfoCache,
|
|
595
|
+
{
|
|
596
|
+
// TypeScript is silly and cannot infer that publicKey is not undefined, without re-referencing it like this, even though we checked already.
|
|
597
|
+
...kasKey,
|
|
598
|
+
publicKey: kasKey.publicKey,
|
|
599
|
+
},
|
|
600
|
+
this.cryptoService
|
|
601
|
+
);
|
|
602
602
|
}
|
|
603
603
|
}
|
|
604
604
|
}
|
|
@@ -606,7 +606,7 @@ export class Client {
|
|
|
606
606
|
const detailedPlan = plan(attributeValues);
|
|
607
607
|
for (const item of detailedPlan) {
|
|
608
608
|
if ('kid' in item.kas) {
|
|
609
|
-
const pemAlgorithm = await algorithmFromPEM(item.kas.pem);
|
|
609
|
+
const pemAlgorithm = await algorithmFromPEM(item.kas.pem, this.cryptoService);
|
|
610
610
|
const kasPublicKeyInfo = await this._doFetchKasKeyWithCache(
|
|
611
611
|
this.kasKeyInfoCache,
|
|
612
612
|
item.kas.kasUri,
|
|
@@ -694,7 +694,7 @@ export class Client {
|
|
|
694
694
|
}
|
|
695
695
|
encryptionInformation.keyAccess = await Promise.all(
|
|
696
696
|
splitPlan.map(async ({ kas, kid, pem, sid }) => {
|
|
697
|
-
const algorithm = await algorithmFromPEM(pem);
|
|
697
|
+
const algorithm = await algorithmFromPEM(pem, this.cryptoService);
|
|
698
698
|
if (algorithm !== wrappingKeyAlgorithm) {
|
|
699
699
|
console.warn(
|
|
700
700
|
`Mismatched wrapping key algorithm: [${algorithm}] is not requested type, [${wrappingKeyAlgorithm}]`
|
|
@@ -722,6 +722,7 @@ export class Client {
|
|
|
722
722
|
publicKey: pem,
|
|
723
723
|
metadata,
|
|
724
724
|
sid,
|
|
725
|
+
cryptoService: this.cryptoService,
|
|
725
726
|
});
|
|
726
727
|
})
|
|
727
728
|
);
|
|
@@ -765,7 +766,7 @@ export class Client {
|
|
|
765
766
|
async decrypt({
|
|
766
767
|
source,
|
|
767
768
|
allowList,
|
|
768
|
-
keyMiddleware = async (key:
|
|
769
|
+
keyMiddleware = async (key: SymmetricKey) => key,
|
|
769
770
|
streamMiddleware = async (stream: DecoratedReadableStream) => stream,
|
|
770
771
|
assertionVerificationKeys,
|
|
771
772
|
noVerifyAssertions,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { base64 } from '../../../src/encodings/index.js';
|
|
2
|
-
import { type
|
|
2
|
+
import { type PemKeyPair } from './declarations.js';
|
|
3
3
|
import { rsaPkcs1Sha256 } from './index.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -73,7 +73,10 @@ export const removePemFormatting = (input: string): string => {
|
|
|
73
73
|
const PEMRE =
|
|
74
74
|
/-----BEGIN\s((?:RSA\s)?(?:PUBLIC\sKEY|PRIVATE\sKEY|CERTIFICATE))-----[\s0-9A-Za-z+/=]+-----END\s\1-----/;
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Type guard to check if a key pair is a PemKeyPair.
|
|
78
|
+
*/
|
|
79
|
+
export const isPemKeyPair = (i: PemKeyPair | CryptoKeyPair): i is PemKeyPair => {
|
|
77
80
|
const { privateKey, publicKey } = i;
|
|
78
81
|
if (typeof privateKey !== 'string' || typeof publicKey !== 'string') {
|
|
79
82
|
return false;
|
|
@@ -89,7 +92,10 @@ export const isPemKeyPair = (i: AnyKeyPair): i is PemKeyPair => {
|
|
|
89
92
|
return true;
|
|
90
93
|
};
|
|
91
94
|
|
|
92
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Type guard to check if a key pair is a CryptoKeyPair.
|
|
97
|
+
*/
|
|
98
|
+
export const isCryptoKeyPair = (i: PemKeyPair | CryptoKeyPair): i is CryptoKeyPair => {
|
|
93
99
|
const { privateKey, publicKey } = i;
|
|
94
100
|
if (typeof privateKey !== 'object' || typeof publicKey !== 'object') {
|
|
95
101
|
return false;
|
|
@@ -100,12 +106,13 @@ export const isCryptoKeyPair = (i: AnyKeyPair): i is CryptoKeyPair => {
|
|
|
100
106
|
return privateKey.type === 'private' && publicKey.type === 'public';
|
|
101
107
|
};
|
|
102
108
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Convert a PemKeyPair to CryptoKeyPair for internal use.
|
|
111
|
+
* This is needed when interfacing with APIs that still require CryptoKey objects.
|
|
112
|
+
*/
|
|
113
|
+
export const toCryptoKeyPair = async (input: PemKeyPair): Promise<CryptoKeyPair> => {
|
|
107
114
|
if (!isPemKeyPair(input)) {
|
|
108
|
-
throw new Error('internal:
|
|
115
|
+
throw new Error('internal: invalid PEM keypair');
|
|
109
116
|
}
|
|
110
117
|
const k = [input.publicKey, input.privateKey]
|
|
111
118
|
.map(removePemFormatting)
|