init project
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Filip Skokan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
# jose
|
||||
|
||||
`jose` is a JavaScript module for JSON Object Signing and Encryption, providing support for JSON Web Tokens (JWT), JSON Web Signature (JWS), JSON Web Encryption (JWE), JSON Web Key (JWK), JSON Web Key Set (JWKS), and more. The module is designed to work across various Web-interoperable runtimes including Node.js, browsers, Cloudflare Workers, Deno, Bun, and others.
|
||||
|
||||
## Sponsor
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/panva/jose/HEAD/sponsor/Auth0byOkta_dark.png">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/panva/jose/HEAD/sponsor/Auth0byOkta_light.png">
|
||||
<img height="65" align="left" alt="Auth0 by Okta" src="https://raw.githubusercontent.com/panva/jose/HEAD/sponsor/Auth0byOkta_light.png">
|
||||
</picture>
|
||||
|
||||
If you want to quickly add JWT authentication to JavaScript apps, feel free to check out Auth0's JavaScript SDK and free plan. [Create an Auth0 account; it's free!][sponsor-auth0]<br><br>
|
||||
|
||||
## [💗 Help the project](https://github.com/sponsors/panva)
|
||||
|
||||
Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva).
|
||||
|
||||
## Dependencies: 0
|
||||
|
||||
`jose` has no dependencies and it exports tree-shakeable ESM[^cjs].
|
||||
|
||||
## Documentation
|
||||
|
||||
`jose` is distributed via [npmjs.com](https://www.npmjs.com/package/jose), [jsr.io](https://jsr.io/@panva/jose), [jsdelivr.com](https://www.jsdelivr.com/package/npm/jose), and [github.com](https://github.com/panva/jose).
|
||||
|
||||
**`example`** ESM import[^cjs]
|
||||
|
||||
```js
|
||||
import * as jose from 'jose'
|
||||
```
|
||||
|
||||
### JSON Web Tokens (JWT)
|
||||
|
||||
The `jose` module supports JSON Web Tokens (JWT) and provides functionality for signing and verifying tokens, as well as their JWT Claims Set validation.
|
||||
|
||||
- [JWT Claims Set Validation & Signature Verification](docs/jwt/verify/functions/jwtVerify.md) using the `jwtVerify` function
|
||||
- [Using a remote JSON Web Key Set (JWKS)](docs/jwks/remote/functions/createRemoteJWKSet.md)
|
||||
- [Using a local JSON Web Key Set (JWKS)](docs/jwks/local/functions/createLocalJWKSet.md)
|
||||
- [Signing](docs/jwt/sign/classes/SignJWT.md) using the `SignJWT` class
|
||||
- Utility functions
|
||||
- [Decoding Token's Protected Header](docs/util/decode_protected_header/functions/decodeProtectedHeader.md)
|
||||
- [Decoding JWT Claims Set](docs/util/decode_jwt/functions/decodeJwt.md) prior to its validation
|
||||
|
||||
### Encrypted JSON Web Tokens
|
||||
|
||||
The `jose` module supports encrypted JSON Web Tokens and provides functionality for encrypting and decrypting tokens, as well as their JWT Claims Set validation.
|
||||
|
||||
- [Decryption & JWT Claims Set Validation](docs/jwt/decrypt/functions/jwtDecrypt.md) using the `jwtDecrypt` function
|
||||
- [Encryption](docs/jwt/encrypt/classes/EncryptJWT.md) using the `EncryptJWT` class
|
||||
- Utility functions
|
||||
- [Decoding Token's Protected Header](docs/util/decode_protected_header/functions/decodeProtectedHeader.md)
|
||||
|
||||
### Key Utilities
|
||||
|
||||
The `jose` module supports importing, exporting, and generating keys and secrets in various formats, including PEM formats like SPKI, X.509 certificate, and PKCS #8, as well as JSON Web Key (JWK).
|
||||
|
||||
- Key Import Functions
|
||||
- [JWK Import](docs/key/import/functions/importJWK.md)
|
||||
- [Public Key Import (SPKI)](docs/key/import/functions/importSPKI.md)
|
||||
- [Public Key Import (X.509 Certificate)](docs/key/import/functions/importX509.md)
|
||||
- [Private Key Import (PKCS #8)](docs/key/import/functions/importPKCS8.md)
|
||||
- Key and Secret Generation Functions
|
||||
- [Asymmetric Key Pair Generation](docs/key/generate_key_pair/functions/generateKeyPair.md)
|
||||
- [Symmetric Secret Generation](docs/key/generate_secret/functions/generateSecret.md)
|
||||
- Key Export Functions
|
||||
- [JWK Export](docs/key/export/functions/exportJWK.md)
|
||||
- [Private Key Export](docs/key/export/functions/exportPKCS8.md)
|
||||
- [Public Key Export](docs/key/export/functions/exportSPKI.md)
|
||||
|
||||
### JSON Web Signature (JWS)
|
||||
|
||||
The `jose` module supports signing and verification of JWS messages with arbitrary payloads in Compact, Flattened JSON, and General JSON serialization syntaxes.
|
||||
|
||||
- Signing - [Compact](docs/jws/compact/sign/classes/CompactSign.md), [Flattened JSON](docs/jws/flattened/sign/classes/FlattenedSign.md), [General JSON](docs/jws/general/sign/classes/GeneralSign.md)
|
||||
- Verification - [Compact](docs/jws/compact/verify/functions/compactVerify.md), [Flattened JSON](docs/jws/flattened/verify/functions/flattenedVerify.md), [General JSON](docs/jws/general/verify/functions/generalVerify.md)
|
||||
- [Using a remote JSON Web Key Set (JWKS)](docs/jwks/remote/functions/createRemoteJWKSet.md)
|
||||
- [Using a local JSON Web Key Set (JWKS)](docs/jwks/local/functions/createLocalJWKSet.md)
|
||||
- Utility functions
|
||||
- [Decoding Token's Protected Header](docs/util/decode_protected_header/functions/decodeProtectedHeader.md)
|
||||
|
||||
### JSON Web Encryption (JWE)
|
||||
|
||||
The `jose` module supports encryption and decryption of JWE messages with arbitrary plaintext in Compact, Flattened JSON, and General JSON serialization syntaxes.
|
||||
|
||||
- Encryption - [Compact](docs/jwe/compact/encrypt/classes/CompactEncrypt.md), [Flattened JSON](docs/jwe/flattened/encrypt/classes/FlattenedEncrypt.md), [General JSON](docs/jwe/general/encrypt/classes/GeneralEncrypt.md)
|
||||
- Decryption - [Compact](docs/jwe/compact/decrypt/functions/compactDecrypt.md), [Flattened JSON](docs/jwe/flattened/decrypt/functions/flattenedDecrypt.md), [General JSON](docs/jwe/general/decrypt/functions/generalDecrypt.md)
|
||||
- Utility functions
|
||||
- [Decoding Token's Protected Header](docs/util/decode_protected_header/functions/decodeProtectedHeader.md)
|
||||
|
||||
### Other
|
||||
|
||||
The following are additional features and utilities provided by the `jose` module:
|
||||
|
||||
- [Calculating JWK Thumbprint](docs/jwk/thumbprint/functions/calculateJwkThumbprint.md)
|
||||
- [Calculating JWK Thumbprint URI](docs/jwk/thumbprint/functions/calculateJwkThumbprintUri.md)
|
||||
- [Verification using a JWK Embedded in a JWS Header](docs/jwk/embedded/functions/EmbeddedJWK.md)
|
||||
- [Unsecured JWT](docs/jwt/unsecured/classes/UnsecuredJWT.md)
|
||||
- [JOSE Errors](docs/util/errors/README.md)
|
||||
|
||||
## Supported Runtimes
|
||||
|
||||
The `jose` module is compatible with JavaScript runtimes that support the utilized Web API globals and standard built-in objects or are Node.js.
|
||||
|
||||
The following runtimes are supported _(this is not an exhaustive list)_:
|
||||
|
||||
- [Bun](https://github.com/panva/jose/issues/471)
|
||||
- [Browsers](https://github.com/panva/jose/issues/263)
|
||||
- [Cloudflare Workers](https://github.com/panva/jose/issues/265)
|
||||
- [Deno](https://github.com/panva/jose/issues/266)
|
||||
- [Electron](https://github.com/panva/jose/issues/264)
|
||||
- [Node.js](https://github.com/panva/jose/issues/262)
|
||||
|
||||
Please note that certain algorithms may not be available depending on the runtime used. You can find a list of available algorithms for each runtime in the specific issue links provided above.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Security Fixes 🔑 | Other Bug Fixes 🐞 | New Features ⭐ | Runtime and Module type |
|
||||
| ----------------------------------------------- | ----------------- | ------------------ | --------------- | ------------------------------- |
|
||||
| [v6.x](https://github.com/panva/jose/tree/v6.x) | [Security Policy] | ✅ | ✅ | Universal[^universal] ESM[^cjs] |
|
||||
|
||||
## Specifications
|
||||
|
||||
<details>
|
||||
<summary>Details</summary>
|
||||
|
||||
- JSON Web Signature (JWS) - [RFC7515](https://www.rfc-editor.org/info/rfc7515/)
|
||||
- JSON Web Encryption (JWE) - [RFC7516](https://www.rfc-editor.org/info/rfc7516/)
|
||||
- JSON Web Key (JWK) - [RFC7517](https://www.rfc-editor.org/info/rfc7517/)
|
||||
- JSON Web Algorithms (JWA) - [RFC7518](https://www.rfc-editor.org/info/rfc7518/)
|
||||
- JSON Web Token (JWT) - [RFC7519](https://www.rfc-editor.org/info/rfc7519/)
|
||||
- JSON Web Key Thumbprint - [RFC7638](https://www.rfc-editor.org/info/rfc7638/)
|
||||
- JSON Web Key Thumbprint URI - [RFC9278](https://www.rfc-editor.org/info/rfc9278/)
|
||||
- JWS Unencoded Payload Option - [RFC7797](https://www.rfc-editor.org/info/rfc7797/)
|
||||
- CFRG Elliptic Curve ECDH and Signatures - [RFC8037](https://www.rfc-editor.org/info/rfc8037/)
|
||||
- Fully-Specified Algorithms for JOSE - [RFC9864](https://www.rfc-editor.org/info/rfc9864/)
|
||||
- ML-DSA for JOSE - [RFC9964](https://www.rfc-editor.org/info/rfc9964/)
|
||||
|
||||
The algorithm implementations in `jose` have been tested using test vectors from their respective specifications as well as [RFC7520](https://www.rfc-editor.org/info/rfc7520/).
|
||||
|
||||
</details>
|
||||
|
||||
[sponsor-auth0]: https://a0.to/signup/panva
|
||||
[WebCryptoAPI]: https://w3c.github.io/webcrypto/
|
||||
[Fetch API]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
|
||||
[Security Policy]: https://github.com/panva/jose/security/policy
|
||||
|
||||
[^cjs]: CJS style `let jose = require('jose')` is possible in Node.js versions where the `require(esm)` feature is enabled by default (^20.19.0 || ^22.12.0 || >= 23.0.0).
|
||||
|
||||
[^universal]: Assumes runtime support of [WebCryptoAPI][] and [Fetch API][]
|
||||
+56
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
{
|
||||
"name": "jose",
|
||||
"version": "6.2.5",
|
||||
"description": "JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes",
|
||||
"keywords": [
|
||||
"akp",
|
||||
"browser",
|
||||
"bun",
|
||||
"cloudflare",
|
||||
"compact",
|
||||
"decode",
|
||||
"decrypt",
|
||||
"deno",
|
||||
"detached",
|
||||
"ec",
|
||||
"ecdsa",
|
||||
"ed25519",
|
||||
"eddsa",
|
||||
"edge",
|
||||
"electron",
|
||||
"embedded",
|
||||
"encrypt",
|
||||
"flattened",
|
||||
"general",
|
||||
"jose",
|
||||
"json web token",
|
||||
"jsonwebtoken",
|
||||
"jwa",
|
||||
"jwe",
|
||||
"jwk",
|
||||
"jwks",
|
||||
"jws",
|
||||
"jwt-decode",
|
||||
"jwt",
|
||||
"ml-dsa",
|
||||
"netlify",
|
||||
"next",
|
||||
"nextjs",
|
||||
"oct",
|
||||
"okp",
|
||||
"payload",
|
||||
"pem",
|
||||
"pkcs8",
|
||||
"rsa",
|
||||
"sign",
|
||||
"signature",
|
||||
"spki",
|
||||
"validate",
|
||||
"vercel",
|
||||
"verify",
|
||||
"webcrypto",
|
||||
"workerd",
|
||||
"workers",
|
||||
"x509"
|
||||
],
|
||||
"homepage": "https://github.com/panva/jose",
|
||||
"repository": "panva/jose",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Filip Skokan <panva.ip@gmail.com>",
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"default": "./dist/webapi/index.js"
|
||||
},
|
||||
"./jwk/embedded": {
|
||||
"types": "./dist/types/jwk/embedded.d.ts",
|
||||
"default": "./dist/webapi/jwk/embedded.js"
|
||||
},
|
||||
"./jwk/thumbprint": {
|
||||
"types": "./dist/types/jwk/thumbprint.d.ts",
|
||||
"default": "./dist/webapi/jwk/thumbprint.js"
|
||||
},
|
||||
"./key/import": {
|
||||
"types": "./dist/types/key/import.d.ts",
|
||||
"default": "./dist/webapi/key/import.js"
|
||||
},
|
||||
"./key/export": {
|
||||
"types": "./dist/types/key/export.d.ts",
|
||||
"default": "./dist/webapi/key/export.js"
|
||||
},
|
||||
"./key/generate/keypair": {
|
||||
"types": "./dist/types/key/generate_key_pair.d.ts",
|
||||
"default": "./dist/webapi/key/generate_key_pair.js"
|
||||
},
|
||||
"./key/generate/secret": {
|
||||
"types": "./dist/types/key/generate_secret.d.ts",
|
||||
"default": "./dist/webapi/key/generate_secret.js"
|
||||
},
|
||||
"./jwks/remote": {
|
||||
"types": "./dist/types/jwks/remote.d.ts",
|
||||
"default": "./dist/webapi/jwks/remote.js"
|
||||
},
|
||||
"./jwks/local": {
|
||||
"types": "./dist/types/jwks/local.d.ts",
|
||||
"default": "./dist/webapi/jwks/local.js"
|
||||
},
|
||||
"./jwt/sign": {
|
||||
"types": "./dist/types/jwt/sign.d.ts",
|
||||
"default": "./dist/webapi/jwt/sign.js"
|
||||
},
|
||||
"./jwt/verify": {
|
||||
"types": "./dist/types/jwt/verify.d.ts",
|
||||
"default": "./dist/webapi/jwt/verify.js"
|
||||
},
|
||||
"./jwt/encrypt": {
|
||||
"types": "./dist/types/jwt/encrypt.d.ts",
|
||||
"default": "./dist/webapi/jwt/encrypt.js"
|
||||
},
|
||||
"./jwt/decrypt": {
|
||||
"types": "./dist/types/jwt/decrypt.d.ts",
|
||||
"default": "./dist/webapi/jwt/decrypt.js"
|
||||
},
|
||||
"./jwt/unsecured": {
|
||||
"types": "./dist/types/jwt/unsecured.d.ts",
|
||||
"default": "./dist/webapi/jwt/unsecured.js"
|
||||
},
|
||||
"./jwt/decode": {
|
||||
"types": "./dist/types/util/decode_jwt.d.ts",
|
||||
"default": "./dist/webapi/util/decode_jwt.js"
|
||||
},
|
||||
"./decode/protected_header": {
|
||||
"types": "./dist/types/util/decode_protected_header.d.ts",
|
||||
"default": "./dist/webapi/util/decode_protected_header.js"
|
||||
},
|
||||
"./jws/compact/sign": {
|
||||
"types": "./dist/types/jws/compact/sign.d.ts",
|
||||
"default": "./dist/webapi/jws/compact/sign.js"
|
||||
},
|
||||
"./jws/compact/verify": {
|
||||
"types": "./dist/types/jws/compact/verify.d.ts",
|
||||
"default": "./dist/webapi/jws/compact/verify.js"
|
||||
},
|
||||
"./jws/flattened/sign": {
|
||||
"types": "./dist/types/jws/flattened/sign.d.ts",
|
||||
"default": "./dist/webapi/jws/flattened/sign.js"
|
||||
},
|
||||
"./jws/flattened/verify": {
|
||||
"types": "./dist/types/jws/flattened/verify.d.ts",
|
||||
"default": "./dist/webapi/jws/flattened/verify.js"
|
||||
},
|
||||
"./jws/general/sign": {
|
||||
"types": "./dist/types/jws/general/sign.d.ts",
|
||||
"default": "./dist/webapi/jws/general/sign.js"
|
||||
},
|
||||
"./jws/general/verify": {
|
||||
"types": "./dist/types/jws/general/verify.d.ts",
|
||||
"default": "./dist/webapi/jws/general/verify.js"
|
||||
},
|
||||
"./jwe/compact/encrypt": {
|
||||
"types": "./dist/types/jwe/compact/encrypt.d.ts",
|
||||
"default": "./dist/webapi/jwe/compact/encrypt.js"
|
||||
},
|
||||
"./jwe/compact/decrypt": {
|
||||
"types": "./dist/types/jwe/compact/decrypt.d.ts",
|
||||
"default": "./dist/webapi/jwe/compact/decrypt.js"
|
||||
},
|
||||
"./jwe/flattened/encrypt": {
|
||||
"types": "./dist/types/jwe/flattened/encrypt.d.ts",
|
||||
"default": "./dist/webapi/jwe/flattened/encrypt.js"
|
||||
},
|
||||
"./jwe/flattened/decrypt": {
|
||||
"types": "./dist/types/jwe/flattened/decrypt.d.ts",
|
||||
"default": "./dist/webapi/jwe/flattened/decrypt.js"
|
||||
},
|
||||
"./jwe/general/encrypt": {
|
||||
"types": "./dist/types/jwe/general/encrypt.d.ts",
|
||||
"default": "./dist/webapi/jwe/general/encrypt.js"
|
||||
},
|
||||
"./jwe/general/decrypt": {
|
||||
"types": "./dist/types/jwe/general/decrypt.d.ts",
|
||||
"default": "./dist/webapi/jwe/general/decrypt.js"
|
||||
},
|
||||
"./errors": {
|
||||
"types": "./dist/types/util/errors.d.ts",
|
||||
"default": "./dist/webapi/util/errors.js"
|
||||
},
|
||||
"./base64url": {
|
||||
"types": "./dist/types/util/base64url.d.ts",
|
||||
"default": "./dist/webapi/util/base64url.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "./dist/webapi/index.js",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"files": [
|
||||
"dist/webapi/**/*.js",
|
||||
"dist/types/**/*.d.ts",
|
||||
"!dist/**/*.bundle.js",
|
||||
"!dist/**/*.umd.js",
|
||||
"!dist/**/*.min.js",
|
||||
"!dist/types/lib/**"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user