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.
@@ -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 the Content Encryption Key.
50
- * You do not need to invoke this method, it is only really intended for test and vector
51
- * validation purposes.
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 when needed
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
- * Note: The function's purpose is to resolve public keys used for verifying signatures and will not
21
- * work for public encryption keys.
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
- * DANGER ZONE - This option has security implications that must be understood, assessed for
113
- * applicability, and accepted before use. It is critical that the JSON Web Key Set cache only be
114
- * writable by your own code.
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
- * Note: The function's purpose is to resolve public keys used for verifying signatures and will not
211
- * work for public encryption keys.
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 when needed
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
- * Note: The `privateKey` is generated with `extractable` set to `false` by default. See
37
- * {@link GenerateKeyPairOptions.extractable} to generate an extractable `privateKey`.
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
- * Note: Because A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512 secrets cannot be represented as
13
- * {@link !CryptoKey} this option has no effect for them.
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
- * Note: The secret key is generated with `extractable` set to `false` by default.
21
+ * > [!NOTE]\
22
+ * > The secret key is generated with `extractable` set to `false` by default.
21
23
  *
22
- * Note: Because A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512 secrets cannot be represented as
23
- * {@link !CryptoKey} this method yields a {@link !Uint8Array} for them instead.
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
- * Note: The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in
19
- * {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption
20
- * (1.2.840.113549.1.1.1) instead for all RSA algorithms.
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
- * Note: The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in
34
- * {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption
35
- * (1.2.840.113549.1.1.1) instead for all RSA algorithms.
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
- * Note: The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in
49
- * {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption
50
- * (1.2.840.113549.1.1.1) instead for all RSA algorithms.
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
- * Note: The JSON Web Key parameters "use", "key_ops", and "ext" are also used in the
65
- * {@link !CryptoKey} import process.
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
- * Note: Symmetric JSON Web Keys (i.e. `kty: "oct"`) yield back an {@link !Uint8Array} instead of a
68
- * {@link !CryptoKey}.
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'`.
@@ -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
- * Note: Unsecured JWTs (`{ "alg": "none" }`) are never accepted by this API.
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.11';
12
+ const VERSION = 'v6.0.13';
13
13
  USER_AGENT = `${NAME}/${VERSION}`;
14
14
  }
15
15
  export const customFetch = Symbol();
@@ -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 findOid = (keyData, oid, from = 0) => {
34
- if (from === 0) {
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
- return sub.every((value, index) => value === oid[index]) || findOid(keyData, oid, i + 1);
45
- };
46
- const getNamedCurve = (keyData) => {
47
- switch (true) {
48
- case findOid(keyData, [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07]):
49
- return 'P-256';
50
- case findOid(keyData, [0x2b, 0x81, 0x04, 0x00, 0x22]):
51
- return 'P-384';
52
- case findOid(keyData, [0x2b, 0x81, 0x04, 0x00, 0x23]):
53
- return 'P-521';
54
- default:
55
- return undefined;
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 (replace, keyFormat, pem, alg, options) => {
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 = isPublic ? ['verify'] : ['sign'];
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 = isPublic ? ['verify'] : ['sign'];
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 = isPublic ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey'];
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
- algorithm = { name: 'ECDSA', namedCurve: 'P-384' };
94
- keyUsages = isPublic ? ['verify'] : ['sign'];
95
- break;
96
- case 'ES512':
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
- const namedCurve = getNamedCurve(keyData);
105
- algorithm = namedCurve?.startsWith('P-') ? { name: 'ECDH', namedCurve } : { name: 'X25519' };
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 = isPublic ? ['verify'] : ['sign'];
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
- return genericImport(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, 'pkcs8', pem, alg, options);
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
- return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, 'spki', pem, alg, options);
124
- };
125
- function getElement(seq) {
126
- const result = [];
127
- let next = 0;
128
- while (next < seq.length) {
129
- const nextPart = parseElement(seq.subarray(next));
130
- result.push(nextPart);
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
- else {
169
- const numberOfDigits = bytes[position] & 0x7f;
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 tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
186
- return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw);
187
- }
188
- let createPublicKey;
189
- function getSPKI(x509) {
190
- try {
191
- createPublicKey ??= globalThis.process?.getBuiltinModule?.('node:crypto')?.createPublicKey;
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
- if (createPublicKey) {
197
- try {
198
- return new createPublicKey(x509).export({ format: 'pem', type: 'spki' });
199
- }
200
- catch { }
220
+ else {
221
+ skipElement(state, 5);
201
222
  }
202
- const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '');
203
- const raw = decodeBase64(pem);
204
- return formatPEM(spkiFromX509(raw), 'PUBLIC KEY');
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 = getSPKI(pem);
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(secret, bits, value) {
8
- const iterations = Math.ceil((bits >> 3) / 32);
9
- const res = new Uint8Array(iterations * 32);
10
- for (let iter = 0; iter < iterations; iter++) {
11
- const buf = new Uint8Array(4 + secret.length + value.length);
12
- buf.set(uint32be(iter + 1));
13
- buf.set(secret, 4);
14
- buf.set(value, 4 + secret.length);
15
- res.set(await digest('sha256', buf), iter * 32);
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 res.slice(0, bits >> 3);
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 value = concat(lengthAndInput(encoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength));
23
- let length;
24
- if (publicKey.algorithm.name === 'X25519') {
25
- length = 256;
26
- }
27
- else {
28
- length =
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, length));
35
- return concatKdf(sharedSecret, keyLength, value);
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jose",
3
- "version": "6.0.11",
3
+ "version": "6.0.13",
4
4
  "description": "JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes",
5
5
  "keywords": [
6
6
  "browser",