jose 6.0.11 → 6.0.13
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/types/jwe/compact/encrypt.d.ts +4 -3
- package/dist/types/jwe/flattened/encrypt.d.ts +3 -3
- package/dist/types/jwks/local.d.ts +3 -2
- package/dist/types/jwks/remote.d.ts +11 -5
- package/dist/types/jwt/encrypt.d.ts +3 -3
- package/dist/types/key/generate_key_pair.d.ts +3 -2
- package/dist/types/key/generate_secret.d.ts +8 -5
- package/dist/types/key/import.d.ts +18 -13
- package/dist/types/types.d.ts +2 -1
- package/dist/webapi/jwks/remote.js +1 -1
- package/dist/webapi/lib/asn1.js +147 -121
- package/dist/webapi/lib/ecdhes.js +28 -22
- package/package.json +1 -1
|
@@ -46,9 +46,10 @@ export declare class CompactEncrypt {
|
|
|
46
46
|
*/
|
|
47
47
|
setProtectedHeader(protectedHeader: types.CompactJWEHeaderParameters): this;
|
|
48
48
|
/**
|
|
49
|
-
* Sets the JWE Key Management parameters to be used when encrypting
|
|
50
|
-
*
|
|
51
|
-
*
|
|
49
|
+
* Sets the JWE Key Management parameters to be used when encrypting. Use of this is method is
|
|
50
|
+
* really only needed for ECDH based algorithms when utilizing the Agreement PartyUInfo or
|
|
51
|
+
* Agreement PartyVInfo parameters. Other parameters will always be randomly generated when needed
|
|
52
|
+
* and missing.
|
|
52
53
|
*
|
|
53
54
|
* @param parameters JWE Key Management parameters.
|
|
54
55
|
*/
|
|
@@ -21,9 +21,9 @@ export declare class FlattenedEncrypt {
|
|
|
21
21
|
constructor(plaintext: Uint8Array);
|
|
22
22
|
/**
|
|
23
23
|
* Sets the JWE Key Management parameters to be used when encrypting. Use of this is method is
|
|
24
|
-
* really only needed for ECDH based algorithms when utilizing the Agreement PartyUInfo or
|
|
25
|
-
* Agreement PartyVInfo parameters. Other parameters will always be randomly generated
|
|
26
|
-
* and missing.
|
|
24
|
+
* really only needed for ECDH based algorithms when utilizing the "apu" (Agreement PartyUInfo) or
|
|
25
|
+
* "apv" (Agreement PartyVInfo) parameters. Other parameters will always be randomly generated
|
|
26
|
+
* when needed and missing.
|
|
27
27
|
*
|
|
28
28
|
* @param parameters JWE Key Management parameters.
|
|
29
29
|
*/
|
|
@@ -17,8 +17,9 @@ import type * as types from '../types.d.ts';
|
|
|
17
17
|
* multiple keys get matched it is possible to opt-in to iterate over the matched keys and attempt
|
|
18
18
|
* verification in an iterative manner.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
20
|
+
* > [!NOTE]\
|
|
21
|
+
* > The function's purpose is to resolve public keys used for verifying signatures and will not work
|
|
22
|
+
* > for public encryption keys.
|
|
22
23
|
*
|
|
23
24
|
* This function is exported (as a named export) from the main `'jose'` module entry point as well
|
|
24
25
|
* as from its subpath export `'jose/jwks/local'`.
|
|
@@ -8,6 +8,10 @@ import type * as types from '../types.d.ts';
|
|
|
8
8
|
* When passed to {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} this allows the resolver
|
|
9
9
|
* to make use of advanced fetch configurations, HTTP Proxies, retry on network errors, etc.
|
|
10
10
|
*
|
|
11
|
+
* > [!NOTE]\
|
|
12
|
+
* > Known caveat: Expect Type-related issues when passing the inputs through to fetch-like modules,
|
|
13
|
+
* > they hardly ever get their typings inline with actual fetch, you should `@ts-expect-error` them.
|
|
14
|
+
*
|
|
11
15
|
* import ky from 'ky'
|
|
12
16
|
*
|
|
13
17
|
* let logRequest!: (request: Request) => void
|
|
@@ -109,9 +113,10 @@ options: {
|
|
|
109
113
|
signal: AbortSignal;
|
|
110
114
|
}) => Promise<Response>;
|
|
111
115
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* writable by your own
|
|
116
|
+
* > [!WARNING]\
|
|
117
|
+
* > This option has security implications that must be understood, assessed for applicability, and
|
|
118
|
+
* > accepted before use. It is critical that the JSON Web Key Set cache only be writable by your own
|
|
119
|
+
* > code.
|
|
115
120
|
*
|
|
116
121
|
* This option is intended for cloud computing runtimes that cannot keep an in memory cache between
|
|
117
122
|
* their code's invocations. Use in runtimes where an in memory cache between requests is available
|
|
@@ -207,8 +212,9 @@ export type JWKSCacheInput = ExportedJWKSCache | Record<string, never>;
|
|
|
207
212
|
* multiple keys get matched it is possible to opt-in to iterate over the matched keys and attempt
|
|
208
213
|
* verification in an iterative manner.
|
|
209
214
|
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
215
|
+
* > [!NOTE]\
|
|
216
|
+
* > The function's purpose is to resolve public keys used for verifying signatures and will not work
|
|
217
|
+
* > for public encryption keys.
|
|
212
218
|
*
|
|
213
219
|
* This function is exported (as a named export) from the main `'jose'` module entry point as well
|
|
214
220
|
* as from its subpath export `'jose/jwks/remote'`.
|
|
@@ -35,9 +35,9 @@ export declare class EncryptJWT implements types.ProduceJWT {
|
|
|
35
35
|
setProtectedHeader(protectedHeader: types.CompactJWEHeaderParameters): this;
|
|
36
36
|
/**
|
|
37
37
|
* Sets the JWE Key Management parameters to be used when encrypting. Use of this is method is
|
|
38
|
-
* really only needed for ECDH based algorithms when utilizing the Agreement PartyUInfo or
|
|
39
|
-
* Agreement PartyVInfo parameters. Other parameters will always be randomly generated
|
|
40
|
-
* and missing.
|
|
38
|
+
* really only needed for ECDH based algorithms when utilizing the "apu" (Agreement PartyUInfo) or
|
|
39
|
+
* "apv" (Agreement PartyVInfo) parameters. Other parameters will always be randomly generated
|
|
40
|
+
* when needed and missing.
|
|
41
41
|
*
|
|
42
42
|
* @param parameters JWE Key Management parameters.
|
|
43
43
|
*/
|
|
@@ -33,8 +33,9 @@ export interface GenerateKeyPairOptions {
|
|
|
33
33
|
* Generates a private and a public key for a given JWA algorithm identifier. This can only generate
|
|
34
34
|
* asymmetric key pairs. For symmetric secrets use the `generateSecret` function.
|
|
35
35
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* > [!NOTE]\
|
|
37
|
+
* > The `privateKey` is generated with `extractable` set to `false` by default. See
|
|
38
|
+
* > {@link GenerateKeyPairOptions.extractable} to generate an extractable `privateKey`.
|
|
38
39
|
*
|
|
39
40
|
* This function is exported (as a named export) from the main `'jose'` module entry point as well
|
|
40
41
|
* as from its subpath export `'jose/generate/keypair'`.
|
|
@@ -9,18 +9,21 @@ export interface GenerateSecretOptions {
|
|
|
9
9
|
/**
|
|
10
10
|
* The value to use as {@link !SubtleCrypto.generateKey} `extractable` argument. Default is false.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* > [!NOTE]\
|
|
13
|
+
* > Because A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512 secrets cannot be represented as
|
|
14
|
+
* > {@link !CryptoKey} this option has no effect for them.
|
|
14
15
|
*/
|
|
15
16
|
extractable?: boolean;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
19
|
* Generates a symmetric secret key for a given JWA algorithm identifier.
|
|
19
20
|
*
|
|
20
|
-
*
|
|
21
|
+
* > [!NOTE]\
|
|
22
|
+
* > The secret key is generated with `extractable` set to `false` by default.
|
|
21
23
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
+
* > [!NOTE]\
|
|
25
|
+
* > Because A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512 secrets cannot be represented as
|
|
26
|
+
* > {@link !CryptoKey} this method yields a {@link !Uint8Array} for them instead.
|
|
24
27
|
*
|
|
25
28
|
* This function is exported (as a named export) from the main `'jose'` module entry point as well
|
|
26
29
|
* as from its subpath export `'jose/generate/secret'`.
|
|
@@ -15,9 +15,10 @@ export interface KeyImportOptions {
|
|
|
15
15
|
/**
|
|
16
16
|
* Imports a PEM-encoded SPKI string as a {@link !CryptoKey}.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* > [!NOTE]\
|
|
19
|
+
* > The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in
|
|
20
|
+
* > {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption
|
|
21
|
+
* > (1.2.840.113549.1.1.1) instead for all RSA algorithms.
|
|
21
22
|
*
|
|
22
23
|
* This function is exported (as a named export) from the main `'jose'` module entry point as well
|
|
23
24
|
* as from its subpath export `'jose/key/import'`.
|
|
@@ -30,9 +31,10 @@ export declare function importSPKI(spki: string, alg: string, options?: KeyImpor
|
|
|
30
31
|
/**
|
|
31
32
|
* Imports the SPKI from an X.509 string certificate as a {@link !CryptoKey}.
|
|
32
33
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
34
|
+
* > [!NOTE]\
|
|
35
|
+
* > The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in
|
|
36
|
+
* > {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption
|
|
37
|
+
* > (1.2.840.113549.1.1.1) instead for all RSA algorithms.
|
|
36
38
|
*
|
|
37
39
|
* This function is exported (as a named export) from the main `'jose'` module entry point as well
|
|
38
40
|
* as from its subpath export `'jose/key/import'`.
|
|
@@ -45,9 +47,10 @@ export declare function importX509(x509: string, alg: string, options?: KeyImpor
|
|
|
45
47
|
/**
|
|
46
48
|
* Imports a PEM-encoded PKCS#8 string as a {@link !CryptoKey}.
|
|
47
49
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
50
|
+
* > [!NOTE]\
|
|
51
|
+
* > The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in
|
|
52
|
+
* > {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption
|
|
53
|
+
* > (1.2.840.113549.1.1.1) instead for all RSA algorithms.
|
|
51
54
|
*
|
|
52
55
|
* This function is exported (as a named export) from the main `'jose'` module entry point as well
|
|
53
56
|
* as from its subpath export `'jose/key/import'`.
|
|
@@ -61,11 +64,13 @@ export declare function importPKCS8(pkcs8: string, alg: string, options?: KeyImp
|
|
|
61
64
|
* Imports a JWK to a {@link !CryptoKey}. Either the JWK "alg" (Algorithm) Parameter, or the optional
|
|
62
65
|
* "alg" argument, must be present for asymmetric JSON Web Key imports.
|
|
63
66
|
*
|
|
64
|
-
*
|
|
65
|
-
* {@link !CryptoKey}
|
|
67
|
+
* > [!NOTE]\
|
|
68
|
+
* > The JSON Web Key parameters "use", "key_ops", and "ext" are also used in the {@link !CryptoKey}
|
|
69
|
+
* > import process.
|
|
66
70
|
*
|
|
67
|
-
*
|
|
68
|
-
* {@link !
|
|
71
|
+
* > [!NOTE]\
|
|
72
|
+
* > Symmetric JSON Web Keys (i.e. `kty: "oct"`) yield back an {@link !Uint8Array} instead of a
|
|
73
|
+
* > {@link !CryptoKey}.
|
|
69
74
|
*
|
|
70
75
|
* This function is exported (as a named export) from the main `'jose'` module entry point as well
|
|
71
76
|
* as from its subpath export `'jose/key/import'`.
|
package/dist/types/types.d.ts
CHANGED
|
@@ -522,7 +522,8 @@ export interface VerifyOptions extends CritOption {
|
|
|
522
522
|
* A list of accepted JWS "alg" (Algorithm) Header Parameter values. By default all "alg"
|
|
523
523
|
* (Algorithm) values applicable for the used key/secret are allowed.
|
|
524
524
|
*
|
|
525
|
-
*
|
|
525
|
+
* > [!NOTE]\
|
|
526
|
+
* > Unsecured JWTs (`{ "alg": "none" }`) are never accepted by this API.
|
|
526
527
|
*/
|
|
527
528
|
algorithms?: string[]
|
|
528
529
|
}
|
|
@@ -9,7 +9,7 @@ function isCloudflareWorkers() {
|
|
|
9
9
|
let USER_AGENT;
|
|
10
10
|
if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
|
|
11
11
|
const NAME = 'jose';
|
|
12
|
-
const VERSION = 'v6.0.
|
|
12
|
+
const VERSION = 'v6.0.13';
|
|
13
13
|
USER_AGENT = `${NAME}/${VERSION}`;
|
|
14
14
|
}
|
|
15
15
|
export const customFetch = Symbol();
|
package/dist/webapi/lib/asn1.js
CHANGED
|
@@ -30,50 +30,112 @@ export const toSPKI = (key) => {
|
|
|
30
30
|
export const toPKCS8 = (key) => {
|
|
31
31
|
return genericExport('private', 'pkcs8', key);
|
|
32
32
|
};
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
oid.unshift(oid.length);
|
|
36
|
-
oid.unshift(0x06);
|
|
37
|
-
}
|
|
38
|
-
const i = keyData.indexOf(oid[0], from);
|
|
39
|
-
if (i === -1)
|
|
40
|
-
return false;
|
|
41
|
-
const sub = keyData.subarray(i, i + oid.length);
|
|
42
|
-
if (sub.length !== oid.length)
|
|
33
|
+
const bytesEqual = (a, b) => {
|
|
34
|
+
if (a.byteLength !== b.length)
|
|
43
35
|
return false;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
36
|
+
for (let i = 0; i < a.byteLength; i++) {
|
|
37
|
+
if (a[i] !== b[i])
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
};
|
|
42
|
+
const createASN1State = (data) => ({ data, pos: 0 });
|
|
43
|
+
const parseLength = (state) => {
|
|
44
|
+
const first = state.data[state.pos++];
|
|
45
|
+
if (first & 0x80) {
|
|
46
|
+
const lengthOfLen = first & 0x7f;
|
|
47
|
+
let length = 0;
|
|
48
|
+
for (let i = 0; i < lengthOfLen; i++) {
|
|
49
|
+
length = (length << 8) | state.data[state.pos++];
|
|
50
|
+
}
|
|
51
|
+
return length;
|
|
52
|
+
}
|
|
53
|
+
return first;
|
|
54
|
+
};
|
|
55
|
+
const skipElement = (state, count = 1) => {
|
|
56
|
+
if (count <= 0)
|
|
57
|
+
return;
|
|
58
|
+
state.pos++;
|
|
59
|
+
const length = parseLength(state);
|
|
60
|
+
state.pos += length;
|
|
61
|
+
if (count > 1) {
|
|
62
|
+
skipElement(state, count - 1);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const expectTag = (state, expectedTag, errorMessage) => {
|
|
66
|
+
if (state.data[state.pos++] !== expectedTag) {
|
|
67
|
+
throw new Error(errorMessage);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const getSubarray = (state, length) => {
|
|
71
|
+
const result = state.data.subarray(state.pos, state.pos + length);
|
|
72
|
+
state.pos += length;
|
|
73
|
+
return result;
|
|
74
|
+
};
|
|
75
|
+
const parseAlgorithmOID = (state) => {
|
|
76
|
+
expectTag(state, 0x06, 'Expected algorithm OID');
|
|
77
|
+
const oidLen = parseLength(state);
|
|
78
|
+
return getSubarray(state, oidLen);
|
|
79
|
+
};
|
|
80
|
+
function parsePKCS8Header(state) {
|
|
81
|
+
expectTag(state, 0x30, 'Invalid PKCS#8 structure');
|
|
82
|
+
parseLength(state);
|
|
83
|
+
expectTag(state, 0x02, 'Expected version field');
|
|
84
|
+
const verLen = parseLength(state);
|
|
85
|
+
state.pos += verLen;
|
|
86
|
+
expectTag(state, 0x30, 'Expected algorithm identifier');
|
|
87
|
+
const algIdLen = parseLength(state);
|
|
88
|
+
const algIdStart = state.pos;
|
|
89
|
+
return { algIdStart, algIdLength: algIdLen };
|
|
90
|
+
}
|
|
91
|
+
function parseSPKIHeader(state) {
|
|
92
|
+
expectTag(state, 0x30, 'Invalid SPKI structure');
|
|
93
|
+
parseLength(state);
|
|
94
|
+
expectTag(state, 0x30, 'Expected algorithm identifier');
|
|
95
|
+
const algIdLen = parseLength(state);
|
|
96
|
+
const algIdStart = state.pos;
|
|
97
|
+
return { algIdStart, algIdLength: algIdLen };
|
|
98
|
+
}
|
|
99
|
+
const parseECAlgorithmIdentifier = (state) => {
|
|
100
|
+
const algOid = parseAlgorithmOID(state);
|
|
101
|
+
if (bytesEqual(algOid, [0x2b, 0x65, 0x6e])) {
|
|
102
|
+
return 'X25519';
|
|
103
|
+
}
|
|
104
|
+
if (!bytesEqual(algOid, [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01])) {
|
|
105
|
+
throw new Error('Unsupported key algorithm');
|
|
106
|
+
}
|
|
107
|
+
expectTag(state, 0x06, 'Expected curve OID');
|
|
108
|
+
const curveOidLen = parseLength(state);
|
|
109
|
+
const curveOid = getSubarray(state, curveOidLen);
|
|
110
|
+
for (const { name, oid } of [
|
|
111
|
+
{ name: 'P-256', oid: [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07] },
|
|
112
|
+
{ name: 'P-384', oid: [0x2b, 0x81, 0x04, 0x00, 0x22] },
|
|
113
|
+
{ name: 'P-521', oid: [0x2b, 0x81, 0x04, 0x00, 0x23] },
|
|
114
|
+
]) {
|
|
115
|
+
if (bytesEqual(curveOid, oid)) {
|
|
116
|
+
return name;
|
|
117
|
+
}
|
|
56
118
|
}
|
|
119
|
+
throw new Error('Unsupported named curve');
|
|
57
120
|
};
|
|
58
|
-
const genericImport = async (
|
|
121
|
+
const genericImport = async (keyFormat, keyData, alg, options) => {
|
|
59
122
|
let algorithm;
|
|
60
123
|
let keyUsages;
|
|
61
|
-
const keyData = new Uint8Array(atob(pem.replace(replace, ''))
|
|
62
|
-
.split('')
|
|
63
|
-
.map((c) => c.charCodeAt(0)));
|
|
64
124
|
const isPublic = keyFormat === 'spki';
|
|
125
|
+
const getSigUsages = () => (isPublic ? ['verify'] : ['sign']);
|
|
126
|
+
const getEncUsages = () => isPublic ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey'];
|
|
65
127
|
switch (alg) {
|
|
66
128
|
case 'PS256':
|
|
67
129
|
case 'PS384':
|
|
68
130
|
case 'PS512':
|
|
69
131
|
algorithm = { name: 'RSA-PSS', hash: `SHA-${alg.slice(-3)}` };
|
|
70
|
-
keyUsages =
|
|
132
|
+
keyUsages = getSigUsages();
|
|
71
133
|
break;
|
|
72
134
|
case 'RS256':
|
|
73
135
|
case 'RS384':
|
|
74
136
|
case 'RS512':
|
|
75
137
|
algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${alg.slice(-3)}` };
|
|
76
|
-
keyUsages =
|
|
138
|
+
keyUsages = getSigUsages();
|
|
77
139
|
break;
|
|
78
140
|
case 'RSA-OAEP':
|
|
79
141
|
case 'RSA-OAEP-256':
|
|
@@ -83,133 +145,97 @@ const genericImport = async (replace, keyFormat, pem, alg, options) => {
|
|
|
83
145
|
name: 'RSA-OAEP',
|
|
84
146
|
hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`,
|
|
85
147
|
};
|
|
86
|
-
keyUsages =
|
|
148
|
+
keyUsages = getEncUsages();
|
|
87
149
|
break;
|
|
88
150
|
case 'ES256':
|
|
89
|
-
algorithm = { name: 'ECDSA', namedCurve: 'P-256' };
|
|
90
|
-
keyUsages = isPublic ? ['verify'] : ['sign'];
|
|
91
|
-
break;
|
|
92
151
|
case 'ES384':
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
algorithm = { name: 'ECDSA', namedCurve: 'P-521' };
|
|
98
|
-
keyUsages = isPublic ? ['verify'] : ['sign'];
|
|
152
|
+
case 'ES512': {
|
|
153
|
+
const curveMap = { ES256: 'P-256', ES384: 'P-384', ES512: 'P-521' };
|
|
154
|
+
algorithm = { name: 'ECDSA', namedCurve: curveMap[alg] };
|
|
155
|
+
keyUsages = getSigUsages();
|
|
99
156
|
break;
|
|
157
|
+
}
|
|
100
158
|
case 'ECDH-ES':
|
|
101
159
|
case 'ECDH-ES+A128KW':
|
|
102
160
|
case 'ECDH-ES+A192KW':
|
|
103
161
|
case 'ECDH-ES+A256KW': {
|
|
104
|
-
|
|
105
|
-
|
|
162
|
+
try {
|
|
163
|
+
const namedCurve = options.getNamedCurve(keyData);
|
|
164
|
+
algorithm = namedCurve === 'X25519' ? { name: 'X25519' } : { name: 'ECDH', namedCurve };
|
|
165
|
+
}
|
|
166
|
+
catch (cause) {
|
|
167
|
+
throw new JOSENotSupported('Invalid or unsupported key format');
|
|
168
|
+
}
|
|
106
169
|
keyUsages = isPublic ? [] : ['deriveBits'];
|
|
107
170
|
break;
|
|
108
171
|
}
|
|
109
172
|
case 'Ed25519':
|
|
110
173
|
case 'EdDSA':
|
|
111
174
|
algorithm = { name: 'Ed25519' };
|
|
112
|
-
keyUsages =
|
|
175
|
+
keyUsages = getSigUsages();
|
|
113
176
|
break;
|
|
114
177
|
default:
|
|
115
178
|
throw new JOSENotSupported('Invalid or unsupported "alg" (Algorithm) value');
|
|
116
179
|
}
|
|
117
180
|
return crypto.subtle.importKey(keyFormat, keyData, algorithm, options?.extractable ?? (isPublic ? true : false), keyUsages);
|
|
118
181
|
};
|
|
182
|
+
const processPEMData = (pem, pattern) => {
|
|
183
|
+
return decodeBase64(pem.replace(pattern, ''));
|
|
184
|
+
};
|
|
119
185
|
export const fromPKCS8 = (pem, alg, options) => {
|
|
120
|
-
|
|
186
|
+
const keyData = processPEMData(pem, /(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g);
|
|
187
|
+
let opts = options;
|
|
188
|
+
if (alg?.startsWith?.('ECDH-ES')) {
|
|
189
|
+
opts ||= {};
|
|
190
|
+
opts.getNamedCurve = (keyData) => {
|
|
191
|
+
const state = createASN1State(keyData);
|
|
192
|
+
parsePKCS8Header(state);
|
|
193
|
+
return parseECAlgorithmIdentifier(state);
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return genericImport('pkcs8', keyData, alg, opts);
|
|
121
197
|
};
|
|
122
198
|
export const fromSPKI = (pem, alg, options) => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
next += nextPart.byteLength;
|
|
132
|
-
}
|
|
133
|
-
return result;
|
|
134
|
-
}
|
|
135
|
-
function parseElement(bytes) {
|
|
136
|
-
let position = 0;
|
|
137
|
-
let tag = bytes[0] & 0x1f;
|
|
138
|
-
position++;
|
|
139
|
-
if (tag === 0x1f) {
|
|
140
|
-
tag = 0;
|
|
141
|
-
while (bytes[position] >= 0x80) {
|
|
142
|
-
tag = tag * 128 + bytes[position] - 0x80;
|
|
143
|
-
position++;
|
|
144
|
-
}
|
|
145
|
-
tag = tag * 128 + bytes[position] - 0x80;
|
|
146
|
-
position++;
|
|
147
|
-
}
|
|
148
|
-
let length = 0;
|
|
149
|
-
if (bytes[position] < 0x80) {
|
|
150
|
-
length = bytes[position];
|
|
151
|
-
position++;
|
|
152
|
-
}
|
|
153
|
-
else if (length === 0x80) {
|
|
154
|
-
length = 0;
|
|
155
|
-
while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) {
|
|
156
|
-
if (length > bytes.byteLength) {
|
|
157
|
-
throw new TypeError('invalid indefinite form length');
|
|
158
|
-
}
|
|
159
|
-
length++;
|
|
160
|
-
}
|
|
161
|
-
const byteLength = position + length + 2;
|
|
162
|
-
return {
|
|
163
|
-
byteLength,
|
|
164
|
-
contents: bytes.subarray(position, position + length),
|
|
165
|
-
raw: bytes.subarray(0, byteLength),
|
|
199
|
+
const keyData = processPEMData(pem, /(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g);
|
|
200
|
+
let opts = options;
|
|
201
|
+
if (alg?.startsWith?.('ECDH-ES')) {
|
|
202
|
+
opts ||= {};
|
|
203
|
+
opts.getNamedCurve = (keyData) => {
|
|
204
|
+
const state = createASN1State(keyData);
|
|
205
|
+
parseSPKIHeader(state);
|
|
206
|
+
return parseECAlgorithmIdentifier(state);
|
|
166
207
|
};
|
|
167
208
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
position++;
|
|
171
|
-
length = 0;
|
|
172
|
-
for (let i = 0; i < numberOfDigits; i++) {
|
|
173
|
-
length = length * 256 + bytes[position];
|
|
174
|
-
position++;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
const byteLength = position + length;
|
|
178
|
-
return {
|
|
179
|
-
byteLength,
|
|
180
|
-
contents: bytes.subarray(position, byteLength),
|
|
181
|
-
raw: bytes.subarray(0, byteLength),
|
|
182
|
-
};
|
|
183
|
-
}
|
|
209
|
+
return genericImport('spki', keyData, alg, opts);
|
|
210
|
+
};
|
|
184
211
|
function spkiFromX509(buf) {
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
catch {
|
|
194
|
-
createPublicKey = 0;
|
|
212
|
+
const state = createASN1State(buf);
|
|
213
|
+
expectTag(state, 0x30, 'Invalid certificate structure');
|
|
214
|
+
parseLength(state);
|
|
215
|
+
expectTag(state, 0x30, 'Invalid tbsCertificate structure');
|
|
216
|
+
parseLength(state);
|
|
217
|
+
if (buf[state.pos] === 0xa0) {
|
|
218
|
+
skipElement(state, 6);
|
|
195
219
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
return new createPublicKey(x509).export({ format: 'pem', type: 'spki' });
|
|
199
|
-
}
|
|
200
|
-
catch { }
|
|
220
|
+
else {
|
|
221
|
+
skipElement(state, 5);
|
|
201
222
|
}
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
223
|
+
const spkiStart = state.pos;
|
|
224
|
+
expectTag(state, 0x30, 'Invalid SPKI structure');
|
|
225
|
+
const spkiContentLen = parseLength(state);
|
|
226
|
+
return buf.subarray(spkiStart, spkiStart + spkiContentLen + (state.pos - spkiStart));
|
|
227
|
+
}
|
|
228
|
+
function extractX509SPKI(x509) {
|
|
229
|
+
const derBytes = processPEMData(x509, /(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g);
|
|
230
|
+
return spkiFromX509(derBytes);
|
|
205
231
|
}
|
|
206
232
|
export const fromX509 = (pem, alg, options) => {
|
|
207
233
|
let spki;
|
|
208
234
|
try {
|
|
209
|
-
spki =
|
|
235
|
+
spki = extractX509SPKI(pem);
|
|
210
236
|
}
|
|
211
237
|
catch (cause) {
|
|
212
238
|
throw new TypeError('Failed to parse the X.509 certificate', { cause });
|
|
213
239
|
}
|
|
214
|
-
return fromSPKI(spki, alg, options);
|
|
240
|
+
return fromSPKI(formatPEM(encodeBase64(spki), 'PUBLIC KEY'), alg, options);
|
|
215
241
|
};
|
|
@@ -4,35 +4,41 @@ import digest from './digest.js';
|
|
|
4
4
|
function lengthAndInput(input) {
|
|
5
5
|
return concat(uint32be(input.length), input);
|
|
6
6
|
}
|
|
7
|
-
async function concatKdf(
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
async function concatKdf(Z, L, OtherInfo) {
|
|
8
|
+
const dkLen = L >> 3;
|
|
9
|
+
const hashLen = 32;
|
|
10
|
+
const reps = Math.ceil(dkLen / hashLen);
|
|
11
|
+
const dk = new Uint8Array(reps * hashLen);
|
|
12
|
+
for (let i = 1; i <= reps; i++) {
|
|
13
|
+
const hashInput = new Uint8Array(4 + Z.length + OtherInfo.length);
|
|
14
|
+
hashInput.set(uint32be(i), 0);
|
|
15
|
+
hashInput.set(Z, 4);
|
|
16
|
+
hashInput.set(OtherInfo, 4 + Z.length);
|
|
17
|
+
const hashResult = await digest('sha256', hashInput);
|
|
18
|
+
dk.set(hashResult, (i - 1) * hashLen);
|
|
16
19
|
}
|
|
17
|
-
return
|
|
20
|
+
return dk.slice(0, dkLen);
|
|
18
21
|
}
|
|
19
22
|
export async function deriveKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) {
|
|
20
23
|
checkEncCryptoKey(publicKey, 'ECDH');
|
|
21
24
|
checkEncCryptoKey(privateKey, 'ECDH', 'deriveBits');
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Math.ceil(parseInt(publicKey.algorithm.namedCurve.slice(-3), 10) / 8) << 3;
|
|
30
|
-
}
|
|
31
|
-
const sharedSecret = new Uint8Array(await crypto.subtle.deriveBits({
|
|
25
|
+
const algorithmID = lengthAndInput(encoder.encode(algorithm));
|
|
26
|
+
const partyUInfo = lengthAndInput(apu);
|
|
27
|
+
const partyVInfo = lengthAndInput(apv);
|
|
28
|
+
const suppPubInfo = uint32be(keyLength);
|
|
29
|
+
const suppPrivInfo = new Uint8Array(0);
|
|
30
|
+
const otherInfo = concat(algorithmID, partyUInfo, partyVInfo, suppPubInfo, suppPrivInfo);
|
|
31
|
+
const Z = new Uint8Array(await crypto.subtle.deriveBits({
|
|
32
32
|
name: publicKey.algorithm.name,
|
|
33
33
|
public: publicKey,
|
|
34
|
-
}, privateKey,
|
|
35
|
-
return concatKdf(
|
|
34
|
+
}, privateKey, getEcdhBitLength(publicKey)));
|
|
35
|
+
return concatKdf(Z, keyLength, otherInfo);
|
|
36
|
+
}
|
|
37
|
+
function getEcdhBitLength(publicKey) {
|
|
38
|
+
if (publicKey.algorithm.name === 'X25519') {
|
|
39
|
+
return 256;
|
|
40
|
+
}
|
|
41
|
+
return (Math.ceil(parseInt(publicKey.algorithm.namedCurve.slice(-3), 10) / 8) << 3);
|
|
36
42
|
}
|
|
37
43
|
export function allowed(key) {
|
|
38
44
|
switch (key.algorithm.namedCurve) {
|