init project

This commit is contained in:
root
2026-07-31 13:12:54 -04:00
parent 0da92d5e02
commit f3863f760c
7215 changed files with 1860260 additions and 1 deletions
+56
View File
@@ -0,0 +1,56 @@
export { compactDecrypt } from './jwe/compact/decrypt.js';
export type { CompactDecryptGetKey } from './jwe/compact/decrypt.js';
export { flattenedDecrypt } from './jwe/flattened/decrypt.js';
export type { FlattenedDecryptGetKey } from './jwe/flattened/decrypt.js';
export { generalDecrypt } from './jwe/general/decrypt.js';
export type { GeneralDecryptGetKey } from './jwe/general/decrypt.js';
export { GeneralEncrypt } from './jwe/general/encrypt.js';
export type { Recipient } from './jwe/general/encrypt.js';
export { compactVerify } from './jws/compact/verify.js';
export type { CompactVerifyGetKey } from './jws/compact/verify.js';
export { flattenedVerify } from './jws/flattened/verify.js';
export type { FlattenedVerifyGetKey } from './jws/flattened/verify.js';
export { generalVerify } from './jws/general/verify.js';
export type { GeneralVerifyGetKey } from './jws/general/verify.js';
export { jwtVerify } from './jwt/verify.js';
export type { JWTVerifyOptions, JWTVerifyGetKey } from './jwt/verify.js';
export { jwtDecrypt } from './jwt/decrypt.js';
export type { JWTDecryptOptions, JWTDecryptGetKey } from './jwt/decrypt.js';
export { CompactEncrypt } from './jwe/compact/encrypt.js';
export { FlattenedEncrypt } from './jwe/flattened/encrypt.js';
export { CompactSign } from './jws/compact/sign.js';
export { FlattenedSign } from './jws/flattened/sign.js';
export { GeneralSign } from './jws/general/sign.js';
export type { Signature } from './jws/general/sign.js';
export { SignJWT } from './jwt/sign.js';
export { EncryptJWT } from './jwt/encrypt.js';
export { calculateJwkThumbprint, calculateJwkThumbprintUri } from './jwk/thumbprint.js';
export { EmbeddedJWK } from './jwk/embedded.js';
export { createLocalJWKSet } from './jwks/local.js';
export type { LocalJWKSet } from './jwks/local.js';
export { createRemoteJWKSet, jwksCache, customFetch } from './jwks/remote.js';
export type { RemoteJWKSet, RemoteJWKSetOptions, JWKSCacheInput, ExportedJWKSCache, FetchImplementation, } from './jwks/remote.js';
export { UnsecuredJWT } from './jwt/unsecured.js';
export type { UnsecuredResult } from './jwt/unsecured.js';
export { exportPKCS8, exportSPKI, exportJWK } from './key/export.js';
export { importSPKI, importPKCS8, importX509, importJWK } from './key/import.js';
export type { KeyImportOptions } from './key/import.js';
export { decodeProtectedHeader } from './util/decode_protected_header.js';
export { decodeJwt } from './util/decode_jwt.js';
export type { ProtectedHeaderParameters } from './util/decode_protected_header.js';
import * as errors from './util/errors.js';
export { errors };
export { generateKeyPair } from './key/generate_key_pair.js';
export type { GenerateKeyPairAlgorithm, GenerateKeyPairResult, GenerateKeyPairOptions, } from './key/generate_key_pair.js';
export { generateSecret } from './key/generate_secret.js';
export type { GenerateSecretAlgorithm, GenerateSecretOptions } from './key/generate_secret.js';
import * as base64url from './util/base64url.js';
export { base64url };
export type { AnyJWK, CompactDecryptResult, CompactJWEHeaderParameters, CompactJWSHeaderParameters, CompactVerifyResult, CritOption, CryptoKey, DecryptOptions, EncryptOptions, FlattenedDecryptResult, FlattenedJWE, FlattenedJWS, FlattenedJWSInput, FlattenedVerifyResult, GeneralDecryptResult, GeneralJWE, GeneralJWS, GeneralJWSInput, GeneralVerifyResult, GetKeyFunction, JoseHeaderParameters, JSONWebKeySet, JWEContentEncryptionAlgorithm, JWEHeaderParameters, JWEKeyManagementAlgorithm, JWEKeyManagementHeaderParameters, JWK_AKP_Private, JWK_AKP_Public, JWK_EC_Private, JWK_EC_Public, JWK_oct, JWK_OKP_Private, JWK_OKP_Public, JWK_RSA_Private, JWK_RSA_Public, JWK, JWKKeyType, JWKParameters, JWSAlgorithm, JWSHeaderParameters, JWTClaimVerificationOptions, JWTDecryptResult, JWTHeaderParameters, JWTPayload, JWTVerifyResult, KeyInput, KeyObject, ProduceJWT, ResolvedKey, SignOptions, VerifyOptions, } from './types.d.ts';
/**
* In prior releases this indicated whether a Node.js-specific build was loaded, this is now fixed
* to `"WebCryptoAPI"`
*
* @deprecated Remove any runtime branching on this value; it is always `"WebCryptoAPI"`.
*/
export declare const cryptoRuntime = "WebCryptoAPI";
+37
View File
@@ -0,0 +1,37 @@
import type * as types from '../../types.d.ts';
/**
* Interface for Compact JWE Decryption dynamic key resolution. No token components have been
* verified at the time of this function call.
*/
export interface CompactDecryptGetKey<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array> extends types.GetKeyFunction<types.CompactJWEHeaderParameters, types.FlattenedJWE, KeyType | types.KeyObject | types.JWK> {
}
/**
* Decrypts a Compact JWE.
*
* @param jwe Compact JWE.
* @param key Private Key or Secret to decrypt the JWE with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Decryption options.
*/
export declare function compactDecrypt(jwe: string | Uint8Array, key: types.KeyInput, options?: types.DecryptOptions): Promise<types.CompactDecryptResult>;
/**
* Decrypts a Compact JWE, resolving the key dynamically. The result additionally carries the
* {@link types.ResolvedKey.key resolved key}.
*
* @param jwe Compact JWE.
* @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Decryption options.
*/
export declare function compactDecrypt<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array>(jwe: string | Uint8Array, getKey: CompactDecryptGetKey<KeyType>, options?: types.DecryptOptions): Promise<types.CompactDecryptResult & types.ResolvedKey<KeyType>>;
/**
* Accepts either form of the `key` argument. Use this overload when forwarding a value that may be
* either a key or a key resolution function; `key` is present on the result only when a resolution
* function was used.
*
* @param jwe Compact JWE.
* @param key Private Key or Secret, or a function resolving one, to decrypt the JWE with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Decryption options.
*/
export declare function compactDecrypt(jwe: string | Uint8Array, key: types.KeyInput | CompactDecryptGetKey, options?: types.DecryptOptions): Promise<types.CompactDecryptResult & Partial<types.ResolvedKey>>;
+53
View File
@@ -0,0 +1,53 @@
import type * as types from '../../types.d.ts';
/** The CompactEncrypt class is used to build and encrypt Compact JWE strings. */
export declare class CompactEncrypt {
#private;
/**
* {@link CompactEncrypt} constructor
*
* @param plaintext Binary representation of the plaintext to encrypt.
*/
constructor(plaintext: Uint8Array);
/**
* Sets a content encryption key to use, by default a random suitable one is generated for the JWE
* "enc" (Encryption Algorithm) Header Parameter.
*
* @deprecated You should not use this method. It is only really intended for test and vector
* validation purposes.
*
* @param cek JWE Content Encryption Key.
*/
setContentEncryptionKey(cek: Uint8Array): this;
/**
* Sets the JWE Initialization Vector to use for content encryption, by default a random suitable
* one is generated for the JWE "enc" (Encryption Algorithm) Header Parameter.
*
* @deprecated You should not use this method. It is only really intended for test and vector
* validation purposes.
*
* @param iv JWE Initialization Vector.
*/
setInitializationVector(iv: Uint8Array): this;
/**
* Sets the JWE Protected Header on the CompactEncrypt object.
*
* @param protectedHeader JWE Protected Header object.
*/
setProtectedHeader(protectedHeader: types.CompactJWEHeaderParameters): this;
/**
* Sets the JWE Key Management parameters to be used when encrypting. For ECDH based algorithms,
* use this method to set the "apu" (Agreement PartyUInfo) or "apv" (Agreement PartyVInfo)
* parameters.
*
* @param parameters JWE Key Management parameters.
*/
setKeyManagementParameters(parameters: types.JWEKeyManagementHeaderParameters): this;
/**
* Encrypts and resolves the value of the Compact JWE string.
*
* @param key Public Key or Secret to encrypt the JWE with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Encryption options.
*/
encrypt(key: types.KeyInput, options?: types.EncryptOptions): Promise<string>;
}
+37
View File
@@ -0,0 +1,37 @@
import type * as types from '../../types.d.ts';
/**
* Interface for Flattened JWE Decryption dynamic key resolution. No token components have been
* verified at the time of this function call.
*/
export interface FlattenedDecryptGetKey<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array> extends types.GetKeyFunction<types.JWEHeaderParameters | undefined, types.FlattenedJWE, KeyType | types.KeyObject | types.JWK> {
}
/**
* Decrypts a Flattened JWE.
*
* @param jwe Flattened JWE.
* @param key Private Key or Secret to decrypt the JWE with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Decryption options.
*/
export declare function flattenedDecrypt(jwe: types.FlattenedJWE, key: types.KeyInput, options?: types.DecryptOptions): Promise<types.FlattenedDecryptResult>;
/**
* Decrypts a Flattened JWE, resolving the key dynamically. The result additionally carries the
* {@link types.ResolvedKey.key resolved key}.
*
* @param jwe Flattened JWE.
* @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Decryption options.
*/
export declare function flattenedDecrypt<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array>(jwe: types.FlattenedJWE, getKey: FlattenedDecryptGetKey<KeyType>, options?: types.DecryptOptions): Promise<types.FlattenedDecryptResult & types.ResolvedKey<KeyType>>;
/**
* Accepts either form of the `key` argument. Use this overload when forwarding a value that may be
* either a key or a key resolution function; `key` is present on the result only when a resolution
* function was used.
*
* @param jwe Flattened JWE.
* @param key Private Key or Secret, or a function resolving one, to decrypt the JWE with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Decryption options.
*/
export declare function flattenedDecrypt(jwe: types.FlattenedJWE, key: types.KeyInput | FlattenedDecryptGetKey, options?: types.DecryptOptions): Promise<types.FlattenedDecryptResult & Partial<types.ResolvedKey>>;
+71
View File
@@ -0,0 +1,71 @@
import type * as types from '../../types.d.ts';
/** The FlattenedEncrypt class is used to build and encrypt Flattened JWE objects. */
export declare class FlattenedEncrypt {
#private;
/**
* {@link FlattenedEncrypt} constructor
*
* @param plaintext Binary representation of the plaintext to encrypt.
*/
constructor(plaintext: Uint8Array);
/**
* Sets the JWE Key Management parameters to be used when encrypting. For ECDH based algorithms,
* use this method to set the "apu" (Agreement PartyUInfo) or "apv" (Agreement PartyVInfo)
* parameters.
*
* @param parameters JWE Key Management parameters.
*/
setKeyManagementParameters(parameters: types.JWEKeyManagementHeaderParameters): this;
/**
* Sets the JWE Protected Header on the FlattenedEncrypt object.
*
* @param protectedHeader JWE Protected Header.
*/
setProtectedHeader(protectedHeader: types.JWEHeaderParameters): this;
/**
* Sets the JWE Shared Unprotected Header on the FlattenedEncrypt object.
*
* @param sharedUnprotectedHeader JWE Shared Unprotected Header.
*/
setSharedUnprotectedHeader(sharedUnprotectedHeader: types.JWEHeaderParameters): this;
/**
* Sets the JWE Per-Recipient Unprotected Header on the FlattenedEncrypt object.
*
* @param unprotectedHeader JWE Per-Recipient Unprotected Header.
*/
setUnprotectedHeader(unprotectedHeader: types.JWEHeaderParameters): this;
/**
* Sets the Additional Authenticated Data on the FlattenedEncrypt object.
*
* @param aad Additional Authenticated Data.
*/
setAdditionalAuthenticatedData(aad: Uint8Array): this;
/**
* Sets a content encryption key to use, by default a random suitable one is generated for the JWE
* "enc" (Encryption Algorithm) Header Parameter.
*
* @deprecated You should not use this method. It is only really intended for test and vector
* validation purposes.
*
* @param cek JWE Content Encryption Key.
*/
setContentEncryptionKey(cek: Uint8Array): this;
/**
* Sets the JWE Initialization Vector to use for content encryption, by default a random suitable
* one is generated for the JWE "enc" (Encryption Algorithm) Header Parameter.
*
* @deprecated You should not use this method. It is only really intended for test and vector
* validation purposes.
*
* @param iv JWE Initialization Vector.
*/
setInitializationVector(iv: Uint8Array): this;
/**
* Encrypts and resolves the value of the Flattened JWE object.
*
* @param key Public Key or Secret to encrypt the JWE with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Encryption options.
*/
encrypt(key: types.KeyInput, options?: types.EncryptOptions): Promise<types.FlattenedJWE>;
}
+43
View File
@@ -0,0 +1,43 @@
import type * as types from '../../types.d.ts';
/**
* Interface for General JWE Decryption dynamic key resolution. No token components have been
* verified at the time of this function call.
*/
export interface GeneralDecryptGetKey<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array> extends types.GetKeyFunction<types.JWEHeaderParameters | undefined, types.FlattenedJWE, KeyType | types.KeyObject | types.JWK> {
}
/**
* Decrypts a General JWE.
*
* > Note: The function iterates over the `recipients` array in the General JWE and returns the decryption
* > result of the first recipient entry that can be successfully decrypted. The result only contains
* > the plaintext and headers of that successfully decrypted recipient entry. Other recipient entries
* > in the General JWE are not validated, and their headers are not included in the returned result.
* > Recipients of a General JWE should only rely on the returned (decrypted) data.
*
* @param jwe General JWE.
* @param key Private Key or Secret to decrypt the JWE with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Decryption options.
*/
export declare function generalDecrypt(jwe: types.GeneralJWE, key: types.KeyInput, options?: types.DecryptOptions): Promise<types.GeneralDecryptResult>;
/**
* Decrypts a General JWE, resolving the key dynamically. The result additionally carries the
* {@link types.ResolvedKey.key resolved key}.
*
* @param jwe General JWE.
* @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Decryption options.
*/
export declare function generalDecrypt<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array>(jwe: types.GeneralJWE, getKey: GeneralDecryptGetKey<KeyType>, options?: types.DecryptOptions): Promise<types.GeneralDecryptResult & types.ResolvedKey<KeyType>>;
/**
* Accepts either form of the `key` argument. Use this overload when forwarding a value that may be
* either a key or a key resolution function; `key` is present on the result only when a resolution
* function was used.
*
* @param jwe General JWE.
* @param key Private Key or Secret, or a function resolving one, to decrypt the JWE with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Decryption options.
*/
export declare function generalDecrypt(jwe: types.GeneralJWE, key: types.KeyInput | GeneralDecryptGetKey, options?: types.DecryptOptions): Promise<types.GeneralDecryptResult & Partial<types.ResolvedKey>>;
+73
View File
@@ -0,0 +1,73 @@
import type * as types from '../../types.d.ts';
/** Used to build General JWE object's individual recipients. */
export interface Recipient {
/**
* Sets the JWE Per-Recipient Unprotected Header on the Recipient object.
*
* @param unprotectedHeader JWE Per-Recipient Unprotected Header.
*/
setUnprotectedHeader(unprotectedHeader: types.JWEHeaderParameters): Recipient;
/**
* Sets the JWE Key Management parameters to be used when encrypting. For ECDH based algorithms,
* use this method to set the "apu" (Agreement PartyUInfo) or "apv" (Agreement PartyVInfo)
* parameters.
*
* @param parameters JWE Key Management parameters.
*/
setKeyManagementParameters(parameters: types.JWEKeyManagementHeaderParameters): Recipient;
/**
* A shorthand for calling {@link GeneralEncrypt.addRecipient addRecipient()} on the enclosing
* {@link GeneralEncrypt} instance.
*
* @param key Public Key or Secret to encrypt the Content Encryption Key for the recipient with.
* See {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Encryption options.
*/
addRecipient(key: types.KeyInput, options?: types.CritOption): Recipient;
/**
* A shorthand for calling {@link GeneralEncrypt.encrypt encrypt()} on the enclosing
* {@link GeneralEncrypt} instance. Takes no arguments — each recipient's key is supplied to
* {@link addRecipient}.
*/
encrypt(): Promise<types.GeneralJWE>;
/** Returns the enclosing {@link GeneralEncrypt} instance */
done(): GeneralEncrypt;
}
/** The GeneralEncrypt class is used to build and encrypt General JWE objects. */
export declare class GeneralEncrypt {
#private;
/**
* {@link GeneralEncrypt} constructor
*
* @param plaintext Binary representation of the plaintext to encrypt.
*/
constructor(plaintext: Uint8Array);
/**
* Adds an additional recipient for the General JWE object.
*
* @param key Public Key or Secret to encrypt the Content Encryption Key for the recipient with.
* See {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Encryption options.
*/
addRecipient(key: types.KeyInput, options?: types.CritOption): Recipient;
/**
* Sets the JWE Protected Header on the GeneralEncrypt object.
*
* @param protectedHeader JWE Protected Header object.
*/
setProtectedHeader(protectedHeader: types.JWEHeaderParameters): this;
/**
* Sets the JWE Shared Unprotected Header on the GeneralEncrypt object.
*
* @param sharedUnprotectedHeader JWE Shared Unprotected Header object.
*/
setSharedUnprotectedHeader(sharedUnprotectedHeader: types.JWEHeaderParameters): this;
/**
* Sets the Additional Authenticated Data on the GeneralEncrypt object.
*
* @param aad Additional Authenticated Data.
*/
setAdditionalAuthenticatedData(aad: Uint8Array): this;
/** Encrypts and resolves the value of the General JWE object. */
encrypt(): Promise<types.GeneralJWE>;
}
+13
View File
@@ -0,0 +1,13 @@
import type * as types from '../types.d.ts';
/**
* EmbeddedJWK is an implementation of a {@link types.GetKeyFunction GetKeyFunction} intended to be
* used with the JWS/JWT verify operations whenever you need to opt-in to verify signatures with a
* public key embedded in the token's "jwk" (JSON Web Key) Header Parameter. It is recommended to
* combine this with the verify function's `algorithms` option to define accepted JWS "alg"
* (Algorithm) Header Parameter values.
*
* @param protectedHeader JWS Protected Header.
* @param token The consumed JWS token.
* @returns The public key from the JWS "jwk" (JSON Web Key) Header Parameter.
*/
export declare function EmbeddedJWK(protectedHeader?: types.JWSHeaderParameters, token?: types.FlattenedJWSInput): Promise<types.CryptoKey>;
+17
View File
@@ -0,0 +1,17 @@
import type * as types from '../types.d.ts';
/**
* Calculates a base64url-encoded JSON Web Key (JWK) Thumbprint
*
* @param key Key to calculate the thumbprint for.
* @param digestAlgorithm Digest Algorithm to use for calculating the thumbprint. Default is
* "sha256".
*/
export declare function calculateJwkThumbprint(key: types.JWK | types.CryptoKey | types.KeyObject, digestAlgorithm?: 'sha256' | 'sha384' | 'sha512'): Promise<string>;
/**
* Calculates a JSON Web Key (JWK) Thumbprint URI
*
* @param key Key to calculate the thumbprint for.
* @param digestAlgorithm Digest Algorithm to use for calculating the thumbprint. Default is
* "sha256".
*/
export declare function calculateJwkThumbprintUri(key: types.CryptoKey | types.KeyObject | types.JWK, digestAlgorithm?: 'sha256' | 'sha384' | 'sha512'): Promise<string>;
+20
View File
@@ -0,0 +1,20 @@
import type * as types from '../types.d.ts';
/** The key resolution function returned by {@link createLocalJWKSet}. */
export interface LocalJWKSet {
(protectedHeader?: types.JWSHeaderParameters, token?: types.FlattenedJWSInput): Promise<types.CryptoKey>;
/** Returns a structured clone of the JSON Web Key Set this resolver was created with. */
jwks: () => types.JSONWebKeySet;
}
/**
* Returns a function that resolves a JWS JOSE Header to a public key object from a locally stored,
* or otherwise available, JSON Web Key Set. Selection respects the header's "alg" (Algorithm) and
* "kid" (Key ID) as well as the JWK's "use" (Public Key Use) and "key_ops" (Key Operations).
* Exactly one key must match; if multiple keys match, the thrown `JWKSMultipleMatchingKeys` can be
* iterated.
*
* > Note: The function's purpose is to resolve public keys used for verifying signatures and will not work
* > for public encryption keys.
*
* @param jwks JSON Web Key Set formatted object.
*/
export declare function createLocalJWKSet(jwks: types.JSONWebKeySet): LocalJWKSet;
+106
View File
@@ -0,0 +1,106 @@
import type * as types from '../types.d.ts';
/**
* When passed to {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} this allows the resolver
* to make use of advanced fetch configurations, HTTP Proxies, retry on network errors, etc.
*
* > Note: Known caveat: Expect Type-related issues when passing the inputs through to fetch-like modules,
* > they hardly ever get their typings inline with actual fetch, you should `@ts-expect-error` them.
*/
export declare const customFetch: unique symbol;
/** See {@link customFetch}. */
export type FetchImplementation = (
/** URL the request is being made sent to {@link !fetch} as the `resource` argument */
url: string,
/** Options otherwise sent to {@link !fetch} as the `options` argument */
options: {
/** HTTP Headers */
headers: Headers;
/** The {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods request method} */
method: 'GET';
/** See {@link !Request.redirect} */
redirect: 'manual';
signal: AbortSignal;
}) => Promise<Response>;
/**
* > Warning: This option has security implications that must be understood, assessed for applicability, and
* > accepted before use. It is critical that the JSON Web Key Set cache only be writable by your own
* > code.
*
* This option is intended for cloud computing runtimes that cannot keep an in memory cache between
* their code's invocations. The supplied writable object seeds the resolver's cache and is updated
* with `jwks` and `uat` after a successful fetch; persist it whenever `uat` changes. Using this in
* runtimes that can keep an in-memory cache between requests is not desirable.
*/
export declare const jwksCache: unique symbol;
/** Options for the remote JSON Web Key Set. */
export interface RemoteJWKSetOptions {
/**
* Timeout (in milliseconds) for the HTTP request. When reached the request will be aborted and
* the verification will fail. Default is 5000 (5 seconds).
*/
timeoutDuration?: number;
/**
* Duration (in milliseconds) for which no more HTTP requests will be triggered after a previous
* successful fetch. Default is 30000 (30 seconds).
*/
cooldownDuration?: number;
/**
* Maximum time (in milliseconds) between successful HTTP requests. Default is 600000 (10
* minutes).
*/
cacheMaxAge?: number | typeof Infinity;
/** Headers to be sent with the HTTP request. */
headers?: Record<string, string>;
/** See {@link jwksCache}. */
[jwksCache]?: JWKSCacheInput;
/** See {@link customFetch}. */
[customFetch]?: FetchImplementation;
}
/** See {@link jwksCache}. */
export interface ExportedJWKSCache {
/** Current cached JSON Web Key Set */
jwks: types.JSONWebKeySet;
/** Last updated at timestamp (seconds since epoch) */
uat: number;
}
/** See {@link jwksCache}. */
export type JWKSCacheInput = ExportedJWKSCache | Record<string, never>;
/** The key resolution function returned by {@link createRemoteJWKSet}. */
export interface RemoteJWKSet {
(protectedHeader?: types.JWSHeaderParameters, token?: types.FlattenedJWSInput): Promise<types.CryptoKey>;
/** Whether the cooldown window following the last successful fetch is still in effect. */
readonly coolingDown: boolean;
/**
* Whether the currently cached JSON Web Key Set is within its
* {@link RemoteJWKSetOptions.cacheMaxAge}.
*/
readonly fresh: boolean;
/** Whether a JSON Web Key Set fetch is currently in flight. */
readonly reloading: boolean;
/**
* Triggers a JSON Web Key Set fetch, bypassing
* {@link RemoteJWKSetOptions.cooldownDuration the cooldown}.
*/
reload: () => Promise<void>;
/**
* The currently cached JSON Web Key Set, or `undefined` when none has been fetched or seeded via
* {@link jwksCache} yet.
*/
jwks: () => types.JSONWebKeySet | undefined;
}
/**
* Returns a function that resolves a JWS JOSE Header to a public key object downloaded from a
* remote endpoint returning a JSON Web Key Set, that is, for example, an OAuth 2.0 or OIDC
* jwks_uri. The JSON Web Key Set is fetched when no key matches the selection process but only as
* frequently as the `cooldownDuration` option allows to prevent abuse. Selection respects the
* header's "alg" (Algorithm) and "kid" (Key ID) as well as the JWK's "use" (Public Key Use) and
* "key_ops" (Key Operations). Exactly one key must match; if multiple keys match, the thrown
* `JWKSMultipleMatchingKeys` can be iterated.
*
* > Note: The function's purpose is to resolve public keys used for verifying signatures and will not work
* > for public encryption keys.
*
* @param url URL to fetch the JSON Web Key Set from.
* @param options Options for the remote JSON Web Key Set.
*/
export declare function createRemoteJWKSet(url: URL, options?: RemoteJWKSetOptions): RemoteJWKSet;
+25
View File
@@ -0,0 +1,25 @@
import type * as types from '../../types.d.ts';
/** The CompactSign class is used to build and sign Compact JWS strings. */
export declare class CompactSign {
#private;
/**
* {@link CompactSign} constructor
*
* @param payload Binary representation of the payload to sign.
*/
constructor(payload: Uint8Array);
/**
* Sets the JWS Protected Header on the CompactSign object.
*
* @param protectedHeader JWS Protected Header.
*/
setProtectedHeader(protectedHeader: types.CompactJWSHeaderParameters): this;
/**
* Signs and resolves the value of the Compact JWS string.
*
* @param key Private Key or Secret to sign the JWS with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Sign options.
*/
sign(key: types.KeyInput, options?: types.SignOptions): Promise<string>;
}
+37
View File
@@ -0,0 +1,37 @@
import type * as types from '../../types.d.ts';
/**
* Interface for Compact JWS Verification dynamic key resolution. No token components have been
* verified at the time of this function call.
*/
export interface CompactVerifyGetKey<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array> extends types.GetKeyFunction<types.CompactJWSHeaderParameters, types.FlattenedJWSInput, KeyType | types.KeyObject | types.JWK> {
}
/**
* Verifies the signature and format of and afterwards decodes the Compact JWS.
*
* @param jws Compact JWS.
* @param key Key to verify the JWS with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Verify options.
*/
export declare function compactVerify(jws: string | Uint8Array, key: types.KeyInput, options?: types.VerifyOptions): Promise<types.CompactVerifyResult>;
/**
* Verifies the signature and format of and afterwards decodes the Compact JWS, resolving the key
* dynamically. The result additionally carries the {@link types.ResolvedKey.key resolved key}.
*
* @param jws Compact JWS.
* @param getKey Function resolving a key to verify the JWS with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Verify options.
*/
export declare function compactVerify<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array>(jws: string | Uint8Array, getKey: CompactVerifyGetKey<KeyType>, options?: types.VerifyOptions): Promise<types.CompactVerifyResult & types.ResolvedKey<KeyType>>;
/**
* Accepts either form of the `key` argument. Use this overload when forwarding a value that may be
* either a key or a key resolution function; `key` is present on the result only when a resolution
* function was used.
*
* @param jws Compact JWS.
* @param key Key, or function resolving a key, to verify the JWS with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Verify options.
*/
export declare function compactVerify(jws: string | Uint8Array, key: types.KeyInput | CompactVerifyGetKey, options?: types.VerifyOptions): Promise<types.CompactVerifyResult & Partial<types.ResolvedKey>>;
+31
View File
@@ -0,0 +1,31 @@
import type * as types from '../../types.d.ts';
/** The FlattenedSign class is used to build and sign Flattened JWS objects. */
export declare class FlattenedSign {
#private;
/**
* {@link FlattenedSign} constructor
*
* @param payload Binary representation of the payload to sign.
*/
constructor(payload: Uint8Array);
/**
* Sets the JWS Protected Header on the FlattenedSign object.
*
* @param protectedHeader JWS Protected Header.
*/
setProtectedHeader(protectedHeader: types.JWSHeaderParameters): this;
/**
* Sets the JWS Unprotected Header on the FlattenedSign object.
*
* @param unprotectedHeader JWS Unprotected Header.
*/
setUnprotectedHeader(unprotectedHeader: types.JWSHeaderParameters): this;
/**
* Signs and resolves the value of the Flattened JWS object.
*
* @param key Private Key or Secret to sign the JWS with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Sign options.
*/
sign(key: types.KeyInput, options?: types.SignOptions): Promise<types.FlattenedJWS>;
}
+37
View File
@@ -0,0 +1,37 @@
import type * as types from '../../types.d.ts';
/**
* Interface for Flattened JWS Verification dynamic key resolution. No token components have been
* verified at the time of this function call.
*/
export interface FlattenedVerifyGetKey<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array> extends types.GetKeyFunction<types.JWSHeaderParameters, types.FlattenedJWSInput, KeyType | types.KeyObject | types.JWK> {
}
/**
* Verifies the signature and format of and afterwards decodes the Flattened JWS.
*
* @param jws Flattened JWS.
* @param key Key to verify the JWS with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Verify options.
*/
export declare function flattenedVerify(jws: types.FlattenedJWSInput, key: types.KeyInput, options?: types.VerifyOptions): Promise<types.FlattenedVerifyResult>;
/**
* Verifies the signature and format of and afterwards decodes the Flattened JWS, resolving the key
* dynamically. The result additionally carries the {@link types.ResolvedKey.key resolved key}.
*
* @param jws Flattened JWS.
* @param getKey Function resolving a key to verify the JWS with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Verify options.
*/
export declare function flattenedVerify<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array>(jws: types.FlattenedJWSInput, getKey: FlattenedVerifyGetKey<KeyType>, options?: types.VerifyOptions): Promise<types.FlattenedVerifyResult & types.ResolvedKey<KeyType>>;
/**
* Accepts either form of the `key` argument. Use this overload when forwarding a value that may be
* either a key or a key resolution function; `key` is present on the result only when a resolution
* function was used.
*
* @param jws Flattened JWS.
* @param key Key, or function resolving a key, to verify the JWS with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Verify options.
*/
export declare function flattenedVerify(jws: types.FlattenedJWSInput, key: types.KeyInput | FlattenedVerifyGetKey, options?: types.VerifyOptions): Promise<types.FlattenedVerifyResult & Partial<types.ResolvedKey>>;
+52
View File
@@ -0,0 +1,52 @@
import type * as types from '../../types.d.ts';
/** Used to build General JWS object's individual signatures. */
export interface Signature {
/**
* Sets the JWS Protected Header on the Signature object.
*
* @param protectedHeader JWS Protected Header.
*/
setProtectedHeader(protectedHeader: types.JWSHeaderParameters): Signature;
/**
* Sets the JWS Unprotected Header on the Signature object.
*
* @param unprotectedHeader JWS Unprotected Header.
*/
setUnprotectedHeader(unprotectedHeader: types.JWSHeaderParameters): Signature;
/**
* A shorthand for calling {@link GeneralSign.addSignature addSignature()} on the enclosing
* {@link GeneralSign} instance.
*
* @param key Private Key or Secret to sign the individual JWS signature with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Sign options.
*/
addSignature(key: types.KeyInput, options?: types.SignOptions): Signature;
/**
* A shorthand for calling {@link GeneralSign.sign sign()} on the enclosing {@link GeneralSign}
* instance. Takes no arguments — each signature's key is supplied to {@link addSignature}.
*/
sign(): Promise<types.GeneralJWS>;
/** Returns the enclosing {@link GeneralSign} instance */
done(): GeneralSign;
}
/** The GeneralSign class is used to build and sign General JWS objects. */
export declare class GeneralSign {
#private;
/**
* {@link GeneralSign} constructor
*
* @param payload Binary representation of the payload to sign.
*/
constructor(payload: Uint8Array);
/**
* Adds an additional signature for the General JWS object.
*
* @param key Private Key or Secret to sign the individual JWS signature with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Sign options.
*/
addSignature(key: types.KeyInput, options?: types.SignOptions): Signature;
/** Signs and resolves the value of the General JWS object. */
sign(): Promise<types.GeneralJWS>;
}
+44
View File
@@ -0,0 +1,44 @@
import type * as types from '../../types.d.ts';
/**
* Interface for General JWS Verification dynamic key resolution. No token components have been
* verified at the time of this function call.
*/
export interface GeneralVerifyGetKey<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array> extends types.GetKeyFunction<types.JWSHeaderParameters, types.FlattenedJWSInput, KeyType | types.KeyObject | types.JWK> {
}
/**
* Verifies the signature and format of and afterwards decodes the General JWS.
*
* > Note: The function iterates over the `signatures` array in the General JWS and returns the verification
* > result of the first signature entry that can be successfully verified. The result only contains
* > the payload, protected header, and unprotected header of that successfully verified signature
* > entry. Other signature entries in the General JWS are not validated, and their headers are not
* > included in the returned result. Recipients of a General JWS should only rely on the returned
* > (verified) data.
*
* @param jws General JWS.
* @param key Key to verify the JWS with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Verify options.
*/
export declare function generalVerify(jws: types.GeneralJWSInput, key: types.KeyInput, options?: types.VerifyOptions): Promise<types.GeneralVerifyResult>;
/**
* Verifies the signature and format of and afterwards decodes the General JWS, resolving the key
* dynamically. The result additionally carries the {@link types.ResolvedKey.key resolved key}.
*
* @param jws General JWS.
* @param getKey Function resolving a key to verify the JWS with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Verify options.
*/
export declare function generalVerify<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array>(jws: types.GeneralJWSInput, getKey: GeneralVerifyGetKey<KeyType>, options?: types.VerifyOptions): Promise<types.GeneralVerifyResult & types.ResolvedKey<KeyType>>;
/**
* Accepts either form of the `key` argument. Use this overload when forwarding a value that may be
* either a key or a key resolution function; `key` is present on the result only when a resolution
* function was used.
*
* @param jws General JWS.
* @param key Key, or function resolving a key, to verify the JWS with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWS Verify options.
*/
export declare function generalVerify(jws: types.GeneralJWSInput, key: types.KeyInput | GeneralVerifyGetKey, options?: types.VerifyOptions): Promise<types.GeneralVerifyResult & Partial<types.ResolvedKey>>;
+41
View File
@@ -0,0 +1,41 @@
import type * as types from '../types.d.ts';
/** Combination of JWE Decryption options and JWT Claims Set verification options. */
export interface JWTDecryptOptions extends types.DecryptOptions, types.JWTClaimVerificationOptions {
}
/**
* Interface for JWT Decryption dynamic key resolution. No token components have been verified at
* the time of this function call.
*/
export interface JWTDecryptGetKey<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array> extends types.GetKeyFunction<types.CompactJWEHeaderParameters, types.FlattenedJWE, KeyType | types.KeyObject | types.JWK> {
}
/**
* Verifies the JWT format (to be a JWE Compact format), decrypts the ciphertext, validates the JWT
* Claims Set.
*
* @param jwt JSON Web Token value (encoded as JWE).
* @param key Private Key or Secret to decrypt and verify the JWT with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWT Decryption and JWT Claims Set validation options.
*/
export declare function jwtDecrypt<PayloadType = types.JWTPayload>(jwt: string | Uint8Array, key: types.KeyInput, options?: JWTDecryptOptions): Promise<types.JWTDecryptResult<PayloadType>>;
/**
* Decrypts a JWT and validates its JWT Claims Set, resolving the key dynamically. The result
* additionally carries the {@link types.ResolvedKey.key resolved key}.
*
* @param jwt JSON Web Token value (encoded as JWE).
* @param getKey Function resolving Private Key or Secret to decrypt and verify the JWT with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWT Decryption and JWT Claims Set validation options.
*/
export declare function jwtDecrypt<PayloadType = types.JWTPayload, KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array>(jwt: string | Uint8Array, getKey: JWTDecryptGetKey<KeyType>, options?: JWTDecryptOptions): Promise<types.JWTDecryptResult<PayloadType> & types.ResolvedKey<KeyType>>;
/**
* Accepts either form of the `key` argument. Use this overload when forwarding a value that may be
* either a key or a key resolution function; `key` is present on the result only when a resolution
* function was used.
*
* @param jwt JSON Web Token value (encoded as JWE).
* @param key Private Key or Secret, or a function resolving one, to decrypt and verify the JWT
* with. See {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWT Decryption and JWT Claims Set validation options.
*/
export declare function jwtDecrypt<PayloadType = types.JWTPayload>(jwt: string | Uint8Array, key: types.KeyInput | JWTDecryptGetKey, options?: JWTDecryptOptions): Promise<types.JWTDecryptResult<PayloadType> & Partial<types.ResolvedKey>>;
+67
View File
@@ -0,0 +1,67 @@
import type * as types from '../types.d.ts';
/** The EncryptJWT class is used to build and encrypt Compact JWE formatted JSON Web Tokens. */
export declare class EncryptJWT implements types.ProduceJWT {
#private;
/**
* {@link EncryptJWT} constructor
*
* @param payload The JWT Claims Set object. Defaults to an empty object.
*/
constructor(payload?: types.JWTPayload);
setIssuer(issuer: string): this;
setSubject(subject: string): this;
setAudience(audience: string | string[]): this;
setJti(jwtId: string): this;
setNotBefore(input: number | string | Date): this;
setExpirationTime(input: number | string | Date): this;
setIssuedAt(input?: number | string | Date): this;
/**
* Sets the JWE Protected Header on the EncryptJWT object.
*
* @param protectedHeader JWE Protected Header. Must contain an "alg" (JWE Algorithm) and "enc"
* (JWE Encryption Algorithm) properties.
*/
setProtectedHeader(protectedHeader: types.CompactJWEHeaderParameters): this;
/**
* Sets the JWE Key Management parameters to be used when encrypting. For ECDH based algorithms,
* use this method to set the "apu" (Agreement PartyUInfo) or "apv" (Agreement PartyVInfo)
* parameters.
*
* @param parameters JWE Key Management parameters.
*/
setKeyManagementParameters(parameters: types.JWEKeyManagementHeaderParameters): this;
/**
* Sets a content encryption key to use, by default a random suitable one is generated for the JWE
* "enc" (Encryption Algorithm) Header Parameter.
*
* @deprecated You should not use this method. It is only really intended for test and vector
* validation purposes.
*
* @param cek JWE Content Encryption Key.
*/
setContentEncryptionKey(cek: Uint8Array): this;
/**
* Sets the JWE Initialization Vector to use for content encryption, by default a random suitable
* one is generated for the JWE "enc" (Encryption Algorithm) Header Parameter.
*
* @deprecated You should not use this method. It is only really intended for test and vector
* validation purposes.
*
* @param iv JWE Initialization Vector.
*/
setInitializationVector(iv: Uint8Array): this;
/** Replicates the "iss" (Issuer) Claim as a JWE Protected Header Parameter. */
replicateIssuerAsHeader(): this;
/** Replicates the "sub" (Subject) Claim as a JWE Protected Header Parameter. */
replicateSubjectAsHeader(): this;
/** Replicates the "aud" (Audience) Claim as a JWE Protected Header Parameter. */
replicateAudienceAsHeader(): this;
/**
* Encrypts and returns the JWT.
*
* @param key Public Key or Secret to encrypt the JWT with. See
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
* @param options JWE Encryption options.
*/
encrypt(key: types.KeyInput, options?: types.EncryptOptions): Promise<string>;
}
+32
View File
@@ -0,0 +1,32 @@
import type * as types from '../types.d.ts';
/** The SignJWT class is used to build and sign Compact JWS formatted JSON Web Tokens. */
export declare class SignJWT implements types.ProduceJWT {
#private;
/**
* {@link SignJWT} constructor
*
* @param payload The JWT Claims Set object. Defaults to an empty object.
*/
constructor(payload?: types.JWTPayload);
setIssuer(issuer: string): this;
setSubject(subject: string): this;
setAudience(audience: string | string[]): this;
setJti(jwtId: string): this;
setNotBefore(input: number | string | Date): this;
setExpirationTime(input: number | string | Date): this;
setIssuedAt(input?: number | string | Date): this;
/**
* Sets the JWS Protected Header on the SignJWT object.
*
* @param protectedHeader JWS Protected Header. Must contain an "alg" (JWS Algorithm) property.
*/
setProtectedHeader(protectedHeader: types.JWTHeaderParameters): this;
/**
* Signs and returns the JWT.
*
* @param key Private Key or Secret to sign the JWT with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWT Sign options.
*/
sign(key: types.KeyInput, options?: types.SignOptions): Promise<string>;
}
+34
View File
@@ -0,0 +1,34 @@
import type * as types from '../types.d.ts';
/** Result of decoding an Unsecured JWT. */
export interface UnsecuredResult<PayloadType = types.JWTPayload> {
/** JWT Claims Set. */
payload: PayloadType & types.JWTPayload & ([PayloadType] extends [object] ? unknown : unknown extends PayloadType ? unknown : never);
/** The decoded JOSE Header; always `{ "alg": "none" }` for an Unsecured JWT. */
header: types.JWSHeaderParameters;
}
/** The UnsecuredJWT class is a utility for dealing with `{ "alg": "none" }` Unsecured JWTs. */
export declare class UnsecuredJWT implements types.ProduceJWT {
#private;
/**
* {@link UnsecuredJWT} constructor
*
* @param payload The JWT Claims Set object. Defaults to an empty object.
*/
constructor(payload?: types.JWTPayload);
/** Encodes the Unsecured JWT. */
encode(): string;
setIssuer(issuer: string): this;
setSubject(subject: string): this;
setAudience(audience: string | string[]): this;
setJti(jwtId: string): this;
setNotBefore(input: number | string | Date): this;
setExpirationTime(input: number | string | Date): this;
setIssuedAt(input?: number | string | Date): this;
/**
* Decodes an unsecured JWT.
*
* @param jwt Unsecured JWT to decode the payload of.
* @param options JWT Claims Set validation options.
*/
static decode<PayloadType = types.JWTPayload>(jwt: string, options?: types.JWTClaimVerificationOptions): UnsecuredResult<PayloadType>;
}
+38
View File
@@ -0,0 +1,38 @@
import type * as types from '../types.d.ts';
/** Combination of JWS Verification options and JWT Claims Set verification options. */
export interface JWTVerifyOptions extends types.VerifyOptions, types.JWTClaimVerificationOptions {
}
/**
* Interface for JWT Verification dynamic key resolution. No token components have been verified at
* the time of this function call.
*/
export interface JWTVerifyGetKey<KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array> extends types.GetKeyFunction<types.CompactJWSHeaderParameters, types.FlattenedJWSInput, KeyType | types.KeyObject | types.JWK> {
}
/**
* Verifies the JWT format (to be a JWS Compact format), verifies the JWS signature, validates the
* JWT Claims Set.
*
* @param jwt JSON Web Token value (encoded as JWS).
* @param key Key to verify the JWT with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWT Decryption and JWT Claims Set validation options.
*/
export declare function jwtVerify<PayloadType = types.JWTPayload>(jwt: string | Uint8Array, key: types.KeyInput, options?: JWTVerifyOptions): Promise<types.JWTVerifyResult<PayloadType>>;
/**
* @param jwt JSON Web Token value (encoded as JWS).
* @param getKey Function resolving a key to verify the JWT with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWT Decryption and JWT Claims Set validation options.
*/
export declare function jwtVerify<PayloadType = types.JWTPayload, KeyType extends types.CryptoKey | Uint8Array = types.CryptoKey | Uint8Array>(jwt: string | Uint8Array, getKey: JWTVerifyGetKey<KeyType>, options?: JWTVerifyOptions): Promise<types.JWTVerifyResult<PayloadType> & types.ResolvedKey<KeyType>>;
/**
* Accepts either form of the `key` argument. Use this overload when forwarding a value that may be
* either a key or a key resolution function; `key` is present on the result only when a resolution
* function was used.
*
* @param jwt JSON Web Token value (encoded as JWS).
* @param key Key, or function resolving a key, to verify the JWT with. See
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
* @param options JWT Decryption and JWT Claims Set validation options.
*/
export declare function jwtVerify<PayloadType = types.JWTPayload>(jwt: string | Uint8Array, key: types.KeyInput | JWTVerifyGetKey, options?: JWTVerifyOptions): Promise<types.JWTVerifyResult<PayloadType> & Partial<types.ResolvedKey>>;
+19
View File
@@ -0,0 +1,19 @@
import type * as types from '../types.d.ts';
/**
* Exports a public {@link !CryptoKey} or {@link !KeyObject} to a PEM-encoded SPKI string format.
*
* @param key Key to export to a PEM-encoded SPKI string format.
*/
export declare function exportSPKI(key: types.CryptoKey | types.KeyObject): Promise<string>;
/**
* Exports a private {@link !CryptoKey} or {@link !KeyObject} to a PEM-encoded PKCS8 string format.
*
* @param key Key to export to a PEM-encoded PKCS8 string format.
*/
export declare function exportPKCS8(key: types.CryptoKey | types.KeyObject): Promise<string>;
/**
* Exports a {@link !CryptoKey}, {@link !KeyObject}, or {@link !Uint8Array} to a JWK.
*
* @param key Key to export as JWK.
*/
export declare function exportJWK(key: types.CryptoKey | types.KeyObject | Uint8Array): Promise<types.JWK>;
+40
View File
@@ -0,0 +1,40 @@
import type * as types from '../types.d.ts';
/**
* JWA Algorithm Identifiers that {@link generateKeyPair} is able to generate a key pair for, subject
* to runtime support.
*/
export type GenerateKeyPairAlgorithm = 'PS256' | 'PS384' | 'PS512' | 'RS256' | 'RS384' | 'RS512' | 'RSA-OAEP' | 'RSA-OAEP-256' | 'RSA-OAEP-384' | 'RSA-OAEP-512' | 'ES256' | 'ES384' | 'ES512' | 'Ed25519' | 'EdDSA' | 'ML-DSA-44' | 'ML-DSA-65' | 'ML-DSA-87' | 'ECDH-ES' | 'ECDH-ES+A128KW' | 'ECDH-ES+A192KW' | 'ECDH-ES+A256KW' | (string & {});
/** Asymmetric key pair generation function result. */
export interface GenerateKeyPairResult {
/** The generated Private Key. */
privateKey: types.CryptoKey;
/** Public Key corresponding to the generated Private Key. */
publicKey: types.CryptoKey;
}
/** Asymmetric key pair generation function options. */
export interface GenerateKeyPairOptions {
/**
* The EC "crv" (Curve) or OKP "crv" (Subtype of Key Pair) value to generate. The curve must be
* both supported on the runtime as well as applicable for the given JWA algorithm identifier.
*/
crv?: string;
/**
* A hint for RSA algorithms to generate an RSA key of a given `modulusLength` (Key size in bits).
* JOSE requires 2048 bits or larger. Default is 2048.
*/
modulusLength?: number;
/** The value to use as {@link !SubtleCrypto.generateKey} `extractable` argument. Default is false. */
extractable?: boolean;
}
/**
* Generates a private and a public key for a given JWA algorithm identifier. This can only generate
* asymmetric key pairs. For symmetric secrets use the `generateSecret` function.
*
* > Note: The `privateKey` is generated with `extractable` set to `false` by default. See
* > {@link GenerateKeyPairOptions.extractable} to generate an extractable `privateKey`.
*
* @param alg JWA Algorithm Identifier to be used with the generated key pair. See
* {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}.
* @param options Additional options passed down to the key pair generation.
*/
export declare function generateKeyPair(alg: GenerateKeyPairAlgorithm, options?: GenerateKeyPairOptions): Promise<GenerateKeyPairResult>;
+37
View File
@@ -0,0 +1,37 @@
import type * as types from '../types.d.ts';
/**
* JWA Algorithm Identifiers that {@link generateSecret} is able to generate a secret for, subject to
* runtime support.
*/
export type GenerateSecretAlgorithm = 'HS256' | 'HS384' | 'HS512' | 'A128CBC-HS256' | 'A192CBC-HS384' | 'A256CBC-HS512' | 'A128KW' | 'A192KW' | 'A256KW' | 'A128GCMKW' | 'A192GCMKW' | 'A256GCMKW' | 'A128GCM' | 'A192GCM' | 'A256GCM' | (string & {});
/**
* Resolves what {@link generateSecret} returns for a given JWA Algorithm Identifier. The
* AES_CBC_HMAC_SHA2 content encryption algorithms have no {@link !CryptoKey} representation, so they
* yield a {@link !Uint8Array}; every other supported identifier yields a
* {@link types.CryptoKey CryptoKey}. When the identifier is not statically known this resolves to
* their union.
*/
export type GeneratedSecret<Alg extends string> = Alg extends 'A128CBC-HS256' | 'A192CBC-HS384' | 'A256CBC-HS512' ? Uint8Array : string extends Alg ? types.CryptoKey | Uint8Array : types.CryptoKey;
/** Secret generation function options. */
export interface GenerateSecretOptions {
/**
* The value to use as {@link !SubtleCrypto.generateKey} `extractable` argument. Default is false.
*
* > Note: Because A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512 secrets cannot be represented as
* > {@link !CryptoKey} this option has no effect for them.
*/
extractable?: boolean;
}
/**
* Generates a symmetric secret key for a given JWA algorithm identifier.
*
* > Note: The secret key is generated with `extractable` set to `false` by default.
*
* > Note: Because A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512 secrets cannot be represented as
* > {@link !CryptoKey} this method yields a {@link !Uint8Array} for them instead.
*
* @param alg JWA Algorithm Identifier to be used with the generated secret. See
* {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}.
* @param options Additional options passed down to the secret generation.
*/
export declare function generateSecret<Alg extends GenerateSecretAlgorithm>(alg: Alg, options?: GenerateSecretOptions): Promise<GeneratedSecret<Alg>>;
+73
View File
@@ -0,0 +1,73 @@
import type * as types from '../types.d.ts';
/**
* Resolves what {@link importJWK} returns for a given JWK type. The "kty" (Key Type) Parameter fully
* determines the outcome at runtime: `"oct"` yields a {@link !Uint8Array} secret, every other
* supported key type yields a {@link types.CryptoKey CryptoKey}. When "kty" is not statically known
* — the usual case for a JWK parsed from JSON, or for a value typed as {@link types.JWK JWK} — this
* resolves to their union.
*/
export type ImportedJWK<JWKType extends types.JWK> = JWKType extends {
kty: 'oct';
} ? Uint8Array : JWKType extends {
kty: 'AKP' | 'EC' | 'OKP' | 'RSA';
} ? types.CryptoKey : types.CryptoKey | Uint8Array;
/** Key Import Function options. */
export interface KeyImportOptions {
/**
* The value to use as {@link !SubtleCrypto.importKey} `extractable` argument. Default is false for
* private keys, true otherwise.
*/
extractable?: boolean;
}
/**
* Imports a PEM-encoded SPKI string as a {@link !CryptoKey}.
*
* > Note: The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in
* > {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption
* > (1.2.840.113549.1.1.1) instead for all RSA algorithms.
*
* @param spki PEM-encoded SPKI string
* @param alg JSON Web Algorithm identifier to be used with the imported key. See
* {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}.
*/
export declare function importSPKI(spki: string, alg: string, options?: KeyImportOptions): Promise<types.CryptoKey>;
/**
* Imports the SPKI from an X.509 string certificate as a {@link !CryptoKey}.
*
* > Note: The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in
* > {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption
* > (1.2.840.113549.1.1.1) instead for all RSA algorithms.
*
* @param x509 X.509 certificate string
* @param alg JSON Web Algorithm identifier to be used with the imported key. See
* {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}.
*/
export declare function importX509(x509: string, alg: string, options?: KeyImportOptions): Promise<types.CryptoKey>;
/**
* Imports a PEM-encoded PKCS#8 string as a {@link !CryptoKey}.
*
* > Note: The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in
* > {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption
* > (1.2.840.113549.1.1.1) instead for all RSA algorithms.
*
* @param pkcs8 PEM-encoded PKCS#8 string
* @param alg JSON Web Algorithm identifier to be used with the imported key. See
* {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}.
*/
export declare function importPKCS8(pkcs8: string, alg: string, options?: KeyImportOptions): Promise<types.CryptoKey>;
/**
* Imports a JWK to a {@link !CryptoKey}. Either the JWK "alg" (Algorithm) Parameter, or the optional
* "alg" argument, must be present for asymmetric JSON Web Key imports.
*
* > Note: The JSON Web Key parameters "key_ops" and "ext" are also used in the {@link !CryptoKey} import
* > process.
*
* > Note: Symmetric JSON Web Keys (i.e. `kty: "oct"`) yield back an {@link !Uint8Array} instead of a
* > {@link !CryptoKey}.
*
* @param jwk JSON Web Key.
* @param alg JSON Web Algorithm identifier to be used with the imported key. Default is the "alg"
* property on the JWK. See
* {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}.
*/
export declare function importJWK<JWKType extends types.JWK>(jwk: JWKType, alg?: string, options?: KeyImportOptions): Promise<ImportedJWK<JWKType>>;
+871
View File
@@ -0,0 +1,871 @@
/**
* JWS "alg" (Algorithm) Header Parameter values supported by this module. Availability of a given
* identifier additionally depends on the runtime.
*/
export type JWSAlgorithm =
| 'HS256'
| 'HS384'
| 'HS512'
| 'RS256'
| 'RS384'
| 'RS512'
| 'PS256'
| 'PS384'
| 'PS512'
| 'ES256'
| 'ES384'
| 'ES512'
| 'EdDSA'
| 'Ed25519'
| 'ML-DSA-44'
| 'ML-DSA-65'
| 'ML-DSA-87'
| (string & {})
/**
* JWE "alg" (Algorithm) Header Parameter values supported by this module. Availability of a given
* identifier additionally depends on the runtime.
*/
export type JWEKeyManagementAlgorithm =
| 'dir'
| 'A128KW'
| 'A192KW'
| 'A256KW'
| 'A128GCMKW'
| 'A192GCMKW'
| 'A256GCMKW'
| 'ECDH-ES'
| 'ECDH-ES+A128KW'
| 'ECDH-ES+A192KW'
| 'ECDH-ES+A256KW'
| 'RSA-OAEP'
| 'RSA-OAEP-256'
| 'RSA-OAEP-384'
| 'RSA-OAEP-512'
| 'PBES2-HS256+A128KW'
| 'PBES2-HS384+A192KW'
| 'PBES2-HS512+A256KW'
| (string & {})
/**
* JWE "enc" (Encryption Algorithm) Header Parameter values supported by this module. Availability
* of a given identifier additionally depends on the runtime.
*/
export type JWEContentEncryptionAlgorithm =
| 'A128CBC-HS256'
| 'A192CBC-HS384'
| 'A256CBC-HS512'
| 'A128GCM'
| 'A192GCM'
| 'A256GCM'
| (string & {})
/** JWK "kty" (Key Type) Parameter values supported by this module. */
export type JWKKeyType = 'EC' | 'RSA' | 'OKP' | 'AKP' | 'oct' | (string & {})
/**
* Generic JSON Web Key Parameters.
*
* > Note: This is declared as a type alias rather than an interface so that it satisfies the implicit index
* > signature of the `JsonWebKey` types shipped by `@types/node` and `lib.dom`.
*/
export type JWKParameters = {
/** JWK "kty" (Key Type) Parameter */
kty?: JWKKeyType
/** JWK "alg" (Algorithm) Parameter */
alg?: JWSAlgorithm | JWEKeyManagementAlgorithm | JWEContentEncryptionAlgorithm
/** JWK "key_ops" (Key Operations) Parameter */
key_ops?: string[]
/** JWK "ext" (Extractable) Parameter */
ext?: boolean
/** JWK "use" (Public Key Use) Parameter */
use?: 'sig' | 'enc' | (string & {})
/** JWK "x5c" (X.509 Certificate Chain) Parameter */
x5c?: string[]
/** JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter */
x5t?: string
/** JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter */
'x5t#S256'?: string
/** JWK "x5u" (X.509 URL) Parameter */
x5u?: string
/** JWK "kid" (Key ID) Parameter */
kid?: string
}
/** Convenience interface for Public OKP JSON Web Keys */
export interface JWK_OKP_Public extends JWKParameters {
/** OKP JWK "crv" (The Subtype of Key Pair) Parameter */
crv: string
/** OKP JWK "x" (The public key) Parameter */
x: string
}
/** Convenience interface for Private OKP JSON Web Keys */
export interface JWK_OKP_Private extends JWK_OKP_Public {
/** OKP JWK "d" (The Private Key) Parameter */
d: string
}
/** Convenience interface for Public AKP JSON Web Keys */
export interface JWK_AKP_Public extends JWKParameters {
/** JWK "alg" (Algorithm) Parameter */
alg: string
/** AKP JWK "pub" (The Public key) Parameter */
pub: string
}
/** Convenience interface for Private AKP JSON Web Keys */
export interface JWK_AKP_Private extends JWK_AKP_Public {
/** AKP JWK "priv" (The Private Key) Parameter */
priv: string
}
/** Convenience interface for Public EC JSON Web Keys */
export interface JWK_EC_Public extends JWKParameters {
/** EC JWK "crv" (Curve) Parameter */
crv: string
/** EC JWK "x" (X Coordinate) Parameter */
x: string
/** EC JWK "y" (Y Coordinate) Parameter */
y: string
}
/** Convenience interface for Private EC JSON Web Keys */
export interface JWK_EC_Private extends JWK_EC_Public {
/** EC JWK "d" (ECC Private Key) Parameter */
d: string
}
/** Convenience interface for Public RSA JSON Web Keys */
export interface JWK_RSA_Public extends JWKParameters {
/** RSA JWK "e" (Exponent) Parameter */
e: string
/** RSA JWK "n" (Modulus) Parameter */
n: string
}
/** Convenience interface for Private RSA JSON Web Keys */
export interface JWK_RSA_Private extends JWK_RSA_Public {
/** RSA JWK "d" (Private Exponent) Parameter */
d: string
/** RSA JWK "dp" (First Factor CRT Exponent) Parameter */
dp: string
/** RSA JWK "dq" (Second Factor CRT Exponent) Parameter */
dq: string
/** RSA JWK "p" (First Prime Factor) Parameter */
p: string
/** RSA JWK "q" (Second Prime Factor) Parameter */
q: string
/** RSA JWK "qi" (First CRT Coefficient) Parameter */
qi: string
}
/** Convenience interface for oct JSON Web Keys */
export interface JWK_oct extends JWKParameters {
/** Oct JWK "k" (Key Value) Parameter */
k: string
}
/**
* JSON Web Key ({@link https://www.rfc-editor.org/info/rfc7517/ JWK}). "RSA", "EC", "OKP", "AKP",
* and "oct" key types are supported.
*
* > Note: This is declared as a type alias rather than an interface so that it satisfies the implicit index
* > signature of the `JsonWebKey` types shipped by `@types/node` and `lib.dom`. It spells out the
* > {@link JWKParameters} members rather than intersecting them so that every JWK member is documented
* > in one place.
*/
export type JWK = {
/** JWK "kty" (Key Type) Parameter */
kty?: JWKKeyType
/** JWK "alg" (Algorithm) Parameter */
alg?: JWSAlgorithm | JWEKeyManagementAlgorithm | JWEContentEncryptionAlgorithm
/** JWK "key_ops" (Key Operations) Parameter */
key_ops?: string[]
/** JWK "ext" (Extractable) Parameter */
ext?: boolean
/** JWK "use" (Public Key Use) Parameter */
use?: 'sig' | 'enc' | (string & {})
/** JWK "x5c" (X.509 Certificate Chain) Parameter */
x5c?: string[]
/** JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter */
x5t?: string
/** JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter */
'x5t#S256'?: string
/** JWK "x5u" (X.509 URL) Parameter */
x5u?: string
/** JWK "kid" (Key ID) Parameter */
kid?: string
/**
* - EC JWK "crv" (Curve) Parameter
* - OKP JWK "crv" (The Subtype of Key Pair) Parameter
*/
crv?: string
/**
* - Private RSA JWK "d" (Private Exponent) Parameter
* - Private EC JWK "d" (ECC Private Key) Parameter
* - Private OKP JWK "d" (The Private Key) Parameter
*/
d?: string
/** Private RSA JWK "dp" (First Factor CRT Exponent) Parameter */
dp?: string
/** Private RSA JWK "dq" (Second Factor CRT Exponent) Parameter */
dq?: string
/** RSA JWK "e" (Exponent) Parameter */
e?: string
/** Oct JWK "k" (Key Value) Parameter */
k?: string
/** RSA JWK "n" (Modulus) Parameter */
n?: string
/** Private RSA JWK "p" (First Prime Factor) Parameter */
p?: string
/** Private RSA JWK "q" (Second Prime Factor) Parameter */
q?: string
/** Private RSA JWK "qi" (First CRT Coefficient) Parameter */
qi?: string
/**
* - EC JWK "x" (X Coordinate) Parameter
* - OKP JWK "x" (The public key) Parameter
*/
x?: string
/** EC JWK "y" (Y Coordinate) Parameter */
y?: string
/** AKP JWK "pub" (Public Key) Parameter */
pub?: string
/** AKP JWK "priv" (Private key) Parameter */
priv?: string
/**
* RSA JWK "oth" (Other Primes Info) Parameter
*
* > Note: Multi-prime RSA keys are not supported; importing a JWK with this parameter present throws.
*/
oth?: Array<{
/** The Factor CRT Exponent */
d?: string
/** The Prime Factor */
r?: string
/** The Factor CRT Coefficient */
t?: string
}>
}
/**
* Discriminated union of the JSON Web Key shapes supported by this module. Unlike {@link JWK}, each
* member requires and fixes the "kty" (Key Type) Parameter to its key type so that the union can be
* narrowed on it.
*/
// The "kty" is intersected into each arm one at a time rather than distributed over a parenthesised
// union - `X & (A | B)` means the same thing, but typedoc renders it without the parentheses, which
// reads as though the second arm carried no "kty" at all.
export type AnyJWK =
| (JWK_EC_Private & { kty: 'EC' })
| (JWK_EC_Public & { kty: 'EC' })
| (JWK_RSA_Private & { kty: 'RSA' })
| (JWK_RSA_Public & { kty: 'RSA' })
| (JWK_OKP_Private & { kty: 'OKP' })
| (JWK_OKP_Public & { kty: 'OKP' })
| (JWK_AKP_Private & { kty: 'AKP' })
| (JWK_AKP_Public & { kty: 'AKP' })
| (JWK_oct & { kty: 'oct' })
/** Key or secret input accepted by all sign, verify, encrypt, and decrypt operations. */
export type KeyInput = CryptoKey | KeyObject | JWK | Uint8Array
export interface GenericGetKeyFunction<IProtectedHeader, IToken, ReturnKeyTypes> {
/**
* Dynamic key resolution function. No token components have been verified at the time of this
* function call. If a suitable key for the token cannot be matched, throw an error instead.
*
* @param protectedHeader JWE or JWS Protected Header.
* @param token The consumed JWE or JWS token.
*/
(protectedHeader: IProtectedHeader, token: IToken): Promise<ReturnKeyTypes> | ReturnKeyTypes
}
/** Interface for consuming operations dynamic key resolution. */
export interface GetKeyFunction<
IProtectedHeader,
IToken,
KeyTypes extends KeyInput = KeyInput,
> extends GenericGetKeyFunction<IProtectedHeader, IToken, KeyTypes> {}
/**
* Flattened JWS definition for verify function inputs, allows payload as {@link !Uint8Array} for
* detached signature validation.
*/
export interface FlattenedJWSInput {
/**
* The "header" member MUST be present and contain the value JWS Unprotected Header when the JWS
* Unprotected Header value is non- empty; otherwise, it MUST be absent. This value is represented
* as an unencoded JSON object, rather than as a string. These Header Parameter values are not
* integrity protected.
*/
header?: JWSHeaderParameters
/**
* The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When RFC7797
* "b64": false is used the value passed may also be a {@link !Uint8Array}.
*/
payload: string | Uint8Array
/**
* The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWS Protected
* Header)) when the JWS Protected Header value is non-empty; otherwise, it MUST be absent. These
* Header Parameter values are integrity protected.
*/
protected?: string
/** The "signature" member MUST be present and contain the value BASE64URL(JWS Signature). */
signature: string
}
/**
* General JWS definition for verify function inputs, allows payload as {@link !Uint8Array} for
* detached signature validation.
*/
export interface GeneralJWSInput {
/**
* The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When when
* JWS Unencoded Payload ({@link https://www.rfc-editor.org/info/rfc7797/ RFC7797}) "b64": false is
* used the value passed may also be a {@link !Uint8Array}.
*/
payload: string | Uint8Array
/**
* The "signatures" member value MUST be an array of JSON objects. Each object represents a
* signature or MAC over the JWS Payload and the JWS Protected Header.
*/
signatures: Omit<FlattenedJWSInput, 'payload'>[]
}
/**
* Flattened JWS JSON Serialization Syntax token. Payload is returned as an empty string when JWS
* Unencoded Payload ({@link https://www.rfc-editor.org/info/rfc7797/ RFC7797}) is used.
*/
export interface FlattenedJWS extends Partial<FlattenedJWSInput> {
payload: string
signature: string
}
/**
* General JWS JSON Serialization Syntax token. Payload is returned as an empty string when JWS
* Unencoded Payload ({@link https://www.rfc-editor.org/info/rfc7797/ RFC7797}) is used.
*/
export interface GeneralJWS {
payload: string
signatures: Omit<FlattenedJWSInput, 'payload'>[]
}
/** Header Parameters common to JWE and JWS */
export interface JoseHeaderParameters {
/** "kid" (Key ID) Header Parameter */
kid?: string
/** "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter */
x5t?: string
/** "x5c" (X.509 Certificate Chain) Header Parameter */
x5c?: string[]
/** "x5u" (X.509 URL) Header Parameter */
x5u?: string
/** "jku" (JWK Set URL) Header Parameter */
jku?: string
/**
* "jwk" (JSON Web Key) Header Parameter. This must be a public JSON Web Key; private and
* symmetric key parameters are not permitted.
*/
jwk?: Omit<JWK, 'd' | 'dp' | 'dq' | 'k' | 'p' | 'q' | 'qi' | 'priv' | 'oth'>
/** "typ" (Type) Header Parameter */
typ?: string
/** "cty" (Content Type) Header Parameter */
cty?: string
}
/** Recognized JWS Header Parameters, any other Header Members may also be present. */
export interface JWSHeaderParameters extends JoseHeaderParameters {
/** JWS "alg" (Algorithm) Header Parameter */
alg?: JWSAlgorithm
/**
* This JWS Extension Header Parameter modifies the JWS Payload representation and the JWS Signing
* Input computation as per {@link https://www.rfc-editor.org/info/rfc7797/ RFC7797}.
*/
b64?: boolean
/** JWS "crit" (Critical) Header Parameter */
crit?: string[]
/** Any other JWS Header member. */
[propName: string]: unknown
}
/** Recognized JWE Key Management-related Header Parameters. */
export interface JWEKeyManagementHeaderParameters {
/**
* ECDH-ES "apu" (Agreement PartyUInfo). This will be used as a JOSE Header Parameter and will be
* used in ECDH's ConcatKDF.
*/
apu?: Uint8Array
/**
* ECDH-ES "apv" (Agreement PartyVInfo). This will be used as a JOSE Header Parameter and will be
* used in ECDH's ConcatKDF.
*/
apv?: Uint8Array
/**
* @deprecated You should not use this parameter. It is only intended for testing and vector
* validation purposes.
*/
p2c?: number
/**
* @deprecated You should not use this parameter. It is only intended for testing and vector
* validation purposes.
*/
p2s?: Uint8Array
/**
* @deprecated You should not use this parameter. It is only intended for testing and vector
* validation purposes.
*/
iv?: Uint8Array
/**
* @deprecated You should not use this parameter. It is only intended for testing and vector
* validation purposes.
*/
epk?: CryptoKey | KeyObject
}
/** Flattened JWE JSON Serialization Syntax token. */
export interface FlattenedJWE {
/**
* The "aad" member MUST be present and contain the value BASE64URL(JWE AAD)) when the JWE AAD
* value is non-empty; otherwise, it MUST be absent. A JWE AAD value can be included to supply a
* base64url-encoded value to be integrity protected but not encrypted.
*/
aad?: string
/** The "ciphertext" member MUST be present and contain the value BASE64URL(JWE Ciphertext). */
ciphertext: string
/**
* The "encrypted_key" member MUST be present and contain the value BASE64URL(JWE Encrypted Key)
* when the JWE Encrypted Key value is non-empty; otherwise, it MUST be absent.
*/
encrypted_key?: string
/**
* The "header" member MUST be present and contain the value JWE Per- Recipient Unprotected Header
* when the JWE Per-Recipient Unprotected Header value is non-empty; otherwise, it MUST be absent.
* This value is represented as an unencoded JSON object, rather than as a string. These Header
* Parameter values are not integrity protected.
*/
header?: JWEHeaderParameters
/**
* The "iv" member MUST be present and contain the value BASE64URL(JWE Initialization Vector) when
* the JWE Initialization Vector value is non-empty; otherwise, it MUST be absent.
*/
iv?: string
/**
* The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWE Protected
* Header)) when the JWE Protected Header value is non-empty; otherwise, it MUST be absent. These
* Header Parameter values are integrity protected.
*/
protected?: string
/**
* The "tag" member MUST be present and contain the value BASE64URL(JWE Authentication Tag) when
* the JWE Authentication Tag value is non-empty; otherwise, it MUST be absent.
*/
tag?: string
/**
* The "unprotected" member MUST be present and contain the value JWE Shared Unprotected Header
* when the JWE Shared Unprotected Header value is non-empty; otherwise, it MUST be absent. This
* value is represented as an unencoded JSON object, rather than as a string. These Header
* Parameter values are not integrity protected.
*/
unprotected?: JWEHeaderParameters
}
/** General JWE JSON Serialization Syntax token. */
export interface GeneralJWE extends Omit<FlattenedJWE, 'encrypted_key' | 'header'> {
recipients: Pick<FlattenedJWE, 'encrypted_key' | 'header'>[]
}
/** Recognized JWE Header Parameters, any other Header members may also be present. */
export interface JWEHeaderParameters extends JoseHeaderParameters {
/** JWE "alg" (Algorithm) Header Parameter */
alg?: JWEKeyManagementAlgorithm
/** JWE "enc" (Encryption Algorithm) Header Parameter */
enc?: JWEContentEncryptionAlgorithm
/** JWE "crit" (Critical) Header Parameter */
crit?: string[]
/**
* JWE "zip" (Compression Algorithm) Header Parameter. The only supported value is `"DEF"`
* (DEFLATE), and it requires the `CompressionStream` / `DecompressionStream` APIs to be available
* in the runtime.
*/
zip?: 'DEF' | (string & {})
/** Any other JWE Header member. */
[propName: string]: unknown
}
/** Shared Interface with a "crit" property for all sign, verify, encrypt and decrypt operations. */
export interface CritOption {
/**
* An object with keys representing recognized "crit" (Critical) Header Parameter names. The value
* for those is either `true` or `false`. `true` when the Header Parameter MUST be integrity
* protected, `false` when it's irrelevant. The JWS extension Header Parameter `b64` is always
* recognized and processed properly; no other registered Header Parameters currently receive this
* built-in treatment.
*
* > Warning: This only checks that the Header Parameter is syntactically correct when provided and,
* > optionally, integrity protected. It does not process the Header Parameter or reject the
* > operation when it is missing. You MUST still verify its presence and process it according to
* > the profile's validation steps after the operation succeeds.
*/
crit?: {
[propName: string]: boolean
}
}
/** JWE Decryption options. */
export interface DecryptOptions extends CritOption {
/**
* A list of accepted JWE "alg" (Algorithm) Header Parameter values. By default all "alg"
* (Algorithm) Header Parameter values applicable for the used key/secret are allowed except for
* all PBES2 Key Management Algorithms, these need to be explicitly allowed using this option.
*/
keyManagementAlgorithms?: JWEKeyManagementAlgorithm[]
/**
* A list of accepted JWE "enc" (Encryption Algorithm) Header Parameter values. By default all
* "enc" (Encryption Algorithm) values applicable for the used key/secret are allowed.
*/
contentEncryptionAlgorithms?: JWEContentEncryptionAlgorithm[]
/**
* (PBES2 Key Management Algorithms only) Maximum allowed "p2c" (PBES2 Count) Header Parameter
* value. The PBKDF2 iteration count defines the algorithm's computational expense. By default
* this value is set to 10000.
*/
maxPBES2Count?: number
/**
* Maximum allowed size (in bytes) of the decompressed plaintext when the JWE `"zip"` (Compression
* Algorithm) Header Parameter is present. By default this value is set to 250000 (250 KB). The
* value must be `0`, a positive safe integer, or `Infinity`. Set it to `0` to reject all
* compressed JWEs during decryption or to `Infinity` to disable the decompressed size limit.
*/
maxDecompressedLength?: number
}
/** JWE Encryption options. */
export interface EncryptOptions extends CritOption {}
/** JWT Claims Set verification options. */
export interface JWTClaimVerificationOptions {
/**
* Expected JWT "aud" (Audience) Claim value(s). This option makes the JWT "aud" (Audience) Claim
* presence required.
*/
audience?: string | string[]
/**
* Clock skew tolerance in seconds when a number (e.g. 5), or resolved into seconds when a string
* (e.g. "5 seconds", "10 minutes", "2 hours"). Used when validating the JWT "nbf" (Not Before)
* and "exp" (Expiration Time) claims, and when validating the "iat" (Issued At) claim if the
* {@link maxTokenAge `maxTokenAge` option} is set.
*/
clockTolerance?: string | number
/**
* Expected JWT "iss" (Issuer) Claim value(s). This option makes the JWT "iss" (Issuer) Claim
* presence required.
*/
issuer?: string | string[]
/**
* Maximum time elapsed from the JWT "iat" (Issued At) Claim value, in seconds when a number (e.g.
* 5), or resolved into seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). This
* option makes the JWT "iat" (Issued At) Claim presence required.
*/
maxTokenAge?: string | number
/**
* Expected JWT "sub" (Subject) Claim value. This option makes the JWT "sub" (Subject) Claim
* presence required.
*/
subject?: string
/**
* Expected JWT "typ" (Type) Header Parameter value. This option makes the JWT "typ" (Type) Header
* Parameter presence required.
*/
typ?: string
/** Date to use when comparing NumericDate claims, defaults to `new Date()`. */
currentDate?: Date
/**
* Array of required Claim Names that must be present in the JWT Claims Set. Default is that: if
* the {@link issuer `issuer` option} is set, then JWT "iss" (Issuer) Claim must be present; if the
* {@link audience `audience` option} is set, then JWT "aud" (Audience) Claim must be present; if
* the {@link subject `subject` option} is set, then JWT "sub" (Subject) Claim must be present; if
* the {@link maxTokenAge `maxTokenAge` option} is set, then JWT "iat" (Issued At) Claim must be
* present.
*/
requiredClaims?: string[]
}
/** JWS Verification options. */
export interface VerifyOptions extends CritOption {
/**
* A list of accepted JWS "alg" (Algorithm) Header Parameter values. By default all "alg"
* (Algorithm) values applicable for the used key/secret are allowed.
*
* > Note: Unsecured JWTs (`{ "alg": "none" }`) are never accepted by this API.
*/
algorithms?: JWSAlgorithm[]
}
/** JWS Signing options. */
export interface SignOptions extends CritOption {}
/** Recognized JWT Claims Set members, any other members may also be present. */
export interface JWTPayload {
/** JWT Issuer */
iss?: string
/** JWT Subject */
sub?: string
/** JWT Audience */
aud?: string | string[]
/** JWT ID */
jti?: string
/** JWT Not Before */
nbf?: number
/** JWT Expiration Time */
exp?: number
/** JWT Issued At */
iat?: number
/** Any other JWT Claim Set member. */
[propName: string]: unknown
}
/** Flattened JWE JSON Serialization Syntax decryption result */
export interface FlattenedDecryptResult {
/** JWE AAD. */
additionalAuthenticatedData?: Uint8Array
/** Plaintext. */
plaintext: Uint8Array
/** JWE Protected Header. */
protectedHeader?: JWEHeaderParameters
/** JWE Shared Unprotected Header. */
sharedUnprotectedHeader?: JWEHeaderParameters
/** JWE Per-Recipient Unprotected Header. */
unprotectedHeader?: JWEHeaderParameters
}
/** General JWE JSON Serialization Syntax decryption result */
export interface GeneralDecryptResult extends FlattenedDecryptResult {}
/** Compact JWE decryption result */
export interface CompactDecryptResult {
/** Plaintext. */
plaintext: Uint8Array
/** JWE Protected Header. */
protectedHeader: CompactJWEHeaderParameters
}
/** Flattened JWS JSON Serialization Syntax verification result */
export interface FlattenedVerifyResult {
/** JWS Payload. */
payload: Uint8Array
/** JWS Protected Header. */
protectedHeader?: JWSHeaderParameters
/** JWS Unprotected Header. */
unprotectedHeader?: JWSHeaderParameters
}
/** General JWS JSON Serialization Syntax verification result */
export interface GeneralVerifyResult extends FlattenedVerifyResult {}
/** Compact JWS verification result */
export interface CompactVerifyResult {
/** JWS Payload. */
payload: Uint8Array
/** JWS Protected Header. */
protectedHeader: CompactJWSHeaderParameters
}
/** Signed JSON Web Token (JWT) verification result */
export interface JWTVerifyResult<PayloadType = JWTPayload> {
/** JWT Claims Set. */
payload: PayloadType &
JWTPayload &
([PayloadType] extends [object] ? unknown : unknown extends PayloadType ? unknown : never)
/** JWS Protected Header. */
protectedHeader: JWTHeaderParameters
}
/** Encrypted JSON Web Token (JWT) decryption result */
export interface JWTDecryptResult<PayloadType = JWTPayload> {
/** JWT Claims Set. */
payload: PayloadType &
JWTPayload &
([PayloadType] extends [object] ? unknown : unknown extends PayloadType ? unknown : never)
/** JWE Protected Header. */
protectedHeader: CompactJWEHeaderParameters
}
/** When key resolver functions are used this becomes part of successful resolves */
export interface ResolvedKey<KeyType extends CryptoKey | Uint8Array = CryptoKey | Uint8Array> {
/** Key resolved from the key resolver function. */
key: KeyType
}
/** Recognized Compact JWS Header Parameters, any other Header Members may also be present. */
export interface CompactJWSHeaderParameters extends JWSHeaderParameters {
alg: JWSAlgorithm
}
/** Recognized Signed JWT Header Parameters, any other Header Members may also be present. */
export interface JWTHeaderParameters extends CompactJWSHeaderParameters {
b64?: boolean
}
/** Recognized Compact JWE Header Parameters, any other Header Members may also be present. */
export interface CompactJWEHeaderParameters extends JWEHeaderParameters {
alg: JWEKeyManagementAlgorithm
enc: JWEContentEncryptionAlgorithm
}
/** JSON Web Key Set */
export interface JSONWebKeySet {
keys: JWK[]
}
/**
* {@link !KeyObject} is a representation of a key/secret available in the Node.js runtime. You may
* use the Node.js runtime APIs {@link !createPublicKey}, {@link !createPrivateKey}, and
* {@link !createSecretKey} to obtain a {@link !KeyObject} from your existing key material.
*/
export interface KeyObject {
type: 'private' | 'public' | 'secret'
}
/**
* {@link !CryptoKey} is a representation of a key/secret available in all supported runtimes. In
* addition to the {@link key/import Key Import Functions} you may use the
* {@link !SubtleCrypto.importKey} API to obtain a {@link !CryptoKey} from your existing key
* material.
*/
export type CryptoKey = typeof globalThis extends {
crypto: { subtle: { generateKey(...args: any[]): Promise<infer R> } }
}
? Extract<R, { type: string }>
: CryptoKeyStructuralFallback
/**
* Used as {@link CryptoKey} only when the host runtime's `crypto` global is not typed at all, e.g. a
* consumer compiling with neither the DOM lib nor `@types/node`. Whenever a `CryptoKey` type is
* available it is aliased instead, deliberately, so that this module never introduces a competing
* nominal `CryptoKey` and values flow freely to and from {@link !SubtleCrypto} APIs.
*/
export interface CryptoKeyStructuralFallback {
readonly algorithm: { name: string }
readonly extractable: boolean
readonly type: 'private' | 'public' | 'secret'
readonly usages: (
'decrypt' | 'deriveBits' | 'deriveKey' | 'encrypt' | 'sign' | 'unwrapKey' | 'verify' | 'wrapKey'
)[]
}
/** Generic interface for JWT producing classes. */
export interface ProduceJWT {
/**
* Set the "iss" (Issuer) Claim.
*
* @param issuer "Issuer" Claim value to set on the JWT Claims Set.
*/
setIssuer(issuer: string): this
/**
* Set the "sub" (Subject) Claim.
*
* @param subject "sub" (Subject) Claim value to set on the JWT Claims Set.
*/
setSubject(subject: string): this
/**
* Set the "aud" (Audience) Claim.
*
* @param audience "aud" (Audience) Claim value to set on the JWT Claims Set.
*/
setAudience(audience: string | string[]): this
/**
* Set the "jti" (JWT ID) Claim.
*
* @param jwtId "jti" (JWT ID) Claim value to set on the JWT Claims Set.
*/
setJti(jwtId: string): this
/**
* Set the "nbf" (Not Before) Claim. A `number` is used directly, a `Date` is converted to a Unix
* timestamp, and a `string` is parsed as a time span relative to the current Unix timestamp.
* String units may be seconds, minutes, hours, days, weeks, or years; months are unsupported and
* a year is 365.25 days. A leading `-` or trailing `"ago"` subtracts the time span.
*
* @param input "nbf" (Not Before) Claim value to set on the JWT Claims Set.
*/
setNotBefore(input: number | string | Date): this
/**
* Set the "exp" (Expiration Time) Claim. A `number` is used directly, a `Date` is converted to a
* Unix timestamp, and a `string` is parsed as a time span relative to the current Unix timestamp.
* String units may be seconds, minutes, hours, days, weeks, or years; months are unsupported and
* a year is 365.25 days. A leading `-` or trailing `"ago"` subtracts the time span.
*
* @param input "exp" (Expiration Time) Claim value to set on the JWT Claims Set.
*/
setExpirationTime(input: number | string | Date): this
/**
* Set the "iat" (Issued At) Claim. With no argument the current Unix timestamp is used. A
* `number` is used directly, a `Date` is converted to a Unix timestamp, and a `string` is parsed
* as a time span relative to the current Unix timestamp. String units may be seconds, minutes,
* hours, days, weeks, or years; months are unsupported and a year is 365.25 days. A leading `-`
* or trailing `"ago"` subtracts the time span.
*
* @param input "iat" (Issued At) Claim value to set on the JWT Claims Set.
*/
setIssuedAt(input?: number | string | Date): this
}
+16
View File
@@ -0,0 +1,16 @@
/**
* Decodes a Base64URL encoded input.
*
* @param input Base64URL encoded input, as a string or its UTF-8 bytes.
* @returns The decoded bytes.
* @throws {!TypeError} When the input is not correctly Base64URL encoded. Standard Base64 input
* (i.e. containing `+` or `/`) is rejected.
*/
export declare function decode(input: Uint8Array | string): Uint8Array;
/**
* Encodes an input using Base64URL with no padding.
*
* @param input Input to encode, as a string or as bytes. Strings are encoded as UTF-8 first.
* @returns The Base64URL encoded, unpadded, representation of the input.
*/
export declare function encode(input: Uint8Array | string): string;
+10
View File
@@ -0,0 +1,10 @@
import type * as types from '../types.d.ts';
/**
* Decodes a signed JSON Web Token payload. This does not validate the JWT Claims Set types or
* values. This does not validate the JWS Signature. For a proper Signed JWT Claims Set validation
* and JWS signature verification use `jose.jwtVerify()`. For an encrypted JWT Claims Set validation
* and JWE decryption use `jose.jwtDecrypt()`.
*
* @param jwt JWT token in compact JWS serialization.
*/
export declare function decodeJwt<PayloadType = types.JWTPayload>(jwt: string): PayloadType & types.JWTPayload & ([PayloadType] extends [object] ? unknown : unknown extends PayloadType ? unknown : never);
+9
View File
@@ -0,0 +1,9 @@
import type * as types from '../types.d.ts';
/** JWE and JWS Header Parameters */
export type ProtectedHeaderParameters = types.JWSHeaderParameters & types.JWEHeaderParameters;
/**
* Decodes the Protected Header of a JWE/JWS/JWT token utilizing any JOSE serialization.
*
* @param token JWE/JWS/JWT token in any JOSE serialization.
*/
export declare function decodeProtectedHeader(token: string | object): ProtectedHeaderParameters;
+220
View File
@@ -0,0 +1,220 @@
import type * as types from '../types.d.ts';
/**
* Every stable error code used by this module. {@link AnyJOSEError} pairs each subclass with the one
* it is thrown with, making that union a discriminated one.
*/
export type JOSEErrorCode = 'ERR_JOSE_ALG_NOT_ALLOWED' | 'ERR_JOSE_GENERIC' | 'ERR_JOSE_NOT_SUPPORTED' | 'ERR_JWE_DECRYPTION_FAILED' | 'ERR_JWE_INVALID' | 'ERR_JWK_INVALID' | 'ERR_JWKS_INVALID' | 'ERR_JWKS_MULTIPLE_MATCHING_KEYS' | 'ERR_JWKS_NO_MATCHING_KEY' | 'ERR_JWKS_TIMEOUT' | 'ERR_JWS_INVALID' | 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED' | 'ERR_JWT_CLAIM_VALIDATION_FAILED' | 'ERR_JWT_EXPIRED' | 'ERR_JWT_INVALID';
/**
* The shape shared by the two errors thrown during JWT Claims Set validation.
*
* > Note: {@link JWTExpired} does not extend {@link JWTClaimValidationFailed}, so `instanceof
* > JWTClaimValidationFailed` is `false` for an expired JWT. Use {@link JWTClaimValidationError} or
* > the {@link JOSEError.code code} discriminant to handle both.
*/
export interface JWTClaimValidationFailure {
/** The Claim for which the validation failed. */
claim: string;
/** Reason code for the validation failure. */
reason: JWTClaimValidationReason;
/** The parsed JWT Claims Set (aka payload). */
payload: types.JWTPayload;
}
/** Reason codes produced by JWT Claims Set validation. */
export type JWTClaimValidationReason = 'check_failed' | 'invalid' | 'mismatch' | 'missing' | 'unspecified' | (string & {});
/** A generic Error that all other JOSE specific Error subclasses extend. */
export declare class JOSEError extends Error {
/** A unique error code for the particular error subclass. */
static code: JOSEErrorCode | (string & {});
/**
* A unique error code for {@link JOSEError}. Each subclass sets its own; see {@link AnyJOSEError}
* to switch over them as a discriminated union.
*/
code: JOSEErrorCode | (string & {});
constructor(message?: string, options?: {
cause?: unknown;
});
}
/** An error subclass thrown when a JWT Claim Set member validation fails. */
export declare class JWTClaimValidationFailed extends JOSEError implements JWTClaimValidationFailure {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JWTClaimValidationFailed}. */
code: JOSEErrorCode | (string & {});
/** The {@link JWTClaimValidationFailure} carried by every instance of this error. */
cause: JWTClaimValidationFailure;
/** The Claim for which the validation failed. */
claim: string;
/** Reason code for the validation failure. */
reason: JWTClaimValidationReason;
/**
* The parsed JWT Claims Set (aka payload). Other JWT claims may or may not have been verified at
* this point. The JSON Web Signature (JWS) or a JSON Web Encryption (JWE) structures' integrity
* has however been verified. Claims Set verification happens after the JWS Signature or JWE
* Decryption processes.
*/
payload: types.JWTPayload;
constructor(message: string, payload: types.JWTPayload, claim?: string, reason?: JWTClaimValidationReason);
}
/** An error subclass thrown when a JWT is expired. */
export declare class JWTExpired extends JOSEError implements JWTClaimValidationFailure {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JWTExpired}. */
code: JOSEErrorCode | (string & {});
/** The {@link JWTClaimValidationFailure} carried by every instance of this error. */
cause: JWTClaimValidationFailure;
/** The Claim for which the validation failed. */
claim: string;
/** Reason code for the validation failure. */
reason: JWTClaimValidationReason;
/**
* The parsed JWT Claims Set (aka payload). Other JWT claims may or may not have been verified at
* this point. The JSON Web Signature (JWS) or a JSON Web Encryption (JWE) structures' integrity
* has however been verified. Claims Set verification happens after the JWS Signature or JWE
* Decryption processes.
*/
payload: types.JWTPayload;
constructor(message: string, payload: types.JWTPayload, claim?: string, reason?: JWTClaimValidationReason);
}
/** An error subclass thrown when a JOSE Algorithm is not allowed per developer preference. */
export declare class JOSEAlgNotAllowed extends JOSEError {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JOSEAlgNotAllowed}. */
code: JOSEErrorCode | (string & {});
}
/**
* An error subclass thrown when a particular feature or algorithm is not supported by this
* implementation or JOSE in general.
*/
export declare class JOSENotSupported extends JOSEError {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JOSENotSupported}. */
code: JOSEErrorCode | (string & {});
}
/** An error subclass thrown when a JWE ciphertext decryption fails. */
export declare class JWEDecryptionFailed extends JOSEError {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JWEDecryptionFailed}. */
code: JOSEErrorCode | (string & {});
constructor(message?: string, options?: {
cause?: unknown;
});
}
/** An error subclass thrown when a JWE is invalid. */
export declare class JWEInvalid extends JOSEError {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JWEInvalid}. */
code: JOSEErrorCode | (string & {});
}
/** An error subclass thrown when a JWS is invalid. */
export declare class JWSInvalid extends JOSEError {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JWSInvalid}. */
code: JOSEErrorCode | (string & {});
}
/** An error subclass thrown when a JWT is invalid. */
export declare class JWTInvalid extends JOSEError {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JWTInvalid}. */
code: JOSEErrorCode | (string & {});
}
/** An error subclass thrown when a JWK is invalid. */
export declare class JWKInvalid extends JOSEError {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JWKInvalid}. */
code: JOSEErrorCode | (string & {});
}
/** An error subclass thrown when a JWKS is invalid. */
export declare class JWKSInvalid extends JOSEError {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JWKSInvalid}. */
code: JOSEErrorCode | (string & {});
}
/** An error subclass thrown when no keys match from a JWKS. */
export declare class JWKSNoMatchingKey extends JOSEError {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JWKSNoMatchingKey}. */
code: JOSEErrorCode | (string & {});
constructor(message?: string, options?: {
cause?: unknown;
});
}
/** An error subclass thrown when multiple keys match from a JWKS. */
export declare class JWKSMultipleMatchingKeys extends JOSEError {
/**
* Iterates the public keys that matched the JWS JOSE Header, so that verification can be
* attempted with each in turn. See the {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet}
* and {@link jwks/local.createLocalJWKSet createLocalJWKSet} examples. Instances thrown by this
* module always iterate the matched keys; an instance constructed by other code iterates
* nothing.
*/
[Symbol.asyncIterator]: () => AsyncIterableIterator<types.CryptoKey>;
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JWKSMultipleMatchingKeys}. */
code: JOSEErrorCode | (string & {});
constructor(message?: string, options?: {
cause?: unknown;
});
}
/** Timeout was reached when retrieving the JWKS response. */
export declare class JWKSTimeout extends JOSEError {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JWKSTimeout}. */
code: JOSEErrorCode | (string & {});
constructor(message?: string, options?: {
cause?: unknown;
});
}
/** An error subclass thrown when JWS signature verification fails. */
export declare class JWSSignatureVerificationFailed extends JOSEError {
static code: JOSEErrorCode | (string & {});
/** A unique error code for {@link JWSSignatureVerificationFailed}. */
code: JOSEErrorCode | (string & {});
constructor(message?: string, options?: {
cause?: unknown;
});
}
/**
* Union of the errors thrown during JWT Claims Set validation. {@link JWTExpired} does not extend
* {@link JWTClaimValidationFailed}, so a single `instanceof` check cannot cover both. Use this type
* — together with the {@link JOSEError.code code} discriminant — when handling either.
*/
export type JWTClaimValidationError = JWTClaimValidationFailed | JWTExpired;
/**
* Union of every {@link JOSEError} subclass this module throws, each paired with the single
* {@link JOSEErrorCode} it is thrown with. That pairing lives here rather than on the classes, so
* that `code` stays assignable, writable, and overridable on them exactly as before, while a value
* of this type can still be switched over as a discriminated union.
*
* > Note: The base {@link JOSEError} is deliberately not a member — its `code` spans every value, which
* > would defeat the discriminant. A small number of JSON Web Key Set HTTP failures are thrown as the
* > base class itself, so `instanceof JOSEError` remains the catch-all; this union is for handling a
* > value already known to be one of the specific errors.
*/
export type AnyJOSEError = (JOSEAlgNotAllowed & {
code: 'ERR_JOSE_ALG_NOT_ALLOWED';
}) | (JOSENotSupported & {
code: 'ERR_JOSE_NOT_SUPPORTED';
}) | (JWEDecryptionFailed & {
code: 'ERR_JWE_DECRYPTION_FAILED';
}) | (JWEInvalid & {
code: 'ERR_JWE_INVALID';
}) | (JWKInvalid & {
code: 'ERR_JWK_INVALID';
}) | (JWKSInvalid & {
code: 'ERR_JWKS_INVALID';
}) | (JWKSMultipleMatchingKeys & {
code: 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
}) | (JWKSNoMatchingKey & {
code: 'ERR_JWKS_NO_MATCHING_KEY';
}) | (JWKSTimeout & {
code: 'ERR_JWKS_TIMEOUT';
}) | (JWSInvalid & {
code: 'ERR_JWS_INVALID';
}) | (JWSSignatureVerificationFailed & {
code: 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';
}) | (JWTClaimValidationFailed & {
code: 'ERR_JWT_CLAIM_VALIDATION_FAILED';
}) | (JWTExpired & {
code: 'ERR_JWT_EXPIRED';
}) | (JWTInvalid & {
code: 'ERR_JWT_INVALID';
});
+32
View File
@@ -0,0 +1,32 @@
export { compactDecrypt } from './jwe/compact/decrypt.js';
export { flattenedDecrypt } from './jwe/flattened/decrypt.js';
export { generalDecrypt } from './jwe/general/decrypt.js';
export { GeneralEncrypt } from './jwe/general/encrypt.js';
export { compactVerify } from './jws/compact/verify.js';
export { flattenedVerify } from './jws/flattened/verify.js';
export { generalVerify } from './jws/general/verify.js';
export { jwtVerify } from './jwt/verify.js';
export { jwtDecrypt } from './jwt/decrypt.js';
export { CompactEncrypt } from './jwe/compact/encrypt.js';
export { FlattenedEncrypt } from './jwe/flattened/encrypt.js';
export { CompactSign } from './jws/compact/sign.js';
export { FlattenedSign } from './jws/flattened/sign.js';
export { GeneralSign } from './jws/general/sign.js';
export { SignJWT } from './jwt/sign.js';
export { EncryptJWT } from './jwt/encrypt.js';
export { calculateJwkThumbprint, calculateJwkThumbprintUri } from './jwk/thumbprint.js';
export { EmbeddedJWK } from './jwk/embedded.js';
export { createLocalJWKSet } from './jwks/local.js';
export { createRemoteJWKSet, jwksCache, customFetch } from './jwks/remote.js';
export { UnsecuredJWT } from './jwt/unsecured.js';
export { exportPKCS8, exportSPKI, exportJWK } from './key/export.js';
export { importSPKI, importPKCS8, importX509, importJWK } from './key/import.js';
export { decodeProtectedHeader } from './util/decode_protected_header.js';
export { decodeJwt } from './util/decode_jwt.js';
import * as errors from './util/errors.js';
export { errors };
export { generateKeyPair } from './key/generate_key_pair.js';
export { generateSecret } from './key/generate_secret.js';
import * as base64url from './util/base64url.js';
export { base64url };
export const cryptoRuntime = 'WebCryptoAPI';
+9
View File
@@ -0,0 +1,9 @@
import { prepareDecrypt, decryptCompact } from '../../lib/jwe_decrypt.js';
export async function compactDecrypt(jwe, key, options) {
const decrypted = await decryptCompact(jwe, prepareDecrypt(options), key);
const result = { plaintext: decrypted.plaintext, protectedHeader: decrypted.parsedProt };
if (typeof key === 'function') {
return { ...result, key: decrypted.key };
}
return result;
}
+27
View File
@@ -0,0 +1,27 @@
import { FlattenedEncrypt } from '../flattened/encrypt.js';
export class CompactEncrypt {
#flattened;
constructor(plaintext) {
this.#flattened = new FlattenedEncrypt(plaintext);
}
setContentEncryptionKey(cek) {
this.#flattened.setContentEncryptionKey(cek);
return this;
}
setInitializationVector(iv) {
this.#flattened.setInitializationVector(iv);
return this;
}
setProtectedHeader(protectedHeader) {
this.#flattened.setProtectedHeader(protectedHeader);
return this;
}
setKeyManagementParameters(parameters) {
this.#flattened.setKeyManagementParameters(parameters);
return this;
}
async encrypt(key, options) {
const jwe = await this.#flattened.encrypt(key, options);
return [jwe.protected, jwe.encrypted_key, jwe.iv, jwe.ciphertext, jwe.tag].join('.');
}
}
+11
View File
@@ -0,0 +1,11 @@
import { JWEInvalid } from '../../util/errors.js';
import { isObject } from '../../lib/type_checks.js';
import { prepareDecrypt, decryptJWE, decryptResult, checkShared, checkRecipient, } from '../../lib/jwe_decrypt.js';
export async function flattenedDecrypt(jwe, key, options) {
if (!isObject(jwe)) {
throw new JWEInvalid('Flattened JWE must be an object');
}
checkShared(jwe);
checkRecipient(jwe);
return decryptResult(jwe, await decryptJWE(jwe, prepareDecrypt(options), key));
}
+72
View File
@@ -0,0 +1,72 @@
import { unprotected, assertNotSet } from '../../lib/helpers.js';
import { JWEInvalid } from '../../util/errors.js';
import { createJWE } from '../../lib/jwe_encrypt.js';
import { validateCritDuplicates } from '../../lib/options.js';
export class FlattenedEncrypt {
#plaintext;
#protectedHeader;
#sharedUnprotectedHeader;
#unprotectedHeader;
#aad;
#cek;
#iv;
#keyManagementParameters;
constructor(plaintext) {
if (!(plaintext instanceof Uint8Array)) {
throw new TypeError('plaintext must be an instance of Uint8Array');
}
this.#plaintext = plaintext;
}
setKeyManagementParameters(parameters) {
assertNotSet(this.#keyManagementParameters, 'setKeyManagementParameters');
this.#keyManagementParameters = parameters;
return this;
}
setProtectedHeader(protectedHeader) {
assertNotSet(this.#protectedHeader, 'setProtectedHeader');
this.#protectedHeader = protectedHeader;
return this;
}
setSharedUnprotectedHeader(sharedUnprotectedHeader) {
assertNotSet(this.#sharedUnprotectedHeader, 'setSharedUnprotectedHeader');
this.#sharedUnprotectedHeader = sharedUnprotectedHeader;
return this;
}
setUnprotectedHeader(unprotectedHeader) {
assertNotSet(this.#unprotectedHeader, 'setUnprotectedHeader');
this.#unprotectedHeader = unprotectedHeader;
return this;
}
setAdditionalAuthenticatedData(aad) {
this.#aad = aad;
return this;
}
setContentEncryptionKey(cek) {
assertNotSet(this.#cek, 'setContentEncryptionKey');
this.#cek = cek;
return this;
}
setInitializationVector(iv) {
assertNotSet(this.#iv, 'setInitializationVector');
this.#iv = iv;
return this;
}
async encrypt(key, options) {
if (!this.#protectedHeader && !this.#unprotectedHeader && !this.#sharedUnprotectedHeader) {
throw new JWEInvalid('either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()');
}
validateCritDuplicates(JWEInvalid, this.#protectedHeader);
return createJWE({
plaintext: this.#plaintext,
protectedHeader: this.#protectedHeader,
unprotectedHeader: this.#unprotectedHeader,
sharedUnprotectedHeader: this.#sharedUnprotectedHeader,
aad: this.#aad,
cek: this.#cek,
iv: this.#iv,
keyManagementParameters: this.#keyManagementParameters,
crit: options?.crit,
unprotectedParameters: options ? unprotected in options : false,
}, key);
}
}
+43
View File
@@ -0,0 +1,43 @@
import { prepareDecrypt, shareJWE, decryptRecipient, decryptResult, checkShared, checkRecipient, } from '../../lib/jwe_decrypt.js';
import { JWEDecryptionFailed, JWEInvalid } from '../../util/errors.js';
import { isObject } from '../../lib/type_checks.js';
export async function generalDecrypt(jwe, key, options) {
if (!isObject(jwe)) {
throw new JWEInvalid('General JWE must be an object');
}
if (!Array.isArray(jwe.recipients) || !jwe.recipients.every(isObject)) {
throw new JWEInvalid('JWE Recipients missing or incorrect type');
}
if (!jwe.recipients.length) {
throw new JWEInvalid('JWE Recipients has no members');
}
let shared;
let token;
try {
checkShared(jwe);
shared = prepareDecrypt(options);
token = shareJWE(jwe);
}
catch {
throw new JWEDecryptionFailed();
}
for (const recipient of jwe.recipients) {
try {
const flattened = {
aad: jwe.aad,
ciphertext: jwe.ciphertext,
encrypted_key: recipient.encrypted_key,
header: recipient.header,
iv: jwe.iv,
protected: jwe.protected,
tag: jwe.tag,
unprotected: jwe.unprotected,
};
checkRecipient(flattened);
return decryptResult(flattened, await decryptRecipient(flattened, token, shared, key));
}
catch {
}
}
throw new JWEDecryptionFailed();
}
+167
View File
@@ -0,0 +1,167 @@
import { FlattenedEncrypt } from '../flattened/encrypt.js';
import { assertNotSet } from '../../lib/helpers.js';
import { JWEInvalid } from '../../util/errors.js';
import { generateCek } from '../../lib/content_encryption.js';
import { encryptKeyManagement } from '../../lib/key_management.js';
import { encode as b64u } from '../../util/base64url.js';
import { validateCritDuplicates } from '../../lib/options.js';
import { checkEncryptHeaders, encryptJWE } from '../../lib/jwe_encrypt.js';
import { prepareKey } from '../../lib/key.js';
import { jweAlgorithm } from '../../lib/jwe_algorithms.js';
class IndividualRecipient {
#parent;
unprotectedHeader;
keyManagementParameters;
key;
options;
constructor(enc, key, options) {
this.#parent = enc;
this.key = key;
this.options = options;
}
setUnprotectedHeader(unprotectedHeader) {
assertNotSet(this.unprotectedHeader, 'setUnprotectedHeader');
this.unprotectedHeader = unprotectedHeader;
return this;
}
setKeyManagementParameters(parameters) {
assertNotSet(this.keyManagementParameters, 'setKeyManagementParameters');
this.keyManagementParameters = parameters;
return this;
}
addRecipient(...args) {
return this.#parent.addRecipient(...args);
}
encrypt(...args) {
return this.#parent.encrypt(...args);
}
done() {
return this.#parent;
}
}
export class GeneralEncrypt {
#plaintext;
#recipients = [];
#protectedHeader;
#unprotectedHeader;
#aad;
constructor(plaintext) {
this.#plaintext = plaintext;
}
addRecipient(key, options) {
const recipient = new IndividualRecipient(this, key, { crit: options?.crit });
this.#recipients.push(recipient);
return recipient;
}
setProtectedHeader(protectedHeader) {
assertNotSet(this.#protectedHeader, 'setProtectedHeader');
this.#protectedHeader = protectedHeader;
return this;
}
setSharedUnprotectedHeader(sharedUnprotectedHeader) {
assertNotSet(this.#unprotectedHeader, 'setSharedUnprotectedHeader');
this.#unprotectedHeader = sharedUnprotectedHeader;
return this;
}
setAdditionalAuthenticatedData(aad) {
this.#aad = aad;
return this;
}
async encrypt() {
if (!this.#recipients.length) {
throw new JWEInvalid('at least one recipient must be added');
}
if (!(this.#plaintext instanceof Uint8Array)) {
throw new TypeError('plaintext must be an instance of Uint8Array');
}
if (this.#recipients.length === 1) {
const [recipient] = this.#recipients;
const flattened = await new FlattenedEncrypt(this.#plaintext)
.setAdditionalAuthenticatedData(this.#aad)
.setProtectedHeader(this.#protectedHeader)
.setSharedUnprotectedHeader(this.#unprotectedHeader)
.setUnprotectedHeader(recipient.unprotectedHeader)
.setKeyManagementParameters(recipient.keyManagementParameters)
.encrypt(recipient.key, { ...recipient.options });
const jwe = {
ciphertext: flattened.ciphertext,
iv: flattened.iv,
recipients: [{}],
tag: flattened.tag,
};
if (flattened.aad)
jwe.aad = flattened.aad;
if (flattened.protected)
jwe.protected = flattened.protected;
if (flattened.unprotected)
jwe.unprotected = flattened.unprotected;
if (flattened.encrypted_key)
jwe.recipients[0].encrypted_key = flattened.encrypted_key;
if (flattened.header)
jwe.recipients[0].header = flattened.header;
return jwe;
}
validateCritDuplicates(JWEInvalid, this.#protectedHeader);
let enc;
const inputs = [];
const checked = [];
for (let i = 0; i < this.#recipients.length; i++) {
const recipient = this.#recipients[i];
const input = {
plaintext: this.#plaintext,
protectedHeader: this.#protectedHeader,
unprotectedHeader: recipient.unprotectedHeader,
sharedUnprotectedHeader: this.#unprotectedHeader,
aad: this.#aad,
keyManagementParameters: recipient.keyManagementParameters,
crit: recipient.options.crit,
unprotectedParameters: true,
};
const headers = checkEncryptHeaders(input);
inputs.push(input);
checked.push(headers);
if (headers.alg === 'dir' || headers.alg === 'ECDH-ES') {
throw new JWEInvalid('"dir" and "ECDH-ES" alg may only be used with a single recipient');
}
if (!enc) {
enc = headers.enc;
}
else if (enc !== headers.enc) {
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients');
}
}
const cek = generateCek(checked[0].encEntry);
const jwe = {
ciphertext: '',
recipients: [],
};
for (let i = 0; i < this.#recipients.length; i++) {
const recipient = this.#recipients[i];
const target = {};
jwe.recipients.push(target);
if (i === 0) {
const flattened = await encryptJWE({ ...inputs[0], cek }, checked[0], recipient.key);
jwe.ciphertext = flattened.ciphertext;
jwe.iv = flattened.iv;
jwe.tag = flattened.tag;
if (flattened.aad)
jwe.aad = flattened.aad;
if (flattened.protected)
jwe.protected = flattened.protected;
if (flattened.unprotected)
jwe.unprotected = flattened.unprotected;
target.encrypted_key = flattened.encrypted_key;
if (flattened.header)
target.header = flattened.header;
continue;
}
const { alg } = checked[i];
const k = await prepareKey(jweAlgorithm(alg), recipient.key, 'encrypt');
const { encryptedKey, parameters } = await encryptKeyManagement(alg, checked[i].encEntry, k, cek, recipient.keyManagementParameters);
target.encrypted_key = b64u(encryptedKey);
if (recipient.unprotectedHeader || parameters)
target.header = { ...recipient.unprotectedHeader, ...parameters };
}
return jwe;
}
}
+19
View File
@@ -0,0 +1,19 @@
import { jwkToKey } from '../lib/jwk_to_key.js';
import { jwsAlgorithm } from '../lib/jws_algorithms.js';
import { isObject } from '../lib/type_checks.js';
import { JWSInvalid } from '../util/errors.js';
export async function EmbeddedJWK(protectedHeader, token) {
const joseHeader = {
...protectedHeader,
...token?.header,
};
if (!isObject(joseHeader.jwk)) {
throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a JSON object');
}
const entry = jwsAlgorithm(joseHeader.alg);
const key = await jwkToKey(entry, { ...joseHeader.jwk, ext: true });
if (key.type !== 'public') {
throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a public key');
}
return key;
}
+68
View File
@@ -0,0 +1,68 @@
import { digest } from '../lib/helpers.js';
import { encode as b64u } from '../util/base64url.js';
import { JOSENotSupported, JWKInvalid } from '../util/errors.js';
import { encode } from '../lib/buffer_utils.js';
import { isKeyLike } from '../lib/is_key_like.js';
import { isJWK } from '../lib/type_checks.js';
import { exportJWK } from '../key/export.js';
import { invalidKeyInput } from '../lib/invalid_key_input.js';
const check = (value, description) => {
if (typeof value !== 'string' || !value) {
throw new JWKInvalid(`${description} missing or invalid`);
}
};
export async function calculateJwkThumbprint(key, digestAlgorithm) {
let jwk;
if (isJWK(key)) {
jwk = key;
}
else if (isKeyLike(key)) {
jwk = await exportJWK(key);
}
else {
throw new TypeError(invalidKeyInput(key, 'CryptoKey', 'KeyObject', 'JSON Web Key'));
}
digestAlgorithm ??= 'sha256';
if (digestAlgorithm !== 'sha256' &&
digestAlgorithm !== 'sha384' &&
digestAlgorithm !== 'sha512') {
throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');
}
let components;
switch (jwk.kty) {
case 'AKP':
check(jwk.alg, '"alg" (Algorithm) Parameter');
check(jwk.pub, '"pub" (Public key) Parameter');
components = { alg: jwk.alg, kty: jwk.kty, pub: jwk.pub };
break;
case 'EC':
check(jwk.crv, '"crv" (Curve) Parameter');
check(jwk.x, '"x" (X Coordinate) Parameter');
check(jwk.y, '"y" (Y Coordinate) Parameter');
components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y };
break;
case 'OKP':
check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter');
check(jwk.x, '"x" (Public Key) Parameter');
components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x };
break;
case 'RSA':
check(jwk.e, '"e" (Exponent) Parameter');
check(jwk.n, '"n" (Modulus) Parameter');
components = { e: jwk.e, kty: jwk.kty, n: jwk.n };
break;
case 'oct':
check(jwk.k, '"k" (Key Value) Parameter');
components = { k: jwk.k, kty: jwk.kty };
break;
default:
throw new JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported');
}
const data = encode(JSON.stringify(components));
return b64u(await digest(digestAlgorithm, data));
}
export async function calculateJwkThumbprintUri(key, digestAlgorithm) {
digestAlgorithm ??= 'sha256';
const thumbprint = await calculateJwkThumbprint(key, digestAlgorithm);
return `urn:ietf:params:oauth:jwk-thumbprint:sha-${digestAlgorithm.slice(-3)}:${thumbprint}`;
}
+99
View File
@@ -0,0 +1,99 @@
import { jwkToKey } from '../lib/jwk_to_key.js';
import { maybeJWSAlgorithm } from '../lib/jws_algorithms.js';
import { JWKSInvalid, JOSENotSupported, JWKSNoMatchingKey, JWKSMultipleMatchingKeys, } from '../util/errors.js';
import { isObject } from '../lib/type_checks.js';
function signatureAlgorithm(alg) {
const entry = typeof alg === 'string' ? maybeJWSAlgorithm(alg) : undefined;
if (!entry || entry.symmetric) {
throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
}
return entry;
}
function isJWKSLike(jwks) {
if (!jwks || typeof jwks !== 'object') {
return false;
}
const { keys } = jwks;
return Array.isArray(keys) && keys.every(isJWKLike);
}
function isJWKLike(key) {
return isObject(key);
}
class LocalJWKSetImpl {
#jwks;
#cached = new WeakMap();
constructor(jwks) {
if (!isJWKSLike(jwks)) {
throw new JWKSInvalid('JSON Web Key Set malformed');
}
this.#jwks = structuredClone(jwks);
}
jwks() {
return this.#jwks;
}
async getKey(protectedHeader, token) {
const { alg, kid } = { ...protectedHeader, ...token?.header };
const entry = signatureAlgorithm(alg);
const candidates = this.#jwks.keys.filter((jwk) => {
let candidate = entry.kty.includes(jwk.kty);
if (candidate && typeof kid === 'string') {
candidate = kid === jwk.kid;
}
if (candidate && (typeof jwk.alg === 'string' || jwk.kty === 'AKP')) {
candidate = alg === jwk.alg;
}
if (candidate && typeof jwk.use === 'string') {
candidate = jwk.use === 'sig';
}
if (candidate && Array.isArray(jwk.key_ops)) {
candidate = jwk.key_ops.includes('verify');
}
if (candidate && entry.crv) {
candidate = jwk.crv === entry.crv;
}
return candidate;
});
const { 0: jwk, length } = candidates;
if (length === 0) {
throw new JWKSNoMatchingKey();
}
if (length !== 1) {
const error = new JWKSMultipleMatchingKeys();
const _cached = this.#cached;
error[Symbol.asyncIterator] = async function* () {
for (const jwk of candidates) {
try {
yield await importWithAlgCache(_cached, jwk, entry);
}
catch { }
}
};
throw error;
}
return importWithAlgCache(this.#cached, jwk, entry);
}
}
async function importWithAlgCache(cache, jwk, entry) {
const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk);
if (cached[entry.alg] === undefined) {
const key = await jwkToKey(entry, { ...jwk, alg: entry.alg, ext: true });
if (key.type !== 'public') {
throw new JWKSInvalid('JSON Web Key Set members must be public keys');
}
cached[entry.alg] = key;
}
return cached[entry.alg];
}
export function createLocalJWKSet(jwks) {
const set = new LocalJWKSetImpl(jwks);
const localJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
Object.defineProperties(localJWKSet, {
jwks: {
value: () => structuredClone(set.jwks()),
enumerable: false,
configurable: false,
writable: false,
},
});
return localJWKSet;
}
+179
View File
@@ -0,0 +1,179 @@
import { JOSEError, JWKSNoMatchingKey, JWKSTimeout } from '../util/errors.js';
import { createLocalJWKSet } from './local.js';
import { isObject } from '../lib/type_checks.js';
function isCloudflareWorkers() {
return (typeof WebSocketPair !== 'undefined' ||
(typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') ||
(typeof EdgeRuntime !== 'undefined' && EdgeRuntime === 'vercel'));
}
let USER_AGENT;
if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
const NAME = 'jose';
const VERSION = 'v6.2.5';
USER_AGENT = `${NAME}/${VERSION}`;
}
export const customFetch = Symbol();
async function fetchJwks(url, headers, signal, fetchImpl = fetch) {
const response = await fetchImpl(url, {
method: 'GET',
signal,
redirect: 'manual',
headers,
}).catch((err) => {
if (err.name === 'TimeoutError') {
throw new JWKSTimeout();
}
throw err;
});
if (response.status !== 200) {
throw new JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response');
}
try {
return await response.json();
}
catch {
throw new JOSEError('Failed to parse the JSON Web Key Set HTTP response as JSON');
}
}
export const jwksCache = Symbol();
function isFreshJwksCache(input, cacheMaxAge) {
if (typeof input !== 'object' || input === null) {
return false;
}
if (!('uat' in input) || typeof input.uat !== 'number' || Date.now() - input.uat >= cacheMaxAge) {
return false;
}
if (!('jwks' in input) ||
!isObject(input.jwks) ||
!Array.isArray(input.jwks.keys) ||
!Array.prototype.every.call(input.jwks.keys, isObject)) {
return false;
}
return true;
}
class RemoteJWKSetImpl {
#url;
#timeoutDuration;
#cooldownDuration;
#cacheMaxAge;
#jwksTimestamp;
#pendingFetch;
#headers;
#customFetch;
#local;
#cache;
constructor(url, options) {
if (!(url instanceof URL)) {
throw new TypeError('url must be an instance of URL');
}
this.#url = new URL(url.href);
this.#timeoutDuration =
typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000;
this.#cooldownDuration =
typeof options?.cooldownDuration === 'number' ? options?.cooldownDuration : 30000;
this.#cacheMaxAge = typeof options?.cacheMaxAge === 'number' ? options?.cacheMaxAge : 600000;
this.#headers = new Headers(options?.headers);
if (USER_AGENT && !this.#headers.has('User-Agent')) {
this.#headers.set('User-Agent', USER_AGENT);
}
if (!this.#headers.has('accept')) {
this.#headers.set('accept', 'application/json');
this.#headers.append('accept', 'application/jwk-set+json');
}
this.#customFetch = options?.[customFetch];
if (options?.[jwksCache] !== undefined) {
this.#cache = options?.[jwksCache];
if (isFreshJwksCache(options?.[jwksCache], this.#cacheMaxAge)) {
this.#jwksTimestamp = this.#cache.uat;
this.#local = createLocalJWKSet(this.#cache.jwks);
}
}
}
pendingFetch() {
return !!this.#pendingFetch;
}
coolingDown() {
return typeof this.#jwksTimestamp === 'number'
? Date.now() < this.#jwksTimestamp + this.#cooldownDuration
: false;
}
fresh() {
return typeof this.#jwksTimestamp === 'number'
? Date.now() < this.#jwksTimestamp + this.#cacheMaxAge
: false;
}
jwks() {
return this.#local?.jwks();
}
async getKey(protectedHeader, token) {
if (!this.#local || !this.fresh()) {
await this.reload();
}
try {
return await this.#local(protectedHeader, token);
}
catch (err) {
if (err instanceof JWKSNoMatchingKey) {
if (this.coolingDown() === false) {
await this.reload();
return this.#local(protectedHeader, token);
}
}
throw err;
}
}
async reload() {
if (this.#pendingFetch && isCloudflareWorkers()) {
this.#pendingFetch = undefined;
}
this.#pendingFetch ||= fetchJwks(this.#url.href, this.#headers, AbortSignal.timeout(this.#timeoutDuration), this.#customFetch)
.then((json) => {
this.#local = createLocalJWKSet(json);
if (this.#cache) {
this.#cache.uat = Date.now();
this.#cache.jwks = json;
}
this.#jwksTimestamp = Date.now();
this.#pendingFetch = undefined;
})
.catch((err) => {
this.#pendingFetch = undefined;
throw err;
});
await this.#pendingFetch;
}
}
export function createRemoteJWKSet(url, options) {
const set = new RemoteJWKSetImpl(url, options);
const remoteJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
Object.defineProperties(remoteJWKSet, {
coolingDown: {
get: () => set.coolingDown(),
enumerable: true,
configurable: false,
},
fresh: {
get: () => set.fresh(),
enumerable: true,
configurable: false,
},
reload: {
value: () => set.reload(),
enumerable: true,
configurable: false,
writable: false,
},
reloading: {
get: () => set.pendingFetch(),
enumerable: true,
configurable: false,
},
jwks: {
value: () => set.jwks(),
enumerable: true,
configurable: false,
writable: false,
},
});
return remoteJWKSet;
}
+21
View File
@@ -0,0 +1,21 @@
import { FlattenedSign } from '../flattened/sign.js';
import { unencodedPayload } from '../../lib/jws_sign.js';
export class CompactSign {
#flattened;
#protectedHeader;
constructor(payload) {
this.#flattened = new FlattenedSign(payload);
}
setProtectedHeader(protectedHeader) {
this.#flattened.setProtectedHeader(protectedHeader);
this.#protectedHeader = protectedHeader;
return this;
}
async sign(key, options) {
if (unencodedPayload(this.#protectedHeader)) {
throw new TypeError('use the flattened module for creating JWS with b64: false');
}
const jws = await this.#flattened.sign(key, options);
return `${jws.protected}.${jws.payload}.${jws.signature}`;
}
}
+9
View File
@@ -0,0 +1,9 @@
import { prepareVerify, verifyCompact } from '../../lib/jws_verify.js';
export async function compactVerify(jws, key, options) {
const verified = await verifyCompact(jws, prepareVerify(options), key);
const result = { payload: verified.payload, protectedHeader: verified.parsedProt };
if (typeof key === 'function') {
return { ...result, key: verified.key };
}
return result;
}
+39
View File
@@ -0,0 +1,39 @@
import { JWSInvalid } from '../../util/errors.js';
import { createSignature } from '../../lib/jws_sign.js';
import { assertNotSet } from '../../lib/helpers.js';
export class FlattenedSign {
#payload;
#protectedHeader;
#unprotectedHeader;
constructor(payload) {
if (!(payload instanceof Uint8Array)) {
throw new TypeError('payload must be an instance of Uint8Array');
}
this.#payload = payload;
}
setProtectedHeader(protectedHeader) {
assertNotSet(this.#protectedHeader, 'setProtectedHeader');
this.#protectedHeader = protectedHeader;
return this;
}
setUnprotectedHeader(unprotectedHeader) {
assertNotSet(this.#unprotectedHeader, 'setUnprotectedHeader');
this.#unprotectedHeader = unprotectedHeader;
return this;
}
async sign(key, options) {
if (!this.#protectedHeader && !this.#unprotectedHeader) {
throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()');
}
const jws = await createSignature({
payload: this.#payload,
protectedHeader: this.#protectedHeader,
unprotectedHeader: this.#unprotectedHeader,
crit: options?.crit,
}, key);
if (this.#unprotectedHeader) {
jws.header = this.#unprotectedHeader;
}
return jws;
}
}
+24
View File
@@ -0,0 +1,24 @@
import { JWSInvalid } from '../../util/errors.js';
import { isObject } from '../../lib/type_checks.js';
import { prepareVerify, verifySignature, verifyResult } from '../../lib/jws_verify.js';
export async function flattenedVerify(jws, key, options) {
if (!isObject(jws)) {
throw new JWSInvalid('Flattened JWS must be an object');
}
if (jws.protected === undefined && jws.header === undefined) {
throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members');
}
if (jws.protected !== undefined && typeof jws.protected !== 'string') {
throw new JWSInvalid('JWS Protected Header incorrect type');
}
if (jws.payload === undefined) {
throw new JWSInvalid('JWS Payload missing');
}
if (typeof jws.signature !== 'string') {
throw new JWSInvalid('JWS Signature missing or incorrect type');
}
if (jws.header !== undefined && !isObject(jws.header)) {
throw new JWSInvalid('JWS Unprotected Header incorrect type');
}
return verifyResult(jws, await verifySignature(jws, prepareVerify(options), key));
}
+83
View File
@@ -0,0 +1,83 @@
import { createSignature } from '../../lib/jws_sign.js';
import { JWSInvalid } from '../../util/errors.js';
import { assertNotSet } from '../../lib/helpers.js';
class IndividualSignature {
#parent;
protectedHeader;
unprotectedHeader;
options;
key;
constructor(sig, key, options) {
this.#parent = sig;
this.key = key;
this.options = options;
}
setProtectedHeader(protectedHeader) {
assertNotSet(this.protectedHeader, 'setProtectedHeader');
this.protectedHeader = protectedHeader;
return this;
}
setUnprotectedHeader(unprotectedHeader) {
assertNotSet(this.unprotectedHeader, 'setUnprotectedHeader');
this.unprotectedHeader = unprotectedHeader;
return this;
}
addSignature(...args) {
return this.#parent.addSignature(...args);
}
sign(...args) {
return this.#parent.sign(...args);
}
done() {
return this.#parent;
}
}
export class GeneralSign {
#payload;
#signatures = [];
constructor(payload) {
this.#payload = payload;
}
addSignature(key, options) {
const signature = new IndividualSignature(this, key, options);
this.#signatures.push(signature);
return signature;
}
async sign() {
if (!this.#signatures.length) {
throw new JWSInvalid('at least one signature must be added');
}
if (!(this.#payload instanceof Uint8Array)) {
throw new TypeError('payload must be an instance of Uint8Array');
}
const jws = {
signatures: [],
payload: '',
};
const encoded = {};
for (let i = 0; i < this.#signatures.length; i++) {
const signature = this.#signatures[i];
if (!signature.protectedHeader && !signature.unprotectedHeader) {
throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()');
}
const { payload, ...rest } = await createSignature({
payload: this.#payload,
protectedHeader: signature.protectedHeader,
unprotectedHeader: signature.unprotectedHeader,
crit: signature.options?.crit,
encoded,
}, signature.key);
if (signature.unprotectedHeader) {
rest.header = signature.unprotectedHeader;
}
if (i === 0) {
jws.payload = payload;
}
else if (jws.payload !== payload) {
throw new JWSInvalid('inconsistent use of JWS Unencoded Payload (RFC7797)');
}
jws.signatures.push(rest);
}
return jws;
}
}
+42
View File
@@ -0,0 +1,42 @@
import { prepareVerify, verifySignature, verifyResult } from '../../lib/jws_verify.js';
import { JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js';
import { isObject } from '../../lib/type_checks.js';
export async function generalVerify(jws, key, options) {
if (!isObject(jws)) {
throw new JWSInvalid('General JWS must be an object');
}
if (!Array.isArray(jws.signatures) || !jws.signatures.every(isObject)) {
throw new JWSInvalid('JWS Signatures missing or incorrect type');
}
let shared;
try {
if (jws.payload === undefined)
throw new Error();
shared = prepareVerify(options);
}
catch {
throw new JWSSignatureVerificationFailed();
}
for (const signature of jws.signatures) {
try {
if (signature.protected === undefined && signature.header === undefined)
throw new Error();
if (signature.protected !== undefined && typeof signature.protected !== 'string') {
throw new Error();
}
if (typeof signature.signature !== 'string')
throw new Error();
if (signature.header !== undefined && !isObject(signature.header))
throw new Error();
return verifyResult(signature, await verifySignature({
header: signature.header,
payload: jws.payload,
protected: signature.protected,
signature: signature.signature,
}, shared, key));
}
catch {
}
}
throw new JWSSignatureVerificationFailed();
}
+23
View File
@@ -0,0 +1,23 @@
import { prepareDecrypt, decryptCompact } from '../lib/jwe_decrypt.js';
import { validateClaimsSet } from '../lib/jwt_claims_set.js';
import { JWTClaimValidationFailed } from '../util/errors.js';
export async function jwtDecrypt(jwt, key, options) {
const decrypted = await decryptCompact(jwt, prepareDecrypt(options), key);
const protectedHeader = decrypted.parsedProt;
const payload = validateClaimsSet(protectedHeader, decrypted.plaintext, options);
if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) {
throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', payload, 'iss', 'mismatch');
}
if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) {
throw new JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', payload, 'sub', 'mismatch');
}
if (protectedHeader.aud !== undefined &&
JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) {
throw new JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', payload, 'aud', 'mismatch');
}
const result = { payload, protectedHeader };
if (typeof key === 'function') {
return { ...result, key: decrypted.key };
}
return result;
}
+101
View File
@@ -0,0 +1,101 @@
import { CompactEncrypt } from '../jwe/compact/encrypt.js';
import { JWTClaimsBuilder } from '../lib/jwt_claims_set.js';
import { assertNotSet } from '../lib/helpers.js';
export class EncryptJWT {
#cek;
#iv;
#keyManagementParameters;
#protectedHeader;
#replicateIssuerAsHeader;
#replicateSubjectAsHeader;
#replicateAudienceAsHeader;
#jwt;
constructor(payload = {}) {
this.#jwt = new JWTClaimsBuilder(payload);
}
setIssuer(issuer) {
this.#jwt.iss = issuer;
return this;
}
setSubject(subject) {
this.#jwt.sub = subject;
return this;
}
setAudience(audience) {
this.#jwt.aud = audience;
return this;
}
setJti(jwtId) {
this.#jwt.jti = jwtId;
return this;
}
setNotBefore(input) {
this.#jwt.nbf = input;
return this;
}
setExpirationTime(input) {
this.#jwt.exp = input;
return this;
}
setIssuedAt(input) {
this.#jwt.iat = input;
return this;
}
setProtectedHeader(protectedHeader) {
assertNotSet(this.#protectedHeader, 'setProtectedHeader');
this.#protectedHeader = protectedHeader;
return this;
}
setKeyManagementParameters(parameters) {
assertNotSet(this.#keyManagementParameters, 'setKeyManagementParameters');
this.#keyManagementParameters = parameters;
return this;
}
setContentEncryptionKey(cek) {
assertNotSet(this.#cek, 'setContentEncryptionKey');
this.#cek = cek;
return this;
}
setInitializationVector(iv) {
assertNotSet(this.#iv, 'setInitializationVector');
this.#iv = iv;
return this;
}
replicateIssuerAsHeader() {
this.#replicateIssuerAsHeader = true;
return this;
}
replicateSubjectAsHeader() {
this.#replicateSubjectAsHeader = true;
return this;
}
replicateAudienceAsHeader() {
this.#replicateAudienceAsHeader = true;
return this;
}
async encrypt(key, options) {
const enc = new CompactEncrypt(this.#jwt.data());
if (this.#protectedHeader &&
(this.#replicateIssuerAsHeader ||
this.#replicateSubjectAsHeader ||
this.#replicateAudienceAsHeader)) {
this.#protectedHeader = {
...this.#protectedHeader,
iss: this.#replicateIssuerAsHeader ? this.#jwt.iss : undefined,
sub: this.#replicateSubjectAsHeader ? this.#jwt.sub : undefined,
aud: this.#replicateAudienceAsHeader ? this.#jwt.aud : undefined,
};
}
enc.setProtectedHeader(this.#protectedHeader);
if (this.#iv) {
enc.setInitializationVector(this.#iv);
}
if (this.#cek) {
enc.setContentEncryptionKey(this.#cek);
}
if (this.#keyManagementParameters) {
enc.setKeyManagementParameters(this.#keyManagementParameters);
}
return enc.encrypt(key, options);
}
}
+51
View File
@@ -0,0 +1,51 @@
import { CompactSign } from '../jws/compact/sign.js';
import { unencodedPayload } from '../lib/jws_sign.js';
import { JWTInvalid } from '../util/errors.js';
import { JWTClaimsBuilder } from '../lib/jwt_claims_set.js';
export class SignJWT {
#protectedHeader;
#jwt;
constructor(payload = {}) {
this.#jwt = new JWTClaimsBuilder(payload);
}
setIssuer(issuer) {
this.#jwt.iss = issuer;
return this;
}
setSubject(subject) {
this.#jwt.sub = subject;
return this;
}
setAudience(audience) {
this.#jwt.aud = audience;
return this;
}
setJti(jwtId) {
this.#jwt.jti = jwtId;
return this;
}
setNotBefore(input) {
this.#jwt.nbf = input;
return this;
}
setExpirationTime(input) {
this.#jwt.exp = input;
return this;
}
setIssuedAt(input) {
this.#jwt.iat = input;
return this;
}
setProtectedHeader(protectedHeader) {
this.#protectedHeader = protectedHeader;
return this;
}
async sign(key, options) {
const sig = new CompactSign(this.#jwt.data());
sig.setProtectedHeader(this.#protectedHeader);
if (unencodedPayload(this.#protectedHeader)) {
throw new JWTInvalid('JWTs MUST NOT use unencoded payload');
}
return sig.sign(key, options);
}
}
+64
View File
@@ -0,0 +1,64 @@
import * as b64u from '../util/base64url.js';
import { strictDecoder } from '../lib/buffer_utils.js';
import { decodeBase64url } from '../lib/helpers.js';
import { JWTInvalid } from '../util/errors.js';
import { validateClaimsSet, JWTClaimsBuilder } from '../lib/jwt_claims_set.js';
export class UnsecuredJWT {
#jwt;
constructor(payload = {}) {
this.#jwt = new JWTClaimsBuilder(payload);
}
encode() {
const header = b64u.encode(JSON.stringify({ alg: 'none' }));
const payload = b64u.encode(this.#jwt.data());
return `${header}.${payload}.`;
}
setIssuer(issuer) {
this.#jwt.iss = issuer;
return this;
}
setSubject(subject) {
this.#jwt.sub = subject;
return this;
}
setAudience(audience) {
this.#jwt.aud = audience;
return this;
}
setJti(jwtId) {
this.#jwt.jti = jwtId;
return this;
}
setNotBefore(input) {
this.#jwt.nbf = input;
return this;
}
setExpirationTime(input) {
this.#jwt.exp = input;
return this;
}
setIssuedAt(input) {
this.#jwt.iat = input;
return this;
}
static decode(jwt, options) {
if (typeof jwt !== 'string') {
throw new JWTInvalid('Unsecured JWT must be a string');
}
const { 0: encodedHeader, 1: encodedPayload, 2: signature, length } = jwt.split('.');
if (length !== 3 || signature !== '') {
throw new JWTInvalid('Invalid Unsecured JWT');
}
let header;
try {
header = JSON.parse(strictDecoder.decode(b64u.decode(encodedHeader)));
if (header.alg !== 'none')
throw new Error();
}
catch {
throw new JWTInvalid('Invalid Unsecured JWT');
}
const payload = validateClaimsSet(header, decodeBase64url(encodedPayload, 'payload', JWTInvalid), options);
return { payload, header };
}
}
+15
View File
@@ -0,0 +1,15 @@
import { prepareVerify, verifyCompact } from '../lib/jws_verify.js';
import { validateClaimsSet } from '../lib/jwt_claims_set.js';
import { JWTInvalid } from '../util/errors.js';
export async function jwtVerify(jwt, key, options) {
const verified = await verifyCompact(jwt, prepareVerify(options), key);
if (!verified.b64) {
throw new JWTInvalid('JWTs MUST NOT use unencoded payload');
}
const payload = validateClaimsSet(verified.parsedProt, verified.payload, options);
const result = { payload, protectedHeader: verified.parsedProt };
if (typeof key === 'function') {
return { ...result, key: verified.key };
}
return result;
}
+44
View File
@@ -0,0 +1,44 @@
import { toSPKI as exportPublic, toPKCS8 as exportPrivate } from '../lib/asn1.js';
import { invalidKeyInput } from '../lib/invalid_key_input.js';
import { encode as b64u } from '../util/base64url.js';
import { isCryptoKey, isKeyObject } from '../lib/is_key_like.js';
function omitUndefinedProperties(jwk) {
return Object.fromEntries(Object.entries(jwk).filter(([, value]) => value !== undefined));
}
async function keyToJWK(key) {
if (isKeyObject(key)) {
if (key.type === 'secret') {
key = key.export();
}
else {
return key.export({ format: 'jwk' });
}
}
if (key instanceof Uint8Array) {
return {
kty: 'oct',
k: b64u(key),
};
}
if (!isCryptoKey(key)) {
throw new TypeError(invalidKeyInput(key, 'CryptoKey', 'KeyObject', 'Uint8Array'));
}
if (!key.extractable) {
throw new TypeError('non-extractable CryptoKey cannot be exported as a JWK');
}
const { ext, key_ops, alg, use, ...jwk } = omitUndefinedProperties(await crypto.subtle.exportKey('jwk', key));
if (jwk.kty === 'AKP') {
;
jwk.alg = alg;
}
return jwk;
}
export async function exportSPKI(key) {
return exportPublic(key);
}
export async function exportPKCS8(key) {
return exportPrivate(key);
}
export async function exportJWK(key) {
return keyToJWK(key);
}
+47
View File
@@ -0,0 +1,47 @@
import { JOSENotSupported } from '../util/errors.js';
import { keyAlgorithm } from '../lib/key_algorithm.js';
function getModulusLengthOption(options) {
const modulusLength = options?.modulusLength ?? 2048;
if (typeof modulusLength !== 'number' || modulusLength < 2048) {
throw new JOSENotSupported('Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used');
}
return modulusLength;
}
export async function generateKeyPair(alg, options) {
const entry = keyAlgorithm(alg);
if (entry.symmetric) {
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
}
let algorithm;
if (entry.subtleFor) {
switch (options?.crv ?? 'P-256') {
case 'P-256':
case 'P-384':
case 'P-521':
algorithm = { name: 'ECDH', namedCurve: options?.crv ?? 'P-256' };
break;
case 'X25519':
algorithm = { name: 'X25519' };
break;
default:
throw new JOSENotSupported('Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, and X25519');
}
}
else {
if (entry.crv !== undefined && options?.crv !== undefined && options.crv !== entry.crv) {
throw new JOSENotSupported(`Invalid or unsupported crv option provided, the only supported value for ${alg} is ${entry.crv}`);
}
algorithm =
entry.kty[0] === 'RSA'
? {
...entry.subtle,
publicExponent: Uint8Array.of(0x01, 0x00, 0x01),
modulusLength: getModulusLengthOption(options),
}
: entry.subtle;
}
return crypto.subtle.generateKey(algorithm, options?.extractable ?? false, [
...entry.usages.private,
...entry.usages.public,
]);
}
+40
View File
@@ -0,0 +1,40 @@
import { JOSENotSupported } from '../util/errors.js';
export async function generateSecret(alg, options) {
let length;
let algorithm;
let keyUsages;
switch (alg) {
case 'HS256':
case 'HS384':
case 'HS512':
length = parseInt(alg.slice(-3), 10);
algorithm = { name: 'HMAC', hash: `SHA-${length}`, length };
keyUsages = ['sign', 'verify'];
break;
case 'A128CBC-HS256':
case 'A192CBC-HS384':
case 'A256CBC-HS512':
length = parseInt(alg.slice(-3), 10);
return crypto.getRandomValues(new Uint8Array(length >> 3));
case 'A128KW':
case 'A192KW':
case 'A256KW':
length = parseInt(alg.slice(1, 4), 10);
algorithm = { name: 'AES-KW', length };
keyUsages = ['wrapKey', 'unwrapKey'];
break;
case 'A128GCMKW':
case 'A192GCMKW':
case 'A256GCMKW':
case 'A128GCM':
case 'A192GCM':
case 'A256GCM':
length = parseInt(alg.slice(1, 4), 10);
algorithm = { name: 'AES-GCM', length };
keyUsages = ['encrypt', 'decrypt'];
break;
default:
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
}
return crypto.subtle.generateKey(algorithm, options?.extractable ?? false, keyUsages);
}
+57
View File
@@ -0,0 +1,57 @@
import { decode as decodeBase64URL } from '../util/base64url.js';
import { fromSPKI, fromPKCS8, fromX509 } from '../lib/asn1.js';
import { jwkToKey } from '../lib/jwk_to_key.js';
import { keyAlgorithm } from '../lib/key_algorithm.js';
import { JOSENotSupported } from '../util/errors.js';
import { isObject } from '../lib/type_checks.js';
export async function importSPKI(spki, alg, options) {
if (typeof spki !== 'string' || spki.indexOf('-----BEGIN PUBLIC KEY-----') !== 0) {
throw new TypeError('"spki" must be SPKI formatted string');
}
return fromSPKI(spki, alg, options);
}
export async function importX509(x509, alg, options) {
if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) {
throw new TypeError('"x509" must be X.509 formatted string');
}
return fromX509(x509, alg, options);
}
export async function importPKCS8(pkcs8, alg, options) {
if (typeof pkcs8 !== 'string' || pkcs8.indexOf('-----BEGIN PRIVATE KEY-----') !== 0) {
throw new TypeError('"pkcs8" must be PKCS#8 formatted string');
}
return fromPKCS8(pkcs8, alg, options);
}
export async function importJWK(jwk, alg, options) {
if (!isObject(jwk)) {
throw new TypeError('JWK must be an object');
}
alg ??= jwk.alg;
const ext = options?.extractable ?? jwk.ext;
if (jwk.kty !== 'oct' && !alg) {
throw new TypeError('"alg" argument is required when "jwk.alg" is not present');
}
switch (jwk.kty) {
case 'oct':
if (typeof jwk.k !== 'string' || !jwk.k) {
throw new TypeError('missing "k" (Key Value) Parameter value');
}
return decodeBase64URL(jwk.k);
case 'RSA':
return jwkToKey(keyAlgorithm(alg), { ...jwk, alg, ext });
case 'AKP': {
if (typeof jwk.alg !== 'string' || !jwk.alg) {
throw new TypeError('missing "alg" (Algorithm) Parameter value');
}
if (alg !== undefined && alg !== jwk.alg) {
throw new TypeError('JWK alg and alg option value mismatch');
}
return jwkToKey(keyAlgorithm(jwk.alg), { ...jwk, ext });
}
case 'EC':
case 'OKP':
return jwkToKey(keyAlgorithm(alg), { ...jwk, alg, ext });
default:
throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value');
}
}
+207
View File
@@ -0,0 +1,207 @@
import { invalidKeyInput } from './invalid_key_input.js';
import { encodeBase64, decodeBase64 } from '../lib/base64.js';
import { JOSENotSupported } from '../util/errors.js';
import { keyAlgorithm } from './key_algorithm.js';
import { isCryptoKey, isKeyObject } from './is_key_like.js';
const formatPEM = (b64, descriptor) => {
const newlined = (b64.match(/.{1,64}/g) || []).join('\n');
return `-----BEGIN ${descriptor}-----\n${newlined}\n-----END ${descriptor}-----`;
};
const genericExport = async (keyType, keyFormat, key) => {
if (isKeyObject(key)) {
if (key.type !== keyType) {
throw new TypeError(`key is not a ${keyType} key`);
}
return key.export({ format: 'pem', type: keyFormat });
}
if (!isCryptoKey(key)) {
throw new TypeError(invalidKeyInput(key, 'CryptoKey', 'KeyObject'));
}
if (!key.extractable) {
throw new TypeError('CryptoKey is not extractable');
}
if (key.type !== keyType) {
throw new TypeError(`key is not a ${keyType} key`);
}
return formatPEM(encodeBase64(new Uint8Array(await crypto.subtle.exportKey(keyFormat, key))), `${keyType.toUpperCase()} KEY`);
};
export const toSPKI = (key) => genericExport('public', 'spki', key);
export const toPKCS8 = (key) => genericExport('private', 'pkcs8', key);
const bytesEqual = (a, b) => {
if (a.byteLength !== b.length)
return false;
for (let i = 0; i < a.byteLength; i++) {
if (a[i] !== b[i])
return false;
}
return true;
};
const createASN1State = (data) => ({ data, pos: 0 });
const readByte = (state) => {
const byte = state.data[state.pos++];
if (byte === undefined) {
throw new Error('Unexpected end of ASN.1 input');
}
return byte;
};
const parseLength = (state) => {
const first = readByte(state);
if (first & 0x80) {
const lengthOfLen = first & 0x7f;
let length = 0;
for (let i = 0; i < lengthOfLen; i++) {
length = (length << 8) | readByte(state);
}
return length;
}
return first;
};
const skipElement = (state, count = 1) => {
if (count <= 0)
return;
state.pos++;
const length = parseLength(state);
state.pos += length;
if (count > 1) {
skipElement(state, count - 1);
}
};
const expectTag = (state, expectedTag, errorMessage) => {
if (readByte(state) !== expectedTag) {
throw new Error(errorMessage);
}
};
const getSubarray = (state, length) => {
if (length < 0 || state.pos + length > state.data.length) {
throw new Error('Unexpected end of ASN.1 input');
}
const result = state.data.subarray(state.pos, state.pos + length);
state.pos += length;
return result;
};
const parseAlgorithmOID = (state) => {
expectTag(state, 0x06, 'Expected algorithm OID');
const oidLen = parseLength(state);
return getSubarray(state, oidLen);
};
function parsePKCS8Header(state) {
expectTag(state, 0x30, 'Invalid PKCS#8 structure');
parseLength(state);
expectTag(state, 0x02, 'Expected version field');
const verLen = parseLength(state);
state.pos += verLen;
expectTag(state, 0x30, 'Expected algorithm identifier');
const algIdLen = parseLength(state);
const algIdStart = state.pos;
return { algIdStart, algIdLength: algIdLen };
}
function parseSPKIHeader(state) {
expectTag(state, 0x30, 'Invalid SPKI structure');
parseLength(state);
expectTag(state, 0x30, 'Expected algorithm identifier');
const algIdLen = parseLength(state);
const algIdStart = state.pos;
return { algIdStart, algIdLength: algIdLen };
}
const parseECAlgorithmIdentifier = (state) => {
const algOid = parseAlgorithmOID(state);
if (bytesEqual(algOid, [0x2b, 0x65, 0x6e])) {
return 'X25519';
}
if (!bytesEqual(algOid, [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01])) {
throw new Error('Unsupported key algorithm');
}
expectTag(state, 0x06, 'Expected curve OID');
const curveOidLen = parseLength(state);
const curveOid = getSubarray(state, curveOidLen);
for (const { name, oid } of [
{ name: 'P-256', oid: [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07] },
{ name: 'P-384', oid: [0x2b, 0x81, 0x04, 0x00, 0x22] },
{ name: 'P-521', oid: [0x2b, 0x81, 0x04, 0x00, 0x23] },
]) {
if (bytesEqual(curveOid, oid)) {
return name;
}
}
throw new Error('Unsupported named curve');
};
const genericImport = async (keyFormat, keyData, alg, options) => {
const entry = keyAlgorithm(alg);
if (entry.symmetric) {
throw new JOSENotSupported('Invalid or unsupported "alg" (Algorithm) value');
}
const isPublic = keyFormat === 'spki';
let algorithm;
if (entry.subtleFor) {
try {
algorithm = entry.subtleFor({ crv: options.getNamedCurve(keyData) });
}
catch (cause) {
throw new JOSENotSupported('Invalid or unsupported key format');
}
}
else {
algorithm = entry.subtle;
}
return crypto.subtle.importKey(keyFormat, keyData, algorithm, options?.extractable ?? (isPublic ? true : false), isPublic ? entry.usages.public : entry.usages.private);
};
const processPEMData = (pem, pattern) => {
return decodeBase64(pem.replace(pattern, ''));
};
export const fromPKCS8 = (pem, alg, options) => {
const keyData = processPEMData(pem, /(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g);
let opts = options;
if (alg?.startsWith?.('ECDH-ES')) {
opts ||= {};
opts.getNamedCurve = (keyData) => {
const state = createASN1State(keyData);
parsePKCS8Header(state);
return parseECAlgorithmIdentifier(state);
};
}
return genericImport('pkcs8', keyData, alg, opts);
};
export const fromSPKI = (pem, alg, options) => {
const keyData = processPEMData(pem, /(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g);
let opts = options;
if (alg?.startsWith?.('ECDH-ES')) {
opts ||= {};
opts.getNamedCurve = (keyData) => {
const state = createASN1State(keyData);
parseSPKIHeader(state);
return parseECAlgorithmIdentifier(state);
};
}
return genericImport('spki', keyData, alg, opts);
};
function spkiFromX509(buf) {
const state = createASN1State(buf);
expectTag(state, 0x30, 'Invalid certificate structure');
parseLength(state);
expectTag(state, 0x30, 'Invalid tbsCertificate structure');
parseLength(state);
if (buf[state.pos] === 0xa0) {
skipElement(state, 6);
}
else {
skipElement(state, 5);
}
const spkiStart = state.pos;
expectTag(state, 0x30, 'Invalid SPKI structure');
const spkiContentLen = parseLength(state);
return buf.subarray(spkiStart, spkiStart + spkiContentLen + (state.pos - spkiStart));
}
function extractX509SPKI(x509) {
const derBytes = processPEMData(x509, /(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g);
return spkiFromX509(derBytes);
}
export const fromX509 = (pem, alg, options) => {
let spki;
try {
spki = extractX509SPKI(pem);
}
catch (cause) {
throw new TypeError('Failed to parse the X.509 certificate', { cause });
}
return fromSPKI(formatPEM(encodeBase64(spki), 'PUBLIC KEY'), alg, options);
};
+22
View File
@@ -0,0 +1,22 @@
export function encodeBase64(input) {
if (Uint8Array.prototype.toBase64) {
return input.toBase64();
}
const CHUNK_SIZE = 0x8000;
const arr = [];
for (let i = 0; i < input.length; i += CHUNK_SIZE) {
arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));
}
return btoa(arr.join(''));
}
export function decodeBase64(encoded) {
if (Uint8Array.fromBase64) {
return Uint8Array.fromBase64(encoded);
}
const binary = atob(encoded);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
}
+44
View File
@@ -0,0 +1,44 @@
export const encoder = new TextEncoder();
export const decoder = new TextDecoder();
export const strictDecoder = new TextDecoder('utf-8', { fatal: true });
const MAX_INT32 = 2 ** 32;
export function concat(...buffers) {
const size = buffers.reduce((acc, { length }) => acc + length, 0);
const buf = new Uint8Array(size);
let i = 0;
for (const buffer of buffers) {
buf.set(buffer, i);
i += buffer.length;
}
return buf;
}
function writeUInt32BE(buf, value, offset) {
if (value < 0 || value >= MAX_INT32) {
throw new RangeError(`value must be >= 0 and <= ${MAX_INT32 - 1}. Received ${value}`);
}
buf.set([value >>> 24, value >>> 16, value >>> 8, value & 0xff], offset);
}
export function uint64be(value) {
const high = Math.floor(value / MAX_INT32);
const low = value % MAX_INT32;
const buf = new Uint8Array(8);
writeUInt32BE(buf, high, 0);
writeUInt32BE(buf, low, 4);
return buf;
}
export function uint32be(value) {
const buf = new Uint8Array(4);
writeUInt32BE(buf, value);
return buf;
}
export function encode(string) {
const bytes = new Uint8Array(string.length);
for (let i = 0; i < string.length; i++) {
const code = string.charCodeAt(i);
if (code > 127) {
throw new TypeError('non-ASCII string encountered in encode()');
}
bytes[i] = code;
}
return bytes;
}
+160
View File
@@ -0,0 +1,160 @@
import { concat, uint64be } from './buffer_utils.js';
import { checkCryptoKey } from './crypto_key.js';
import { invalidKeyInput } from './invalid_key_input.js';
import { JWEDecryptionFailed, JWEInvalid } from '../util/errors.js';
import { isCryptoKey } from './is_key_like.js';
export const generateCek = (enc) => crypto.getRandomValues(new Uint8Array(enc.cekBits >> 3));
function checkCekLength(cek, expected) {
const actual = cek.byteLength << 3;
if (actual !== expected) {
throw new JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`);
}
}
export const generateIv = (enc) => crypto.getRandomValues(new Uint8Array(enc.ivBits >> 3));
export function checkIvLength(enc, iv) {
if (iv.length << 3 !== enc.ivBits) {
throw new JWEInvalid('Invalid Initialization Vector length');
}
}
async function cbcKeySetup(enc, cek, usage) {
if (!(cek instanceof Uint8Array)) {
throw new TypeError(invalidKeyInput(cek, 'Uint8Array'));
}
const keySize = enc.cekBits >> 1;
const encKey = await crypto.subtle.importKey('raw', cek.subarray(keySize >> 3), 'AES-CBC', false, [usage]);
const macKey = await crypto.subtle.importKey('raw', cek.subarray(0, keySize >> 3), {
hash: `SHA-${keySize << 1}`,
name: 'HMAC',
}, false, ['sign']);
return { encKey, macKey, keySize };
}
async function cbcHmacTag(macKey, macData, keySize) {
return new Uint8Array((await crypto.subtle.sign('HMAC', macKey, macData)).slice(0, keySize >> 3));
}
async function cbcEncrypt(enc, plaintext, cek, iv, aad) {
const { encKey, macKey, keySize } = await cbcKeySetup(enc, cek, 'encrypt');
const ciphertext = new Uint8Array(await crypto.subtle.encrypt({
iv: iv,
name: 'AES-CBC',
}, encKey, plaintext));
const macData = concat(aad, iv, ciphertext, uint64be(aad.length * 8));
const tag = await cbcHmacTag(macKey, macData, keySize);
return { ciphertext, tag, iv };
}
async function timingSafeEqual(a, b) {
if (!(a instanceof Uint8Array)) {
throw new TypeError('First argument must be a buffer');
}
if (!(b instanceof Uint8Array)) {
throw new TypeError('Second argument must be a buffer');
}
const algorithm = { name: 'HMAC', hash: 'SHA-256' };
const key = (await crypto.subtle.generateKey(algorithm, false, ['sign']));
const aHmac = new Uint8Array(await crypto.subtle.sign(algorithm, key, a));
const bHmac = new Uint8Array(await crypto.subtle.sign(algorithm, key, b));
let out = 0;
let i = -1;
while (++i < 32) {
out |= aHmac[i] ^ bHmac[i];
}
return out === 0;
}
async function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) {
const { encKey, macKey, keySize } = await cbcKeySetup(enc, cek, 'decrypt');
const macData = concat(aad, iv, ciphertext, uint64be(aad.length * 8));
const expectedTag = await cbcHmacTag(macKey, macData, keySize);
let macCheckPassed;
try {
macCheckPassed = await timingSafeEqual(tag, expectedTag);
}
catch {
}
if (!macCheckPassed) {
throw new JWEDecryptionFailed();
}
let plaintext;
try {
plaintext = new Uint8Array(await crypto.subtle.decrypt({ iv: iv, name: 'AES-CBC' }, encKey, ciphertext));
}
catch {
}
if (!plaintext) {
throw new JWEDecryptionFailed();
}
return plaintext;
}
async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
let encKey;
if (cek instanceof Uint8Array) {
encKey = await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, ['encrypt']);
}
else {
checkCryptoKey(cek, enc.subtle, 'encrypt');
encKey = cek;
}
const encrypted = new Uint8Array(await crypto.subtle.encrypt({
additionalData: aad,
iv: iv,
name: 'AES-GCM',
tagLength: 128,
}, encKey, plaintext));
const tag = encrypted.slice(-16);
const ciphertext = encrypted.slice(0, -16);
return { ciphertext, tag, iv };
}
async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
let encKey;
if (cek instanceof Uint8Array) {
encKey = await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, ['decrypt']);
}
else {
checkCryptoKey(cek, enc.subtle, 'decrypt');
encKey = cek;
}
try {
return new Uint8Array(await crypto.subtle.decrypt({
additionalData: aad,
iv: iv,
name: 'AES-GCM',
tagLength: 128,
}, encKey, concat(ciphertext, tag)));
}
catch {
throw new JWEDecryptionFailed();
}
}
export async function encrypt(enc, plaintext, cek, iv, aad) {
if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) {
throw new TypeError(invalidKeyInput(cek, 'CryptoKey', 'KeyObject', 'Uint8Array', 'JSON Web Key'));
}
if (iv) {
checkIvLength(enc, iv);
}
else {
iv = generateIv(enc);
}
if (cek instanceof Uint8Array) {
checkCekLength(cek, enc.cekBits);
}
return enc.cbc
? cbcEncrypt(enc, plaintext, cek, iv, aad)
: gcmEncrypt(enc, plaintext, cek, iv, aad);
}
export async function decrypt(enc, cek, ciphertext, iv, tag, aad) {
if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) {
throw new TypeError(invalidKeyInput(cek, 'CryptoKey', 'KeyObject', 'Uint8Array', 'JSON Web Key'));
}
if (!iv) {
throw new JWEInvalid('JWE Initialization Vector missing');
}
if (!tag) {
throw new JWEInvalid('JWE Authentication Tag missing');
}
checkIvLength(enc, iv);
if (cek instanceof Uint8Array) {
checkCekLength(cek, enc.cekBits);
}
return enc.cbc
? cbcDecrypt(enc, cek, ciphertext, iv, tag, aad)
: gcmDecrypt(enc, cek, ciphertext, iv, tag, aad);
}
+22
View File
@@ -0,0 +1,22 @@
const unusable = (name, prop = 'algorithm.name') => new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
export function checkUsage(key, usage) {
if (usage && !key.usages.includes(usage)) {
throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
}
}
export function checkCryptoKey(key, expected, usage) {
const algorithm = key.algorithm;
if (algorithm.name !== expected.name) {
throw unusable(expected.name);
}
if (expected.hash && algorithm.hash?.name !== expected.hash) {
throw unusable(expected.hash, 'algorithm.hash');
}
if (expected.namedCurve && algorithm.namedCurve !== expected.namedCurve) {
throw unusable(expected.namedCurve, 'algorithm.namedCurve');
}
if (expected.length !== undefined && algorithm.length !== expected.length) {
throw unusable(expected.length, 'algorithm.length');
}
checkUsage(key, usage);
}
+44
View File
@@ -0,0 +1,44 @@
import { JOSENotSupported, JWEInvalid } from '../util/errors.js';
import { concat } from './buffer_utils.js';
function supported(name) {
if (typeof globalThis[name] === 'undefined') {
throw new JOSENotSupported(`JWE "zip" (Compression Algorithm) Header Parameter requires the ${name} API.`);
}
}
export async function compress(input) {
supported('CompressionStream');
const cs = new CompressionStream('deflate-raw');
const writer = cs.writable.getWriter();
writer.write(input).catch(() => { });
writer.close().catch(() => { });
const chunks = [];
const reader = cs.readable.getReader();
for (;;) {
const { value, done } = await reader.read();
if (done)
break;
chunks.push(value);
}
return concat(...chunks);
}
export async function decompress(input, maxLength) {
supported('DecompressionStream');
const ds = new DecompressionStream('deflate-raw');
const writer = ds.writable.getWriter();
writer.write(input).catch(() => { });
writer.close().catch(() => { });
const chunks = [];
let length = 0;
const reader = ds.readable.getReader();
for (;;) {
const { value, done } = await reader.read();
if (done)
break;
chunks.push(value);
length += value.byteLength;
if (maxLength !== Infinity && length > maxLength) {
throw new JWEInvalid('Decompressed plaintext exceeded the configured limit');
}
}
return concat(...chunks);
}
+42
View File
@@ -0,0 +1,42 @@
import { decode } from '../util/base64url.js';
import { encode, strictDecoder } from './buffer_utils.js';
import { isObject } from './type_checks.js';
export const unprotected = Symbol();
export function assertNotSet(value, name) {
if (value) {
throw new TypeError(`${name} can only be called once`);
}
}
export function decodeBase64url(value, label, ErrorClass) {
try {
return decode(value);
}
catch {
throw new ErrorClass(`Failed to base64url decode the ${label}`);
}
}
export function encodeBase64url(value, label, ErrorClass) {
try {
return encode(value);
}
catch {
throw new ErrorClass(`The ${label} is not a valid base64url string`);
}
}
export async function digest(algorithm, data) {
const subtleDigest = `SHA-${algorithm.slice(-3)}`;
return new Uint8Array(await crypto.subtle.digest(subtleDigest, data));
}
export function parseJoseHeader(b64, ErrorClass, message) {
let parsed;
try {
parsed = JSON.parse(strictDecoder.decode(decode(b64)));
}
catch {
throw new ErrorClass(message);
}
if (!isObject(parsed)) {
throw new ErrorClass(message);
}
return parsed;
}
+27
View File
@@ -0,0 +1,27 @@
function message(msg, actual, ...types) {
types = types.filter(Boolean);
if (types.length > 2) {
const last = types.pop();
msg += `one of type ${types.join(', ')}, or ${last}.`;
}
else if (types.length === 2) {
msg += `one of type ${types[0]} or ${types[1]}.`;
}
else {
msg += `of type ${types[0]}.`;
}
if (actual == null) {
msg += ` Received ${actual}`;
}
else if (typeof actual === 'function' && actual.name) {
msg += ` Received function ${actual.name}`;
}
else if (typeof actual === 'object' && actual != null) {
if (actual.constructor?.name) {
msg += ` Received an instance of ${actual.constructor.name}`;
}
}
return msg;
}
export const invalidKeyInput = (actual, ...types) => message('Key must be ', actual, ...types);
export const withAlg = (alg, actual, ...types) => message(`Key for the ${alg} algorithm must be `, actual, ...types);
+17
View File
@@ -0,0 +1,17 @@
export function assertCryptoKey(key) {
if (!isCryptoKey(key)) {
throw new Error('CryptoKey instance expected');
}
}
export const isCryptoKey = (key) => {
if (key?.[Symbol.toStringTag] === 'CryptoKey')
return true;
try {
return key instanceof CryptoKey;
}
catch {
return false;
}
};
export const isKeyObject = (key) => key?.[Symbol.toStringTag] === 'KeyObject';
export const isKeyLike = (key) => isCryptoKey(key) || isKeyObject(key);
+143
View File
@@ -0,0 +1,143 @@
import { JOSENotSupported } from '../util/errors.js';
import { table } from './key_descriptor.js';
const wrap = {
public: ['encrypt', 'wrapKey'],
private: ['decrypt', 'unwrapKey'],
};
const derive = { public: [], private: ['deriveBits'] };
const none = { public: [], private: [] };
function rsaes(bits) {
return {
kty: ['RSA'],
subtle: { name: 'RSA-OAEP', hash: `SHA-${bits}` },
usages: wrap,
minModulusLength: 2048,
keyOps: { encrypt: 'wrapKey', decrypt: 'unwrapKey' },
};
}
function ecdh(kwBits) {
return {
kty: ['EC', 'OKP'],
subtle: { name: 'ECDH' },
subtleFor: ({ kty, crv, asymmetricKeyType }) => {
if (crv === 'X25519' || asymmetricKeyType === 'x25519') {
return { name: 'X25519' };
}
if (kty === 'OKP') {
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
}
return { name: 'ECDH', namedCurve: crv };
},
usages: derive,
kwBits,
keyOps: { decrypt: 'deriveBits' },
};
}
function aeskw(bits) {
return {
kty: ['oct'],
symmetric: true,
subtle: { name: 'AES-KW', length: bits },
usages: none,
keyOps: { encrypt: 'wrapKey', decrypt: 'unwrapKey' },
};
}
function aesgcmkw(bits) {
return {
kty: ['oct'],
symmetric: true,
subtle: { name: 'AES-GCM', length: bits },
usages: none,
gcmkw: `A${bits}GCM`,
keyOps: { encrypt: 'encrypt', decrypt: 'decrypt' },
};
}
function pbes2(bits, kwBits) {
return {
kty: ['oct'],
symmetric: true,
subtle: { name: 'PBKDF2' },
usages: none,
pbes2Hash: `SHA-${bits}`,
kwBits,
keyOps: { encrypt: 'deriveBits', decrypt: 'deriveBits' },
};
}
const JWE = table({
dir: {
kty: ['oct'],
symmetric: true,
subtle: { name: 'AES-GCM' },
usages: none,
keyOps: { encrypt: 'encrypt', decrypt: 'decrypt' },
},
'RSA-OAEP': rsaes(1),
'RSA-OAEP-256': rsaes(256),
'RSA-OAEP-384': rsaes(384),
'RSA-OAEP-512': rsaes(512),
'ECDH-ES': ecdh(),
'ECDH-ES+A128KW': ecdh(128),
'ECDH-ES+A192KW': ecdh(192),
'ECDH-ES+A256KW': ecdh(256),
A128KW: aeskw(128),
A192KW: aeskw(192),
A256KW: aeskw(256),
A128GCMKW: aesgcmkw(128),
A192GCMKW: aesgcmkw(192),
A256GCMKW: aesgcmkw(256),
'PBES2-HS256+A128KW': pbes2(256, 128),
'PBES2-HS384+A192KW': pbes2(384, 192),
'PBES2-HS512+A256KW': pbes2(512, 256),
});
const content = { public: [], private: [] };
const contentOps = { encrypt: 'encrypt', decrypt: 'decrypt' };
function gcm(bits) {
return {
kty: ['oct'],
symmetric: true,
subtle: { name: 'AES-GCM', length: bits },
usages: content,
keyOps: contentOps,
cekBits: bits,
ivBits: 96,
cbc: false,
};
}
function cbc(bits) {
return {
kty: ['oct'],
symmetric: true,
subtle: { name: 'AES-CBC', length: bits },
usages: content,
keyOps: contentOps,
cekBits: bits,
ivBits: 128,
cbc: true,
};
}
const ENC = table({
A128GCM: gcm(128),
A192GCM: gcm(192),
A256GCM: gcm(256),
'A128CBC-HS256': cbc(256),
'A192CBC-HS384': cbc(384),
'A256CBC-HS512': cbc(512),
});
const unsupportedAlgHeader = 'Invalid or unsupported "alg" (JWE Algorithm) header value';
export function jweAlgorithm(alg) {
const entry = JWE[alg];
if (!entry) {
throw new JOSENotSupported(unsupportedAlgHeader);
}
return entry;
}
export function maybeJWEAlgorithm(alg) {
return JWE[alg];
}
export function jweEncryption(enc) {
const entry = ENC[enc];
if (!entry) {
throw new JOSENotSupported(`Unsupported JWE Algorithm: ${enc}`);
}
return entry;
}
+181
View File
@@ -0,0 +1,181 @@
import { decrypt, generateCek } from './content_encryption.js';
import { decodeBase64url, encodeBase64url, parseJoseHeader } from './helpers.js';
import { JOSEAlgNotAllowed, JOSENotSupported, JWEInvalid } from '../util/errors.js';
import { isDisjoint, isObject } from './type_checks.js';
import { decryptKeyManagement } from './key_management.js';
import { concat, decoder, encode } from './buffer_utils.js';
import { validateCrit, validateAlgorithms, JWE_RECOGNIZED } from './options.js';
import { prepareKey } from './key.js';
import { jweAlgorithm, jweEncryption } from './jwe_algorithms.js';
import { decompress } from './deflate.js';
export function checkShared(jwe) {
if (jwe.iv !== undefined && typeof jwe.iv !== 'string') {
throw new JWEInvalid('JWE Initialization Vector incorrect type');
}
if (typeof jwe.ciphertext !== 'string') {
throw new JWEInvalid('JWE Ciphertext missing or incorrect type');
}
if (jwe.tag !== undefined && typeof jwe.tag !== 'string') {
throw new JWEInvalid('JWE Authentication Tag incorrect type');
}
if (jwe.protected !== undefined && typeof jwe.protected !== 'string') {
throw new JWEInvalid('JWE Protected Header incorrect type');
}
if (jwe.aad !== undefined && typeof jwe.aad !== 'string') {
throw new JWEInvalid('JWE AAD incorrect type');
}
if (jwe.unprotected !== undefined && !isObject(jwe.unprotected)) {
throw new JWEInvalid('JWE Shared Unprotected Header incorrect type');
}
}
export function checkRecipient(jwe) {
if (jwe.encrypted_key !== undefined && typeof jwe.encrypted_key !== 'string') {
throw new JWEInvalid('JWE Encrypted Key incorrect type');
}
if (jwe.header !== undefined && !isObject(jwe.header)) {
throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type');
}
if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) {
throw new JWEInvalid('JOSE Header missing');
}
}
export function shareJWE(jwe) {
let parsedProt;
if (jwe.protected) {
parsedProt = parseJoseHeader(jwe.protected, JWEInvalid, 'JWE Protected Header is invalid');
}
const protectedHeader = jwe.protected !== undefined ? encode(jwe.protected) : new Uint8Array();
return {
parsedProt,
ciphertext: decodeBase64url(jwe.ciphertext, 'ciphertext', JWEInvalid),
iv: jwe.iv !== undefined ? decodeBase64url(jwe.iv, 'iv', JWEInvalid) : undefined,
tag: jwe.tag !== undefined ? decodeBase64url(jwe.tag, 'tag', JWEInvalid) : undefined,
additionalData: jwe.aad !== undefined
? concat(protectedHeader, encode('.'), encodeBase64url(jwe.aad, 'aad', JWEInvalid))
: protectedHeader,
};
}
export function decryptResult(jwe, decrypted) {
const result = { plaintext: decrypted.plaintext };
if (jwe.protected !== undefined) {
result.protectedHeader = decrypted.parsedProt;
}
if (jwe.aad !== undefined) {
result.additionalAuthenticatedData = decodeBase64url(jwe.aad, 'aad', JWEInvalid);
}
if (jwe.unprotected !== undefined) {
result.sharedUnprotectedHeader = jwe.unprotected;
}
if (jwe.header !== undefined) {
result.unprotectedHeader = jwe.header;
}
if (decrypted.resolvedKey) {
return { ...result, key: decrypted.key };
}
return result;
}
export function prepareDecrypt(options) {
return {
keyManagementAlgorithms: options && validateAlgorithms('keyManagementAlgorithms', options.keyManagementAlgorithms),
contentEncryptionAlgorithms: options &&
validateAlgorithms('contentEncryptionAlgorithms', options.contentEncryptionAlgorithms),
options,
};
}
export async function decryptRecipient(jwe, token, shared, key) {
const { options } = shared;
const { parsedProt } = token;
let joseHeader;
if (jwe.header !== undefined || jwe.unprotected !== undefined) {
if (!isDisjoint(parsedProt, jwe.header, jwe.unprotected)) {
throw new JWEInvalid('JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint');
}
joseHeader = { ...parsedProt, ...jwe.header, ...jwe.unprotected };
}
else {
joseHeader = parsedProt ?? {};
}
validateCrit(JWEInvalid, JWE_RECOGNIZED, options?.crit, parsedProt, joseHeader);
if (joseHeader.zip !== undefined && joseHeader.zip !== 'DEF') {
throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value.');
}
if (joseHeader.zip !== undefined && !parsedProt?.zip) {
throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.');
}
const { alg, enc } = joseHeader;
if (typeof alg !== 'string' || !alg) {
throw new JWEInvalid('missing JWE Algorithm (alg) in JWE Header');
}
if (typeof enc !== 'string' || !enc) {
throw new JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header');
}
const { keyManagementAlgorithms, contentEncryptionAlgorithms } = shared;
if ((keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) ||
(!keyManagementAlgorithms && alg.startsWith('PBES2'))) {
throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
}
if (contentEncryptionAlgorithms && !contentEncryptionAlgorithms.has(enc)) {
throw new JOSEAlgNotAllowed('"enc" (Encryption Algorithm) Header Parameter value not allowed');
}
const encEntry = jweEncryption(enc);
let encryptedKey;
if (jwe.encrypted_key !== undefined) {
encryptedKey = decodeBase64url(jwe.encrypted_key, 'encrypted_key', JWEInvalid);
}
let resolvedKey = false;
if (typeof key === 'function') {
key = await key(parsedProt, jwe);
resolvedKey = true;
}
const algEntry = jweAlgorithm(alg);
const k = await prepareKey(alg === 'dir' ? encEntry : algEntry, key, 'decrypt');
let cek;
try {
cek = await decryptKeyManagement(alg, encEntry, k, encryptedKey, joseHeader, options);
}
catch (err) {
if (err instanceof TypeError || err instanceof JWEInvalid || err instanceof JOSENotSupported) {
throw err;
}
cek = generateCek(encEntry);
}
let plaintext = await decrypt(encEntry, cek, token.ciphertext, token.iv, token.tag, token.additionalData);
if (joseHeader.zip === 'DEF') {
const maxDecompressedLength = options?.maxDecompressedLength ?? 250_000;
if (maxDecompressedLength === 0) {
throw new JOSENotSupported('JWE "zip" (Compression Algorithm) Header Parameter is not supported.');
}
if (maxDecompressedLength !== Infinity &&
(!Number.isSafeInteger(maxDecompressedLength) || maxDecompressedLength < 1)) {
throw new TypeError('maxDecompressedLength must be 0, a positive safe integer, or Infinity');
}
plaintext = await decompress(plaintext, maxDecompressedLength).catch((cause) => {
if (cause instanceof JWEInvalid)
throw cause;
throw new JWEInvalid('Failed to decompress plaintext', { cause });
});
}
return { plaintext, parsedProt, key: k, resolvedKey };
}
export async function decryptJWE(jwe, shared, key) {
return decryptRecipient(jwe, shareJWE(jwe), shared, key);
}
export async function decryptCompact(jwe, shared, key) {
if (jwe instanceof Uint8Array) {
jwe = decoder.decode(jwe);
}
if (typeof jwe !== 'string') {
throw new JWEInvalid('Compact JWE must be a string or Uint8Array');
}
const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length, } = jwe.split('.');
if (length !== 5) {
throw new JWEInvalid('Invalid Compact JWE');
}
return decryptJWE({
ciphertext,
iv: iv || undefined,
protected: protectedHeader,
tag: tag || undefined,
encrypted_key: encryptedKey || undefined,
}, shared, key);
}
+109
View File
@@ -0,0 +1,109 @@
import { encode as b64u } from '../util/base64url.js';
import { encrypt } from './content_encryption.js';
import { encryptKeyManagement } from './key_management.js';
import { JOSENotSupported, JWEInvalid } from '../util/errors.js';
import { isDisjoint } from './type_checks.js';
import { concat, encode } from './buffer_utils.js';
import { validateCrit, JWE_RECOGNIZED } from './options.js';
import { prepareKey } from './key.js';
import { jweAlgorithm, jweEncryption } from './jwe_algorithms.js';
import { compress } from './deflate.js';
export function checkEncryptHeaders(input) {
const { protectedHeader, unprotectedHeader, sharedUnprotectedHeader } = input;
if (!isDisjoint(protectedHeader, unprotectedHeader, sharedUnprotectedHeader)) {
throw new JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
}
const joseHeader = {
...protectedHeader,
...unprotectedHeader,
...sharedUnprotectedHeader,
};
validateCrit(JWEInvalid, JWE_RECOGNIZED, input.crit, protectedHeader, joseHeader);
if (joseHeader.zip !== undefined && joseHeader.zip !== 'DEF') {
throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value.');
}
if (joseHeader.zip !== undefined && !protectedHeader?.zip) {
throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.');
}
const { alg, enc } = joseHeader;
if (typeof alg !== 'string' || !alg) {
throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid');
}
if (typeof enc !== 'string' || !enc) {
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid');
}
return { joseHeader, alg, enc, encEntry: jweEncryption(enc) };
}
export async function encryptJWE(input, checked, key) {
const { joseHeader, alg, encEntry } = checked;
let { protectedHeader, unprotectedHeader } = input;
const { sharedUnprotectedHeader } = input;
if (input.cek && (alg === 'dir' || alg === 'ECDH-ES')) {
throw new TypeError(`setContentEncryptionKey cannot be called with JWE "alg" (Algorithm) Header ${alg}`);
}
const algEntry = jweAlgorithm(alg);
const k = await prepareKey(alg === 'dir' ? encEntry : algEntry, key, 'encrypt');
const { cek, encryptedKey, parameters } = await encryptKeyManagement(alg, encEntry, k, input.cek, input.keyManagementParameters);
if (parameters) {
if (input.unprotectedParameters) {
unprotectedHeader = unprotectedHeader ? { ...unprotectedHeader, ...parameters } : parameters;
}
else {
protectedHeader = protectedHeader ? { ...protectedHeader, ...parameters } : parameters;
}
}
let protectedHeaderS;
let protectedHeaderB;
if (protectedHeader) {
protectedHeaderS = b64u(JSON.stringify(protectedHeader));
protectedHeaderB = encode(protectedHeaderS);
}
else {
protectedHeaderS = '';
protectedHeaderB = new Uint8Array();
}
let additionalData;
let aadMember;
if (input.aad?.byteLength) {
aadMember = b64u(input.aad);
additionalData = concat(protectedHeaderB, encode('.'), encode(aadMember));
}
else {
additionalData = protectedHeaderB;
}
let plaintext = input.plaintext;
if (joseHeader.zip === 'DEF') {
plaintext = await compress(plaintext).catch((cause) => {
throw new JWEInvalid('Failed to compress plaintext', { cause });
});
}
const { ciphertext, tag, iv } = await encrypt(encEntry, plaintext, cek, input.iv, additionalData);
const jwe = {
ciphertext: b64u(ciphertext),
};
if (iv) {
jwe.iv = b64u(iv);
}
if (tag) {
jwe.tag = b64u(tag);
}
if (encryptedKey) {
jwe.encrypted_key = b64u(encryptedKey);
}
if (aadMember) {
jwe.aad = aadMember;
}
if (protectedHeader) {
jwe.protected = protectedHeaderS;
}
if (sharedUnprotectedHeader) {
jwe.unprotected = sharedUnprotectedHeader;
}
if (unprotectedHeader) {
jwe.header = unprotectedHeader;
}
return jwe;
}
export async function createJWE(input, key) {
return encryptJWE(input, checkEncryptHeaders(input), key);
}
+22
View File
@@ -0,0 +1,22 @@
import { JOSENotSupported } from '../util/errors.js';
const unsupportedAlg = 'Invalid or unsupported JWK "alg" (Algorithm) Parameter value';
function subtleParams(entry, jwk) {
if (!entry.kty.includes(jwk.kty)) {
throw new JOSENotSupported(unsupportedAlg);
}
return entry.subtleFor?.({ kty: jwk.kty, crv: jwk.crv }) ?? entry.subtle;
}
export async function jwkToKey(entry, jwk) {
if (jwk.kty === 'RSA' && 'oth' in jwk && jwk.oth !== undefined) {
throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');
}
const algorithm = subtleParams(entry, jwk);
const isPrivate = !!(jwk.d || jwk.priv);
const keyUsages = isPrivate ? entry.usages.private : entry.usages.public;
const keyData = { ...jwk };
if (keyData.kty !== 'AKP') {
delete keyData.alg;
}
delete keyData.use;
return crypto.subtle.importKey('jwk', keyData, algorithm, jwk.ext ?? (isPrivate ? false : true), jwk.key_ops ?? keyUsages);
}
+74
View File
@@ -0,0 +1,74 @@
import { JOSENotSupported } from '../util/errors.js';
import { table } from './key_descriptor.js';
const sig = { public: ['verify'], private: ['sign'] };
function hmac(bits) {
const subtle = { name: 'HMAC', hash: `SHA-${bits}` };
return { kty: ['oct'], symmetric: true, subtle, operation: subtle, usages: sig };
}
function rsa(name, bits, saltLength) {
const subtle = { name, hash: `SHA-${bits}` };
return {
kty: ['RSA'],
subtle,
operation: saltLength ? { ...subtle, saltLength } : subtle,
usages: sig,
minModulusLength: 2048,
};
}
function ecdsa(crv, bits) {
return {
kty: ['EC'],
crv,
subtle: { name: 'ECDSA', namedCurve: crv },
operation: { name: 'ECDSA', hash: `SHA-${bits}` },
usages: sig,
};
}
function eddsa() {
const subtle = { name: 'Ed25519' };
return {
kty: ['OKP'],
crv: 'Ed25519',
subtle,
operation: subtle,
usages: sig,
};
}
function mldsa(name) {
const subtle = { name };
return {
kty: ['AKP'],
subtle,
operation: subtle,
usages: sig,
};
}
const JWS = table({
HS256: hmac(256),
HS384: hmac(384),
HS512: hmac(512),
RS256: rsa('RSASSA-PKCS1-v1_5', 256),
RS384: rsa('RSASSA-PKCS1-v1_5', 384),
RS512: rsa('RSASSA-PKCS1-v1_5', 512),
PS256: rsa('RSA-PSS', 256, 32),
PS384: rsa('RSA-PSS', 384, 48),
PS512: rsa('RSA-PSS', 512, 64),
ES256: ecdsa('P-256', 256),
ES384: ecdsa('P-384', 384),
ES512: ecdsa('P-521', 512),
EdDSA: eddsa(),
Ed25519: eddsa(),
'ML-DSA-44': mldsa('ML-DSA-44'),
'ML-DSA-65': mldsa('ML-DSA-65'),
'ML-DSA-87': mldsa('ML-DSA-87'),
});
export function jwsAlgorithm(alg) {
const entry = JWS[alg];
if (!entry) {
throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
}
return entry;
}
export function maybeJWSAlgorithm(alg) {
return JWS[alg];
}
+68
View File
@@ -0,0 +1,68 @@
import { encode as b64u } from '../util/base64url.js';
import { sign } from './signing.js';
import { jwsAlgorithm } from './jws_algorithms.js';
import { isDisjoint } from './type_checks.js';
import { JWSInvalid } from '../util/errors.js';
import { concat, encode } from './buffer_utils.js';
import { validateCrit, validateCritDuplicates, JWS_RECOGNIZED } from './options.js';
import { prepareKey } from './key.js';
export function unencodedPayload(protectedHeader) {
return (protectedHeader?.b64 === false &&
Array.isArray(protectedHeader.crit) &&
protectedHeader.crit.includes('b64'));
}
export async function createSignature(input, key) {
const { protectedHeader, unprotectedHeader } = input;
if (!isDisjoint(protectedHeader, unprotectedHeader)) {
throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');
}
const joseHeader = { ...protectedHeader, ...unprotectedHeader };
validateCritDuplicates(JWSInvalid, protectedHeader);
const extensions = validateCrit(JWSInvalid, JWS_RECOGNIZED, input.crit, protectedHeader, joseHeader);
let b64 = true;
if (extensions.has('b64')) {
b64 = protectedHeader.b64;
if (typeof b64 !== 'boolean') {
throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
}
}
const { alg } = joseHeader;
if (typeof alg !== 'string' || !alg) {
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
}
const entry = jwsAlgorithm(alg);
let payloadS;
let payloadB;
if (b64) {
const encoded = (input.encoded ??= {});
encoded.b64 ??= b64u(input.payload);
encoded.raw ??= encode(encoded.b64);
payloadS = encoded.b64;
payloadB = encoded.raw;
}
else {
payloadB = input.payload;
payloadS = '';
}
let protectedHeaderString;
let protectedHeaderBytes;
if (protectedHeader) {
protectedHeaderString = b64u(JSON.stringify(protectedHeader));
protectedHeaderBytes = encode(protectedHeaderString);
}
else {
protectedHeaderString = '';
protectedHeaderBytes = new Uint8Array();
}
const data = concat(protectedHeaderBytes, encode('.'), payloadB);
const k = await prepareKey(entry, key, 'sign');
const signature = await sign(entry, k, data);
const jws = {
signature: b64u(signature),
payload: payloadS,
};
if (protectedHeader) {
jws.protected = protectedHeaderString;
}
return jws;
}
+108
View File
@@ -0,0 +1,108 @@
import { verify } from './signing.js';
import { jwsAlgorithm } from './jws_algorithms.js';
import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../util/errors.js';
import { concat, decoder, encoder, encode } from './buffer_utils.js';
import { decodeBase64url, encodeBase64url, parseJoseHeader } from './helpers.js';
import { isDisjoint } from './type_checks.js';
import { validateCrit, validateAlgorithms, JWS_RECOGNIZED } from './options.js';
import { prepareKey } from './key.js';
export function verifyResult(jws, verified) {
const result = { payload: verified.payload };
if (jws.protected !== undefined) {
result.protectedHeader = verified.parsedProt;
}
if (jws.header !== undefined) {
result.unprotectedHeader = jws.header;
}
if (verified.resolvedKey) {
return { ...result, key: verified.key };
}
return result;
}
export function prepareVerify(options) {
return {
algorithms: options && validateAlgorithms('algorithms', options.algorithms),
crit: options?.crit,
};
}
export async function verifySignature(jws, shared, key) {
let parsedProt = {};
if (jws.protected) {
parsedProt = parseJoseHeader(jws.protected, JWSInvalid, 'JWS Protected Header is invalid');
}
let joseHeader;
if (jws.header !== undefined) {
if (!isDisjoint(parsedProt, jws.header)) {
throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');
}
joseHeader = { ...parsedProt, ...jws.header };
}
else {
joseHeader = parsedProt;
}
const extensions = validateCrit(JWSInvalid, JWS_RECOGNIZED, shared.crit, parsedProt, joseHeader);
let b64 = true;
if (extensions.has('b64')) {
b64 = parsedProt.b64;
if (typeof b64 !== 'boolean') {
throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
}
}
const { alg } = joseHeader;
if (typeof alg !== 'string' || !alg) {
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
}
if (shared.algorithms && !shared.algorithms.has(alg)) {
throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
}
if (b64) {
if (typeof jws.payload !== 'string') {
throw new JWSInvalid('JWS Payload must be a string');
}
}
else if (typeof jws.payload !== 'string' && !(jws.payload instanceof Uint8Array)) {
throw new JWSInvalid('JWS Payload must be a string or an Uint8Array instance');
}
let resolvedKey = false;
if (typeof key === 'function') {
key = await key(parsedProt, jws);
resolvedKey = true;
}
const entry = jwsAlgorithm(alg);
const data = concat(jws.protected !== undefined ? encode(jws.protected) : new Uint8Array(), encode('.'), typeof jws.payload === 'string'
? b64
?
(shared.b64p ??= encodeBase64url(jws.payload, 'payload', JWSInvalid))
: encoder.encode(jws.payload)
: jws.payload);
const signature = decodeBase64url(jws.signature, 'signature', JWSInvalid);
const k = await prepareKey(entry, key, 'verify');
const verified = await verify(entry, k, signature, data);
if (!verified) {
throw new JWSSignatureVerificationFailed();
}
let payload;
if (b64) {
payload = decodeBase64url(jws.payload, 'payload', JWSInvalid);
}
else if (typeof jws.payload === 'string') {
payload = encoder.encode(jws.payload);
}
else {
payload = jws.payload;
}
return { payload, parsedProt, b64, key: k, resolvedKey };
}
export async function verifyCompact(jws, shared, key) {
if (jws instanceof Uint8Array) {
jws = decoder.decode(jws);
}
if (typeof jws !== 'string') {
throw new JWSInvalid('Compact JWS must be a string or Uint8Array');
}
const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.');
if (length !== 3) {
throw new JWSInvalid('Invalid Compact JWS');
}
return verifySignature({ payload, protected: protectedHeader, signature }, shared, key);
}
+239
View File
@@ -0,0 +1,239 @@
import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.js';
import { encoder, strictDecoder } from './buffer_utils.js';
import { isObject } from './type_checks.js';
const epoch = (date) => Math.floor(date.getTime() / 1000);
const minute = 60;
const hour = minute * 60;
const day = hour * 24;
const week = day * 7;
const year = day * 365.25;
const REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;
export function secs(str) {
const matched = REGEX.exec(str);
if (!matched || (matched[4] && matched[1])) {
throw new TypeError('Invalid time period format');
}
const value = parseFloat(matched[2]);
const unit = matched[3].toLowerCase();
let numericDate;
switch (unit) {
case 'sec':
case 'secs':
case 'second':
case 'seconds':
case 's':
numericDate = Math.round(value);
break;
case 'minute':
case 'minutes':
case 'min':
case 'mins':
case 'm':
numericDate = Math.round(value * minute);
break;
case 'hour':
case 'hours':
case 'hr':
case 'hrs':
case 'h':
numericDate = Math.round(value * hour);
break;
case 'day':
case 'days':
case 'd':
numericDate = Math.round(value * day);
break;
case 'week':
case 'weeks':
case 'w':
numericDate = Math.round(value * week);
break;
default:
numericDate = Math.round(value * year);
break;
}
if (matched[1] === '-' || matched[4] === 'ago') {
return -numericDate;
}
return numericDate;
}
function validateInput(label, input) {
if (!Number.isFinite(input)) {
throw new TypeError(`Invalid ${label} input`);
}
return input;
}
const normalizeTyp = (value) => {
if (value.includes('/')) {
return value.toLowerCase();
}
return `application/${value.toLowerCase()}`;
};
const checkAudiencePresence = (audPayload, audOption) => {
if (typeof audPayload === 'string') {
return audOption.includes(audPayload);
}
if (Array.isArray(audPayload)) {
return audOption.some(Set.prototype.has.bind(new Set(audPayload)));
}
return false;
};
export function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {
let payload;
try {
payload = JSON.parse(strictDecoder.decode(encodedPayload));
}
catch {
}
if (!isObject(payload)) {
throw new JWTInvalid('JWT Claims Set must be a top-level JSON object');
}
const { typ } = options;
if (typ &&
(typeof protectedHeader.typ !== 'string' ||
normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, 'typ', 'check_failed');
}
const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;
const presenceCheck = [...requiredClaims];
if (maxTokenAge !== undefined)
presenceCheck.push('iat');
if (audience !== undefined)
presenceCheck.push('aud');
if (subject !== undefined)
presenceCheck.push('sub');
if (issuer !== undefined)
presenceCheck.push('iss');
for (const claim of new Set(presenceCheck.reverse())) {
if (!(claim in payload)) {
throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, 'missing');
}
}
if (issuer !== undefined &&
!(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {
throw new JWTClaimValidationFailed('unexpected "iss" claim value', payload, 'iss', 'check_failed');
}
if (subject !== undefined && payload.sub !== subject) {
throw new JWTClaimValidationFailed('unexpected "sub" claim value', payload, 'sub', 'check_failed');
}
if (audience !== undefined &&
!checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) {
throw new JWTClaimValidationFailed('unexpected "aud" claim value', payload, 'aud', 'check_failed');
}
let tolerance;
switch (typeof options.clockTolerance) {
case 'string':
tolerance = secs(options.clockTolerance);
break;
case 'number':
tolerance = options.clockTolerance;
break;
case 'undefined':
tolerance = 0;
break;
default:
throw new TypeError('Invalid clockTolerance option type');
}
validateInput('clockTolerance option', tolerance);
const { currentDate } = options;
const now = validateInput('currentDate option', epoch(currentDate || new Date()));
if ((payload.iat !== undefined || maxTokenAge !== undefined) && typeof payload.iat !== 'number') {
throw new JWTClaimValidationFailed('"iat" claim must be a number', payload, 'iat', 'invalid');
}
if (payload.nbf !== undefined) {
if (typeof payload.nbf !== 'number') {
throw new JWTClaimValidationFailed('"nbf" claim must be a number', payload, 'nbf', 'invalid');
}
if (payload.nbf > now + tolerance) {
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, 'nbf', 'check_failed');
}
}
if (payload.exp !== undefined) {
if (typeof payload.exp !== 'number') {
throw new JWTClaimValidationFailed('"exp" claim must be a number', payload, 'exp', 'invalid');
}
if (payload.exp <= now - tolerance) {
throw new JWTExpired('"exp" claim timestamp check failed', payload, 'exp', 'check_failed');
}
}
if (maxTokenAge !== undefined) {
const age = now - payload.iat;
const max = typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge);
if (age - tolerance > max) {
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, 'iat', 'check_failed');
}
if (age < 0 - tolerance) {
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, 'iat', 'check_failed');
}
}
return payload;
}
export class JWTClaimsBuilder {
#payload;
constructor(payload) {
if (!isObject(payload)) {
throw new TypeError('JWT Claims Set MUST be an object');
}
this.#payload = structuredClone(payload);
}
data() {
return encoder.encode(JSON.stringify(this.#payload));
}
get iss() {
return this.#payload.iss;
}
set iss(value) {
this.#payload.iss = value;
}
get sub() {
return this.#payload.sub;
}
set sub(value) {
this.#payload.sub = value;
}
get aud() {
return this.#payload.aud;
}
set aud(value) {
this.#payload.aud = value;
}
set jti(value) {
this.#payload.jti = value;
}
set nbf(value) {
if (typeof value === 'number') {
this.#payload.nbf = validateInput('setNotBefore', value);
}
else if (value instanceof Date) {
this.#payload.nbf = validateInput('setNotBefore', epoch(value));
}
else {
this.#payload.nbf = epoch(new Date()) + secs(value);
}
}
set exp(value) {
if (typeof value === 'number') {
this.#payload.exp = validateInput('setExpirationTime', value);
}
else if (value instanceof Date) {
this.#payload.exp = validateInput('setExpirationTime', epoch(value));
}
else {
this.#payload.exp = epoch(new Date()) + secs(value);
}
}
set iat(value) {
if (value === undefined) {
this.#payload.iat = epoch(new Date());
}
else if (value instanceof Date) {
this.#payload.iat = validateInput('setIssuedAt', epoch(value));
}
else if (typeof value === 'string') {
this.#payload.iat = validateInput('setIssuedAt', epoch(new Date()) + secs(value));
}
else {
this.#payload.iat = validateInput('setIssuedAt', value);
}
}
}
+170
View File
@@ -0,0 +1,170 @@
import { withAlg as invalidKeyInput } from './invalid_key_input.js';
import { isKeyLike, isCryptoKey } from './is_key_like.js';
import * as jwk from './type_checks.js';
import { decode } from '../util/base64url.js';
import { jwkToKey } from './jwk_to_key.js';
const tag = (key) => key[Symbol.toStringTag];
const jwkMatchesOp = (entry, key, usage) => {
const { alg } = entry;
if (key.use !== undefined) {
let expected;
switch (usage) {
case 'sign':
case 'verify':
expected = 'sig';
break;
case 'encrypt':
case 'decrypt':
expected = 'enc';
break;
}
if (key.use !== expected) {
throw new TypeError(`Invalid key for this operation, its "use" must be "${expected}" when present`);
}
}
if (key.alg !== undefined && key.alg !== alg) {
throw new TypeError(`Invalid key for this operation, its "alg" must be "${alg}" when present`);
}
if (Array.isArray(key.key_ops)) {
const expectedKeyOp = usage === 'encrypt' || usage === 'decrypt' ? entry.keyOps?.[usage] : usage;
if (expectedKeyOp && key.key_ops?.includes?.(expectedKeyOp) === false) {
throw new TypeError(`Invalid key for this operation, its "key_ops" must include "${expectedKeyOp}" when present`);
}
}
return true;
};
const symmetricTypeCheck = (entry, key, usage) => {
const { alg } = entry;
if (key instanceof Uint8Array)
return { kind: BYTES, key };
if (jwk.isJWK(key)) {
if (jwk.isSecretJWK(key) && jwkMatchesOp(entry, key, usage))
return { kind: JWK, key };
throw new TypeError(`JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present`);
}
if (!isKeyLike(key)) {
throw new TypeError(invalidKeyInput(alg, key, 'CryptoKey', 'KeyObject', 'JSON Web Key', 'Uint8Array'));
}
if (key.type !== 'secret') {
throw new TypeError(`${tag(key)} instances for symmetric algorithms must be of type "secret"`);
}
return isCryptoKey(key) ? { kind: CRYPTO, key } : { kind: KEYOBJECT, key };
};
const asymmetricTypeCheck = (entry, key, usage) => {
const { alg } = entry;
if (jwk.isJWK(key)) {
switch (usage) {
case 'decrypt':
case 'sign':
if (jwk.isPrivateJWK(key) && jwkMatchesOp(entry, key, usage))
return { kind: JWK, key };
throw new TypeError(`JSON Web Key for this operation must be a private JWK`);
case 'encrypt':
case 'verify':
if (jwk.isPublicJWK(key) && jwkMatchesOp(entry, key, usage))
return { kind: JWK, key };
throw new TypeError(`JSON Web Key for this operation must be a public JWK`);
}
}
if (!isKeyLike(key)) {
throw new TypeError(invalidKeyInput(alg, key, 'CryptoKey', 'KeyObject', 'JSON Web Key'));
}
if (key.type === 'secret') {
throw new TypeError(`${tag(key)} instances for asymmetric algorithms must not be of type "secret"`);
}
if (key.type === 'public') {
switch (usage) {
case 'sign':
throw new TypeError(`${tag(key)} instances for asymmetric algorithm signing must be of type "private"`);
case 'decrypt':
throw new TypeError(`${tag(key)} instances for asymmetric algorithm decryption must be of type "private"`);
}
}
if (key.type === 'private') {
switch (usage) {
case 'verify':
throw new TypeError(`${tag(key)} instances for asymmetric algorithm verifying must be of type "public"`);
case 'encrypt':
throw new TypeError(`${tag(key)} instances for asymmetric algorithm encryption must be of type "public"`);
}
}
return isCryptoKey(key) ? { kind: CRYPTO, key } : { kind: KEYOBJECT, key };
};
const BYTES = Symbol();
const CRYPTO = Symbol();
const KEYOBJECT = Symbol();
const JWK = Symbol();
export function checkKeyType(entry, key, usage) {
return entry.symmetric
? symmetricTypeCheck(entry, key, usage)
: asymmetricTypeCheck(entry, key, usage);
}
let cache;
const nist = {
__proto__: null,
prime256v1: 'P-256',
secp384r1: 'P-384',
secp521r1: 'P-521',
};
function cached(key, alg) {
cache ||= new WeakMap();
return cache.get(key)?.[alg];
}
function store(key, alg, cryptoKey) {
const entry = cache.get(key);
if (entry) {
entry[alg] = cryptoKey;
}
else {
cache.set(key, { [alg]: cryptoKey });
}
return cryptoKey;
}
const handleJWK = async (key, jwk, entry) => {
const hit = cached(key, entry.alg);
if (hit)
return hit;
const cryptoKey = await jwkToKey(entry, { ...jwk, alg: entry.alg });
return store(key, entry.alg, cryptoKey);
};
const handleKeyObject = (keyObject, entry) => {
const hit = cached(keyObject, entry.alg);
if (hit)
return hit;
const isPublic = keyObject.type === 'public';
const usages = isPublic ? entry.usages.public : entry.usages.private;
const { asymmetricKeyType } = keyObject;
const crv = nist[keyObject.asymmetricKeyDetails?.namedCurve];
const params = entry.subtleFor?.({ crv, asymmetricKeyType }) ?? entry.subtle;
return store(keyObject, entry.alg, keyObject.toCryptoKey(params, isPublic, usages));
};
export async function prepareKey(entry, key, usage) {
const tagged = checkKeyType(entry, key, usage);
switch (tagged.kind) {
case BYTES:
case CRYPTO:
return tagged.key;
case JWK: {
if (tagged.key.k) {
return decode(tagged.key.k);
}
if (!Object.isFrozen(tagged.key)) {
const { key_ops } = tagged.key;
if (Array.isArray(key_ops))
Object.freeze(key_ops);
Object.freeze(tagged.key);
}
return handleJWK(tagged.key, tagged.key, entry);
}
case KEYOBJECT: {
const keyObject = tagged.key;
if (keyObject.type === 'secret') {
return keyObject.export();
}
if ('toCryptoKey' in keyObject && typeof keyObject.toCryptoKey === 'function') {
return handleKeyObject(keyObject, entry);
}
return handleJWK(keyObject, keyObject.export({ format: 'jwk' }), entry);
}
}
}
+16
View File
@@ -0,0 +1,16 @@
import { JOSENotSupported } from '../util/errors.js';
import { maybeJWSAlgorithm } from './jws_algorithms.js';
import { maybeJWEAlgorithm } from './jwe_algorithms.js';
function unsupportedAlgorithm() {
return new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
}
export function keyAlgorithm(alg) {
if (typeof alg !== 'string') {
throw unsupportedAlgorithm();
}
const entry = maybeJWSAlgorithm(alg) ?? maybeJWEAlgorithm(alg);
if (!entry) {
throw unsupportedAlgorithm();
}
return entry;
}
+7
View File
@@ -0,0 +1,7 @@
export function table(entries) {
const out = { __proto__: null };
for (const alg of Object.keys(entries)) {
out[alg] = { ...entries[alg], alg };
}
return out;
}
+343
View File
@@ -0,0 +1,343 @@
import { encode as b64u } from '../util/base64url.js';
import { prepareKey } from './key.js';
import { jwkToKey } from './jwk_to_key.js';
import { jweAlgorithm, jweEncryption } from './jwe_algorithms.js';
import { JOSENotSupported, JWEInvalid } from '../util/errors.js';
import { decodeBase64url, digest } from './helpers.js';
import { generateCek, encrypt, decrypt } from './content_encryption.js';
import { isObject } from './type_checks.js';
import { checkCryptoKey, checkUsage } from './crypto_key.js';
import { checkModulusLength } from './signing.js';
import { concat, encode, uint32be } from './buffer_utils.js';
import { assertCryptoKey } from './is_key_like.js';
function checkEcdhCryptoKey(key, usage) {
switch (key.algorithm.name) {
case 'ECDH':
case 'X25519':
break;
default:
throw new TypeError('CryptoKey does not support this operation, its algorithm.name must be ECDH or X25519');
}
checkUsage(key, usage);
}
function checkKeySize(key, alg) {
if (key.algorithm.length !== parseInt(alg.slice(1, 4), 10)) {
throw new TypeError(`Invalid key size for alg: ${alg}`);
}
}
function aeskwCryptoKey(key, alg, usage) {
if (key instanceof Uint8Array) {
return crypto.subtle.importKey('raw', key, 'AES-KW', true, [usage]);
}
checkCryptoKey(key, jweAlgorithm(alg).subtle, usage);
return key;
}
async function aeskwWrap(alg, key, cek) {
const cryptoKey = await aeskwCryptoKey(key, alg, 'wrapKey');
checkKeySize(cryptoKey, alg);
const cryptoKeyCek = await crypto.subtle.importKey('raw', cek, { hash: 'SHA-256', name: 'HMAC' }, true, ['sign']);
return new Uint8Array(await crypto.subtle.wrapKey('raw', cryptoKeyCek, cryptoKey, 'AES-KW'));
}
async function aeskwUnwrap(alg, key, encryptedKey) {
const cryptoKey = await aeskwCryptoKey(key, alg, 'unwrapKey');
checkKeySize(cryptoKey, alg);
const cryptoKeyCek = await crypto.subtle.unwrapKey('raw', encryptedKey, cryptoKey, 'AES-KW', { hash: 'SHA-256', name: 'HMAC' }, true, ['sign']);
return new Uint8Array(await crypto.subtle.exportKey('raw', cryptoKeyCek));
}
async function aesGcmKwWrap(gcm, key, cek, iv) {
const wrapped = await encrypt(gcm, cek, key, iv, new Uint8Array());
return {
encryptedKey: wrapped.ciphertext,
iv: b64u(wrapped.iv),
tag: b64u(wrapped.tag),
};
}
async function aesGcmKwUnwrap(gcm, key, encryptedKey, iv, tag) {
return decrypt(gcm, key, encryptedKey, iv, tag, new Uint8Array());
}
const subtleAlgorithm = (alg) => {
switch (alg) {
case 'RSA-OAEP':
case 'RSA-OAEP-256':
case 'RSA-OAEP-384':
case 'RSA-OAEP-512':
return 'RSA-OAEP';
default:
throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
}
};
async function rsaesEncrypt(alg, key, cek) {
checkCryptoKey(key, jweAlgorithm(alg).subtle, 'encrypt');
checkModulusLength(alg, key);
return new Uint8Array(await crypto.subtle.encrypt(subtleAlgorithm(alg), key, cek));
}
async function rsaesDecrypt(alg, key, encryptedKey) {
checkCryptoKey(key, jweAlgorithm(alg).subtle, 'decrypt');
checkModulusLength(alg, key);
return new Uint8Array(await crypto.subtle.decrypt(subtleAlgorithm(alg), key, encryptedKey));
}
function pbes2CryptoKey(key, alg) {
if (key instanceof Uint8Array) {
return crypto.subtle.importKey('raw', key, 'PBKDF2', false, [
'deriveBits',
]);
}
checkCryptoKey(key, jweAlgorithm(alg).subtle, 'deriveBits');
return key;
}
const concatSalt = (alg, p2sInput) => concat(encode(alg), Uint8Array.of(0x00), p2sInput);
async function deriveKey(p2s, alg, p2c, key) {
if (!(p2s instanceof Uint8Array) || p2s.length < 8) {
throw new JWEInvalid('PBES2 Salt Input must be 8 or more octets');
}
if (!Number.isSafeInteger(p2c) || Math.sign(p2c) !== 1) {
throw new JWEInvalid('PBES2 Count Input must be a positive integer');
}
const salt = concatSalt(alg, p2s);
const keylen = parseInt(alg.slice(13, 16), 10);
const subtleAlg = {
hash: `SHA-${alg.slice(8, 11)}`,
iterations: p2c,
name: 'PBKDF2',
salt,
};
const cryptoKey = await pbes2CryptoKey(key, alg);
return new Uint8Array(await crypto.subtle.deriveBits(subtleAlg, cryptoKey, keylen));
}
async function pbes2kwWrap(alg, key, cek, p2c = 2048, p2s = crypto.getRandomValues(new Uint8Array(16))) {
const derived = await deriveKey(p2s, alg, p2c, key);
const encryptedKey = await aeskwWrap(alg.slice(-6), derived, cek);
return { encryptedKey, p2c, p2s: b64u(p2s) };
}
async function pbes2kwUnwrap(alg, key, encryptedKey, p2c, p2s) {
const derived = await deriveKey(p2s, alg, p2c, key);
return aeskwUnwrap(alg.slice(-6), derived, encryptedKey);
}
function lengthAndInput(input) {
return concat(uint32be(input.length), input);
}
async function concatKdf(Z, L, OtherInfo) {
const dkLen = L >> 3;
const hashLen = 32;
const reps = Math.ceil(dkLen / hashLen);
const dk = new Uint8Array(reps * hashLen);
for (let i = 1; i <= reps; i++) {
const hashInput = new Uint8Array(4 + Z.length + OtherInfo.length);
hashInput.set(uint32be(i), 0);
hashInput.set(Z, 4);
hashInput.set(OtherInfo, 4 + Z.length);
const hashResult = await digest('sha256', hashInput);
dk.set(hashResult, (i - 1) * hashLen);
}
return dk.slice(0, dkLen);
}
async function ecdhesDeriveKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(), apv = new Uint8Array()) {
checkEcdhCryptoKey(publicKey);
checkEcdhCryptoKey(privateKey, 'deriveBits');
const algorithmID = lengthAndInput(encode(algorithm));
const partyUInfo = lengthAndInput(apu);
const partyVInfo = lengthAndInput(apv);
const suppPubInfo = uint32be(keyLength);
const suppPrivInfo = new Uint8Array();
const otherInfo = concat(algorithmID, partyUInfo, partyVInfo, suppPubInfo, suppPrivInfo);
const Z = new Uint8Array(await crypto.subtle.deriveBits({
name: publicKey.algorithm.name,
public: publicKey,
}, privateKey, getEcdhBitLength(publicKey)));
return concatKdf(Z, keyLength, otherInfo);
}
function getEcdhBitLength(publicKey) {
if (publicKey.algorithm.name === 'X25519') {
return 256;
}
return (Math.ceil(parseInt(publicKey.algorithm.namedCurve.slice(-3), 10) / 8) << 3);
}
function ecdhesAllowed(key) {
switch (key.algorithm.namedCurve) {
case 'P-256':
case 'P-384':
case 'P-521':
return true;
default:
return key.algorithm.name === 'X25519';
}
}
const unsupportedAlgHeader = 'Invalid or unsupported "alg" (JWE Algorithm) header value';
function assertEncryptedKey(encryptedKey) {
if (encryptedKey === undefined)
throw new JWEInvalid('JWE Encrypted Key missing');
}
export async function decryptKeyManagement(alg, enc, key, encryptedKey, joseHeader, options) {
switch (alg) {
case 'dir': {
if (encryptedKey !== undefined)
throw new JWEInvalid('Encountered unexpected JWE Encrypted Key');
return key;
}
case 'ECDH-ES':
if (encryptedKey !== undefined)
throw new JWEInvalid('Encountered unexpected JWE Encrypted Key');
case 'ECDH-ES+A128KW':
case 'ECDH-ES+A192KW':
case 'ECDH-ES+A256KW': {
if (!isObject(joseHeader.epk))
throw new JWEInvalid(`JOSE Header "epk" (Ephemeral Public Key) missing or invalid`);
assertCryptoKey(key);
if (!ecdhesAllowed(key))
throw new JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime');
const epk = await jwkToKey(jweAlgorithm(alg), joseHeader.epk);
let partyUInfo;
let partyVInfo;
if (joseHeader.apu !== undefined) {
if (typeof joseHeader.apu !== 'string')
throw new JWEInvalid(`JOSE Header "apu" (Agreement PartyUInfo) invalid`);
partyUInfo = decodeBase64url(joseHeader.apu, 'apu', JWEInvalid);
}
if (joseHeader.apv !== undefined) {
if (typeof joseHeader.apv !== 'string')
throw new JWEInvalid(`JOSE Header "apv" (Agreement PartyVInfo) invalid`);
partyVInfo = decodeBase64url(joseHeader.apv, 'apv', JWEInvalid);
}
const sharedSecret = await ecdhesDeriveKey(epk, key, alg === 'ECDH-ES' ? enc.alg : alg, alg === 'ECDH-ES' ? enc.cekBits : parseInt(alg.slice(-5, -2), 10), partyUInfo, partyVInfo);
if (alg === 'ECDH-ES')
return sharedSecret;
assertEncryptedKey(encryptedKey);
return aeskwUnwrap(alg.slice(-6), sharedSecret, encryptedKey);
}
case 'RSA-OAEP':
case 'RSA-OAEP-256':
case 'RSA-OAEP-384':
case 'RSA-OAEP-512': {
assertEncryptedKey(encryptedKey);
assertCryptoKey(key);
return rsaesDecrypt(alg, key, encryptedKey);
}
case 'PBES2-HS256+A128KW':
case 'PBES2-HS384+A192KW':
case 'PBES2-HS512+A256KW': {
assertEncryptedKey(encryptedKey);
if (typeof joseHeader.p2c !== 'number')
throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`);
const p2cLimit = options?.maxPBES2Count || 10_000;
if (joseHeader.p2c > p2cLimit)
throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`);
if (typeof joseHeader.p2s !== 'string')
throw new JWEInvalid(`JOSE Header "p2s" (PBES2 Salt) missing or invalid`);
let p2s;
p2s = decodeBase64url(joseHeader.p2s, 'p2s', JWEInvalid);
return pbes2kwUnwrap(alg, key, encryptedKey, joseHeader.p2c, p2s);
}
case 'A128KW':
case 'A192KW':
case 'A256KW': {
assertEncryptedKey(encryptedKey);
return aeskwUnwrap(alg, key, encryptedKey);
}
case 'A128GCMKW':
case 'A192GCMKW':
case 'A256GCMKW': {
assertEncryptedKey(encryptedKey);
if (typeof joseHeader.iv !== 'string')
throw new JWEInvalid(`JOSE Header "iv" (Initialization Vector) missing or invalid`);
if (typeof joseHeader.tag !== 'string')
throw new JWEInvalid(`JOSE Header "tag" (Authentication Tag) missing or invalid`);
let iv;
iv = decodeBase64url(joseHeader.iv, 'iv', JWEInvalid);
let tag;
tag = decodeBase64url(joseHeader.tag, 'tag', JWEInvalid);
return aesGcmKwUnwrap(jweEncryption(jweAlgorithm(alg).gcmkw), key, encryptedKey, iv, tag);
}
default: {
throw new JOSENotSupported(unsupportedAlgHeader);
}
}
}
export async function encryptKeyManagement(alg, enc, key, providedCek, providedParameters = {}) {
let encryptedKey;
let parameters;
let cek;
switch (alg) {
case 'dir': {
cek = key;
break;
}
case 'ECDH-ES':
case 'ECDH-ES+A128KW':
case 'ECDH-ES+A192KW':
case 'ECDH-ES+A256KW': {
assertCryptoKey(key);
if (!ecdhesAllowed(key)) {
throw new JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime');
}
const { apu, apv } = providedParameters;
let ephemeralKey;
if (providedParameters.epk) {
ephemeralKey = (await prepareKey(jweAlgorithm(alg), providedParameters.epk, 'decrypt'));
}
else {
ephemeralKey = (await crypto.subtle.generateKey(key.algorithm, true, ['deriveBits'])).privateKey;
}
const subtle = crypto.subtle;
let exportableEpk = ephemeralKey;
if (!exportableEpk.extractable) {
if (typeof subtle.getPublicKey !== 'function') {
throw new TypeError('CryptoKey for "epk" must be extractable');
}
exportableEpk = await subtle.getPublicKey(ephemeralKey, []);
}
const { x, y, crv, kty } = (await subtle.exportKey('jwk', exportableEpk));
const sharedSecret = await ecdhesDeriveKey(key, ephemeralKey, alg === 'ECDH-ES' ? enc.alg : alg, alg === 'ECDH-ES' ? enc.cekBits : parseInt(alg.slice(-5, -2), 10), apu, apv);
parameters = { epk: { x, crv, kty } };
if (kty === 'EC')
parameters.epk.y = y;
if (apu)
parameters.apu = b64u(apu);
if (apv)
parameters.apv = b64u(apv);
if (alg === 'ECDH-ES') {
cek = sharedSecret;
break;
}
cek = providedCek || generateCek(enc);
const kwAlg = alg.slice(-6);
encryptedKey = await aeskwWrap(kwAlg, sharedSecret, cek);
break;
}
case 'RSA-OAEP':
case 'RSA-OAEP-256':
case 'RSA-OAEP-384':
case 'RSA-OAEP-512': {
cek = providedCek || generateCek(enc);
assertCryptoKey(key);
encryptedKey = await rsaesEncrypt(alg, key, cek);
break;
}
case 'PBES2-HS256+A128KW':
case 'PBES2-HS384+A192KW':
case 'PBES2-HS512+A256KW': {
cek = providedCek || generateCek(enc);
const { p2c, p2s } = providedParameters;
({ encryptedKey, ...parameters } = await pbes2kwWrap(alg, key, cek, p2c, p2s));
break;
}
case 'A128KW':
case 'A192KW':
case 'A256KW': {
cek = providedCek || generateCek(enc);
encryptedKey = await aeskwWrap(alg, key, cek);
break;
}
case 'A128GCMKW':
case 'A192GCMKW':
case 'A256GCMKW': {
cek = providedCek || generateCek(enc);
const { iv } = providedParameters;
({ encryptedKey, ...parameters } = await aesGcmKwWrap(jweEncryption(jweAlgorithm(alg).gcmkw), key, cek, iv));
break;
}
default: {
throw new JOSENotSupported(unsupportedAlgHeader);
}
}
return { cek, encryptedKey, parameters };
}
+51
View File
@@ -0,0 +1,51 @@
import { JOSENotSupported, JWEInvalid, JWSInvalid } from '../util/errors.js';
export const JWS_RECOGNIZED = new Map([['b64', true]]);
export const JWE_RECOGNIZED = new Map();
export function validateAlgorithms(option, algorithms) {
if (algorithms !== undefined &&
(!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== 'string'))) {
throw new TypeError(`"${option}" option must be an array of strings`);
}
if (!algorithms) {
return undefined;
}
return new Set(algorithms);
}
export function validateCritDuplicates(Err, protectedHeader) {
const { crit } = protectedHeader ?? {};
if (Array.isArray(crit) && new Set(crit).size !== crit.length) {
throw new Err('"crit" (Critical) Header Parameter MUST NOT contain duplicate values');
}
}
export function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {
if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined) {
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected');
}
if (!protectedHeader || protectedHeader.crit === undefined) {
return new Set();
}
if (!Array.isArray(protectedHeader.crit) ||
protectedHeader.crit.length === 0 ||
protectedHeader.crit.some((input) => typeof input !== 'string' || input.length === 0)) {
throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');
}
let recognized;
if (recognizedOption !== undefined) {
recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]);
}
else {
recognized = recognizedDefault;
}
for (const parameter of protectedHeader.crit) {
if (!recognized.has(parameter)) {
throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`);
}
if (joseHeader[parameter] === undefined) {
throw new Err(`Extension Header Parameter "${parameter}" is missing`);
}
if (recognized.get(parameter) && protectedHeader[parameter] === undefined) {
throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`);
}
}
return new Set(protectedHeader.crit);
}
+36
View File
@@ -0,0 +1,36 @@
import { checkCryptoKey } from './crypto_key.js';
export function checkModulusLength(alg, key) {
const { modulusLength } = key.algorithm;
if (typeof modulusLength !== 'number' || modulusLength < 2048) {
throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
}
}
function checkSigCryptoKey(entry, key, usage) {
checkCryptoKey(key, entry.subtle, usage);
if (entry.minModulusLength) {
checkModulusLength(entry.alg, key);
}
}
async function getSigKey(entry, key, usage) {
if (key instanceof Uint8Array) {
return crypto.subtle.importKey('raw', key, entry.subtle, false, [
usage,
]);
}
checkSigCryptoKey(entry, key, usage);
return key;
}
export async function sign(entry, key, data) {
const cryptoKey = await getSigKey(entry, key, 'sign');
const signature = await crypto.subtle.sign(entry.operation, cryptoKey, data);
return new Uint8Array(signature);
}
export async function verify(entry, key, signature, data) {
const cryptoKey = await getSigKey(entry, key, 'verify');
try {
return await crypto.subtle.verify(entry.operation, cryptoKey, signature, data);
}
catch {
return false;
}
}
+40
View File
@@ -0,0 +1,40 @@
const isObjectLike = (value) => typeof value === 'object' && value !== null;
export function isObject(input) {
if (!isObjectLike(input) || Object.prototype.toString.call(input) !== '[object Object]') {
return false;
}
if (Object.getPrototypeOf(input) === null) {
return true;
}
let proto = input;
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto);
}
return Object.getPrototypeOf(input) === proto;
}
export function isDisjoint(...headers) {
const sources = headers.filter(Boolean);
if (sources.length === 0 || sources.length === 1) {
return true;
}
let acc;
for (const header of sources) {
const parameters = Object.keys(header);
if (!acc || acc.size === 0) {
acc = new Set(parameters);
continue;
}
for (const parameter of parameters) {
if (acc.has(parameter)) {
return false;
}
acc.add(parameter);
}
}
return true;
}
export const isJWK = (key) => isObject(key) && typeof key.kty === 'string';
export const isPrivateJWK = (key) => key.kty !== 'oct' &&
((key.kty === 'AKP' && typeof key.priv === 'string') || typeof key.d === 'string');
export const isPublicJWK = (key) => key.kty !== 'oct' && key.d === undefined && key.priv === undefined;
export const isSecretJWK = (key) => key.kty === 'oct' && typeof key.k === 'string';
+38
View File
@@ -0,0 +1,38 @@
import { encoder, decoder } from '../lib/buffer_utils.js';
import { encodeBase64, decodeBase64 } from '../lib/base64.js';
export function decode(input) {
if (Uint8Array.fromBase64) {
try {
return Uint8Array.fromBase64(typeof input === 'string' ? input : decoder.decode(input), {
alphabet: 'base64url',
});
}
catch (cause) {
throw new TypeError('The input to be decoded is not correctly encoded.', { cause });
}
}
let encoded = input;
if (encoded instanceof Uint8Array) {
encoded = decoder.decode(encoded);
}
if (encoded.includes('+') || encoded.includes('/')) {
throw new TypeError('The input to be decoded is not correctly encoded.');
}
encoded = encoded.replace(/-/g, '+').replace(/_/g, '/');
try {
return decodeBase64(encoded);
}
catch {
throw new TypeError('The input to be decoded is not correctly encoded.');
}
}
export function encode(input) {
let unencoded = input;
if (typeof unencoded === 'string') {
unencoded = encoder.encode(unencoded);
}
if (Uint8Array.prototype.toBase64) {
return unencoded.toBase64({ alphabet: 'base64url', omitPadding: true });
}
return encodeBase64(unencoded).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
}
+32
View File
@@ -0,0 +1,32 @@
import { decode as b64u } from './base64url.js';
import { strictDecoder } from '../lib/buffer_utils.js';
import { isObject } from '../lib/type_checks.js';
import { JWTInvalid } from './errors.js';
export function decodeJwt(jwt) {
if (typeof jwt !== 'string')
throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string');
const { 1: payload, length } = jwt.split('.');
if (length === 5)
throw new JWTInvalid('Only JWTs using Compact JWS serialization can be decoded');
if (length !== 3)
throw new JWTInvalid('Invalid JWT');
if (!payload)
throw new JWTInvalid('JWTs must contain a payload');
let decoded;
try {
decoded = b64u(payload);
}
catch {
throw new JWTInvalid('Failed to base64url decode the payload');
}
let result;
try {
result = JSON.parse(strictDecoder.decode(decoded));
}
catch {
throw new JWTInvalid('Failed to parse the decoded payload as JSON');
}
if (!isObject(result))
throw new JWTInvalid('Invalid JWT Claims Set');
return result;
}
+24
View File
@@ -0,0 +1,24 @@
import { parseJoseHeader } from '../lib/helpers.js';
export function decodeProtectedHeader(token) {
let protectedB64u;
if (typeof token === 'string') {
const parts = token.split('.');
if (parts.length === 3 || parts.length === 5) {
;
[protectedB64u] = parts;
}
}
else if (typeof token === 'object' && token) {
if ('protected' in token) {
protectedB64u = token.protected;
}
else {
throw new TypeError('Token does not contain a Protected Header');
}
}
const invalid = 'Invalid Token or Protected Header formatting';
if (typeof protectedB64u !== 'string' || !protectedB64u) {
throw new TypeError(invalid);
}
return parseJoseHeader(protectedB64u, TypeError, invalid);
}
+99
View File
@@ -0,0 +1,99 @@
export class JOSEError extends Error {
static code = 'ERR_JOSE_GENERIC';
code = 'ERR_JOSE_GENERIC';
constructor(message, options) {
super(message, options);
this.name = this.constructor.name;
Error.captureStackTrace?.(this, this.constructor);
}
}
export class JWTClaimValidationFailed extends JOSEError {
static code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';
code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';
claim;
reason;
payload;
constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {
super(message, { cause: { claim, reason, payload } });
this.claim = claim;
this.reason = reason;
this.payload = payload;
}
}
export class JWTExpired extends JOSEError {
static code = 'ERR_JWT_EXPIRED';
code = 'ERR_JWT_EXPIRED';
claim;
reason;
payload;
constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {
super(message, { cause: { claim, reason, payload } });
this.claim = claim;
this.reason = reason;
this.payload = payload;
}
}
export class JOSEAlgNotAllowed extends JOSEError {
static code = 'ERR_JOSE_ALG_NOT_ALLOWED';
code = 'ERR_JOSE_ALG_NOT_ALLOWED';
}
export class JOSENotSupported extends JOSEError {
static code = 'ERR_JOSE_NOT_SUPPORTED';
code = 'ERR_JOSE_NOT_SUPPORTED';
}
export class JWEDecryptionFailed extends JOSEError {
static code = 'ERR_JWE_DECRYPTION_FAILED';
code = 'ERR_JWE_DECRYPTION_FAILED';
constructor(message = 'decryption operation failed', options) {
super(message, options);
}
}
export class JWEInvalid extends JOSEError {
static code = 'ERR_JWE_INVALID';
code = 'ERR_JWE_INVALID';
}
export class JWSInvalid extends JOSEError {
static code = 'ERR_JWS_INVALID';
code = 'ERR_JWS_INVALID';
}
export class JWTInvalid extends JOSEError {
static code = 'ERR_JWT_INVALID';
code = 'ERR_JWT_INVALID';
}
export class JWKInvalid extends JOSEError {
static code = 'ERR_JWK_INVALID';
code = 'ERR_JWK_INVALID';
}
export class JWKSInvalid extends JOSEError {
static code = 'ERR_JWKS_INVALID';
code = 'ERR_JWKS_INVALID';
}
export class JWKSNoMatchingKey extends JOSEError {
static code = 'ERR_JWKS_NO_MATCHING_KEY';
code = 'ERR_JWKS_NO_MATCHING_KEY';
constructor(message = 'no applicable key found in the JSON Web Key Set', options) {
super(message, options);
}
}
export class JWKSMultipleMatchingKeys extends JOSEError {
[Symbol.asyncIterator] = async function* () { };
static code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
constructor(message = 'multiple matching keys found in the JSON Web Key Set', options) {
super(message, options);
}
}
export class JWKSTimeout extends JOSEError {
static code = 'ERR_JWKS_TIMEOUT';
code = 'ERR_JWKS_TIMEOUT';
constructor(message = 'request timed out', options) {
super(message, options);
}
}
export class JWSSignatureVerificationFailed extends JOSEError {
static code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';
code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';
constructor(message = 'signature verification failed', options) {
super(message, options);
}
}