mirror of
https://github.com/hanzoai/jose.git
synced 2026-09-24 17:00:01 +00:00
refactor: restructure src/lib and src/runtime now that runtime is fixed
BREAKING CHANGE: createRemoteJWKSet now uses fetch, because of that its Node.js only options.agent property has been removed and new fetch-related options were added
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@ echo $(electron -i <<< 'process.exit(0)' 2> /dev/null | grep "Using" | awk '{$1=
|
||||
|
||||
electron -i <<< 'process.exit(parseInt(process.versions.node, 10))' &> /dev/null
|
||||
NODE_VERSION=$?
|
||||
export NODE_OPTIONS='--no-warnings --enable-source-maps'
|
||||
export NODE_OPTIONS='--no-warnings --enable-source-maps --import=tsx/esm'
|
||||
|
||||
if [[ $NODE_VERSION -eq 18 ]]; then
|
||||
export NODE_OPTIONS+=' --experimental-global-webcrypto'
|
||||
|
||||
@@ -13,30 +13,6 @@ jobs:
|
||||
build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
|
||||
typings:
|
||||
needs:
|
||||
- build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup node
|
||||
id: node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'npm'
|
||||
- run: npm clean-install
|
||||
- name: Load cached dist
|
||||
uses: actions/cache@v4
|
||||
id: dist
|
||||
with:
|
||||
path: dist
|
||||
key: ${{ needs.build.outputs.cache-key }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Check TypeScript type definitions
|
||||
run: npm run test:types
|
||||
|
||||
node-versions:
|
||||
uses: panva/.github/.github/workflows/node-versions.yml@main
|
||||
with:
|
||||
@@ -45,7 +21,6 @@ jobs:
|
||||
node:
|
||||
needs:
|
||||
- node-versions
|
||||
- build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -66,19 +41,10 @@ jobs:
|
||||
cache: 'npm'
|
||||
check-latest: true
|
||||
- run: npm clean-install
|
||||
- name: Load cached dist
|
||||
uses: actions/cache@v4
|
||||
id: dist
|
||||
with:
|
||||
path: dist
|
||||
key: ${{ needs.build.outputs.cache-key }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Run Test Suite
|
||||
run: npm run ${{ matrix.suite }}
|
||||
|
||||
deno:
|
||||
needs:
|
||||
- build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -86,27 +52,20 @@ jobs:
|
||||
- uses: denoland/setup-deno@v1
|
||||
with:
|
||||
deno-version: latest
|
||||
- name: Load cached dist
|
||||
uses: actions/cache@v4
|
||||
id: dist
|
||||
with:
|
||||
path: dist
|
||||
key: ${{ needs.build.outputs.cache-key }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Test Deno Definitions
|
||||
run: deno check dist/deno/index.ts
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'npm'
|
||||
- run: npm clean-install
|
||||
- name: Test Deno Definitions
|
||||
run: |
|
||||
npm run build:deno
|
||||
deno check dist/deno/index.ts
|
||||
- name: Test Deno
|
||||
run: npm run tap:deno
|
||||
|
||||
bun:
|
||||
needs:
|
||||
- build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -114,13 +73,6 @@ jobs:
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: latest
|
||||
- name: Load cached dist
|
||||
uses: actions/cache@v4
|
||||
id: dist
|
||||
with:
|
||||
path: dist
|
||||
key: ${{ needs.build.outputs.cache-key }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -131,8 +83,6 @@ jobs:
|
||||
run: npm run tap:bun
|
||||
|
||||
workerd:
|
||||
needs:
|
||||
- build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -144,19 +94,10 @@ jobs:
|
||||
cache: 'npm'
|
||||
- run: npm clean-install
|
||||
- run: npm install --global workerd
|
||||
- name: Load cached dist
|
||||
uses: actions/cache@v4
|
||||
id: dist
|
||||
with:
|
||||
path: dist
|
||||
key: ${{ needs.build.outputs.cache-key }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Run Test Suite
|
||||
run: npm run tap:workerd
|
||||
|
||||
edge-runtime:
|
||||
needs:
|
||||
- build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -167,19 +108,10 @@ jobs:
|
||||
node-version: lts/*
|
||||
cache: 'npm'
|
||||
- run: npm clean-install
|
||||
- name: Load cached dist
|
||||
uses: actions/cache@v4
|
||||
id: dist
|
||||
with:
|
||||
path: dist
|
||||
key: ${{ needs.build.outputs.cache-key }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Run Test Suite
|
||||
run: npm run tap:edge-runtime
|
||||
|
||||
browsers:
|
||||
needs:
|
||||
- build
|
||||
runs-on: ${{ matrix.runs-on || 'ubuntu-latest' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -206,21 +138,12 @@ jobs:
|
||||
run: |
|
||||
sudo sqlite3 "$HOME/Library/Application Support/com.apple.TCC/TCC.db" "INSERT OR REPLACE INTO access VALUES('kTCCServiceAppleEvents','com.devexpress.testcafe-browser-tools',0,2,3,1,X'fade0c0000000068000000010000000700000007000000080000001443fa4ca5141baeda21aeca1f50894673b440d4690000000800000014f8afcf6e69791b283e55bd0b03e39e422745770e0000000800000014bf4fc1aed64c871a49fc6bc9dd3878ce5d4d17c6',NULL,0,'com.apple.Safari',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e53616661726900000003',NULL,1687952810,NULL,NULL,'UNUSED',1687952810);"
|
||||
sudo sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" "INSERT OR REPLACE INTO access VALUES('kTCCServiceScreenCapture','com.devexpress.testcafe-browser-tools',0,2,3,1,X'fade0c0000000068000000010000000700000007000000080000001443fa4ca5141baeda21aeca1f50894673b440d4690000000800000014f8afcf6e69791b283e55bd0b03e39e422745770e0000000800000014bf4fc1aed64c871a49fc6bc9dd3878ce5d4d17c6',NULL,0,'UNUSED',NULL,0,1687952810,NULL,NULL,'UNUSED',1687952810);"
|
||||
- name: Load cached dist
|
||||
uses: actions/cache@v4
|
||||
id: dist
|
||||
with:
|
||||
path: dist
|
||||
key: ${{ needs.build.outputs.cache-key }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Run Test Suite
|
||||
run: npm run tap:browsers
|
||||
env:
|
||||
BROWSER: ${{ matrix.browser }}
|
||||
|
||||
electron:
|
||||
needs:
|
||||
- build
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -231,13 +154,6 @@ jobs:
|
||||
node-version: lts/*
|
||||
cache: 'npm'
|
||||
- run: npm clean-install
|
||||
- name: Load cached dist
|
||||
uses: actions/cache@v4
|
||||
id: dist
|
||||
with:
|
||||
path: dist
|
||||
key: ${{ needs.build.outputs.cache-key }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Install Electron
|
||||
run: npm install --global electron
|
||||
- name: Run Test Suite
|
||||
|
||||
@@ -124,6 +124,8 @@ tap/*.js
|
||||
tap/*.js.map
|
||||
tap/run-*.mjs
|
||||
tap/run-*.mjs.map
|
||||
tap/run-*.cjs
|
||||
tap/run-*.cjs.map
|
||||
*.bak
|
||||
*.bun
|
||||
tap/.workerd.capnp
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ echo "Using Node.js $(node --version)"
|
||||
|
||||
node -e 'process.exit(parseInt(process.versions.node, 10))' &> /dev/null
|
||||
NODE_VERSION=$?
|
||||
export NODE_OPTIONS='--no-warnings --enable-source-maps'
|
||||
export NODE_OPTIONS='--no-warnings --enable-source-maps --import=tsx/esm'
|
||||
|
||||
if [[ $NODE_VERSION -eq 18 ]]; then
|
||||
export NODE_OPTIONS+=' --experimental-global-webcrypto'
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
const files = [
|
||||
"./test/**/*.test.mjs"
|
||||
]
|
||||
|
||||
const environmentVariables = {}
|
||||
|
||||
if ('CITGM' in process.env) {
|
||||
files.push("!**/remote.test.mjs")
|
||||
}
|
||||
|
||||
if ('electron' in process.versions) {
|
||||
files.push("!**/rsa-pss.test.mjs")
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
require: [
|
||||
"./test/.require.mjs"
|
||||
],
|
||||
files,
|
||||
environmentVariables,
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
const files = ['test/**/*.test.ts']
|
||||
|
||||
if ('CITGM' in process.env) {
|
||||
files.push("!**/remote.test.ts")
|
||||
}
|
||||
|
||||
export default {
|
||||
extensions: {
|
||||
ts: 'module',
|
||||
mjs: true,
|
||||
},
|
||||
files,
|
||||
workerThreads: false,
|
||||
nodeArguments: ['--enable-source-maps'],
|
||||
}
|
||||
Generated
+1152
-1170
File diff suppressed because it is too large
Load Diff
+10
-12
@@ -206,19 +206,17 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build-all": "run-s clear build:*",
|
||||
"build:webapi": "npm run build -- -p ./tsconfig/webapi.json",
|
||||
"build:webapi-bundle": "esbuild --bundle dist/webapi/index.js --format=esm --target=es2020 --outfile=dist/webapi/index.bundle.js",
|
||||
"build:webapi-bundle-min": "esbuild --minify --bundle dist/webapi/index.js --format=esm --target=es2020 --outfile=dist/webapi/index.bundle.min.js",
|
||||
"build:webapi-umd": "rollup dist/webapi/index.bundle.js --format umd --name jose -o dist/webapi/index.umd.js && rollup dist/webapi/index.bundle.min.js --compact --format umd --name jose -o dist/webapi/index.umd.min.js",
|
||||
"build:deno": "mkdir -p dist/deno && cp -R src/. dist/deno && find dist/deno -name '*.ts' -type f -print0 | xargs -0 sed -i.bak -e \"s/\\.js'/.ts'/g\" && npm run-script sedcleanup",
|
||||
"build:types": "npm run-script build -- -p ./tsconfig/types.json && cd src && find . -name '*.d.ts' -maxdepth 2 -type f -exec rsync -R \"{}\" ../dist/types \\; && cd .. && run-s -s types:find | xargs -0 sed -i.bak -e \"s/\\.js'/'/g\" -e \"s/\\.d'/'/g\" && npm run-script sedcleanup",
|
||||
"build-all": "run-s clear build build:*",
|
||||
"build:bundle": "esbuild --bundle dist/webapi/index.js --format=esm --target=es2020 --outfile=dist/webapi/index.bundle.js",
|
||||
"build:bundle-min": "esbuild --minify --bundle dist/webapi/index.js --format=esm --target=es2020 --outfile=dist/webapi/index.bundle.min.js",
|
||||
"build:umd": "rollup dist/webapi/index.bundle.js --format umd --name jose -o dist/webapi/index.umd.js && rollup dist/webapi/index.bundle.min.js --compact --format umd --name jose -o dist/webapi/index.umd.min.js",
|
||||
"build:deno": "mkdir -p dist/deno && cp -R src/. dist/deno && find dist/deno -name '*.ts' -type f -print0 | xargs -0 sed -i.bak -e \"s/\\.js'/.ts'/g\" && npm run sedcleanup",
|
||||
"build:types": "npm run build -- -p ./tsconfig/types.json && cd src && find . -name '*.d.ts' -maxdepth 2 -type f -exec rsync -R \"{}\" ../dist/types \\; && cd .. && run-s -s types:find | xargs -0 sed -i.bak -e \"s/\\.js'/'/g\" -e \"s/\\.d.ts'/'/g\" && npm run sedcleanup",
|
||||
"clear": "rm -Rf dist",
|
||||
"sedcleanup": "find . -name '*.bak' -type f -print0 | xargs -0 rm -f",
|
||||
"docs:generate": "typedoc",
|
||||
"types:find": "find dist/types -name '*.d.ts' -type f -print0",
|
||||
"test": "ava",
|
||||
"test:types": "tsd -f test/types",
|
||||
"test": "bash -c 'source .node_flags.sh && ava'",
|
||||
"format": "prettier --log-level silent --write ./test ./tap ./src ./tools ./cookbook ./tsconfig",
|
||||
"tap:browsers": "./tap/.browsers.sh",
|
||||
"tap:bun": "./tap/.bun.sh",
|
||||
@@ -235,7 +233,6 @@
|
||||
"edge-runtime": "^4.0.1",
|
||||
"esbuild": "^0.25.0",
|
||||
"glob": "^11.0.1",
|
||||
"nock": "^13.5.6",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"patch-package": "^8.0.0",
|
||||
"prettier": "^3.5.1",
|
||||
@@ -244,10 +241,11 @@
|
||||
"rollup": "^4.34.8",
|
||||
"tar": "^7.4.3",
|
||||
"timekeeper": "^2.3.1",
|
||||
"tsd": "^0.31.2",
|
||||
"tsx": "^4.19.2",
|
||||
"typedoc": "^0.27.2",
|
||||
"typedoc-plugin-markdown": "^4.3.1",
|
||||
"typedoc-plugin-mdn-links": "^4.0.3",
|
||||
"typescript": "^5.7.3"
|
||||
"typescript": "^5.7.3",
|
||||
"undici": "^6.21.1"
|
||||
}
|
||||
}
|
||||
|
||||
+45
-41
@@ -36,7 +36,13 @@ export { EmbeddedJWK } from './jwk/embedded.js'
|
||||
|
||||
export { createLocalJWKSet } from './jwks/local.js'
|
||||
export { createRemoteJWKSet, jwksCache } from './jwks/remote.js'
|
||||
export type { RemoteJWKSetOptions, JWKSCacheInput, ExportedJWKSCache } from './jwks/remote.js'
|
||||
export type {
|
||||
RemoteJWKSetOptions,
|
||||
JWKSCacheInput,
|
||||
ExportedJWKSCache,
|
||||
customFetch,
|
||||
FetchImplementation,
|
||||
} from './jwks/remote.js'
|
||||
|
||||
export { UnsecuredJWT } from './jwt/unsecured.js'
|
||||
export type { UnsecuredResult } from './jwt/unsecured.js'
|
||||
@@ -60,50 +66,48 @@ export type { GenerateSecretOptions } from './key/generate_secret.js'
|
||||
export * as base64url from './util/base64url.js'
|
||||
|
||||
export type {
|
||||
CryptoKey,
|
||||
KeyObject,
|
||||
KeyLike,
|
||||
JWK,
|
||||
JWKParameters,
|
||||
JWK_OKP_Public,
|
||||
JWK_OKP_Private,
|
||||
JWK_EC_Public,
|
||||
JWK_EC_Private,
|
||||
JWK_RSA_Public,
|
||||
JWK_RSA_Private,
|
||||
JWK_oct,
|
||||
FlattenedJWSInput,
|
||||
GeneralJWSInput,
|
||||
FlattenedJWS,
|
||||
GeneralJWS,
|
||||
JoseHeaderParameters,
|
||||
JWSHeaderParameters,
|
||||
JWEKeyManagementHeaderParameters,
|
||||
FlattenedJWE,
|
||||
GeneralJWE,
|
||||
JWEHeaderParameters,
|
||||
CritOption,
|
||||
DecryptOptions,
|
||||
EncryptOptions,
|
||||
JWTClaimVerificationOptions,
|
||||
VerifyOptions,
|
||||
SignOptions,
|
||||
JWTPayload,
|
||||
FlattenedDecryptResult,
|
||||
GeneralDecryptResult,
|
||||
CompactDecryptResult,
|
||||
FlattenedVerifyResult,
|
||||
GeneralVerifyResult,
|
||||
CompactVerifyResult,
|
||||
JWTVerifyResult,
|
||||
JWTDecryptResult,
|
||||
ResolvedKey,
|
||||
CompactJWEHeaderParameters,
|
||||
CompactJWSHeaderParameters,
|
||||
JWTHeaderParameters,
|
||||
JSONWebKeySet,
|
||||
CryptoRuntime,
|
||||
CompactVerifyResult,
|
||||
CritOption,
|
||||
CryptoKey,
|
||||
DecryptOptions,
|
||||
EncryptOptions,
|
||||
FlattenedDecryptResult,
|
||||
FlattenedJWE,
|
||||
FlattenedJWS,
|
||||
FlattenedJWSInput,
|
||||
FlattenedVerifyResult,
|
||||
GeneralDecryptResult,
|
||||
GeneralJWE,
|
||||
GeneralJWS,
|
||||
GeneralJWSInput,
|
||||
GeneralVerifyResult,
|
||||
GetKeyFunction,
|
||||
JoseHeaderParameters,
|
||||
JSONWebKeySet,
|
||||
JWEHeaderParameters,
|
||||
JWEKeyManagementHeaderParameters,
|
||||
JWK_EC_Private,
|
||||
JWK_EC_Public,
|
||||
JWK_oct,
|
||||
JWK_OKP_Private,
|
||||
JWK_OKP_Public,
|
||||
JWK_RSA_Private,
|
||||
JWK_RSA_Public,
|
||||
JWK,
|
||||
JWKParameters,
|
||||
JWSHeaderParameters,
|
||||
JWTClaimVerificationOptions,
|
||||
JWTDecryptResult,
|
||||
JWTHeaderParameters,
|
||||
JWTPayload,
|
||||
JWTVerifyResult,
|
||||
KeyObject,
|
||||
ResolvedKey,
|
||||
SignOptions,
|
||||
VerifyOptions,
|
||||
} from './types.d.ts'
|
||||
|
||||
export const cryptoRuntime = 'WebCryptoAPI'
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
import { flattenedDecrypt } from '../flattened/decrypt.js'
|
||||
import { JWEInvalid } from '../../util/errors.js'
|
||||
import { decoder } from '../../lib/buffer_utils.js'
|
||||
import type {
|
||||
CryptoKey,
|
||||
DecryptOptions,
|
||||
CompactJWEHeaderParameters,
|
||||
GetKeyFunction,
|
||||
FlattenedJWE,
|
||||
CompactDecryptResult,
|
||||
ResolvedKey,
|
||||
KeyObject,
|
||||
JWK,
|
||||
} from '../../types.d.ts'
|
||||
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
|
||||
extends GetKeyFunction<CompactJWEHeaderParameters, FlattenedJWE> {}
|
||||
extends types.GetKeyFunction<types.CompactJWEHeaderParameters, types.FlattenedJWE> {}
|
||||
|
||||
/**
|
||||
* Decrypts a Compact JWE.
|
||||
@@ -45,9 +35,9 @@ export interface CompactDecryptGetKey
|
||||
*/
|
||||
export async function compactDecrypt(
|
||||
jwe: string | Uint8Array,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: DecryptOptions,
|
||||
): Promise<CompactDecryptResult>
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.DecryptOptions,
|
||||
): Promise<types.CompactDecryptResult>
|
||||
/**
|
||||
* @param jwe Compact JWE.
|
||||
* @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See
|
||||
@@ -57,12 +47,12 @@ export async function compactDecrypt(
|
||||
export async function compactDecrypt(
|
||||
jwe: string | Uint8Array,
|
||||
getKey: CompactDecryptGetKey,
|
||||
options?: DecryptOptions,
|
||||
): Promise<CompactDecryptResult & ResolvedKey>
|
||||
options?: types.DecryptOptions,
|
||||
): Promise<types.CompactDecryptResult & types.ResolvedKey>
|
||||
export async function compactDecrypt(
|
||||
jwe: string | Uint8Array,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array | CompactDecryptGetKey,
|
||||
options?: DecryptOptions,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | CompactDecryptGetKey,
|
||||
options?: types.DecryptOptions,
|
||||
) {
|
||||
if (jwe instanceof Uint8Array) {
|
||||
jwe = decoder.decode(jwe)
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
import type * as types from '../../types.d.ts'
|
||||
import { FlattenedEncrypt } from '../flattened/encrypt.js'
|
||||
import type {
|
||||
KeyObject,
|
||||
CryptoKey,
|
||||
JWEKeyManagementHeaderParameters,
|
||||
CompactJWEHeaderParameters,
|
||||
EncryptOptions,
|
||||
JWK,
|
||||
} from '../../types.d.ts'
|
||||
|
||||
/**
|
||||
* The CompactEncrypt class is used to build and encrypt Compact JWE strings.
|
||||
@@ -67,7 +60,7 @@ export class CompactEncrypt {
|
||||
*
|
||||
* @param protectedHeader JWE Protected Header object.
|
||||
*/
|
||||
setProtectedHeader(protectedHeader: CompactJWEHeaderParameters): this {
|
||||
setProtectedHeader(protectedHeader: types.CompactJWEHeaderParameters): this {
|
||||
this._flattened.setProtectedHeader(protectedHeader)
|
||||
return this
|
||||
}
|
||||
@@ -79,7 +72,7 @@ export class CompactEncrypt {
|
||||
*
|
||||
* @param parameters JWE Key Management parameters.
|
||||
*/
|
||||
setKeyManagementParameters(parameters: JWEKeyManagementHeaderParameters): this {
|
||||
setKeyManagementParameters(parameters: types.JWEKeyManagementHeaderParameters): this {
|
||||
this._flattened.setKeyManagementParameters(parameters)
|
||||
return this
|
||||
}
|
||||
@@ -92,8 +85,8 @@ export class CompactEncrypt {
|
||||
* @param options JWE Encryption options.
|
||||
*/
|
||||
async encrypt(
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: EncryptOptions,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.EncryptOptions,
|
||||
): Promise<string> {
|
||||
const jwe = await this._flattened.encrypt(key, options)
|
||||
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
import { decode as base64url } from '../../runtime/base64url.js'
|
||||
import decrypt from '../../runtime/decrypt.js'
|
||||
|
||||
import type * as types from '../../types.d.ts'
|
||||
import { decode as base64url } from '../../lib/base64url.js'
|
||||
import decrypt from '../../lib/decrypt.js'
|
||||
import { JOSEAlgNotAllowed, JOSENotSupported, JWEInvalid } from '../../util/errors.js'
|
||||
import isDisjoint from '../../lib/is_disjoint.js'
|
||||
import isObject from '../../lib/is_object.js'
|
||||
import decryptKeyManagement from '../../lib/decrypt_key_management.js'
|
||||
import type {
|
||||
FlattenedDecryptResult,
|
||||
CryptoKey,
|
||||
FlattenedJWE,
|
||||
JWEHeaderParameters,
|
||||
DecryptOptions,
|
||||
GetKeyFunction,
|
||||
ResolvedKey,
|
||||
KeyObject,
|
||||
JWK,
|
||||
} from '../../types.d.ts'
|
||||
import { encoder, decoder, concat } from '../../lib/buffer_utils.js'
|
||||
import generateCek from '../../lib/cek.js'
|
||||
import validateCrit from '../../lib/validate_crit.js'
|
||||
import validateAlgorithms from '../../lib/validate_algorithms.js'
|
||||
import normalizeKey from '../../runtime/normalize_key.js'
|
||||
import normalizeKey from '../../lib/normalize_key.js'
|
||||
import checkKeyType from '../../lib/check_key_type.js'
|
||||
|
||||
/**
|
||||
@@ -28,7 +17,7 @@ import checkKeyType from '../../lib/check_key_type.js'
|
||||
* verified at the time of this function call.
|
||||
*/
|
||||
export interface FlattenedDecryptGetKey
|
||||
extends GetKeyFunction<JWEHeaderParameters | undefined, FlattenedJWE> {}
|
||||
extends types.GetKeyFunction<types.JWEHeaderParameters | undefined, types.FlattenedJWE> {}
|
||||
|
||||
/**
|
||||
* Decrypts a Flattened JWE.
|
||||
@@ -64,10 +53,10 @@ export interface FlattenedDecryptGetKey
|
||||
* @param options JWE Decryption options.
|
||||
*/
|
||||
export function flattenedDecrypt(
|
||||
jwe: FlattenedJWE,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: DecryptOptions,
|
||||
): Promise<FlattenedDecryptResult>
|
||||
jwe: types.FlattenedJWE,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.DecryptOptions,
|
||||
): Promise<types.FlattenedDecryptResult>
|
||||
/**
|
||||
* @param jwe Flattened JWE.
|
||||
* @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See
|
||||
@@ -75,14 +64,14 @@ export function flattenedDecrypt(
|
||||
* @param options JWE Decryption options.
|
||||
*/
|
||||
export function flattenedDecrypt(
|
||||
jwe: FlattenedJWE,
|
||||
jwe: types.FlattenedJWE,
|
||||
getKey: FlattenedDecryptGetKey,
|
||||
options?: DecryptOptions,
|
||||
): Promise<FlattenedDecryptResult & ResolvedKey>
|
||||
options?: types.DecryptOptions,
|
||||
): Promise<types.FlattenedDecryptResult & types.ResolvedKey>
|
||||
export async function flattenedDecrypt(
|
||||
jwe: FlattenedJWE,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array | FlattenedDecryptGetKey,
|
||||
options?: DecryptOptions,
|
||||
jwe: types.FlattenedJWE,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | FlattenedDecryptGetKey,
|
||||
options?: types.DecryptOptions,
|
||||
) {
|
||||
if (!isObject(jwe)) {
|
||||
throw new JWEInvalid('Flattened JWE must be an object')
|
||||
@@ -124,7 +113,7 @@ export async function flattenedDecrypt(
|
||||
throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type')
|
||||
}
|
||||
|
||||
let parsedProt!: JWEHeaderParameters
|
||||
let parsedProt!: types.JWEHeaderParameters
|
||||
if (jwe.protected) {
|
||||
try {
|
||||
const protectedHeader = base64url(jwe.protected)
|
||||
@@ -139,7 +128,7 @@ export async function flattenedDecrypt(
|
||||
)
|
||||
}
|
||||
|
||||
const joseHeader: JWEHeaderParameters = {
|
||||
const joseHeader: types.JWEHeaderParameters = {
|
||||
...parsedProt,
|
||||
...jwe.header,
|
||||
...jwe.unprotected,
|
||||
@@ -197,7 +186,7 @@ export async function flattenedDecrypt(
|
||||
checkKeyType(alg === 'dir' ? enc : alg, key, 'decrypt')
|
||||
|
||||
const k = await normalizeKey(key, alg)
|
||||
let cek: CryptoKey | Uint8Array
|
||||
let cek: types.CryptoKey | Uint8Array
|
||||
try {
|
||||
cek = await decryptKeyManagement(alg, k, encryptedKey, joseHeader, options)
|
||||
} catch (err) {
|
||||
@@ -248,7 +237,7 @@ export async function flattenedDecrypt(
|
||||
}
|
||||
const plaintext = await decrypt(enc, cek, ciphertext, iv, tag, additionalData)
|
||||
|
||||
const result: FlattenedDecryptResult = { plaintext }
|
||||
const result: types.FlattenedDecryptResult = { plaintext }
|
||||
|
||||
if (jwe.protected !== undefined) {
|
||||
result.protectedHeader = parsedProt
|
||||
|
||||
@@ -1,22 +1,13 @@
|
||||
import { encode as base64url } from '../../runtime/base64url.js'
|
||||
import { encode as base64url } from '../../lib/base64url.js'
|
||||
import { unprotected } from '../../lib/private_symbols.js'
|
||||
import encrypt from '../../runtime/encrypt.js'
|
||||
|
||||
import type {
|
||||
CryptoKey,
|
||||
FlattenedJWE,
|
||||
JWEHeaderParameters,
|
||||
JWEKeyManagementHeaderParameters,
|
||||
EncryptOptions,
|
||||
KeyObject,
|
||||
JWK,
|
||||
} from '../../types.d.ts'
|
||||
import encrypt from '../../lib/encrypt.js'
|
||||
import type * as types from '../../types.d.ts'
|
||||
import encryptKeyManagement from '../../lib/encrypt_key_management.js'
|
||||
import { JOSENotSupported, JWEInvalid } from '../../util/errors.js'
|
||||
import isDisjoint from '../../lib/is_disjoint.js'
|
||||
import { encoder, decoder, concat } from '../../lib/buffer_utils.js'
|
||||
import validateCrit from '../../lib/validate_crit.js'
|
||||
import normalizeKey from '../../runtime/normalize_key.js'
|
||||
import normalizeKey from '../../lib/normalize_key.js'
|
||||
import checkKeyType from '../../lib/check_key_type.js'
|
||||
|
||||
/**
|
||||
@@ -41,11 +32,11 @@ import checkKeyType from '../../lib/check_key_type.js'
|
||||
export class FlattenedEncrypt {
|
||||
private _plaintext: Uint8Array
|
||||
|
||||
private _protectedHeader!: JWEHeaderParameters | undefined
|
||||
private _protectedHeader!: types.JWEHeaderParameters | undefined
|
||||
|
||||
private _sharedUnprotectedHeader!: JWEHeaderParameters | undefined
|
||||
private _sharedUnprotectedHeader!: types.JWEHeaderParameters | undefined
|
||||
|
||||
private _unprotectedHeader!: JWEHeaderParameters | undefined
|
||||
private _unprotectedHeader!: types.JWEHeaderParameters | undefined
|
||||
|
||||
private _aad!: Uint8Array | undefined
|
||||
|
||||
@@ -53,7 +44,7 @@ export class FlattenedEncrypt {
|
||||
|
||||
private _iv!: Uint8Array | undefined
|
||||
|
||||
private _keyManagementParameters!: JWEKeyManagementHeaderParameters
|
||||
private _keyManagementParameters!: types.JWEKeyManagementHeaderParameters
|
||||
|
||||
/** @param plaintext Binary representation of the plaintext to encrypt. */
|
||||
constructor(plaintext: Uint8Array) {
|
||||
@@ -71,7 +62,7 @@ export class FlattenedEncrypt {
|
||||
*
|
||||
* @param parameters JWE Key Management parameters.
|
||||
*/
|
||||
setKeyManagementParameters(parameters: JWEKeyManagementHeaderParameters): this {
|
||||
setKeyManagementParameters(parameters: types.JWEKeyManagementHeaderParameters): this {
|
||||
if (this._keyManagementParameters) {
|
||||
throw new TypeError('setKeyManagementParameters can only be called once')
|
||||
}
|
||||
@@ -84,7 +75,7 @@ export class FlattenedEncrypt {
|
||||
*
|
||||
* @param protectedHeader JWE Protected Header.
|
||||
*/
|
||||
setProtectedHeader(protectedHeader: JWEHeaderParameters): this {
|
||||
setProtectedHeader(protectedHeader: types.JWEHeaderParameters): this {
|
||||
if (this._protectedHeader) {
|
||||
throw new TypeError('setProtectedHeader can only be called once')
|
||||
}
|
||||
@@ -97,7 +88,7 @@ export class FlattenedEncrypt {
|
||||
*
|
||||
* @param sharedUnprotectedHeader JWE Shared Unprotected Header.
|
||||
*/
|
||||
setSharedUnprotectedHeader(sharedUnprotectedHeader: JWEHeaderParameters): this {
|
||||
setSharedUnprotectedHeader(sharedUnprotectedHeader: types.JWEHeaderParameters): this {
|
||||
if (this._sharedUnprotectedHeader) {
|
||||
throw new TypeError('setSharedUnprotectedHeader can only be called once')
|
||||
}
|
||||
@@ -110,7 +101,7 @@ export class FlattenedEncrypt {
|
||||
*
|
||||
* @param unprotectedHeader JWE Per-Recipient Unprotected Header.
|
||||
*/
|
||||
setUnprotectedHeader(unprotectedHeader: JWEHeaderParameters): this {
|
||||
setUnprotectedHeader(unprotectedHeader: types.JWEHeaderParameters): this {
|
||||
if (this._unprotectedHeader) {
|
||||
throw new TypeError('setUnprotectedHeader can only be called once')
|
||||
}
|
||||
@@ -170,9 +161,9 @@ export class FlattenedEncrypt {
|
||||
* @param options JWE Encryption options.
|
||||
*/
|
||||
async encrypt(
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: EncryptOptions,
|
||||
): Promise<FlattenedJWE> {
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.EncryptOptions,
|
||||
): Promise<types.FlattenedJWE> {
|
||||
if (!this._protectedHeader && !this._unprotectedHeader && !this._sharedUnprotectedHeader) {
|
||||
throw new JWEInvalid(
|
||||
'either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()',
|
||||
@@ -187,7 +178,7 @@ export class FlattenedEncrypt {
|
||||
)
|
||||
}
|
||||
|
||||
const joseHeader: JWEHeaderParameters = {
|
||||
const joseHeader: types.JWEHeaderParameters = {
|
||||
...this._protectedHeader,
|
||||
...this._unprotectedHeader,
|
||||
...this._sharedUnprotectedHeader,
|
||||
@@ -221,7 +212,7 @@ export class FlattenedEncrypt {
|
||||
|
||||
checkKeyType(alg === 'dir' ? enc : alg, key, 'encrypt')
|
||||
|
||||
let cek: CryptoKey | Uint8Array
|
||||
let cek: types.CryptoKey | Uint8Array
|
||||
{
|
||||
let parameters: { [propName: string]: unknown } | undefined
|
||||
const k = await normalizeKey(key, alg)
|
||||
@@ -272,7 +263,7 @@ export class FlattenedEncrypt {
|
||||
additionalData,
|
||||
)
|
||||
|
||||
const jwe: FlattenedJWE = {
|
||||
const jwe: types.FlattenedJWE = {
|
||||
ciphertext: base64url(ciphertext),
|
||||
}
|
||||
|
||||
|
||||
+13
-23
@@ -1,24 +1,14 @@
|
||||
import { flattenedDecrypt } from '../flattened/decrypt.js'
|
||||
import { JWEDecryptionFailed, JWEInvalid } from '../../util/errors.js'
|
||||
import type {
|
||||
CryptoKey,
|
||||
DecryptOptions,
|
||||
JWEHeaderParameters,
|
||||
GetKeyFunction,
|
||||
FlattenedJWE,
|
||||
GeneralJWE,
|
||||
GeneralDecryptResult,
|
||||
ResolvedKey,
|
||||
KeyObject,
|
||||
JWK,
|
||||
} from '../../types.d.ts'
|
||||
import type * as types from '../../types.d.ts'
|
||||
import isObject from '../../lib/is_object.js'
|
||||
|
||||
/**
|
||||
* Interface for General JWE Decryption dynamic key resolution. No token components have been
|
||||
* verified at the time of this function call.
|
||||
*/
|
||||
export interface GeneralDecryptGetKey extends GetKeyFunction<JWEHeaderParameters, FlattenedJWE> {}
|
||||
export interface GeneralDecryptGetKey
|
||||
extends types.GetKeyFunction<types.JWEHeaderParameters, types.FlattenedJWE> {}
|
||||
|
||||
/**
|
||||
* Decrypts a General JWE.
|
||||
@@ -58,10 +48,10 @@ export interface GeneralDecryptGetKey extends GetKeyFunction<JWEHeaderParameters
|
||||
* @param options JWE Decryption options.
|
||||
*/
|
||||
export function generalDecrypt(
|
||||
jwe: GeneralJWE,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: DecryptOptions,
|
||||
): Promise<GeneralDecryptResult>
|
||||
jwe: types.GeneralJWE,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.DecryptOptions,
|
||||
): Promise<types.GeneralDecryptResult>
|
||||
/**
|
||||
* @param jwe General JWE.
|
||||
* @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See
|
||||
@@ -69,14 +59,14 @@ export function generalDecrypt(
|
||||
* @param options JWE Decryption options.
|
||||
*/
|
||||
export function generalDecrypt(
|
||||
jwe: GeneralJWE,
|
||||
jwe: types.GeneralJWE,
|
||||
getKey: GeneralDecryptGetKey,
|
||||
options?: DecryptOptions,
|
||||
): Promise<GeneralDecryptResult & ResolvedKey>
|
||||
options?: types.DecryptOptions,
|
||||
): Promise<types.GeneralDecryptResult & types.ResolvedKey>
|
||||
export async function generalDecrypt(
|
||||
jwe: GeneralJWE,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array | GeneralDecryptGetKey,
|
||||
options?: DecryptOptions,
|
||||
jwe: types.GeneralJWE,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | GeneralDecryptGetKey,
|
||||
options?: types.DecryptOptions,
|
||||
) {
|
||||
if (!isObject(jwe)) {
|
||||
throw new JWEInvalid('General JWE must be an object')
|
||||
|
||||
+24
-29
@@ -1,21 +1,13 @@
|
||||
import type * as types from '../../types.d.ts'
|
||||
import { FlattenedEncrypt } from '../flattened/encrypt.js'
|
||||
import { unprotected } from '../../lib/private_symbols.js'
|
||||
import { JOSENotSupported, JWEInvalid } from '../../util/errors.js'
|
||||
import generateCek from '../../lib/cek.js'
|
||||
import isDisjoint from '../../lib/is_disjoint.js'
|
||||
import encryptKeyManagement from '../../lib/encrypt_key_management.js'
|
||||
import { encode as base64url } from '../../runtime/base64url.js'
|
||||
import { encode as base64url } from '../../lib/base64url.js'
|
||||
import validateCrit from '../../lib/validate_crit.js'
|
||||
|
||||
import type {
|
||||
CryptoKey,
|
||||
GeneralJWE,
|
||||
JWEHeaderParameters,
|
||||
CritOption,
|
||||
KeyObject,
|
||||
JWK,
|
||||
} from '../../types.d.ts'
|
||||
import normalizeKey from '../../runtime/normalize_key.js'
|
||||
import normalizeKey from '../../lib/normalize_key.js'
|
||||
import checkKeyType from '../../lib/check_key_type.js'
|
||||
|
||||
export interface Recipient {
|
||||
@@ -24,13 +16,13 @@ export interface Recipient {
|
||||
*
|
||||
* @param unprotectedHeader JWE Per-Recipient Unprotected Header.
|
||||
*/
|
||||
setUnprotectedHeader(unprotectedHeader: JWEHeaderParameters): Recipient
|
||||
setUnprotectedHeader(unprotectedHeader: types.JWEHeaderParameters): Recipient
|
||||
|
||||
/** A shorthand for calling addRecipient() on the enclosing GeneralEncrypt instance */
|
||||
addRecipient(...args: Parameters<GeneralEncrypt['addRecipient']>): Recipient
|
||||
|
||||
/** A shorthand for calling encrypt() on the enclosing GeneralEncrypt instance */
|
||||
encrypt(...args: Parameters<GeneralEncrypt['encrypt']>): Promise<GeneralJWE>
|
||||
encrypt(...args: Parameters<GeneralEncrypt['encrypt']>): Promise<types.GeneralJWE>
|
||||
|
||||
/** Returns the enclosing GeneralEncrypt */
|
||||
done(): GeneralEncrypt
|
||||
@@ -38,21 +30,21 @@ export interface Recipient {
|
||||
|
||||
class IndividualRecipient implements Recipient {
|
||||
private parent: GeneralEncrypt
|
||||
unprotectedHeader?: JWEHeaderParameters
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array
|
||||
options: CritOption
|
||||
unprotectedHeader?: types.JWEHeaderParameters
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array
|
||||
options: types.CritOption
|
||||
|
||||
constructor(
|
||||
enc: GeneralEncrypt,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options: CritOption,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options: types.CritOption,
|
||||
) {
|
||||
this.parent = enc
|
||||
this.key = key
|
||||
this.options = options
|
||||
}
|
||||
|
||||
setUnprotectedHeader(unprotectedHeader: JWEHeaderParameters) {
|
||||
setUnprotectedHeader(unprotectedHeader: types.JWEHeaderParameters) {
|
||||
if (this.unprotectedHeader) {
|
||||
throw new TypeError('setUnprotectedHeader can only be called once')
|
||||
}
|
||||
@@ -100,9 +92,9 @@ export class GeneralEncrypt {
|
||||
|
||||
private _recipients: IndividualRecipient[] = []
|
||||
|
||||
private _protectedHeader!: JWEHeaderParameters
|
||||
private _protectedHeader!: types.JWEHeaderParameters
|
||||
|
||||
private _unprotectedHeader!: JWEHeaderParameters
|
||||
private _unprotectedHeader!: types.JWEHeaderParameters
|
||||
|
||||
private _aad!: Uint8Array
|
||||
|
||||
@@ -118,7 +110,10 @@ export class GeneralEncrypt {
|
||||
* See {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
|
||||
* @param options JWE Encryption options.
|
||||
*/
|
||||
addRecipient(key: CryptoKey | KeyObject | JWK | Uint8Array, options?: CritOption): Recipient {
|
||||
addRecipient(
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.CritOption,
|
||||
): Recipient {
|
||||
const recipient = new IndividualRecipient(this, key, { crit: options?.crit })
|
||||
this._recipients.push(recipient)
|
||||
return recipient
|
||||
@@ -129,7 +124,7 @@ export class GeneralEncrypt {
|
||||
*
|
||||
* @param protectedHeader JWE Protected Header object.
|
||||
*/
|
||||
setProtectedHeader(protectedHeader: JWEHeaderParameters): this {
|
||||
setProtectedHeader(protectedHeader: types.JWEHeaderParameters): this {
|
||||
if (this._protectedHeader) {
|
||||
throw new TypeError('setProtectedHeader can only be called once')
|
||||
}
|
||||
@@ -142,7 +137,7 @@ export class GeneralEncrypt {
|
||||
*
|
||||
* @param sharedUnprotectedHeader JWE Shared Unprotected Header object.
|
||||
*/
|
||||
setSharedUnprotectedHeader(sharedUnprotectedHeader: JWEHeaderParameters): this {
|
||||
setSharedUnprotectedHeader(sharedUnprotectedHeader: types.JWEHeaderParameters): this {
|
||||
if (this._unprotectedHeader) {
|
||||
throw new TypeError('setSharedUnprotectedHeader can only be called once')
|
||||
}
|
||||
@@ -161,7 +156,7 @@ export class GeneralEncrypt {
|
||||
}
|
||||
|
||||
/** Encrypts and resolves the value of the General JWE object. */
|
||||
async encrypt(): Promise<GeneralJWE> {
|
||||
async encrypt(): Promise<types.GeneralJWE> {
|
||||
if (!this._recipients.length) {
|
||||
throw new JWEInvalid('at least one recipient must be added')
|
||||
}
|
||||
@@ -176,7 +171,7 @@ export class GeneralEncrypt {
|
||||
.setUnprotectedHeader(recipient.unprotectedHeader!)
|
||||
.encrypt(recipient.key, { ...recipient.options })
|
||||
|
||||
const jwe: GeneralJWE = {
|
||||
const jwe: types.GeneralJWE = {
|
||||
ciphertext: flattened.ciphertext,
|
||||
iv: flattened.iv,
|
||||
recipients: [{}],
|
||||
@@ -242,7 +237,7 @@ export class GeneralEncrypt {
|
||||
|
||||
const cek = generateCek(enc)
|
||||
|
||||
const jwe: GeneralJWE = {
|
||||
const jwe: types.GeneralJWE = {
|
||||
ciphertext: '',
|
||||
iv: '',
|
||||
recipients: [],
|
||||
@@ -251,7 +246,7 @@ export class GeneralEncrypt {
|
||||
|
||||
for (let i = 0; i < this._recipients.length; i++) {
|
||||
const recipient = this._recipients[i]
|
||||
const target: Record<string, string | JWEHeaderParameters> = {}
|
||||
const target: Record<string, string | types.JWEHeaderParameters> = {}
|
||||
jwe.recipients!.push(target)
|
||||
|
||||
const joseHeader = {
|
||||
@@ -304,6 +299,6 @@ export class GeneralEncrypt {
|
||||
target.header = { ...recipient.unprotectedHeader, ...parameters }
|
||||
}
|
||||
|
||||
return jwe as GeneralJWE
|
||||
return jwe as types.GeneralJWE
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
import type { CryptoKey, FlattenedJWSInput, JWSHeaderParameters } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import { importJWK } from '../key/import.js'
|
||||
import isObject from '../lib/is_object.js'
|
||||
import { JWSInvalid } from '../util/errors.js'
|
||||
@@ -28,9 +28,9 @@ import { JWSInvalid } from '../util/errors.js'
|
||||
* ```
|
||||
*/
|
||||
export async function EmbeddedJWK(
|
||||
protectedHeader?: JWSHeaderParameters,
|
||||
token?: FlattenedJWSInput,
|
||||
): Promise<CryptoKey> {
|
||||
protectedHeader?: types.JWSHeaderParameters,
|
||||
token?: types.FlattenedJWSInput,
|
||||
): Promise<types.CryptoKey> {
|
||||
const joseHeader = {
|
||||
...protectedHeader,
|
||||
...token?.header,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import digest from '../runtime/digest.js'
|
||||
import { encode as base64url } from '../runtime/base64url.js'
|
||||
import digest from '../lib/digest.js'
|
||||
import { encode as base64url } from '../lib/base64url.js'
|
||||
|
||||
import { JOSENotSupported, JWKInvalid } from '../util/errors.js'
|
||||
import { encoder } from '../lib/buffer_utils.js'
|
||||
import type { JWK } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import isObject from '../lib/is_object.js'
|
||||
|
||||
const check = (value: unknown, description: string) => {
|
||||
@@ -39,7 +39,7 @@ const check = (value: unknown, description: string) => {
|
||||
* @see {@link https://www.rfc-editor.org/rfc/rfc7638 RFC7638}
|
||||
*/
|
||||
export async function calculateJwkThumbprint(
|
||||
jwk: JWK,
|
||||
jwk: types.JWK,
|
||||
digestAlgorithm?: 'sha256' | 'sha384' | 'sha512',
|
||||
): Promise<string> {
|
||||
if (!isObject(jwk)) {
|
||||
@@ -56,7 +56,7 @@ export async function calculateJwkThumbprint(
|
||||
throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"')
|
||||
}
|
||||
|
||||
let components: JWK
|
||||
let components: types.JWK
|
||||
switch (jwk.kty) {
|
||||
case 'EC':
|
||||
check(jwk.crv, '"crv" (Curve) Parameter')
|
||||
@@ -113,7 +113,7 @@ export async function calculateJwkThumbprint(
|
||||
* @see {@link https://www.rfc-editor.org/rfc/rfc9278 RFC9278}
|
||||
*/
|
||||
export async function calculateJwkThumbprintUri(
|
||||
jwk: JWK,
|
||||
jwk: types.JWK,
|
||||
digestAlgorithm?: 'sha256' | 'sha384' | 'sha512',
|
||||
): Promise<string> {
|
||||
digestAlgorithm ??= 'sha256'
|
||||
|
||||
+19
-24
@@ -1,10 +1,4 @@
|
||||
import type {
|
||||
CryptoKey,
|
||||
JWSHeaderParameters,
|
||||
JWK,
|
||||
JSONWebKeySet,
|
||||
FlattenedJWSInput,
|
||||
} from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import { importJWK } from '../key/import.js'
|
||||
import {
|
||||
JWKSInvalid,
|
||||
@@ -29,10 +23,10 @@ function getKtyFromAlg(alg: unknown) {
|
||||
}
|
||||
|
||||
interface Cache {
|
||||
[alg: string]: CryptoKey
|
||||
[alg: string]: types.CryptoKey
|
||||
}
|
||||
|
||||
function isJWKSLike(jwks: unknown): jwks is JSONWebKeySet {
|
||||
function isJWKSLike(jwks: unknown): jwks is types.JSONWebKeySet {
|
||||
return (
|
||||
jwks &&
|
||||
typeof jwks === 'object' &&
|
||||
@@ -44,13 +38,11 @@ function isJWKSLike(jwks: unknown): jwks is JSONWebKeySet {
|
||||
}
|
||||
|
||||
function isJWKLike(key: unknown) {
|
||||
return isObject<JWK>(key)
|
||||
return isObject<types.JWK>(key)
|
||||
}
|
||||
|
||||
function clone<T>(obj: T): T {
|
||||
// @ts-ignore
|
||||
if (typeof structuredClone === 'function') {
|
||||
// @ts-ignore
|
||||
return structuredClone(obj)
|
||||
}
|
||||
|
||||
@@ -58,22 +50,22 @@ function clone<T>(obj: T): T {
|
||||
}
|
||||
|
||||
class LocalJWKSet {
|
||||
private _jwks?: JSONWebKeySet
|
||||
private _jwks?: types.JSONWebKeySet
|
||||
|
||||
private _cached: WeakMap<JWK, Cache> = new WeakMap()
|
||||
private _cached: WeakMap<types.JWK, Cache> = new WeakMap()
|
||||
|
||||
constructor(jwks: unknown) {
|
||||
if (!isJWKSLike(jwks)) {
|
||||
throw new JWKSInvalid('JSON Web Key Set malformed')
|
||||
}
|
||||
|
||||
this._jwks = clone<JSONWebKeySet>(jwks)
|
||||
this._jwks = clone<types.JSONWebKeySet>(jwks)
|
||||
}
|
||||
|
||||
async getKey(
|
||||
protectedHeader?: JWSHeaderParameters,
|
||||
token?: FlattenedJWSInput,
|
||||
): Promise<CryptoKey> {
|
||||
protectedHeader?: types.JWSHeaderParameters,
|
||||
token?: types.FlattenedJWSInput,
|
||||
): Promise<types.CryptoKey> {
|
||||
const { alg, kid } = { ...protectedHeader, ...token?.header }
|
||||
const kty = getKtyFromAlg(alg)
|
||||
|
||||
@@ -154,7 +146,7 @@ class LocalJWKSet {
|
||||
}
|
||||
}
|
||||
|
||||
async function importWithAlgCache(cache: WeakMap<JWK, Cache>, jwk: JWK, alg: string) {
|
||||
async function importWithAlgCache(cache: WeakMap<types.JWK, Cache>, jwk: types.JWK, alg: string) {
|
||||
const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk)!
|
||||
if (cached[alg] === undefined) {
|
||||
const key = await importJWK({ ...jwk, ext: true }, alg)
|
||||
@@ -252,14 +244,17 @@ async function importWithAlgCache(cache: WeakMap<JWK, Cache>, jwk: JWK, alg: str
|
||||
* @param jwks JSON Web Key Set formatted object.
|
||||
*/
|
||||
export function createLocalJWKSet(
|
||||
jwks: JSONWebKeySet,
|
||||
): (protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput) => Promise<CryptoKey> {
|
||||
jwks: types.JSONWebKeySet,
|
||||
): (
|
||||
protectedHeader?: types.JWSHeaderParameters,
|
||||
token?: types.FlattenedJWSInput,
|
||||
) => Promise<types.CryptoKey> {
|
||||
const set = new LocalJWKSet(jwks)
|
||||
|
||||
const localJWKSet = async (
|
||||
protectedHeader?: JWSHeaderParameters,
|
||||
token?: FlattenedJWSInput,
|
||||
): Promise<CryptoKey> => set.getKey(protectedHeader, token)
|
||||
protectedHeader?: types.JWSHeaderParameters,
|
||||
token?: types.FlattenedJWSInput,
|
||||
): Promise<types.CryptoKey> => set.getKey(protectedHeader, token)
|
||||
|
||||
Object.defineProperties(localJWKSet, {
|
||||
jwks: {
|
||||
|
||||
+186
-39
@@ -1,12 +1,5 @@
|
||||
import fetchJwks from '../runtime/fetch_jwks.js'
|
||||
|
||||
import type {
|
||||
CryptoKey,
|
||||
JWSHeaderParameters,
|
||||
FlattenedJWSInput,
|
||||
JSONWebKeySet,
|
||||
} from '../types.d.ts'
|
||||
import { JWKSNoMatchingKey } from '../util/errors.js'
|
||||
import type * as types from '../types.d.ts'
|
||||
import { JOSEError, JWKSNoMatchingKey, JWKSTimeout } from '../util/errors.js'
|
||||
|
||||
import { createLocalJWKSet } from './local.js'
|
||||
import isObject from '../lib/is_object.js'
|
||||
@@ -33,6 +26,160 @@ if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozi
|
||||
USER_AGENT = `${NAME}/${VERSION}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* Using [sindresorhus/ky](https://github.com/sindresorhus/ky) for retries and its hooks feature for
|
||||
* logging outgoing requests and their responses.
|
||||
*
|
||||
* ```ts
|
||||
* import ky from 'ky'
|
||||
*
|
||||
* let logRequest!: (request: Request) => void
|
||||
* let logResponse!: (request: Request, response: Response) => void
|
||||
* let logRetry!: (request: Request, error: Error, retryCount: number) => void
|
||||
*
|
||||
* const JWKS = jose.createRemoteJWKSet(url, {
|
||||
* [jose.customFetch]: (...args) =>
|
||||
* ky(args[0], {
|
||||
* ...args[1],
|
||||
* hooks: {
|
||||
* beforeRequest: [
|
||||
* (request) => {
|
||||
* logRequest(request)
|
||||
* },
|
||||
* ],
|
||||
* beforeRetry: [
|
||||
* ({ request, error, retryCount }) => {
|
||||
* logRetry(request, error, retryCount)
|
||||
* },
|
||||
* ],
|
||||
* afterResponse: [
|
||||
* (request, _, response) => {
|
||||
* logResponse(request, response)
|
||||
* },
|
||||
* ],
|
||||
* },
|
||||
* }),
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* Using [nodejs/undici](https://github.com/nodejs/undici) to detect and use HTTP proxies.
|
||||
*
|
||||
* ```ts
|
||||
* import * as undici from 'undici'
|
||||
*
|
||||
* // see https://undici.nodejs.org/#/docs/api/EnvHttpProxyAgent
|
||||
* let envHttpProxyAgent = new undici.EnvHttpProxyAgent()
|
||||
*
|
||||
* // @ts-ignore
|
||||
* const JWKS = jose.createRemoteJWKSet(url, {
|
||||
* [jose.customFetch]: (...args) => {
|
||||
* // @ts-ignore
|
||||
* return undici.fetch(args[0], { ...args[1], dispatcher: envHttpProxyAgent }) // prettier-ignore
|
||||
* },
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* Using [nodejs/undici](https://github.com/nodejs/undici) to automatically retry network errors.
|
||||
*
|
||||
* ```ts
|
||||
* import * as undici from 'undici'
|
||||
*
|
||||
* // see https://undici.nodejs.org/#/docs/api/RetryAgent
|
||||
* let retryAgent = new undici.RetryAgent(new undici.Agent(), {
|
||||
* statusCodes: [],
|
||||
* errorCodes: [
|
||||
* 'ECONNRESET',
|
||||
* 'ECONNREFUSED',
|
||||
* 'ENOTFOUND',
|
||||
* 'ENETDOWN',
|
||||
* 'ENETUNREACH',
|
||||
* 'EHOSTDOWN',
|
||||
* 'UND_ERR_SOCKET',
|
||||
* ],
|
||||
* })
|
||||
*
|
||||
* // @ts-ignore
|
||||
* const JWKS = jose.createRemoteJWKSet(url, {
|
||||
* [jose.customFetch]: (...args) => {
|
||||
* // @ts-ignore
|
||||
* return undici.fetch(args[0], { ...args[1], dispatcher: retryAgent }) // prettier-ignore
|
||||
* },
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* Using [nodejs/undici](https://github.com/nodejs/undici) to mock responses in tests.
|
||||
*
|
||||
* ```ts
|
||||
* import * as undici from 'undici'
|
||||
*
|
||||
* // see https://undici.nodejs.org/#/docs/api/MockAgent
|
||||
* let mockAgent = new undici.MockAgent()
|
||||
* mockAgent.disableNetConnect()
|
||||
*
|
||||
* // @ts-ignore
|
||||
* const JWKS = jose.createRemoteJWKSet(url, {
|
||||
* [jose.customFetch]: (...args) => {
|
||||
* // @ts-ignore
|
||||
* return undici.fetch(args[0], { ...args[1], dispatcher: mockAgent }) // prettier-ignore
|
||||
* },
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export const customFetch: unique symbol = Symbol()
|
||||
|
||||
export type FetchImplementation = (
|
||||
url: string,
|
||||
options: {
|
||||
headers: Headers
|
||||
method: 'GET'
|
||||
redirect: 'manual'
|
||||
signal: AbortSignal
|
||||
},
|
||||
) => Promise<Response>
|
||||
|
||||
async function fetchJwks(
|
||||
url: string,
|
||||
options: {
|
||||
headers: Headers
|
||||
signal: AbortSignal
|
||||
[customFetch]?: FetchImplementation
|
||||
},
|
||||
) {
|
||||
const response = await (options?.[customFetch] || fetch)(url, {
|
||||
method: 'GET',
|
||||
signal: options.signal,
|
||||
redirect: 'manual',
|
||||
headers: options.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')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DANGER ZONE - 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
|
||||
@@ -108,29 +255,18 @@ export interface RemoteJWKSetOptions {
|
||||
*/
|
||||
cacheMaxAge?: number | typeof Infinity
|
||||
|
||||
/**
|
||||
* An instance of {@link https://nodejs.org/api/http.html#class-httpagent http.Agent} or
|
||||
* {@link https://nodejs.org/api/https.html#class-httpsagent https.Agent} to pass to the
|
||||
* {@link https://nodejs.org/api/http.html#httpgetoptions-callback http.get} or
|
||||
* {@link https://nodejs.org/api/https.html#httpsgetoptions-callback https.get} method's options.
|
||||
* Use when behind an http(s) proxy. This is a Node.js runtime specific option, it is ignored when
|
||||
* used outside of Node.js runtime.
|
||||
*/
|
||||
agent?: any
|
||||
|
||||
/**
|
||||
* Headers to be sent with the HTTP request. Default is that `User-Agent: jose/v${version}` header
|
||||
* is added unless the runtime is a browser in which adding an explicit headers fetch
|
||||
* configuration would cause an unnecessary CORS preflight request.
|
||||
*/
|
||||
/** Headers to be sent with the HTTP request. */
|
||||
headers?: Record<string, string>
|
||||
|
||||
/** See {@link jwksCache}. */
|
||||
[jwksCache]?: JWKSCacheInput
|
||||
|
||||
/** See {@link customFetch}. */
|
||||
[customFetch]?: FetchImplementation
|
||||
}
|
||||
|
||||
export interface ExportedJWKSCache {
|
||||
jwks: JSONWebKeySet
|
||||
jwks: types.JSONWebKeySet
|
||||
uat: number
|
||||
}
|
||||
|
||||
@@ -147,7 +283,7 @@ function isFreshJwksCache(input: unknown, cacheMaxAge: number): input is Exporte
|
||||
|
||||
if (
|
||||
!('jwks' in input) ||
|
||||
!isObject<JSONWebKeySet>(input.jwks) ||
|
||||
!isObject<types.JSONWebKeySet>(input.jwks) ||
|
||||
!Array.isArray(input.jwks.keys) ||
|
||||
!Array.prototype.every.call(input.jwks.keys, isObject)
|
||||
) {
|
||||
@@ -170,7 +306,7 @@ class RemoteJWKSet {
|
||||
|
||||
private _pendingFetch?: Promise<unknown>
|
||||
|
||||
private _options: Pick<RemoteJWKSetOptions, 'agent' | 'headers'>
|
||||
private _options: Pick<RemoteJWKSetOptions, 'headers'>
|
||||
|
||||
private _local!: ReturnType<typeof createLocalJWKSet>
|
||||
|
||||
@@ -181,7 +317,7 @@ class RemoteJWKSet {
|
||||
throw new TypeError('url must be an instance of URL')
|
||||
}
|
||||
this._url = new URL(url.href)
|
||||
this._options = { agent: options?.agent, headers: options?.headers }
|
||||
this._options = { headers: options?.headers }
|
||||
this._timeoutDuration =
|
||||
typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000
|
||||
this._cooldownDuration =
|
||||
@@ -210,9 +346,9 @@ class RemoteJWKSet {
|
||||
}
|
||||
|
||||
async getKey(
|
||||
protectedHeader?: JWSHeaderParameters,
|
||||
token?: FlattenedJWSInput,
|
||||
): Promise<CryptoKey> {
|
||||
protectedHeader?: types.JWSHeaderParameters,
|
||||
token?: types.FlattenedJWSInput,
|
||||
): Promise<types.CryptoKey> {
|
||||
if (!this._local || !this.fresh()) {
|
||||
await this.reload()
|
||||
}
|
||||
@@ -243,12 +379,20 @@ class RemoteJWKSet {
|
||||
this._options.headers = Object.fromEntries(headers.entries())
|
||||
}
|
||||
|
||||
this._pendingFetch ||= fetchJwks(this._url, this._timeoutDuration, this._options)
|
||||
if (!headers.has('accept')) {
|
||||
headers.set('accept', 'application/json')
|
||||
headers.append('accept', 'application/jwk-set+json')
|
||||
}
|
||||
|
||||
this._pendingFetch ||= fetchJwks(this._url.href, {
|
||||
headers,
|
||||
signal: AbortSignal.timeout(this._timeoutDuration),
|
||||
})
|
||||
.then((json) => {
|
||||
this._local = createLocalJWKSet(json as unknown as JSONWebKeySet)
|
||||
this._local = createLocalJWKSet(json as unknown as types.JSONWebKeySet)
|
||||
if (this._cache) {
|
||||
this._cache.uat = Date.now()
|
||||
this._cache.jwks = json as unknown as JSONWebKeySet
|
||||
this._cache.jwks = json as unknown as types.JSONWebKeySet
|
||||
}
|
||||
this._jwksTimestamp = Date.now()
|
||||
this._pendingFetch = undefined
|
||||
@@ -335,7 +479,10 @@ export function createRemoteJWKSet(
|
||||
url: URL,
|
||||
options?: RemoteJWKSetOptions,
|
||||
): {
|
||||
(protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput): Promise<CryptoKey>
|
||||
(
|
||||
protectedHeader?: types.JWSHeaderParameters,
|
||||
token?: types.FlattenedJWSInput,
|
||||
): Promise<types.CryptoKey>
|
||||
/** @ignore */
|
||||
coolingDown: boolean
|
||||
/** @ignore */
|
||||
@@ -345,14 +492,14 @@ export function createRemoteJWKSet(
|
||||
/** @ignore */
|
||||
reload: () => Promise<void>
|
||||
/** @ignore */
|
||||
jwks: () => JSONWebKeySet | undefined
|
||||
jwks: () => types.JSONWebKeySet | undefined
|
||||
} {
|
||||
const set = new RemoteJWKSet(url, options)
|
||||
|
||||
const remoteJWKSet = async (
|
||||
protectedHeader?: JWSHeaderParameters,
|
||||
token?: FlattenedJWSInput,
|
||||
): Promise<CryptoKey> => set.getKey(protectedHeader, token)
|
||||
protectedHeader?: types.JWSHeaderParameters,
|
||||
token?: types.FlattenedJWSInput,
|
||||
): Promise<types.CryptoKey> => set.getKey(protectedHeader, token)
|
||||
|
||||
Object.defineProperties(remoteJWKSet, {
|
||||
coolingDown: {
|
||||
|
||||
+4
-10
@@ -1,11 +1,5 @@
|
||||
import type * as types from '../../types.d.ts'
|
||||
import { FlattenedSign } from '../flattened/sign.js'
|
||||
import type {
|
||||
JWK,
|
||||
CompactJWSHeaderParameters,
|
||||
CryptoKey,
|
||||
SignOptions,
|
||||
KeyObject,
|
||||
} from '../../types.d.ts'
|
||||
|
||||
/**
|
||||
* The CompactSign class is used to build and sign Compact JWS strings.
|
||||
@@ -38,7 +32,7 @@ export class CompactSign {
|
||||
*
|
||||
* @param protectedHeader JWS Protected Header.
|
||||
*/
|
||||
setProtectedHeader(protectedHeader: CompactJWSHeaderParameters): this {
|
||||
setProtectedHeader(protectedHeader: types.CompactJWSHeaderParameters): this {
|
||||
this._flattened.setProtectedHeader(protectedHeader)
|
||||
return this
|
||||
}
|
||||
@@ -51,8 +45,8 @@ export class CompactSign {
|
||||
* @param options JWS Sign options.
|
||||
*/
|
||||
async sign(
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: SignOptions,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.SignOptions,
|
||||
): Promise<string> {
|
||||
const jws = await this._flattened.sign(key, options)
|
||||
|
||||
|
||||
+12
-22
@@ -1,17 +1,7 @@
|
||||
import type * as types from '../../types.d.ts'
|
||||
import { flattenedVerify } from '../flattened/verify.js'
|
||||
import { JWSInvalid } from '../../util/errors.js'
|
||||
import { decoder } from '../../lib/buffer_utils.js'
|
||||
import type {
|
||||
JWK,
|
||||
CompactVerifyResult,
|
||||
FlattenedJWSInput,
|
||||
GenericGetKeyFunction,
|
||||
CompactJWSHeaderParameters,
|
||||
CryptoKey,
|
||||
VerifyOptions,
|
||||
ResolvedKey,
|
||||
KeyObject,
|
||||
} from '../../types.d.ts'
|
||||
|
||||
/**
|
||||
* Interface for Compact JWS Verification dynamic key resolution. No token components have been
|
||||
@@ -20,10 +10,10 @@ import type {
|
||||
* @see {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} to verify using a remote JSON Web Key Set.
|
||||
*/
|
||||
export interface CompactVerifyGetKey
|
||||
extends GenericGetKeyFunction<
|
||||
CompactJWSHeaderParameters,
|
||||
FlattenedJWSInput,
|
||||
CryptoKey | KeyObject | JWK | Uint8Array
|
||||
extends types.GenericGetKeyFunction<
|
||||
types.CompactJWSHeaderParameters,
|
||||
types.FlattenedJWSInput,
|
||||
types.CryptoKey | types.KeyObject | types.JWK | Uint8Array
|
||||
> {}
|
||||
|
||||
/**
|
||||
@@ -51,9 +41,9 @@ export interface CompactVerifyGetKey
|
||||
*/
|
||||
export function compactVerify(
|
||||
jws: string | Uint8Array,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: VerifyOptions,
|
||||
): Promise<CompactVerifyResult>
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.VerifyOptions,
|
||||
): Promise<types.CompactVerifyResult>
|
||||
/**
|
||||
* @param jws Compact JWS.
|
||||
* @param getKey Function resolving a key to verify the JWS with. See
|
||||
@@ -63,12 +53,12 @@ export function compactVerify(
|
||||
export function compactVerify(
|
||||
jws: string | Uint8Array,
|
||||
getKey: CompactVerifyGetKey,
|
||||
options?: VerifyOptions,
|
||||
): Promise<CompactVerifyResult & ResolvedKey>
|
||||
options?: types.VerifyOptions,
|
||||
): Promise<types.CompactVerifyResult & types.ResolvedKey>
|
||||
export async function compactVerify(
|
||||
jws: string | Uint8Array,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array | CompactVerifyGetKey,
|
||||
options?: VerifyOptions,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | CompactVerifyGetKey,
|
||||
options?: types.VerifyOptions,
|
||||
) {
|
||||
if (jws instanceof Uint8Array) {
|
||||
jws = decoder.decode(jws)
|
||||
|
||||
+13
-20
@@ -1,20 +1,13 @@
|
||||
import { encode as base64url } from '../../runtime/base64url.js'
|
||||
import sign from '../../runtime/sign.js'
|
||||
import type * as types from '../../types.d.ts'
|
||||
import { encode as base64url } from '../../lib/base64url.js'
|
||||
import sign from '../../lib/sign.js'
|
||||
|
||||
import isDisjoint from '../../lib/is_disjoint.js'
|
||||
import { JWSInvalid } from '../../util/errors.js'
|
||||
import { encoder, decoder, concat } from '../../lib/buffer_utils.js'
|
||||
import type {
|
||||
JWK,
|
||||
CryptoKey,
|
||||
FlattenedJWS,
|
||||
JWSHeaderParameters,
|
||||
SignOptions,
|
||||
KeyObject,
|
||||
} from '../../types.d.ts'
|
||||
import checkKeyType from '../../lib/check_key_type.js'
|
||||
import validateCrit from '../../lib/validate_crit.js'
|
||||
import normalizeKey from '../../runtime/normalize_key.js'
|
||||
import normalizeKey from '../../lib/normalize_key.js'
|
||||
|
||||
/**
|
||||
* The FlattenedSign class is used to build and sign Flattened JWS objects.
|
||||
@@ -37,9 +30,9 @@ import normalizeKey from '../../runtime/normalize_key.js'
|
||||
export class FlattenedSign {
|
||||
private _payload: Uint8Array
|
||||
|
||||
private _protectedHeader!: JWSHeaderParameters
|
||||
private _protectedHeader!: types.JWSHeaderParameters
|
||||
|
||||
private _unprotectedHeader!: JWSHeaderParameters
|
||||
private _unprotectedHeader!: types.JWSHeaderParameters
|
||||
|
||||
/** @param payload Binary representation of the payload to sign. */
|
||||
constructor(payload: Uint8Array) {
|
||||
@@ -54,7 +47,7 @@ export class FlattenedSign {
|
||||
*
|
||||
* @param protectedHeader JWS Protected Header.
|
||||
*/
|
||||
setProtectedHeader(protectedHeader: JWSHeaderParameters): this {
|
||||
setProtectedHeader(protectedHeader: types.JWSHeaderParameters): this {
|
||||
if (this._protectedHeader) {
|
||||
throw new TypeError('setProtectedHeader can only be called once')
|
||||
}
|
||||
@@ -67,7 +60,7 @@ export class FlattenedSign {
|
||||
*
|
||||
* @param unprotectedHeader JWS Unprotected Header.
|
||||
*/
|
||||
setUnprotectedHeader(unprotectedHeader: JWSHeaderParameters): this {
|
||||
setUnprotectedHeader(unprotectedHeader: types.JWSHeaderParameters): this {
|
||||
if (this._unprotectedHeader) {
|
||||
throw new TypeError('setUnprotectedHeader can only be called once')
|
||||
}
|
||||
@@ -83,9 +76,9 @@ export class FlattenedSign {
|
||||
* @param options JWS Sign options.
|
||||
*/
|
||||
async sign(
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: SignOptions,
|
||||
): Promise<FlattenedJWS> {
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.SignOptions,
|
||||
): Promise<types.FlattenedJWS> {
|
||||
if (!this._protectedHeader && !this._unprotectedHeader) {
|
||||
throw new JWSInvalid(
|
||||
'either setProtectedHeader or setUnprotectedHeader must be called before #sign()',
|
||||
@@ -98,7 +91,7 @@ export class FlattenedSign {
|
||||
)
|
||||
}
|
||||
|
||||
const joseHeader: JWSHeaderParameters = {
|
||||
const joseHeader: types.JWSHeaderParameters = {
|
||||
...this._protectedHeader,
|
||||
...this._unprotectedHeader,
|
||||
}
|
||||
@@ -146,7 +139,7 @@ export class FlattenedSign {
|
||||
const k = await normalizeKey(key, alg)
|
||||
const signature = await sign(alg, k, data)
|
||||
|
||||
const jws: FlattenedJWS = {
|
||||
const jws: types.FlattenedJWS = {
|
||||
signature: base64url(signature),
|
||||
payload: '',
|
||||
}
|
||||
|
||||
+21
-32
@@ -1,5 +1,6 @@
|
||||
import { decode as base64url } from '../../runtime/base64url.js'
|
||||
import verify from '../../runtime/verify.js'
|
||||
import type * as types from '../../types.d.ts'
|
||||
import { decode as base64url } from '../../lib/base64url.js'
|
||||
import verify from '../../lib/verify.js'
|
||||
|
||||
import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js'
|
||||
import { concat, encoder, decoder } from '../../lib/buffer_utils.js'
|
||||
@@ -8,19 +9,7 @@ import isObject from '../../lib/is_object.js'
|
||||
import checkKeyType from '../../lib/check_key_type.js'
|
||||
import validateCrit from '../../lib/validate_crit.js'
|
||||
import validateAlgorithms from '../../lib/validate_algorithms.js'
|
||||
import normalizeKey from '../../runtime/normalize_key.js'
|
||||
|
||||
import type {
|
||||
JWK,
|
||||
FlattenedVerifyResult,
|
||||
CryptoKey,
|
||||
FlattenedJWSInput,
|
||||
JWSHeaderParameters,
|
||||
VerifyOptions,
|
||||
GenericGetKeyFunction,
|
||||
ResolvedKey,
|
||||
KeyObject,
|
||||
} from '../../types.d.ts'
|
||||
import normalizeKey from '../../lib/normalize_key.js'
|
||||
|
||||
/**
|
||||
* Interface for Flattened JWS Verification dynamic key resolution. No token components have been
|
||||
@@ -29,10 +18,10 @@ import type {
|
||||
* @see {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} to verify using a remote JSON Web Key Set.
|
||||
*/
|
||||
export interface FlattenedVerifyGetKey
|
||||
extends GenericGetKeyFunction<
|
||||
JWSHeaderParameters | undefined,
|
||||
FlattenedJWSInput,
|
||||
CryptoKey | KeyObject | JWK | Uint8Array
|
||||
extends types.GenericGetKeyFunction<
|
||||
types.JWSHeaderParameters | undefined,
|
||||
types.FlattenedJWSInput,
|
||||
types.CryptoKey | types.KeyObject | types.JWK | Uint8Array
|
||||
> {}
|
||||
|
||||
/**
|
||||
@@ -64,10 +53,10 @@ export interface FlattenedVerifyGetKey
|
||||
* @param options JWS Verify options.
|
||||
*/
|
||||
export function flattenedVerify(
|
||||
jws: FlattenedJWSInput,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: VerifyOptions,
|
||||
): Promise<FlattenedVerifyResult>
|
||||
jws: types.FlattenedJWSInput,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.VerifyOptions,
|
||||
): Promise<types.FlattenedVerifyResult>
|
||||
/**
|
||||
* @param jws Flattened JWS.
|
||||
* @param getKey Function resolving a key to verify the JWS with. See
|
||||
@@ -75,14 +64,14 @@ export function flattenedVerify(
|
||||
* @param options JWS Verify options.
|
||||
*/
|
||||
export function flattenedVerify(
|
||||
jws: FlattenedJWSInput,
|
||||
jws: types.FlattenedJWSInput,
|
||||
getKey: FlattenedVerifyGetKey,
|
||||
options?: VerifyOptions,
|
||||
): Promise<FlattenedVerifyResult & ResolvedKey>
|
||||
options?: types.VerifyOptions,
|
||||
): Promise<types.FlattenedVerifyResult & types.ResolvedKey>
|
||||
export async function flattenedVerify(
|
||||
jws: FlattenedJWSInput,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array | FlattenedVerifyGetKey,
|
||||
options?: VerifyOptions,
|
||||
jws: types.FlattenedJWSInput,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | FlattenedVerifyGetKey,
|
||||
options?: types.VerifyOptions,
|
||||
) {
|
||||
if (!isObject(jws)) {
|
||||
throw new JWSInvalid('Flattened JWS must be an object')
|
||||
@@ -108,7 +97,7 @@ export async function flattenedVerify(
|
||||
throw new JWSInvalid('JWS Unprotected Header incorrect type')
|
||||
}
|
||||
|
||||
let parsedProt: JWSHeaderParameters = {}
|
||||
let parsedProt: types.JWSHeaderParameters = {}
|
||||
if (jws.protected) {
|
||||
try {
|
||||
const protectedHeader = base64url(jws.protected)
|
||||
@@ -123,7 +112,7 @@ export async function flattenedVerify(
|
||||
)
|
||||
}
|
||||
|
||||
const joseHeader: JWSHeaderParameters = {
|
||||
const joseHeader: types.JWSHeaderParameters = {
|
||||
...parsedProt,
|
||||
...jws.header,
|
||||
}
|
||||
@@ -206,7 +195,7 @@ export async function flattenedVerify(
|
||||
payload = jws.payload
|
||||
}
|
||||
|
||||
const result: FlattenedVerifyResult = { payload }
|
||||
const result: types.FlattenedVerifyResult = { payload }
|
||||
|
||||
if (jws.protected !== undefined) {
|
||||
result.protectedHeader = parsedProt
|
||||
|
||||
+18
-23
@@ -1,35 +1,27 @@
|
||||
import type * as types from '../../types.d.ts'
|
||||
import { FlattenedSign } from '../flattened/sign.js'
|
||||
import { JWSInvalid } from '../../util/errors.js'
|
||||
|
||||
import type {
|
||||
JWK,
|
||||
CryptoKey,
|
||||
GeneralJWS,
|
||||
JWSHeaderParameters,
|
||||
SignOptions,
|
||||
KeyObject,
|
||||
} from '../../types.d.ts'
|
||||
|
||||
export interface Signature {
|
||||
/**
|
||||
* Sets the JWS Protected Header on the Signature object.
|
||||
*
|
||||
* @param protectedHeader JWS Protected Header.
|
||||
*/
|
||||
setProtectedHeader(protectedHeader: JWSHeaderParameters): Signature
|
||||
setProtectedHeader(protectedHeader: types.JWSHeaderParameters): Signature
|
||||
|
||||
/**
|
||||
* Sets the JWS Unprotected Header on the Signature object.
|
||||
*
|
||||
* @param unprotectedHeader JWS Unprotected Header.
|
||||
*/
|
||||
setUnprotectedHeader(unprotectedHeader: JWSHeaderParameters): Signature
|
||||
setUnprotectedHeader(unprotectedHeader: types.JWSHeaderParameters): Signature
|
||||
|
||||
/** A shorthand for calling addSignature() on the enclosing GeneralSign instance */
|
||||
addSignature(...args: Parameters<GeneralSign['addSignature']>): Signature
|
||||
|
||||
/** A shorthand for calling encrypt() on the enclosing GeneralSign instance */
|
||||
sign(...args: Parameters<GeneralSign['sign']>): Promise<GeneralJWS>
|
||||
sign(...args: Parameters<GeneralSign['sign']>): Promise<types.GeneralJWS>
|
||||
|
||||
/** Returns the enclosing GeneralSign */
|
||||
done(): GeneralSign
|
||||
@@ -38,22 +30,22 @@ export interface Signature {
|
||||
class IndividualSignature implements Signature {
|
||||
private parent: GeneralSign
|
||||
|
||||
protectedHeader?: JWSHeaderParameters
|
||||
unprotectedHeader?: JWSHeaderParameters
|
||||
options?: SignOptions
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array
|
||||
protectedHeader?: types.JWSHeaderParameters
|
||||
unprotectedHeader?: types.JWSHeaderParameters
|
||||
options?: types.SignOptions
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array
|
||||
|
||||
constructor(
|
||||
sig: GeneralSign,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: SignOptions,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.SignOptions,
|
||||
) {
|
||||
this.parent = sig
|
||||
this.key = key
|
||||
this.options = options
|
||||
}
|
||||
|
||||
setProtectedHeader(protectedHeader: JWSHeaderParameters) {
|
||||
setProtectedHeader(protectedHeader: types.JWSHeaderParameters) {
|
||||
if (this.protectedHeader) {
|
||||
throw new TypeError('setProtectedHeader can only be called once')
|
||||
}
|
||||
@@ -61,7 +53,7 @@ class IndividualSignature implements Signature {
|
||||
return this
|
||||
}
|
||||
|
||||
setUnprotectedHeader(unprotectedHeader: JWSHeaderParameters) {
|
||||
setUnprotectedHeader(unprotectedHeader: types.JWSHeaderParameters) {
|
||||
if (this.unprotectedHeader) {
|
||||
throw new TypeError('setUnprotectedHeader can only be called once')
|
||||
}
|
||||
@@ -120,19 +112,22 @@ export class GeneralSign {
|
||||
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
|
||||
* @param options JWS Sign options.
|
||||
*/
|
||||
addSignature(key: CryptoKey | KeyObject | JWK | Uint8Array, options?: SignOptions): Signature {
|
||||
addSignature(
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.SignOptions,
|
||||
): Signature {
|
||||
const signature = new IndividualSignature(this, key, options)
|
||||
this._signatures.push(signature)
|
||||
return signature
|
||||
}
|
||||
|
||||
/** Signs and resolves the value of the General JWS object. */
|
||||
async sign(): Promise<GeneralJWS> {
|
||||
async sign(): Promise<types.GeneralJWS> {
|
||||
if (!this._signatures.length) {
|
||||
throw new JWSInvalid('at least one signature must be added')
|
||||
}
|
||||
|
||||
const jws: GeneralJWS = {
|
||||
const jws: types.GeneralJWS = {
|
||||
signatures: [],
|
||||
payload: '',
|
||||
}
|
||||
|
||||
+15
-26
@@ -1,16 +1,5 @@
|
||||
import type * as types from '../../types.d.ts'
|
||||
import { flattenedVerify } from '../flattened/verify.js'
|
||||
import type {
|
||||
JWK,
|
||||
GeneralJWSInput,
|
||||
GeneralVerifyResult,
|
||||
FlattenedJWSInput,
|
||||
GenericGetKeyFunction,
|
||||
JWSHeaderParameters,
|
||||
CryptoKey,
|
||||
VerifyOptions,
|
||||
ResolvedKey,
|
||||
KeyObject,
|
||||
} from '../../types.d.ts'
|
||||
import { JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js'
|
||||
import isObject from '../../lib/is_object.js'
|
||||
|
||||
@@ -21,10 +10,10 @@ import isObject from '../../lib/is_object.js'
|
||||
* @see {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} to verify using a remote JSON Web Key Set.
|
||||
*/
|
||||
export interface GeneralVerifyGetKey
|
||||
extends GenericGetKeyFunction<
|
||||
JWSHeaderParameters,
|
||||
FlattenedJWSInput,
|
||||
CryptoKey | KeyObject | JWK | Uint8Array
|
||||
extends types.GenericGetKeyFunction<
|
||||
types.JWSHeaderParameters,
|
||||
types.FlattenedJWSInput,
|
||||
types.CryptoKey | types.KeyObject | types.JWK | Uint8Array
|
||||
> {}
|
||||
|
||||
/**
|
||||
@@ -59,10 +48,10 @@ export interface GeneralVerifyGetKey
|
||||
* @param options JWS Verify options.
|
||||
*/
|
||||
export function generalVerify(
|
||||
jws: GeneralJWSInput,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: VerifyOptions,
|
||||
): Promise<GeneralVerifyResult>
|
||||
jws: types.GeneralJWSInput,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.VerifyOptions,
|
||||
): Promise<types.GeneralVerifyResult>
|
||||
/**
|
||||
* @param jws General JWS.
|
||||
* @param getKey Function resolving a key to verify the JWS with. See
|
||||
@@ -70,14 +59,14 @@ export function generalVerify(
|
||||
* @param options JWS Verify options.
|
||||
*/
|
||||
export function generalVerify(
|
||||
jws: GeneralJWSInput,
|
||||
jws: types.GeneralJWSInput,
|
||||
getKey: GeneralVerifyGetKey,
|
||||
options?: VerifyOptions,
|
||||
): Promise<GeneralVerifyResult & ResolvedKey>
|
||||
options?: types.VerifyOptions,
|
||||
): Promise<types.GeneralVerifyResult & types.ResolvedKey>
|
||||
export async function generalVerify(
|
||||
jws: GeneralJWSInput,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array | GeneralVerifyGetKey,
|
||||
options?: VerifyOptions,
|
||||
jws: types.GeneralJWSInput,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | GeneralVerifyGetKey,
|
||||
options?: types.VerifyOptions,
|
||||
) {
|
||||
if (!isObject(jws)) {
|
||||
throw new JWSInvalid('General JWS must be an object')
|
||||
|
||||
+11
-21
@@ -1,29 +1,19 @@
|
||||
import type * as types from '../types.d.ts'
|
||||
import { compactDecrypt } from '../jwe/compact/decrypt.js'
|
||||
import type {
|
||||
JWTPayload,
|
||||
CryptoKey,
|
||||
DecryptOptions,
|
||||
JWTClaimVerificationOptions,
|
||||
GetKeyFunction,
|
||||
CompactJWEHeaderParameters,
|
||||
FlattenedJWE,
|
||||
JWTDecryptResult,
|
||||
ResolvedKey,
|
||||
KeyObject,
|
||||
JWK,
|
||||
} from '../types.d.ts'
|
||||
import jwtPayload from '../lib/jwt_claims_set.js'
|
||||
import { JWTClaimValidationFailed } from '../util/errors.js'
|
||||
|
||||
/** Combination of JWE Decryption options and JWT Claims Set verification options. */
|
||||
export interface JWTDecryptOptions extends DecryptOptions, JWTClaimVerificationOptions {}
|
||||
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
|
||||
extends GetKeyFunction<CompactJWEHeaderParameters, FlattenedJWE> {}
|
||||
extends types.GetKeyFunction<types.CompactJWEHeaderParameters, types.FlattenedJWE> {}
|
||||
|
||||
/**
|
||||
* Verifies the JWT format (to be a JWE Compact format), decrypts the ciphertext, validates the JWT
|
||||
@@ -53,25 +43,25 @@ export interface JWTDecryptGetKey
|
||||
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
|
||||
* @param options JWT Decryption and JWT Claims Set validation options.
|
||||
*/
|
||||
export async function jwtDecrypt<PayloadType = JWTPayload>(
|
||||
export async function jwtDecrypt<PayloadType = types.JWTPayload>(
|
||||
jwt: string | Uint8Array,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: JWTDecryptOptions,
|
||||
): Promise<JWTDecryptResult<PayloadType>>
|
||||
): Promise<types.JWTDecryptResult<PayloadType>>
|
||||
/**
|
||||
* @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 async function jwtDecrypt<PayloadType = JWTPayload>(
|
||||
export async function jwtDecrypt<PayloadType = types.JWTPayload>(
|
||||
jwt: string | Uint8Array,
|
||||
getKey: JWTDecryptGetKey,
|
||||
options?: JWTDecryptOptions,
|
||||
): Promise<JWTDecryptResult<PayloadType> & ResolvedKey>
|
||||
): Promise<types.JWTDecryptResult<PayloadType> & types.ResolvedKey>
|
||||
export async function jwtDecrypt(
|
||||
jwt: string | Uint8Array,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array | JWTDecryptGetKey,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | JWTDecryptGetKey,
|
||||
options?: JWTDecryptOptions,
|
||||
) {
|
||||
const decrypted = await compactDecrypt(jwt, key as Parameters<typeof compactDecrypt>[1], options)
|
||||
|
||||
+7
-14
@@ -1,12 +1,5 @@
|
||||
import type * as types from '../types.d.ts'
|
||||
import { CompactEncrypt } from '../jwe/compact/encrypt.js'
|
||||
import type {
|
||||
EncryptOptions,
|
||||
CompactJWEHeaderParameters,
|
||||
JWEKeyManagementHeaderParameters,
|
||||
CryptoKey,
|
||||
KeyObject,
|
||||
JWK,
|
||||
} from '../types.d.ts'
|
||||
import { encoder } from '../lib/buffer_utils.js'
|
||||
import { ProduceJWT } from './produce.js'
|
||||
|
||||
@@ -36,9 +29,9 @@ export class EncryptJWT extends ProduceJWT {
|
||||
|
||||
private _iv!: Uint8Array
|
||||
|
||||
private _keyManagementParameters!: JWEKeyManagementHeaderParameters
|
||||
private _keyManagementParameters!: types.JWEKeyManagementHeaderParameters
|
||||
|
||||
private _protectedHeader!: CompactJWEHeaderParameters
|
||||
private _protectedHeader!: types.CompactJWEHeaderParameters
|
||||
|
||||
private _replicateIssuerAsHeader!: boolean
|
||||
|
||||
@@ -52,7 +45,7 @@ export class EncryptJWT extends ProduceJWT {
|
||||
* @param protectedHeader JWE Protected Header. Must contain an "alg" (JWE Algorithm) and "enc"
|
||||
* (JWE Encryption Algorithm) properties.
|
||||
*/
|
||||
setProtectedHeader(protectedHeader: CompactJWEHeaderParameters): this {
|
||||
setProtectedHeader(protectedHeader: types.CompactJWEHeaderParameters): this {
|
||||
if (this._protectedHeader) {
|
||||
throw new TypeError('setProtectedHeader can only be called once')
|
||||
}
|
||||
@@ -68,7 +61,7 @@ export class EncryptJWT extends ProduceJWT {
|
||||
*
|
||||
* @param parameters JWE Key Management parameters.
|
||||
*/
|
||||
setKeyManagementParameters(parameters: JWEKeyManagementHeaderParameters): this {
|
||||
setKeyManagementParameters(parameters: types.JWEKeyManagementHeaderParameters): this {
|
||||
if (this._keyManagementParameters) {
|
||||
throw new TypeError('setKeyManagementParameters can only be called once')
|
||||
}
|
||||
@@ -148,8 +141,8 @@ export class EncryptJWT extends ProduceJWT {
|
||||
* @param options JWE Encryption options.
|
||||
*/
|
||||
async encrypt(
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: EncryptOptions,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.EncryptOptions,
|
||||
): Promise<string> {
|
||||
const enc = new CompactEncrypt(encoder.encode(JSON.stringify(this._payload)))
|
||||
if (this._replicateIssuerAsHeader) {
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
import type { JWTPayload } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import epoch from '../lib/epoch.js'
|
||||
import isObject from '../lib/is_object.js'
|
||||
import secs from '../lib/secs.js'
|
||||
@@ -13,10 +13,10 @@ function validateInput(label: string, input: number) {
|
||||
|
||||
/** Generic class for JWT producing. */
|
||||
export class ProduceJWT {
|
||||
protected _payload!: JWTPayload
|
||||
protected _payload!: types.JWTPayload
|
||||
|
||||
/** @param payload The JWT Claims Set object. Defaults to an empty object. */
|
||||
constructor(payload: JWTPayload = {}) {
|
||||
constructor(payload: types.JWTPayload = {}) {
|
||||
if (!isObject(payload)) {
|
||||
throw new TypeError('JWT Claims Set MUST be an object')
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
import { CompactSign } from '../jws/compact/sign.js'
|
||||
import { JWTInvalid } from '../util/errors.js'
|
||||
import type { JWK, JWTHeaderParameters, CryptoKey, SignOptions, KeyObject } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import { encoder } from '../lib/buffer_utils.js'
|
||||
import { ProduceJWT } from './produce.js'
|
||||
|
||||
@@ -109,14 +109,14 @@ import { ProduceJWT } from './produce.js'
|
||||
* ```
|
||||
*/
|
||||
export class SignJWT extends ProduceJWT {
|
||||
private _protectedHeader!: JWTHeaderParameters
|
||||
private _protectedHeader!: types.JWTHeaderParameters
|
||||
|
||||
/**
|
||||
* Sets the JWS Protected Header on the SignJWT object.
|
||||
*
|
||||
* @param protectedHeader JWS Protected Header. Must contain an "alg" (JWS Algorithm) property.
|
||||
*/
|
||||
setProtectedHeader(protectedHeader: JWTHeaderParameters): this {
|
||||
setProtectedHeader(protectedHeader: types.JWTHeaderParameters): this {
|
||||
this._protectedHeader = protectedHeader
|
||||
return this
|
||||
}
|
||||
@@ -129,8 +129,8 @@ export class SignJWT extends ProduceJWT {
|
||||
* @param options JWT Sign options.
|
||||
*/
|
||||
async sign(
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
options?: SignOptions,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: types.SignOptions,
|
||||
): Promise<string> {
|
||||
const sig = new CompactSign(encoder.encode(JSON.stringify(this._payload)))
|
||||
sig.setProtectedHeader(this._protectedHeader)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import * as base64url from '../runtime/base64url.js'
|
||||
import * as base64url from '../lib/base64url.js'
|
||||
|
||||
import type { JWSHeaderParameters, JWTClaimVerificationOptions, JWTPayload } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import { decoder } from '../lib/buffer_utils.js'
|
||||
import { JWTInvalid } from '../util/errors.js'
|
||||
import jwtPayload from '../lib/jwt_claims_set.js'
|
||||
import { ProduceJWT } from './produce.js'
|
||||
|
||||
export interface UnsecuredResult<PayloadType = JWTPayload> {
|
||||
payload: PayloadType & JWTPayload
|
||||
header: JWSHeaderParameters
|
||||
export interface UnsecuredResult<PayloadType = types.JWTPayload> {
|
||||
payload: PayloadType & types.JWTPayload
|
||||
header: types.JWSHeaderParameters
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,9 +60,9 @@ export class UnsecuredJWT extends ProduceJWT {
|
||||
* @param jwt Unsecured JWT to decode the payload of.
|
||||
* @param options JWT Claims Set validation options.
|
||||
*/
|
||||
static decode<PayloadType = JWTPayload>(
|
||||
static decode<PayloadType = types.JWTPayload>(
|
||||
jwt: string,
|
||||
options?: JWTClaimVerificationOptions,
|
||||
options?: types.JWTClaimVerificationOptions,
|
||||
): UnsecuredResult<PayloadType> {
|
||||
if (typeof jwt !== 'string') {
|
||||
throw new JWTInvalid('Unsecured JWT must be a string')
|
||||
@@ -73,7 +73,7 @@ export class UnsecuredJWT extends ProduceJWT {
|
||||
throw new JWTInvalid('Invalid Unsecured JWT')
|
||||
}
|
||||
|
||||
let header: JWSHeaderParameters
|
||||
let header: types.JWSHeaderParameters
|
||||
try {
|
||||
header = JSON.parse(decoder.decode(base64url.decode(encodedHeader)))
|
||||
if (header.alg !== 'none') throw new Error()
|
||||
|
||||
+12
-24
@@ -1,22 +1,10 @@
|
||||
import { compactVerify } from '../jws/compact/verify.js'
|
||||
import type {
|
||||
JWK,
|
||||
JWTPayload,
|
||||
CryptoKey,
|
||||
VerifyOptions,
|
||||
JWTClaimVerificationOptions,
|
||||
JWTHeaderParameters,
|
||||
GenericGetKeyFunction,
|
||||
FlattenedJWSInput,
|
||||
JWTVerifyResult,
|
||||
ResolvedKey,
|
||||
KeyObject,
|
||||
} from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import jwtPayload from '../lib/jwt_claims_set.js'
|
||||
import { JWTInvalid } from '../util/errors.js'
|
||||
|
||||
/** Combination of JWS Verification options and JWT Claims Set verification options. */
|
||||
export interface JWTVerifyOptions extends VerifyOptions, JWTClaimVerificationOptions {}
|
||||
export interface JWTVerifyOptions extends types.VerifyOptions, types.JWTClaimVerificationOptions {}
|
||||
|
||||
/**
|
||||
* Interface for JWT Verification dynamic key resolution. No token components have been verified at
|
||||
@@ -25,10 +13,10 @@ export interface JWTVerifyOptions extends VerifyOptions, JWTClaimVerificationOpt
|
||||
* @see {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} to verify using a remote JSON Web Key Set.
|
||||
*/
|
||||
export interface JWTVerifyGetKey
|
||||
extends GenericGetKeyFunction<
|
||||
JWTHeaderParameters,
|
||||
FlattenedJWSInput,
|
||||
CryptoKey | KeyObject | JWK | Uint8Array
|
||||
extends types.GenericGetKeyFunction<
|
||||
types.JWTHeaderParameters,
|
||||
types.FlattenedJWSInput,
|
||||
types.CryptoKey | types.KeyObject | types.JWK | Uint8Array
|
||||
> {}
|
||||
|
||||
/**
|
||||
@@ -115,11 +103,11 @@ export interface JWTVerifyGetKey
|
||||
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
|
||||
* @param options JWT Decryption and JWT Claims Set validation options.
|
||||
*/
|
||||
export async function jwtVerify<PayloadType = JWTPayload>(
|
||||
export async function jwtVerify<PayloadType = types.JWTPayload>(
|
||||
jwt: string | Uint8Array,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
options?: JWTVerifyOptions,
|
||||
): Promise<JWTVerifyResult<PayloadType>>
|
||||
): Promise<types.JWTVerifyResult<PayloadType>>
|
||||
|
||||
/**
|
||||
* @example
|
||||
@@ -142,15 +130,15 @@ export async function jwtVerify<PayloadType = JWTPayload>(
|
||||
* {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}.
|
||||
* @param options JWT Decryption and JWT Claims Set validation options.
|
||||
*/
|
||||
export async function jwtVerify<PayloadType = JWTPayload>(
|
||||
export async function jwtVerify<PayloadType = types.JWTPayload>(
|
||||
jwt: string | Uint8Array,
|
||||
getKey: JWTVerifyGetKey,
|
||||
options?: JWTVerifyOptions,
|
||||
): Promise<JWTVerifyResult<PayloadType> & ResolvedKey>
|
||||
): Promise<types.JWTVerifyResult<PayloadType> & types.ResolvedKey>
|
||||
|
||||
export async function jwtVerify(
|
||||
jwt: string | Uint8Array,
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array | JWTVerifyGetKey,
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | JWTVerifyGetKey,
|
||||
options?: JWTVerifyOptions,
|
||||
) {
|
||||
const verified = await compactVerify(jwt, key as Parameters<typeof compactVerify>[1], options)
|
||||
|
||||
+6
-7
@@ -1,8 +1,7 @@
|
||||
import { toSPKI as exportPublic } from '../runtime/asn1.js'
|
||||
import { toPKCS8 as exportPrivate } from '../runtime/asn1.js'
|
||||
import keyToJWK from '../runtime/key_to_jwk.js'
|
||||
import { toSPKI as exportPublic, toPKCS8 as exportPrivate } from '../lib/asn1.js'
|
||||
import keyToJWK from '../lib/key_to_jwk.js'
|
||||
|
||||
import type { JWK, CryptoKey } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
|
||||
/**
|
||||
* Exports a public {@link !CryptoKey} to a PEM-encoded SPKI string format.
|
||||
@@ -20,7 +19,7 @@ import type { JWK, CryptoKey } from '../types.d.ts'
|
||||
*
|
||||
* @param key Key to export to a PEM-encoded SPKI string format.
|
||||
*/
|
||||
export async function exportSPKI(key: CryptoKey): Promise<string> {
|
||||
export async function exportSPKI(key: types.CryptoKey): Promise<string> {
|
||||
return exportPublic(key)
|
||||
}
|
||||
|
||||
@@ -40,7 +39,7 @@ export async function exportSPKI(key: CryptoKey): Promise<string> {
|
||||
*
|
||||
* @param key Key to export to a PEM-encoded PKCS8 string format.
|
||||
*/
|
||||
export async function exportPKCS8(key: CryptoKey): Promise<string> {
|
||||
export async function exportPKCS8(key: types.CryptoKey): Promise<string> {
|
||||
return exportPrivate(key)
|
||||
}
|
||||
|
||||
@@ -62,6 +61,6 @@ export async function exportPKCS8(key: CryptoKey): Promise<string> {
|
||||
*
|
||||
* @param key Key to export as JWK.
|
||||
*/
|
||||
export async function exportJWK(key: CryptoKey | Uint8Array): Promise<JWK> {
|
||||
export async function exportJWK(key: types.CryptoKey | Uint8Array): Promise<types.JWK> {
|
||||
return keyToJWK(key)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { generateKeyPair as generate } from '../runtime/generate.js'
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
|
||||
export interface GenerateKeyPairResult {
|
||||
/** The generated Private Key. */
|
||||
privateKey: CryptoKey
|
||||
privateKey: types.CryptoKey
|
||||
|
||||
/** Public Key corresponding to the generated Private Key. */
|
||||
publicKey: CryptoKey
|
||||
publicKey: types.CryptoKey
|
||||
}
|
||||
|
||||
export interface GenerateKeyPairOptions {
|
||||
@@ -39,6 +39,16 @@ export interface GenerateKeyPairOptions {
|
||||
extractable?: boolean
|
||||
}
|
||||
|
||||
function getModulusLengthOption(options?: GenerateKeyPairOptions) {
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -65,6 +75,91 @@ export async function generateKeyPair(
|
||||
alg: string,
|
||||
options?: GenerateKeyPairOptions,
|
||||
): Promise<GenerateKeyPairResult> {
|
||||
// @ts-ignore
|
||||
return generate(alg, options)
|
||||
let algorithm: RsaHashedKeyGenParams | EcKeyGenParams | KeyAlgorithm
|
||||
let keyUsages: KeyUsage[]
|
||||
|
||||
switch (alg) {
|
||||
case 'PS256':
|
||||
case 'PS384':
|
||||
case 'PS512':
|
||||
algorithm = {
|
||||
name: 'RSA-PSS',
|
||||
hash: `SHA-${alg.slice(-3)}`,
|
||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||
modulusLength: getModulusLengthOption(options),
|
||||
}
|
||||
keyUsages = ['sign', 'verify']
|
||||
break
|
||||
case 'RS256':
|
||||
case 'RS384':
|
||||
case 'RS512':
|
||||
algorithm = {
|
||||
name: 'RSASSA-PKCS1-v1_5',
|
||||
hash: `SHA-${alg.slice(-3)}`,
|
||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||
modulusLength: getModulusLengthOption(options),
|
||||
}
|
||||
keyUsages = ['sign', 'verify']
|
||||
break
|
||||
case 'RSA-OAEP':
|
||||
case 'RSA-OAEP-256':
|
||||
case 'RSA-OAEP-384':
|
||||
case 'RSA-OAEP-512':
|
||||
algorithm = {
|
||||
name: 'RSA-OAEP',
|
||||
hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`,
|
||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||
modulusLength: getModulusLengthOption(options),
|
||||
}
|
||||
keyUsages = ['decrypt', 'unwrapKey', 'encrypt', 'wrapKey']
|
||||
break
|
||||
case 'ES256':
|
||||
algorithm = { name: 'ECDSA', namedCurve: 'P-256' }
|
||||
keyUsages = ['sign', 'verify']
|
||||
break
|
||||
case 'ES384':
|
||||
algorithm = { name: 'ECDSA', namedCurve: 'P-384' }
|
||||
keyUsages = ['sign', 'verify']
|
||||
break
|
||||
case 'ES512':
|
||||
algorithm = { name: 'ECDSA', namedCurve: 'P-521' }
|
||||
keyUsages = ['sign', 'verify']
|
||||
break
|
||||
case 'EdDSA': {
|
||||
keyUsages = ['sign', 'verify']
|
||||
algorithm = { name: 'Ed25519' }
|
||||
break
|
||||
}
|
||||
case 'ECDH-ES':
|
||||
case 'ECDH-ES+A128KW':
|
||||
case 'ECDH-ES+A192KW':
|
||||
case 'ECDH-ES+A256KW': {
|
||||
keyUsages = ['deriveBits']
|
||||
const crv = options?.crv ?? 'P-256'
|
||||
switch (crv) {
|
||||
case 'P-256':
|
||||
case 'P-384':
|
||||
case 'P-521': {
|
||||
algorithm = { name: 'ECDH', namedCurve: crv }
|
||||
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',
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
default:
|
||||
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')
|
||||
}
|
||||
|
||||
return crypto.subtle.generateKey(
|
||||
algorithm,
|
||||
options?.extractable ?? false,
|
||||
keyUsages,
|
||||
) as Promise<GenerateKeyPairResult>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { generateSecret as generate } from '../runtime/generate.js'
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
|
||||
export interface GenerateSecretOptions {
|
||||
/** The value to use as {@link !SubtleCrypto.generateKey} `extractable` argument. Default is false. */
|
||||
@@ -29,7 +29,43 @@ export interface GenerateSecretOptions {
|
||||
export async function generateSecret(
|
||||
alg: string,
|
||||
options?: GenerateSecretOptions,
|
||||
): Promise<CryptoKey | Uint8Array> {
|
||||
// @ts-ignore
|
||||
return generate(alg, options)
|
||||
): Promise<types.CryptoKey | Uint8Array> {
|
||||
let length: number
|
||||
let algorithm: AesKeyGenParams | HmacKeyGenParams
|
||||
let keyUsages: KeyUsage[]
|
||||
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)
|
||||
}
|
||||
|
||||
+11
-12
@@ -1,10 +1,10 @@
|
||||
import { decode as decodeBase64URL } from '../runtime/base64url.js'
|
||||
import { fromSPKI, fromPKCS8, fromX509 } from '../runtime/asn1.js'
|
||||
import asKeyObject from '../runtime/jwk_to_key.js'
|
||||
import { decode as decodeBase64URL } from '../lib/base64url.js'
|
||||
import { fromSPKI, fromPKCS8, fromX509 } from '../lib/asn1.js'
|
||||
import asKeyObject from '../lib/jwk_to_key.js'
|
||||
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
import isObject from '../lib/is_object.js'
|
||||
import type { JWK, CryptoKey } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
|
||||
export interface PEMImportOptions {
|
||||
/** The value to use as {@link !SubtleCrypto.importKey} `extractable` argument. Default is false. */
|
||||
@@ -40,11 +40,10 @@ export async function importSPKI(
|
||||
spki: string,
|
||||
alg: string,
|
||||
options?: PEMImportOptions,
|
||||
): Promise<CryptoKey> {
|
||||
): Promise<types.CryptoKey> {
|
||||
if (typeof spki !== 'string' || spki.indexOf('-----BEGIN PUBLIC KEY-----') !== 0) {
|
||||
throw new TypeError('"spki" must be SPKI formatted string')
|
||||
}
|
||||
// @ts-ignore
|
||||
return fromSPKI(spki, alg, options)
|
||||
}
|
||||
|
||||
@@ -83,11 +82,10 @@ export async function importX509(
|
||||
x509: string,
|
||||
alg: string,
|
||||
options?: PEMImportOptions,
|
||||
): Promise<CryptoKey> {
|
||||
): Promise<types.CryptoKey> {
|
||||
if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) {
|
||||
throw new TypeError('"x509" must be X.509 formatted string')
|
||||
}
|
||||
// @ts-ignore
|
||||
return fromX509(x509, alg, options)
|
||||
}
|
||||
|
||||
@@ -121,11 +119,10 @@ export async function importPKCS8(
|
||||
pkcs8: string,
|
||||
alg: string,
|
||||
options?: PEMImportOptions,
|
||||
): Promise<CryptoKey> {
|
||||
): Promise<types.CryptoKey> {
|
||||
if (typeof pkcs8 !== 'string' || pkcs8.indexOf('-----BEGIN PRIVATE KEY-----') !== 0) {
|
||||
throw new TypeError('"pkcs8" must be PKCS#8 formatted string')
|
||||
}
|
||||
// @ts-ignore
|
||||
return fromPKCS8(pkcs8, alg, options)
|
||||
}
|
||||
|
||||
@@ -167,7 +164,10 @@ export async function importPKCS8(
|
||||
* property on the JWK. See
|
||||
* {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}.
|
||||
*/
|
||||
export async function importJWK(jwk: JWK, alg?: string): Promise<CryptoKey | Uint8Array> {
|
||||
export async function importJWK(
|
||||
jwk: types.JWK,
|
||||
alg?: string,
|
||||
): Promise<types.CryptoKey | Uint8Array> {
|
||||
if (!isObject(jwk)) {
|
||||
throw new TypeError('JWK must be an object')
|
||||
}
|
||||
@@ -189,7 +189,6 @@ export async function importJWK(jwk: JWK, alg?: string): Promise<CryptoKey | Uin
|
||||
}
|
||||
case 'EC':
|
||||
case 'OKP':
|
||||
// @ts-ignore
|
||||
return asKeyObject({ ...jwk, alg })
|
||||
default:
|
||||
throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value')
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import encrypt from '../runtime/encrypt.js'
|
||||
import decrypt from '../runtime/decrypt.js'
|
||||
import { encode as base64url } from '../runtime/base64url.js'
|
||||
import encrypt from './encrypt.js'
|
||||
import decrypt from './decrypt.js'
|
||||
import { encode as base64url } from '../lib/base64url.js'
|
||||
|
||||
export async function wrap(alg: string, key: unknown, cek: Uint8Array, iv?: Uint8Array) {
|
||||
const jweAlgorithm = alg.slice(0, 7)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import { checkEncCryptoKey } from '../lib/crypto_key.js'
|
||||
import type * as types from '../types.d.ts'
|
||||
import { checkEncCryptoKey } from './crypto_key.js'
|
||||
|
||||
function checkKeySize(key: CryptoKey, alg: string) {
|
||||
function checkKeySize(key: types.CryptoKey, alg: string) {
|
||||
if ((key.algorithm as AesKeyAlgorithm).length !== parseInt(alg.slice(1, 4), 10)) {
|
||||
throw new TypeError(`Invalid key size for alg: ${alg}`)
|
||||
}
|
||||
}
|
||||
|
||||
function getCryptoKey(key: CryptoKey | Uint8Array, alg: string, usage: KeyUsage) {
|
||||
function getCryptoKey(key: types.CryptoKey | Uint8Array, alg: string, usage: KeyUsage) {
|
||||
if (key instanceof Uint8Array) {
|
||||
return crypto.subtle.importKey('raw', key, 'AES-KW', true, [usage])
|
||||
}
|
||||
@@ -15,7 +15,7 @@ function getCryptoKey(key: CryptoKey | Uint8Array, alg: string, usage: KeyUsage)
|
||||
return key
|
||||
}
|
||||
|
||||
export async function wrap(alg: string, key: CryptoKey | Uint8Array, cek: Uint8Array) {
|
||||
export async function wrap(alg: string, key: types.CryptoKey | Uint8Array, cek: Uint8Array) {
|
||||
const cryptoKey = await getCryptoKey(key, alg, 'wrapKey')
|
||||
|
||||
checkKeySize(cryptoKey, alg)
|
||||
@@ -32,7 +32,11 @@ export async function wrap(alg: string, key: CryptoKey | Uint8Array, cek: Uint8A
|
||||
return new Uint8Array(await crypto.subtle.wrapKey('raw', cryptoKeyCek, cryptoKey, 'AES-KW'))
|
||||
}
|
||||
|
||||
export async function unwrap(alg: string, key: CryptoKey | Uint8Array, encryptedKey: Uint8Array) {
|
||||
export async function unwrap(
|
||||
alg: string,
|
||||
key: types.CryptoKey | Uint8Array,
|
||||
encryptedKey: Uint8Array,
|
||||
) {
|
||||
const cryptoKey = await getCryptoKey(key, alg, 'unwrapKey')
|
||||
|
||||
checkKeySize(cryptoKey, alg)
|
||||
@@ -1,12 +1,16 @@
|
||||
import type { PEMExportFunction, PEMImportFunction } from './interfaces.d.ts'
|
||||
import invalidKeyInput from '../lib/invalid_key_input.js'
|
||||
import type * as types from '../types.d.ts'
|
||||
import invalidKeyInput from './invalid_key_input.js'
|
||||
import { encodeBase64, decodeBase64 } from './base64url.js'
|
||||
import formatPEM from '../lib/format_pem.js'
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
import { isCryptoKey } from './is_key_like.js'
|
||||
|
||||
import type { PEMImportOptions } from '../key/import.js'
|
||||
|
||||
const formatPEM = (b64: string, descriptor: string) => {
|
||||
const newlined = (b64.match(/.{1,64}/g) || []).join('\n')
|
||||
return `-----BEGIN ${descriptor}-----\n${newlined}\n-----END ${descriptor}-----`
|
||||
}
|
||||
|
||||
const genericExport = async (
|
||||
keyType: 'private' | 'public',
|
||||
keyFormat: 'spki' | 'pkcs8',
|
||||
@@ -30,11 +34,11 @@ const genericExport = async (
|
||||
)
|
||||
}
|
||||
|
||||
export const toSPKI: PEMExportFunction = (key) => {
|
||||
export const toSPKI = (key: unknown): Promise<string> => {
|
||||
return genericExport('public', 'spki', key)
|
||||
}
|
||||
|
||||
export const toPKCS8: PEMExportFunction = (key) => {
|
||||
export const toPKCS8 = (key: unknown): Promise<string> => {
|
||||
return genericExport('private', 'pkcs8', key)
|
||||
}
|
||||
|
||||
@@ -50,7 +54,7 @@ const findOid = (keyData: Uint8Array, oid: number[], from = 0): boolean => {
|
||||
return sub.every((value, index) => value === oid[index]) || findOid(keyData, oid, i + 1)
|
||||
}
|
||||
|
||||
const getNamedCurve = (keyData: Uint8Array): string => {
|
||||
const getNamedCurve = (keyData: Uint8Array): string | undefined => {
|
||||
switch (true) {
|
||||
case findOid(keyData, [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07]):
|
||||
return 'P-256'
|
||||
@@ -58,12 +62,8 @@ const getNamedCurve = (keyData: Uint8Array): string => {
|
||||
return 'P-384'
|
||||
case findOid(keyData, [0x2b, 0x81, 0x04, 0x00, 0x23]):
|
||||
return 'P-521'
|
||||
case findOid(keyData, [0x2b, 0x65, 0x6e]):
|
||||
return 'X25519'
|
||||
case findOid(keyData, [0x2b, 0x65, 0x70]):
|
||||
return 'Ed25519'
|
||||
default:
|
||||
throw new JOSENotSupported('Invalid or unsupported EC Key Curve or OKP Key Sub Type')
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ const genericImport = async (
|
||||
case 'ECDH-ES+A192KW':
|
||||
case 'ECDH-ES+A256KW': {
|
||||
const namedCurve = getNamedCurve(keyData)
|
||||
algorithm = namedCurve.startsWith('P-') ? { name: 'ECDH', namedCurve } : { name: namedCurve }
|
||||
algorithm = namedCurve?.startsWith('P-') ? { name: 'ECDH', namedCurve } : { name: 'X25519' }
|
||||
keyUsages = isPublic ? [] : ['deriveBits']
|
||||
break
|
||||
}
|
||||
@@ -134,7 +134,7 @@ const genericImport = async (
|
||||
keyUsages = isPublic ? ['verify'] : ['sign']
|
||||
break
|
||||
case 'EdDSA':
|
||||
algorithm = { name: getNamedCurve(keyData) }
|
||||
algorithm = { name: 'Ed25519' }
|
||||
keyUsages = isPublic ? ['verify'] : ['sign']
|
||||
break
|
||||
default:
|
||||
@@ -150,6 +150,12 @@ const genericImport = async (
|
||||
)
|
||||
}
|
||||
|
||||
type PEMImportFunction = (
|
||||
pem: string,
|
||||
alg: string,
|
||||
options?: PEMImportOptions,
|
||||
) => Promise<types.CryptoKey>
|
||||
|
||||
export const fromPKCS8: PEMImportFunction = (pem, alg, options?) => {
|
||||
return genericImport(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, 'pkcs8', pem, alg, options)
|
||||
}
|
||||
@@ -230,7 +236,16 @@ function spkiFromX509(buf: Uint8Array) {
|
||||
return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw)
|
||||
}
|
||||
|
||||
let X509Certificate: any
|
||||
function getSPKI(x509: string): string {
|
||||
if (
|
||||
// @ts-ignore
|
||||
(X509Certificate ||= globalThis.process?.getBuiltinModule?.('node:crypto')?.X509Certificate)
|
||||
) {
|
||||
try {
|
||||
return new X509Certificate(x509).publicKey.export({ format: 'pem', type: 'spki' })
|
||||
} catch {}
|
||||
}
|
||||
const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '')
|
||||
const raw = decodeBase64(pem)
|
||||
return formatPEM(spkiFromX509(raw), 'PUBLIC KEY')
|
||||
@@ -241,7 +256,6 @@ export const fromX509: PEMImportFunction = (pem, alg, options?) => {
|
||||
try {
|
||||
spki = getSPKI(pem)
|
||||
} catch (cause) {
|
||||
// @ts-ignore
|
||||
throw new TypeError('Failed to parse the X.509 certificate', { cause })
|
||||
}
|
||||
return fromSPKI(spki, alg, options)
|
||||
@@ -1,6 +1,7 @@
|
||||
import { encoder, decoder } from '../lib/buffer_utils.js'
|
||||
import { encoder, decoder, Buffer } from '../lib/buffer_utils.js'
|
||||
|
||||
export const encodeBase64 = (input: Uint8Array | string) => {
|
||||
export function encodeBase64(input: Uint8Array | string): string {
|
||||
if (Buffer) return Buffer.from(input).toString('base64')
|
||||
let unencoded = input
|
||||
if (typeof unencoded === 'string') {
|
||||
unencoded = encoder.encode(unencoded)
|
||||
@@ -14,11 +15,13 @@ export const encodeBase64 = (input: Uint8Array | string) => {
|
||||
return btoa(arr.join(''))
|
||||
}
|
||||
|
||||
export const encode = (input: Uint8Array | string) => {
|
||||
export function encode(input: Uint8Array | string): string {
|
||||
if (Buffer) return Buffer.from(input).toString('base64url')
|
||||
return encodeBase64(input).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
|
||||
}
|
||||
|
||||
export const decodeBase64 = (encoded: string): Uint8Array => {
|
||||
export function decodeBase64(encoded: string): Uint8Array {
|
||||
if (Buffer) return Buffer.from(encoded, 'base64')
|
||||
const binary = atob(encoded)
|
||||
const bytes = new Uint8Array(binary.length)
|
||||
for (let i = 0; i < binary.length; i++) {
|
||||
@@ -27,11 +30,12 @@ export const decodeBase64 = (encoded: string): Uint8Array => {
|
||||
return bytes
|
||||
}
|
||||
|
||||
export const decode = (input: Uint8Array | string) => {
|
||||
export function decode(input: Uint8Array | string): Uint8Array {
|
||||
let encoded = input
|
||||
if (encoded instanceof Uint8Array) {
|
||||
encoded = decoder.decode(encoded)
|
||||
}
|
||||
if (Buffer) return Buffer.from(encoded, 'base64url')
|
||||
encoded = encoded.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, '')
|
||||
try {
|
||||
return decodeBase64(encoded)
|
||||
+6
-23
@@ -1,11 +1,15 @@
|
||||
import digest from '../runtime/digest.js'
|
||||
|
||||
export const encoder = new TextEncoder()
|
||||
export const decoder = new TextDecoder()
|
||||
|
||||
const MAX_INT32 = 2 ** 32
|
||||
|
||||
// @ts-expect-error
|
||||
const Buffer: any = globalThis.process?.getBuiltinModule?.('node:buffer')?.Buffer
|
||||
|
||||
export { Buffer }
|
||||
|
||||
export function concat(...buffers: Uint8Array[]): Uint8Array {
|
||||
if (Buffer) return Buffer.concat(buffers)
|
||||
const size = buffers.reduce((acc, { length }) => acc + length, 0)
|
||||
const buf = new Uint8Array(size)
|
||||
let i = 0
|
||||
@@ -16,10 +20,6 @@ export function concat(...buffers: Uint8Array[]): Uint8Array {
|
||||
return buf
|
||||
}
|
||||
|
||||
export function p2s(alg: string, p2sInput: Uint8Array) {
|
||||
return concat(encoder.encode(alg), new Uint8Array([0]), p2sInput)
|
||||
}
|
||||
|
||||
function writeUInt32BE(buf: Uint8Array, value: number, offset?: number) {
|
||||
if (value < 0 || value >= MAX_INT32) {
|
||||
throw new RangeError(`value must be >= 0 and <= ${MAX_INT32 - 1}. Received ${value}`)
|
||||
@@ -41,20 +41,3 @@ export function uint32be(value: number) {
|
||||
writeUInt32BE(buf, value)
|
||||
return buf
|
||||
}
|
||||
|
||||
export function lengthAndInput(input: Uint8Array) {
|
||||
return concat(uint32be(input.length), input)
|
||||
}
|
||||
|
||||
export async function concatKdf(secret: Uint8Array, bits: number, value: Uint8Array) {
|
||||
const iterations = Math.ceil((bits >> 3) / 32)
|
||||
const res = new Uint8Array(iterations * 32)
|
||||
for (let iter = 0; iter < iterations; iter++) {
|
||||
const buf = new Uint8Array(4 + secret.length + value.length)
|
||||
buf.set(uint32be(iter + 1))
|
||||
buf.set(secret, 4)
|
||||
buf.set(value, 4 + secret.length)
|
||||
res.set(await digest('sha256', buf), iter * 32)
|
||||
}
|
||||
return res.slice(0, bits >> 3)
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,4 @@
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
import random from '../runtime/random.js'
|
||||
|
||||
export function bitLength(alg: string) {
|
||||
switch (alg) {
|
||||
@@ -18,4 +17,5 @@ export function bitLength(alg: string) {
|
||||
throw new JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`)
|
||||
}
|
||||
}
|
||||
export default (alg: string): Uint8Array => random(new Uint8Array(bitLength(alg) >> 3))
|
||||
export default (alg: string): Uint8Array =>
|
||||
crypto.getRandomValues(new Uint8Array(bitLength(alg) >> 3))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { JWEInvalid } from '../util/errors.js'
|
||||
|
||||
const checkCekLength = (cek: Uint8Array, expected: number) => {
|
||||
export default (cek: Uint8Array, expected: number) => {
|
||||
const actual = cek.byteLength << 3
|
||||
if (actual !== expected) {
|
||||
throw new JWEInvalid(
|
||||
@@ -8,5 +8,3 @@ const checkCekLength = (cek: Uint8Array, expected: number) => {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default checkCekLength
|
||||
@@ -1,10 +1,8 @@
|
||||
import { JWEInvalid } from '../util/errors.js'
|
||||
import { bitLength } from './iv.js'
|
||||
|
||||
const checkIvLength = (enc: string, iv: Uint8Array) => {
|
||||
export default (enc: string, iv: Uint8Array) => {
|
||||
if (iv.length << 3 !== bitLength(enc)) {
|
||||
throw new JWEInvalid('Invalid Initialization Vector length')
|
||||
}
|
||||
}
|
||||
|
||||
export default checkIvLength
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
|
||||
export default (alg: string, key: CryptoKey) => {
|
||||
export default (alg: string, key: types.CryptoKey) => {
|
||||
if (alg.startsWith('RS') || alg.startsWith('PS')) {
|
||||
const { modulusLength } = key.algorithm as RsaKeyAlgorithm
|
||||
if (typeof modulusLength !== 'number' || modulusLength < 2048) {
|
||||
+30
-24
@@ -1,12 +1,12 @@
|
||||
import { withAlg as invalidKeyInput } from './invalid_key_input.js'
|
||||
import isKeyLike from '../runtime/is_key_like.js'
|
||||
import isKeyLike from './is_key_like.js'
|
||||
import * as jwk from './is_jwk.js'
|
||||
import type { JWK } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
|
||||
// @ts-expect-error
|
||||
const tag = (key: unknown): string => key?.[Symbol.toStringTag]
|
||||
|
||||
const jwkMatchesOp = (alg: string, key: JWK, usage: Usage) => {
|
||||
const jwkMatchesOp = (alg: string, key: types.JWK, usage: Usage) => {
|
||||
if (key.use !== undefined) {
|
||||
let expected: string
|
||||
switch (usage) {
|
||||
@@ -112,34 +112,40 @@ const asymmetricTypeCheck = (alg: string, key: unknown, usage: Usage) => {
|
||||
)
|
||||
}
|
||||
|
||||
if (usage === 'sign' && key.type === 'public') {
|
||||
throw new TypeError(
|
||||
`${tag(key)} instances for asymmetric algorithm signing must be of type "private"`,
|
||||
)
|
||||
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"`,
|
||||
)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (usage === 'decrypt' && key.type === 'public') {
|
||||
throw new TypeError(
|
||||
`${tag(key)} instances for asymmetric algorithm decryption must be of type "private"`,
|
||||
)
|
||||
}
|
||||
|
||||
if (usage === 'verify' && key.type === 'private') {
|
||||
throw new TypeError(
|
||||
`${tag(key)} instances for asymmetric algorithm verifying must be of type "public"`,
|
||||
)
|
||||
}
|
||||
|
||||
if (usage === 'encrypt' && key.type === 'private') {
|
||||
throw new TypeError(
|
||||
`${tag(key)} instances for asymmetric algorithm encryption must be of type "public"`,
|
||||
)
|
||||
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"`,
|
||||
)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Usage = 'sign' | 'verify' | 'encrypt' | 'decrypt'
|
||||
|
||||
export default function checkKeyType(alg: string, key: unknown, usage: Usage): void {
|
||||
export default (alg: string, key: unknown, usage: Usage): void => {
|
||||
const symmetric =
|
||||
alg.startsWith('HS') ||
|
||||
alg === 'dir' ||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { JWEInvalid } from '../util/errors.js'
|
||||
|
||||
export default function checkP2s(p2s: Uint8Array) {
|
||||
if (!(p2s instanceof Uint8Array) || p2s.length < 8) {
|
||||
throw new JWEInvalid('PBES2 Salt Input must be 8 or more octets')
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
|
||||
function unusable(name: string | number, prop = 'algorithm.name') {
|
||||
return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`)
|
||||
@@ -28,7 +28,7 @@ function getNamedCurve(alg: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function checkUsage(key: CryptoKey, usage?: KeyUsage) {
|
||||
function checkUsage(key: types.CryptoKey, usage?: KeyUsage) {
|
||||
if (usage && !key.usages.includes(usage)) {
|
||||
throw new TypeError(
|
||||
`CryptoKey does not support this operation, its usages must include ${usage}.`,
|
||||
@@ -36,7 +36,7 @@ function checkUsage(key: CryptoKey, usage?: KeyUsage) {
|
||||
}
|
||||
}
|
||||
|
||||
export function checkSigCryptoKey(key: CryptoKey, alg: string, usage: KeyUsage) {
|
||||
export function checkSigCryptoKey(key: types.CryptoKey, alg: string, usage: KeyUsage) {
|
||||
switch (alg) {
|
||||
case 'HS256':
|
||||
case 'HS384':
|
||||
@@ -90,7 +90,7 @@ export function checkSigCryptoKey(key: CryptoKey, alg: string, usage: KeyUsage)
|
||||
checkUsage(key, usage)
|
||||
}
|
||||
|
||||
export function checkEncCryptoKey(key: CryptoKey, alg: string, usage?: KeyUsage) {
|
||||
export function checkEncCryptoKey(key: types.CryptoKey, alg: string, usage?: KeyUsage) {
|
||||
switch (alg) {
|
||||
case 'A128GCM':
|
||||
case 'A192GCM':
|
||||
|
||||
@@ -1,18 +1,40 @@
|
||||
import { concat, uint64be } from '../lib/buffer_utils.js'
|
||||
import { concat, uint64be } from './buffer_utils.js'
|
||||
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import type { DecryptFunction } from './interfaces.d.ts'
|
||||
import checkIvLength from '../lib/check_iv_length.js'
|
||||
import type * as types from '../types.d.ts'
|
||||
import checkIvLength from './check_iv_length.js'
|
||||
import checkCekLength from './check_cek_length.js'
|
||||
import timingSafeEqual from './timing_safe_equal.js'
|
||||
import { JOSENotSupported, JWEDecryptionFailed, JWEInvalid } from '../util/errors.js'
|
||||
import { checkEncCryptoKey } from '../lib/crypto_key.js'
|
||||
import invalidKeyInput from '../lib/invalid_key_input.js'
|
||||
import { checkEncCryptoKey } from './crypto_key.js'
|
||||
import invalidKeyInput from './invalid_key_input.js'
|
||||
import { isCryptoKey } from './is_key_like.js'
|
||||
|
||||
let timingSafeEqual: (a: Uint8Array, b: Uint8Array) => boolean =
|
||||
// @ts-expect-error
|
||||
globalThis.process?.getBuiltinModule?.('node:crypto')?.timingSafeEqual
|
||||
|
||||
timingSafeEqual ||= (a: Uint8Array, b: Uint8Array): boolean => {
|
||||
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')
|
||||
}
|
||||
if (a.length !== b.length) {
|
||||
throw new TypeError('Input buffers must have the same length')
|
||||
}
|
||||
|
||||
const len = a.length
|
||||
let out = 0
|
||||
let i = -1
|
||||
while (++i < len) {
|
||||
out |= a[i] ^ b[i]
|
||||
}
|
||||
return out === 0
|
||||
}
|
||||
|
||||
async function cbcDecrypt(
|
||||
enc: string,
|
||||
cek: Uint8Array | CryptoKey,
|
||||
cek: Uint8Array | types.CryptoKey,
|
||||
ciphertext: Uint8Array,
|
||||
iv: Uint8Array,
|
||||
tag: Uint8Array,
|
||||
@@ -72,13 +94,13 @@ async function cbcDecrypt(
|
||||
|
||||
async function gcmDecrypt(
|
||||
enc: string,
|
||||
cek: Uint8Array | CryptoKey,
|
||||
cek: Uint8Array | types.CryptoKey,
|
||||
ciphertext: Uint8Array,
|
||||
iv: Uint8Array,
|
||||
tag: Uint8Array,
|
||||
aad: Uint8Array,
|
||||
) {
|
||||
let encKey: CryptoKey
|
||||
let encKey: types.CryptoKey
|
||||
if (cek instanceof Uint8Array) {
|
||||
encKey = await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, ['decrypt'])
|
||||
} else {
|
||||
@@ -104,16 +126,18 @@ async function gcmDecrypt(
|
||||
}
|
||||
}
|
||||
|
||||
const decrypt: DecryptFunction = async (
|
||||
export default async (
|
||||
enc: string,
|
||||
cek: unknown,
|
||||
ciphertext: Uint8Array,
|
||||
iv: Uint8Array | undefined,
|
||||
tag: Uint8Array | undefined,
|
||||
aad: Uint8Array,
|
||||
) => {
|
||||
): Promise<Uint8Array> => {
|
||||
if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) {
|
||||
throw new TypeError(invalidKeyInput(cek, 'CryptoKey', 'KeyObject', 'Uint8Array'))
|
||||
throw new TypeError(
|
||||
invalidKeyInput(cek, 'CryptoKey', 'KeyObject', 'Uint8Array', 'JSON Web Key'),
|
||||
)
|
||||
}
|
||||
|
||||
if (!iv) {
|
||||
@@ -140,5 +164,3 @@ const decrypt: DecryptFunction = async (
|
||||
throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm')
|
||||
}
|
||||
}
|
||||
|
||||
export default decrypt
|
||||
@@ -1,24 +1,24 @@
|
||||
import { unwrap as aesKw } from '../runtime/aeskw.js'
|
||||
import * as ECDH from '../runtime/ecdhes.js'
|
||||
import { decrypt as pbes2Kw } from '../runtime/pbes2kw.js'
|
||||
import { decrypt as rsaEs } from '../runtime/rsaes.js'
|
||||
import { decode as base64url } from '../runtime/base64url.js'
|
||||
import * as aeskw from './aeskw.js'
|
||||
import * as ecdhes from './ecdhes.js'
|
||||
import * as pbes2kw from './pbes2kw.js'
|
||||
import * as rsaes from './rsaes.js'
|
||||
import * as base64url from '../lib/base64url.js'
|
||||
|
||||
import type { DecryptOptions, JWEHeaderParameters, CryptoKey, JWK } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import { JOSENotSupported, JWEInvalid } from '../util/errors.js'
|
||||
import { bitLength as cekLength } from '../lib/cek.js'
|
||||
import { importJWK } from '../key/import.js'
|
||||
import isObject from './is_object.js'
|
||||
import { unwrap as aesGcmKw } from './aesgcmkw.js'
|
||||
import { assertCryptoKey } from '../runtime/is_key_like.js'
|
||||
import { assertCryptoKey } from './is_key_like.js'
|
||||
|
||||
async function decryptKeyManagement(
|
||||
export default async (
|
||||
alg: string,
|
||||
key: CryptoKey | Uint8Array,
|
||||
key: types.CryptoKey | Uint8Array,
|
||||
encryptedKey: Uint8Array | undefined,
|
||||
joseHeader: JWEHeaderParameters,
|
||||
options?: DecryptOptions,
|
||||
): Promise<CryptoKey | Uint8Array> {
|
||||
joseHeader: types.JWEHeaderParameters,
|
||||
options?: types.DecryptOptions,
|
||||
): Promise<types.CryptoKey | Uint8Array> => {
|
||||
switch (alg) {
|
||||
case 'dir': {
|
||||
// Direct Encryption
|
||||
@@ -36,11 +36,11 @@ async function decryptKeyManagement(
|
||||
case 'ECDH-ES+A192KW':
|
||||
case 'ECDH-ES+A256KW': {
|
||||
// Direct Key Agreement
|
||||
if (!isObject<JWK>(joseHeader.epk))
|
||||
if (!isObject<types.JWK>(joseHeader.epk))
|
||||
throw new JWEInvalid(`JOSE Header "epk" (Ephemeral Public Key) missing or invalid`)
|
||||
|
||||
assertCryptoKey(key)
|
||||
if (!ECDH.ecdhAllowed(key))
|
||||
if (!ecdhes.allowed(key))
|
||||
throw new JOSENotSupported(
|
||||
'ECDH with the provided key is not allowed or not supported by your javascript runtime',
|
||||
)
|
||||
@@ -54,7 +54,7 @@ async function decryptKeyManagement(
|
||||
if (typeof joseHeader.apu !== 'string')
|
||||
throw new JWEInvalid(`JOSE Header "apu" (Agreement PartyUInfo) invalid`)
|
||||
try {
|
||||
partyUInfo = base64url(joseHeader.apu)
|
||||
partyUInfo = base64url.decode(joseHeader.apu)
|
||||
} catch {
|
||||
throw new JWEInvalid('Failed to base64url decode the apu')
|
||||
}
|
||||
@@ -64,13 +64,13 @@ async function decryptKeyManagement(
|
||||
if (typeof joseHeader.apv !== 'string')
|
||||
throw new JWEInvalid(`JOSE Header "apv" (Agreement PartyVInfo) invalid`)
|
||||
try {
|
||||
partyVInfo = base64url(joseHeader.apv)
|
||||
partyVInfo = base64url.decode(joseHeader.apv)
|
||||
} catch {
|
||||
throw new JWEInvalid('Failed to base64url decode the apv')
|
||||
}
|
||||
}
|
||||
|
||||
const sharedSecret = await ECDH.deriveKey(
|
||||
const sharedSecret = await ecdhes.deriveKey(
|
||||
epk,
|
||||
key,
|
||||
alg === 'ECDH-ES' ? joseHeader.enc! : alg,
|
||||
@@ -84,7 +84,7 @@ async function decryptKeyManagement(
|
||||
// Key Agreement with Key Wrapping
|
||||
if (encryptedKey === undefined) throw new JWEInvalid('JWE Encrypted Key missing')
|
||||
|
||||
return aesKw(alg.slice(-6), sharedSecret, encryptedKey)
|
||||
return aeskw.unwrap(alg.slice(-6), sharedSecret, encryptedKey)
|
||||
}
|
||||
case 'RSA-OAEP':
|
||||
case 'RSA-OAEP-256':
|
||||
@@ -93,7 +93,7 @@ async function decryptKeyManagement(
|
||||
// Key Encryption (RSA)
|
||||
if (encryptedKey === undefined) throw new JWEInvalid('JWE Encrypted Key missing')
|
||||
assertCryptoKey(key)
|
||||
return rsaEs(alg, key, encryptedKey)
|
||||
return rsaes.decrypt(alg, key, encryptedKey)
|
||||
}
|
||||
case 'PBES2-HS256+A128KW':
|
||||
case 'PBES2-HS384+A192KW':
|
||||
@@ -114,11 +114,11 @@ async function decryptKeyManagement(
|
||||
|
||||
let p2s: Uint8Array
|
||||
try {
|
||||
p2s = base64url(joseHeader.p2s)
|
||||
p2s = base64url.decode(joseHeader.p2s)
|
||||
} catch {
|
||||
throw new JWEInvalid('Failed to base64url decode the p2s')
|
||||
}
|
||||
return pbes2Kw(alg, key, encryptedKey, joseHeader.p2c, p2s)
|
||||
return pbes2kw.unwrap(alg, key, encryptedKey, joseHeader.p2c, p2s)
|
||||
}
|
||||
case 'A128KW':
|
||||
case 'A192KW':
|
||||
@@ -126,7 +126,7 @@ async function decryptKeyManagement(
|
||||
// Key Wrapping (AES KW)
|
||||
if (encryptedKey === undefined) throw new JWEInvalid('JWE Encrypted Key missing')
|
||||
|
||||
return aesKw(alg, key, encryptedKey)
|
||||
return aeskw.unwrap(alg, key, encryptedKey)
|
||||
}
|
||||
case 'A128GCMKW':
|
||||
case 'A192GCMKW':
|
||||
@@ -142,13 +142,13 @@ async function decryptKeyManagement(
|
||||
|
||||
let iv: Uint8Array
|
||||
try {
|
||||
iv = base64url(joseHeader.iv)
|
||||
iv = base64url.decode(joseHeader.iv)
|
||||
} catch {
|
||||
throw new JWEInvalid('Failed to base64url decode the iv')
|
||||
}
|
||||
let tag: Uint8Array
|
||||
try {
|
||||
tag = base64url(joseHeader.tag)
|
||||
tag = base64url.decode(joseHeader.tag)
|
||||
} catch {
|
||||
throw new JWEInvalid('Failed to base64url decode the tag')
|
||||
}
|
||||
@@ -160,5 +160,3 @@ async function decryptKeyManagement(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default decryptKeyManagement
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import type { DigestFunction } from './interfaces.d.ts'
|
||||
|
||||
const digest: DigestFunction = async (
|
||||
export default async (
|
||||
algorithm: 'sha256' | 'sha384' | 'sha512',
|
||||
data: Uint8Array,
|
||||
): Promise<Uint8Array> => {
|
||||
const subtleDigest = `SHA-${algorithm.slice(-3)}`
|
||||
return new Uint8Array(await crypto.subtle.digest(subtleDigest, data))
|
||||
}
|
||||
export default digest
|
||||
@@ -0,0 +1,72 @@
|
||||
import type * as types from '../types.d.ts'
|
||||
import { encoder, concat, uint32be } from './buffer_utils.js'
|
||||
import { checkEncCryptoKey } from './crypto_key.js'
|
||||
import digest from './digest.js'
|
||||
|
||||
function lengthAndInput(input: Uint8Array) {
|
||||
return concat(uint32be(input.length), input)
|
||||
}
|
||||
|
||||
async function concatKdf(secret: Uint8Array, bits: number, value: Uint8Array) {
|
||||
const iterations = Math.ceil((bits >> 3) / 32)
|
||||
const res = new Uint8Array(iterations * 32)
|
||||
for (let iter = 0; iter < iterations; iter++) {
|
||||
const buf = new Uint8Array(4 + secret.length + value.length)
|
||||
buf.set(uint32be(iter + 1))
|
||||
buf.set(secret, 4)
|
||||
buf.set(value, 4 + secret.length)
|
||||
res.set(await digest('sha256', buf), iter * 32)
|
||||
}
|
||||
return res.slice(0, bits >> 3)
|
||||
}
|
||||
|
||||
export async function deriveKey(
|
||||
publicKey: types.CryptoKey,
|
||||
privateKey: types.CryptoKey,
|
||||
algorithm: string,
|
||||
keyLength: number,
|
||||
apu: Uint8Array = new Uint8Array(0),
|
||||
apv: Uint8Array = new Uint8Array(0),
|
||||
) {
|
||||
checkEncCryptoKey(publicKey, 'ECDH')
|
||||
checkEncCryptoKey(privateKey, 'ECDH', 'deriveBits')
|
||||
|
||||
const value = concat(
|
||||
lengthAndInput(encoder.encode(algorithm)),
|
||||
lengthAndInput(apu),
|
||||
lengthAndInput(apv),
|
||||
uint32be(keyLength),
|
||||
)
|
||||
|
||||
let length: number
|
||||
if (publicKey.algorithm.name === 'X25519') {
|
||||
length = 256
|
||||
} else {
|
||||
length =
|
||||
Math.ceil(parseInt((publicKey.algorithm as EcKeyAlgorithm).namedCurve.slice(-3), 10) / 8) << 3
|
||||
}
|
||||
|
||||
const sharedSecret = new Uint8Array(
|
||||
await crypto.subtle.deriveBits(
|
||||
{
|
||||
name: publicKey.algorithm.name,
|
||||
public: publicKey,
|
||||
},
|
||||
privateKey,
|
||||
length,
|
||||
),
|
||||
)
|
||||
|
||||
return concatKdf(sharedSecret, keyLength, value)
|
||||
}
|
||||
|
||||
export function allowed(key: types.CryptoKey) {
|
||||
switch ((key.algorithm as EcKeyAlgorithm).namedCurve) {
|
||||
case 'P-256':
|
||||
case 'P-384':
|
||||
case 'P-521':
|
||||
return true
|
||||
default:
|
||||
return key.algorithm.name === 'X25519'
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,17 @@
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import { concat, uint64be } from '../lib/buffer_utils.js'
|
||||
import type { EncryptFunction } from './interfaces.d.ts'
|
||||
import checkIvLength from '../lib/check_iv_length.js'
|
||||
import type * as types from '../types.d.ts'
|
||||
import { concat, uint64be } from './buffer_utils.js'
|
||||
import checkIvLength from './check_iv_length.js'
|
||||
import checkCekLength from './check_cek_length.js'
|
||||
import { checkEncCryptoKey } from '../lib/crypto_key.js'
|
||||
import invalidKeyInput from '../lib/invalid_key_input.js'
|
||||
import generateIv from '../lib/iv.js'
|
||||
import { checkEncCryptoKey } from './crypto_key.js'
|
||||
import invalidKeyInput from './invalid_key_input.js'
|
||||
import generateIv from './iv.js'
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
import { isCryptoKey } from './is_key_like.js'
|
||||
|
||||
async function cbcEncrypt(
|
||||
enc: string,
|
||||
plaintext: Uint8Array,
|
||||
cek: Uint8Array | CryptoKey,
|
||||
cek: Uint8Array | types.CryptoKey,
|
||||
iv: Uint8Array,
|
||||
aad: Uint8Array,
|
||||
) {
|
||||
@@ -60,11 +59,11 @@ async function cbcEncrypt(
|
||||
async function gcmEncrypt(
|
||||
enc: string,
|
||||
plaintext: Uint8Array,
|
||||
cek: Uint8Array | CryptoKey,
|
||||
cek: Uint8Array | types.CryptoKey,
|
||||
iv: Uint8Array,
|
||||
aad: Uint8Array,
|
||||
) {
|
||||
let encKey: CryptoKey
|
||||
let encKey: types.CryptoKey
|
||||
if (cek instanceof Uint8Array) {
|
||||
encKey = await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, ['encrypt'])
|
||||
} else {
|
||||
@@ -91,15 +90,21 @@ async function gcmEncrypt(
|
||||
return { ciphertext, tag, iv }
|
||||
}
|
||||
|
||||
const encrypt: EncryptFunction = async (
|
||||
export default async (
|
||||
enc: string,
|
||||
plaintext: Uint8Array,
|
||||
cek: unknown,
|
||||
iv: Uint8Array | undefined,
|
||||
aad: Uint8Array,
|
||||
) => {
|
||||
): Promise<{
|
||||
ciphertext: Uint8Array
|
||||
tag: Uint8Array | undefined
|
||||
iv: Uint8Array | undefined
|
||||
}> => {
|
||||
if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) {
|
||||
throw new TypeError(invalidKeyInput(cek, 'CryptoKey', 'KeyObject', 'Uint8Array'))
|
||||
throw new TypeError(
|
||||
invalidKeyInput(cek, 'CryptoKey', 'KeyObject', 'Uint8Array', 'JSON Web Key'),
|
||||
)
|
||||
}
|
||||
|
||||
if (iv) {
|
||||
@@ -127,5 +132,3 @@ const encrypt: EncryptFunction = async (
|
||||
throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm')
|
||||
}
|
||||
}
|
||||
|
||||
export default encrypt
|
||||
@@ -1,36 +1,32 @@
|
||||
import { wrap as aesKw } from '../runtime/aeskw.js'
|
||||
import * as ECDH from '../runtime/ecdhes.js'
|
||||
import { encrypt as pbes2Kw } from '../runtime/pbes2kw.js'
|
||||
import { encrypt as rsaEs } from '../runtime/rsaes.js'
|
||||
import { encode as base64url } from '../runtime/base64url.js'
|
||||
import normalizeKey from '../runtime/normalize_key.js'
|
||||
import type * as types from '../types.d.ts'
|
||||
import * as aeskw from './aeskw.js'
|
||||
import * as ecdhes from './ecdhes.js'
|
||||
import * as pbes2kw from './pbes2kw.js'
|
||||
import * as rsaes from './rsaes.js'
|
||||
import * as base64url from '../lib/base64url.js'
|
||||
import normalizeKey from './normalize_key.js'
|
||||
|
||||
import type {
|
||||
CryptoKey,
|
||||
JWEKeyManagementHeaderParameters,
|
||||
JWEHeaderParameters,
|
||||
JWK,
|
||||
} from '../types.d.ts'
|
||||
import type { JWEKeyManagementHeaderParameters, JWEHeaderParameters, JWK } from '../types.d.ts'
|
||||
import generateCek, { bitLength as cekLength } from '../lib/cek.js'
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
import { exportJWK } from '../key/export.js'
|
||||
import { wrap as aesGcmKw } from './aesgcmkw.js'
|
||||
import { assertCryptoKey } from '../runtime/is_key_like.js'
|
||||
import { assertCryptoKey } from './is_key_like.js'
|
||||
|
||||
async function encryptKeyManagement(
|
||||
export default async (
|
||||
alg: string,
|
||||
enc: string,
|
||||
key: CryptoKey | Uint8Array,
|
||||
key: types.CryptoKey | Uint8Array,
|
||||
providedCek?: Uint8Array,
|
||||
providedParameters: JWEKeyManagementHeaderParameters = {},
|
||||
): Promise<{
|
||||
cek: CryptoKey | Uint8Array
|
||||
cek: types.CryptoKey | Uint8Array
|
||||
encryptedKey?: Uint8Array
|
||||
parameters?: JWEHeaderParameters
|
||||
}> {
|
||||
}> => {
|
||||
let encryptedKey: Uint8Array | undefined
|
||||
let parameters: (JWEHeaderParameters & { epk?: JWK }) | undefined
|
||||
let cek: CryptoKey | Uint8Array
|
||||
let cek: types.CryptoKey | Uint8Array
|
||||
|
||||
switch (alg) {
|
||||
case 'dir': {
|
||||
@@ -44,20 +40,22 @@ async function encryptKeyManagement(
|
||||
case 'ECDH-ES+A256KW': {
|
||||
assertCryptoKey(key)
|
||||
// Direct Key Agreement
|
||||
if (!ECDH.ecdhAllowed(key)) {
|
||||
if (!ecdhes.allowed(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: CryptoKey
|
||||
let ephemeralKey: types.CryptoKey
|
||||
if (providedParameters.epk) {
|
||||
ephemeralKey = (await normalizeKey(providedParameters.epk, alg)) as CryptoKey
|
||||
ephemeralKey = (await normalizeKey(providedParameters.epk, alg)) as types.CryptoKey
|
||||
} else {
|
||||
ephemeralKey = (await ECDH.generateEpk(key)).privateKey
|
||||
ephemeralKey = (
|
||||
await crypto.subtle.generateKey(key.algorithm as EcKeyAlgorithm, true, ['deriveBits'])
|
||||
).privateKey
|
||||
}
|
||||
const { x, y, crv, kty } = await exportJWK(ephemeralKey!)
|
||||
const sharedSecret = await ECDH.deriveKey(
|
||||
const sharedSecret = await ecdhes.deriveKey(
|
||||
key,
|
||||
ephemeralKey,
|
||||
alg === 'ECDH-ES' ? enc : alg,
|
||||
@@ -67,8 +65,8 @@ async function encryptKeyManagement(
|
||||
)
|
||||
parameters = { epk: { x, crv, kty } }
|
||||
if (kty === 'EC') parameters.epk!.y = y
|
||||
if (apu) parameters.apu = base64url(apu)
|
||||
if (apv) parameters.apv = base64url(apv)
|
||||
if (apu) parameters.apu = base64url.encode(apu)
|
||||
if (apv) parameters.apv = base64url.encode(apv)
|
||||
|
||||
if (alg === 'ECDH-ES') {
|
||||
cek = sharedSecret
|
||||
@@ -78,7 +76,7 @@ async function encryptKeyManagement(
|
||||
// Key Agreement with Key Wrapping
|
||||
cek = providedCek || generateCek(enc)
|
||||
const kwAlg = alg.slice(-6)
|
||||
encryptedKey = await aesKw(kwAlg, sharedSecret, cek)
|
||||
encryptedKey = await aeskw.wrap(kwAlg, sharedSecret, cek)
|
||||
break
|
||||
}
|
||||
case 'RSA-OAEP':
|
||||
@@ -88,7 +86,7 @@ async function encryptKeyManagement(
|
||||
// Key Encryption (RSA)
|
||||
cek = providedCek || generateCek(enc)
|
||||
assertCryptoKey(key)
|
||||
encryptedKey = await rsaEs(alg, key, cek)
|
||||
encryptedKey = await rsaes.encrypt(alg, key, cek)
|
||||
break
|
||||
}
|
||||
case 'PBES2-HS256+A128KW':
|
||||
@@ -97,7 +95,7 @@ async function encryptKeyManagement(
|
||||
// Key Encryption (PBES2)
|
||||
cek = providedCek || generateCek(enc)
|
||||
const { p2c, p2s } = providedParameters
|
||||
;({ encryptedKey, ...parameters } = await pbes2Kw(alg, key, cek, p2c, p2s))
|
||||
;({ encryptedKey, ...parameters } = await pbes2kw.wrap(alg, key, cek, p2c, p2s))
|
||||
break
|
||||
}
|
||||
case 'A128KW':
|
||||
@@ -105,7 +103,7 @@ async function encryptKeyManagement(
|
||||
case 'A256KW': {
|
||||
// Key Wrapping (AES KW)
|
||||
cek = providedCek || generateCek(enc)
|
||||
encryptedKey = await aesKw(alg, key, cek)
|
||||
encryptedKey = await aeskw.wrap(alg, key, cek)
|
||||
break
|
||||
}
|
||||
case 'A128GCMKW':
|
||||
@@ -124,5 +122,3 @@ async function encryptKeyManagement(
|
||||
|
||||
return { cek, encryptedKey, parameters }
|
||||
}
|
||||
|
||||
export default encryptKeyManagement
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export default (b64: string, descriptor: string) => {
|
||||
const newlined = (b64.match(/.{1,64}/g) || []).join('\n')
|
||||
return `-----BEGIN ${descriptor}-----\n${newlined}\n-----END ${descriptor}-----`
|
||||
}
|
||||
@@ -1,15 +1,11 @@
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import { checkSigCryptoKey } from '../lib/crypto_key.js'
|
||||
import invalidKeyInput from '../lib/invalid_key_input.js'
|
||||
import type * as types from '../types.d.ts'
|
||||
import { checkSigCryptoKey } from './crypto_key.js'
|
||||
import invalidKeyInput from './invalid_key_input.js'
|
||||
|
||||
export default async function getCryptoKey(
|
||||
alg: string,
|
||||
key: CryptoKey | Uint8Array,
|
||||
usage: KeyUsage,
|
||||
) {
|
||||
export default async (alg: string, key: types.CryptoKey | Uint8Array, usage: KeyUsage) => {
|
||||
if (key instanceof Uint8Array) {
|
||||
if (!alg.startsWith('HS')) {
|
||||
throw new TypeError(invalidKeyInput(key, 'CryptoKey', 'KeyObject'))
|
||||
throw new TypeError(invalidKeyInput(key, 'CryptoKey', 'KeyObject', 'JSON Web Key'))
|
||||
}
|
||||
return crypto.subtle.importKey(
|
||||
'raw',
|
||||
@@ -1,4 +1,4 @@
|
||||
const isDisjoint = (...headers: Array<object | undefined>) => {
|
||||
export default (...headers: Array<object | undefined>) => {
|
||||
const sources = headers.filter(Boolean) as object[]
|
||||
|
||||
if (sources.length === 0 || sources.length === 1) {
|
||||
@@ -23,4 +23,3 @@ const isDisjoint = (...headers: Array<object | undefined>) => {
|
||||
|
||||
return true
|
||||
}
|
||||
export default isDisjoint
|
||||
|
||||
+6
-6
@@ -1,18 +1,18 @@
|
||||
import type { JWK } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import isObject from './is_object.js'
|
||||
|
||||
export function isJWK(key: unknown): key is JWK & { kty: string } {
|
||||
return isObject<JWK>(key) && typeof key.kty === 'string'
|
||||
export function isJWK(key: unknown): key is types.JWK & { kty: string } {
|
||||
return isObject<types.JWK>(key) && typeof key.kty === 'string'
|
||||
}
|
||||
|
||||
export function isPrivateJWK(key: JWK & { kty: string }) {
|
||||
export function isPrivateJWK(key: types.JWK & { kty: string }) {
|
||||
return key.kty !== 'oct' && typeof key.d === 'string'
|
||||
}
|
||||
|
||||
export function isPublicJWK(key: JWK & { kty: string }) {
|
||||
export function isPublicJWK(key: types.JWK & { kty: string }) {
|
||||
return key.kty !== 'oct' && typeof key.d === 'undefined'
|
||||
}
|
||||
|
||||
export function isSecretJWK(key: JWK & { kty: string }) {
|
||||
export function isSecretJWK(key: types.JWK & { kty: string }) {
|
||||
return key.kty === 'oct' && typeof key.k === 'string'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import type * as types from '../types.d.ts'
|
||||
|
||||
export function assertCryptoKey(key: unknown): asserts key is types.CryptoKey {
|
||||
if (!isCryptoKey(key)) {
|
||||
throw new Error('CryptoKey instance expected')
|
||||
}
|
||||
}
|
||||
|
||||
export function isCryptoKey(key: unknown): key is types.CryptoKey {
|
||||
// @ts-expect-error
|
||||
return key?.[Symbol.toStringTag] === 'CryptoKey'
|
||||
}
|
||||
|
||||
export function isKeyObject<T extends types.KeyObject = types.KeyObject>(key: unknown): key is T {
|
||||
// @ts-expect-error
|
||||
return key?.[Symbol.toStringTag] === 'KeyObject'
|
||||
}
|
||||
|
||||
export default (key: unknown): key is types.CryptoKey | types.KeyObject => {
|
||||
return isCryptoKey(key) || isKeyObject(key)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ function isObjectLike(value: unknown) {
|
||||
return typeof value === 'object' && value !== null
|
||||
}
|
||||
|
||||
export default function isObject<T = object>(input: unknown): input is T {
|
||||
export default <T = object>(input: unknown): input is T => {
|
||||
if (!isObjectLike(input) || Object.prototype.toString.call(input) !== '[object Object]') {
|
||||
return false
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,4 @@
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
import random from '../runtime/random.js'
|
||||
|
||||
export function bitLength(alg: string) {
|
||||
switch (alg) {
|
||||
@@ -18,4 +17,5 @@ export function bitLength(alg: string) {
|
||||
throw new JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`)
|
||||
}
|
||||
}
|
||||
export default (alg: string): Uint8Array => random(new Uint8Array(bitLength(alg) >> 3))
|
||||
export default (alg: string): Uint8Array =>
|
||||
crypto.getRandomValues(new Uint8Array(bitLength(alg) >> 3))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
import type { CryptoKey, JWK } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
|
||||
function subtleMapping(jwk: JWK): {
|
||||
function subtleMapping(jwk: types.JWK): {
|
||||
algorithm: RsaHashedImportParams | EcKeyAlgorithm | Algorithm
|
||||
keyUsages: KeyUsage[]
|
||||
} {
|
||||
@@ -71,7 +71,7 @@ function subtleMapping(jwk: JWK): {
|
||||
keyUsages = jwk.d ? ['sign'] : ['verify']
|
||||
break
|
||||
case 'EdDSA':
|
||||
algorithm = { name: jwk.crv! }
|
||||
algorithm = { name: 'Ed25519' }
|
||||
keyUsages = jwk.d ? ['sign'] : ['verify']
|
||||
break
|
||||
case 'ECDH-ES':
|
||||
@@ -93,21 +93,22 @@ function subtleMapping(jwk: JWK): {
|
||||
return { algorithm, keyUsages }
|
||||
}
|
||||
|
||||
const parse = async (jwk: JWK): Promise<CryptoKey> => {
|
||||
export default async (jwk: types.JWK): Promise<types.CryptoKey> => {
|
||||
if (!jwk.alg) {
|
||||
throw new TypeError('"alg" argument is required when "jwk.alg" is not present')
|
||||
}
|
||||
|
||||
const { algorithm, keyUsages } = subtleMapping(jwk)
|
||||
const rest: [RsaHashedImportParams | EcKeyAlgorithm | Algorithm, boolean, KeyUsage[]] = [
|
||||
algorithm,
|
||||
jwk.ext ?? false,
|
||||
(jwk.key_ops as KeyUsage[]) ?? keyUsages,
|
||||
]
|
||||
|
||||
const keyData: JWK = { ...jwk }
|
||||
const keyData: types.JWK = { ...jwk }
|
||||
delete keyData.alg
|
||||
delete keyData.use
|
||||
return crypto.subtle.importKey('jwk', keyData, ...rest)
|
||||
|
||||
return crypto.subtle.importKey(
|
||||
'jwk',
|
||||
keyData,
|
||||
algorithm,
|
||||
jwk.ext ?? (jwk.d ? false : true),
|
||||
(jwk.key_ops as KeyUsage[]) ?? keyUsages,
|
||||
)
|
||||
}
|
||||
export default parse
|
||||
@@ -1,9 +1,4 @@
|
||||
import type {
|
||||
JWTPayload,
|
||||
JWTClaimVerificationOptions,
|
||||
JWEHeaderParameters,
|
||||
JWSHeaderParameters,
|
||||
} from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.js'
|
||||
import { decoder } from './buffer_utils.js'
|
||||
import epoch from './epoch.js'
|
||||
@@ -27,9 +22,9 @@ const checkAudiencePresence = (audPayload: unknown, audOption: unknown[]) => {
|
||||
}
|
||||
|
||||
export default (
|
||||
protectedHeader: JWEHeaderParameters | JWSHeaderParameters,
|
||||
protectedHeader: types.JWEHeaderParameters | types.JWSHeaderParameters,
|
||||
encodedPayload: Uint8Array,
|
||||
options: JWTClaimVerificationOptions = {},
|
||||
options: types.JWTClaimVerificationOptions = {},
|
||||
) => {
|
||||
let payload!: { [propName: string]: unknown }
|
||||
try {
|
||||
@@ -177,5 +172,5 @@ export default (
|
||||
}
|
||||
}
|
||||
|
||||
return payload as JWTPayload
|
||||
return payload as types.JWTPayload
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import type { JWKExportFunction } from './interfaces.d.ts'
|
||||
import type { JWK } from '../types.d.ts'
|
||||
import invalidKeyInput from '../lib/invalid_key_input.js'
|
||||
import type * as types from '../types.d.ts'
|
||||
import invalidKeyInput from './invalid_key_input.js'
|
||||
import { encode as base64url } from './base64url.js'
|
||||
import { isCryptoKey } from './is_key_like.js'
|
||||
|
||||
const keyToJWK: JWKExportFunction = async (key: unknown): Promise<JWK> => {
|
||||
export default async (key: unknown): Promise<types.JWK> => {
|
||||
if (key instanceof Uint8Array) {
|
||||
return {
|
||||
kty: 'oct',
|
||||
@@ -19,6 +18,5 @@ const keyToJWK: JWKExportFunction = async (key: unknown): Promise<JWK> => {
|
||||
}
|
||||
const { ext, key_ops, alg, use, ...jwk } = await crypto.subtle.exportKey('jwk', key)
|
||||
|
||||
return jwk as JWK
|
||||
return jwk as types.JWK
|
||||
}
|
||||
export default keyToJWK
|
||||
@@ -1,14 +1,14 @@
|
||||
import { isJWK } from '../lib/is_jwk.js'
|
||||
import type { JWK, CryptoKey, KeyObject } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import { isJWK } from './is_jwk.js'
|
||||
import { decode } from './base64url.js'
|
||||
import importJWK from './jwk_to_key.js'
|
||||
import { isKeyObject } from './is_key_like.js'
|
||||
import { isCryptoKey, isKeyObject } from './is_key_like.js'
|
||||
|
||||
let cache: WeakMap<object, Record<string, CryptoKey>>
|
||||
|
||||
interface ConvertableKeyObject extends KeyObject {
|
||||
interface ConvertableKeyObject extends types.KeyObject {
|
||||
export(): Uint8Array
|
||||
export(opts: { format: 'jwk' }): JWK
|
||||
export(opts: { format: 'jwk' }): types.JWK
|
||||
asymmetricKeyType?: string
|
||||
asymmetricKeyDetails?: { namedCurve?: string }
|
||||
toCryptoKey(
|
||||
@@ -20,10 +20,15 @@ interface ConvertableKeyObject extends KeyObject {
|
||||
| AesKeyAlgorithm,
|
||||
extractable: boolean,
|
||||
usages: string[],
|
||||
): CryptoKey
|
||||
): types.CryptoKey
|
||||
}
|
||||
|
||||
const handleJWK = async (key: KeyObject | JWK, jwk: JWK, alg: string, freeze = false) => {
|
||||
const handleJWK = async (
|
||||
key: types.KeyObject | types.JWK,
|
||||
jwk: types.JWK,
|
||||
alg: string,
|
||||
freeze = false,
|
||||
) => {
|
||||
cache ||= new WeakMap()
|
||||
let cached = cache.get(key)
|
||||
if (cached?.[alg]) {
|
||||
@@ -40,15 +45,18 @@ const handleJWK = async (key: KeyObject | JWK, jwk: JWK, alg: string, freeze = f
|
||||
return cryptoKey
|
||||
}
|
||||
|
||||
const handleKeyObject = (key: ConvertableKeyObject, alg: string) => {
|
||||
const handleKeyObject = (keyObject: ConvertableKeyObject, alg: string) => {
|
||||
cache ||= new WeakMap()
|
||||
let cached = cache.get(key)
|
||||
let cached = cache.get(keyObject)
|
||||
if (cached?.[alg]) {
|
||||
return cached[alg]
|
||||
}
|
||||
|
||||
let cryptoKey: CryptoKey | undefined
|
||||
if (key.asymmetricKeyType === 'x25519') {
|
||||
const isPublic = keyObject.type === 'public'
|
||||
const extractable = isPublic ? true : false
|
||||
|
||||
let cryptoKey: types.CryptoKey | undefined
|
||||
if (keyObject.asymmetricKeyType === 'x25519') {
|
||||
switch (alg) {
|
||||
case 'ECDH-ES':
|
||||
case 'ECDH-ES+A128KW':
|
||||
@@ -57,27 +65,27 @@ const handleKeyObject = (key: ConvertableKeyObject, alg: string) => {
|
||||
break
|
||||
|
||||
default:
|
||||
throw new TypeError('TODO')
|
||||
throw new TypeError('given KeyObject instance cannot be used for this algorithm')
|
||||
}
|
||||
|
||||
cryptoKey = key.toCryptoKey(
|
||||
key.asymmetricKeyType,
|
||||
true,
|
||||
key.type === 'private' ? ['deriveBits'] : [],
|
||||
cryptoKey = keyObject.toCryptoKey(
|
||||
keyObject.asymmetricKeyType,
|
||||
extractable,
|
||||
isPublic ? [] : ['deriveBits'],
|
||||
)
|
||||
}
|
||||
|
||||
if (key.asymmetricKeyType === 'ed25519') {
|
||||
if (keyObject.asymmetricKeyType === 'ed25519') {
|
||||
if (alg !== 'EdDSA') {
|
||||
throw new TypeError('TODO')
|
||||
throw new TypeError('given KeyObject instance cannot be used for this algorithm')
|
||||
}
|
||||
|
||||
cryptoKey = key.toCryptoKey(key.asymmetricKeyType, true, [
|
||||
key.type === 'private' ? 'sign' : 'verify',
|
||||
cryptoKey = keyObject.toCryptoKey(keyObject.asymmetricKeyType, extractable, [
|
||||
isPublic ? 'verify' : 'sign',
|
||||
])
|
||||
}
|
||||
|
||||
if (key.asymmetricKeyType === 'rsa') {
|
||||
if (keyObject.asymmetricKeyType === 'rsa') {
|
||||
let hash: string
|
||||
switch (alg) {
|
||||
case 'RSA-OAEP':
|
||||
@@ -100,127 +108,136 @@ const handleKeyObject = (key: ConvertableKeyObject, alg: string) => {
|
||||
break
|
||||
|
||||
default:
|
||||
throw new TypeError('unsupported algorithm')
|
||||
throw new TypeError('given KeyObject instance cannot be used for this algorithm')
|
||||
}
|
||||
|
||||
if (alg.startsWith('RSA-OAEP')) {
|
||||
return key.toCryptoKey(
|
||||
return keyObject.toCryptoKey(
|
||||
{
|
||||
name: 'RSA-OAEP',
|
||||
hash,
|
||||
},
|
||||
true,
|
||||
key.type === 'private' ? ['decrypt', 'unwrapKey'] : ['encrypt', 'wrapKey'],
|
||||
extractable,
|
||||
isPublic ? ['encrypt'] : ['decrypt'],
|
||||
)
|
||||
}
|
||||
|
||||
cryptoKey = key.toCryptoKey(
|
||||
cryptoKey = keyObject.toCryptoKey(
|
||||
{
|
||||
name: alg.startsWith('PS') ? 'RSA-PSS' : 'RSASSA-PKCS1-v1_5',
|
||||
hash,
|
||||
},
|
||||
true,
|
||||
[key.type === 'private' ? 'sign' : 'verify'],
|
||||
extractable,
|
||||
[isPublic ? 'verify' : 'sign'],
|
||||
)
|
||||
}
|
||||
|
||||
if (key.asymmetricKeyType === 'ec') {
|
||||
if (keyObject.asymmetricKeyType === 'ec') {
|
||||
const nist = new Map<unknown, string>([
|
||||
['prime256v1', 'P-256'],
|
||||
['secp384r1', 'P-384'],
|
||||
['secp521r1', 'P-521'],
|
||||
])
|
||||
|
||||
const namedCurve = nist.get(key.asymmetricKeyDetails?.namedCurve)
|
||||
const namedCurve = nist.get(keyObject.asymmetricKeyDetails?.namedCurve)
|
||||
if (!namedCurve) {
|
||||
throw new TypeError('TODO')
|
||||
throw new TypeError('given KeyObject instance cannot be used for this algorithm')
|
||||
}
|
||||
|
||||
if (alg === 'ES256' && namedCurve === 'P-256') {
|
||||
cryptoKey = key.toCryptoKey(
|
||||
cryptoKey = keyObject.toCryptoKey(
|
||||
{
|
||||
name: 'ECDSA',
|
||||
namedCurve,
|
||||
},
|
||||
true,
|
||||
[key.type === 'private' ? 'sign' : 'verify'],
|
||||
extractable,
|
||||
[isPublic ? 'verify' : 'sign'],
|
||||
)
|
||||
}
|
||||
|
||||
if (alg === 'ES384' && namedCurve === 'P-384') {
|
||||
cryptoKey = key.toCryptoKey(
|
||||
cryptoKey = keyObject.toCryptoKey(
|
||||
{
|
||||
name: 'ECDSA',
|
||||
namedCurve,
|
||||
},
|
||||
true,
|
||||
[key.type === 'private' ? 'sign' : 'verify'],
|
||||
extractable,
|
||||
[isPublic ? 'verify' : 'sign'],
|
||||
)
|
||||
}
|
||||
|
||||
if (alg === 'ES512' && namedCurve === 'P-521') {
|
||||
cryptoKey = key.toCryptoKey(
|
||||
cryptoKey = keyObject.toCryptoKey(
|
||||
{
|
||||
name: 'ECDSA',
|
||||
namedCurve,
|
||||
},
|
||||
true,
|
||||
[key.type === 'private' ? 'sign' : 'verify'],
|
||||
extractable,
|
||||
[isPublic ? 'verify' : 'sign'],
|
||||
)
|
||||
}
|
||||
|
||||
if (alg.startsWith('ECDH-ES')) {
|
||||
cryptoKey = key.toCryptoKey(
|
||||
cryptoKey = keyObject.toCryptoKey(
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve,
|
||||
},
|
||||
true,
|
||||
key.type === 'private' ? ['deriveBits'] : [],
|
||||
extractable,
|
||||
isPublic ? [] : ['deriveBits'],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (!cryptoKey) {
|
||||
throw new TypeError('TODO')
|
||||
throw new TypeError('given KeyObject instance cannot be used for this algorithm')
|
||||
}
|
||||
|
||||
if (!cached) {
|
||||
cache.set(key, { [alg]: cryptoKey })
|
||||
cache.set(keyObject, { [alg]: cryptoKey })
|
||||
} else {
|
||||
cached[alg] = cryptoKey
|
||||
}
|
||||
return cryptoKey
|
||||
}
|
||||
|
||||
export default async function normalizeKey(
|
||||
key: CryptoKey | KeyObject | JWK | Uint8Array,
|
||||
export default async (
|
||||
key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array,
|
||||
alg: string,
|
||||
): Promise<CryptoKey | Uint8Array> {
|
||||
): Promise<types.CryptoKey | Uint8Array> => {
|
||||
if (key instanceof Uint8Array) {
|
||||
return key
|
||||
}
|
||||
|
||||
let normalized: CryptoKey | undefined
|
||||
if (isKeyObject<KeyObject>(key)) {
|
||||
if (isCryptoKey(key)) {
|
||||
return key
|
||||
}
|
||||
|
||||
if (isKeyObject(key)) {
|
||||
if (key.type === 'secret') {
|
||||
return (key as ConvertableKeyObject).export()
|
||||
}
|
||||
|
||||
if ('toCryptoKey' in key && typeof key.toCryptoKey === 'function') {
|
||||
normalized = handleKeyObject(key as ConvertableKeyObject, alg)
|
||||
} else {
|
||||
let jwk: JWK = (key as ConvertableKeyObject).export({ format: 'jwk' })
|
||||
normalized = await handleJWK(key, jwk, alg)
|
||||
try {
|
||||
return handleKeyObject(key as ConvertableKeyObject, alg)
|
||||
} catch (err) {
|
||||
if (err instanceof TypeError) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isJWK(key)) {
|
||||
|
||||
let jwk: types.JWK = (key as ConvertableKeyObject).export({ format: 'jwk' })
|
||||
return handleJWK(key, jwk, alg)
|
||||
}
|
||||
|
||||
if (isJWK(key)) {
|
||||
if (key.k) {
|
||||
return decode(key.k)
|
||||
}
|
||||
normalized = await handleJWK(key, key, alg, true)
|
||||
} else {
|
||||
normalized = key
|
||||
return handleJWK(key, key, alg, true)
|
||||
}
|
||||
|
||||
return normalized
|
||||
throw new Error('unreachable')
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import type * as types from '../types.d.ts'
|
||||
import { encode as base64url } from './base64url.js'
|
||||
import * as aeskw from './aeskw.js'
|
||||
import { checkEncCryptoKey } from './crypto_key.js'
|
||||
import { concat, encoder } from './buffer_utils.js'
|
||||
import { JWEInvalid } from '../util/errors.js'
|
||||
|
||||
function getCryptoKey(key: types.CryptoKey | Uint8Array, alg: string) {
|
||||
if (key instanceof Uint8Array) {
|
||||
return crypto.subtle.importKey('raw', key, 'PBKDF2', false, ['deriveBits'])
|
||||
}
|
||||
|
||||
checkEncCryptoKey(key, alg, 'deriveBits')
|
||||
return key
|
||||
}
|
||||
|
||||
const concatSalt = (alg: string, p2sInput: Uint8Array) =>
|
||||
concat(encoder.encode(alg), new Uint8Array([0]), p2sInput)
|
||||
|
||||
async function deriveKey(
|
||||
p2s: Uint8Array,
|
||||
alg: string,
|
||||
p2c: number,
|
||||
key: types.CryptoKey | Uint8Array,
|
||||
) {
|
||||
if (!(p2s instanceof Uint8Array) || p2s.length < 8) {
|
||||
throw new JWEInvalid('PBES2 Salt Input must be 8 or more octets')
|
||||
}
|
||||
|
||||
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 getCryptoKey(key, alg)
|
||||
|
||||
return new Uint8Array(await crypto.subtle.deriveBits(subtleAlg, cryptoKey, keylen))
|
||||
}
|
||||
|
||||
export async function wrap(
|
||||
alg: string,
|
||||
key: types.CryptoKey | Uint8Array,
|
||||
cek: Uint8Array,
|
||||
p2c = 2048,
|
||||
p2s: Uint8Array = crypto.getRandomValues(new Uint8Array(16)),
|
||||
) {
|
||||
const derived = await deriveKey(p2s, alg, p2c, key)
|
||||
|
||||
const encryptedKey = await aeskw.wrap(alg.slice(-6), derived, cek)
|
||||
|
||||
return { encryptedKey, p2c, p2s: base64url(p2s) }
|
||||
}
|
||||
|
||||
export async function unwrap(
|
||||
alg: string,
|
||||
key: types.CryptoKey | Uint8Array,
|
||||
encryptedKey: Uint8Array,
|
||||
p2c: number,
|
||||
p2s: Uint8Array,
|
||||
) {
|
||||
const derived = await deriveKey(p2s, alg, p2c, key)
|
||||
|
||||
return aeskw.unwrap(alg.slice(-6), derived, encryptedKey)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type * as types from '../types.d.ts'
|
||||
import { checkEncCryptoKey } from './crypto_key.js'
|
||||
import checkKeyLength from './check_key_length.js'
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
|
||||
const subtleAlgorithm = (alg: string) => {
|
||||
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`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function encrypt(alg: string, key: types.CryptoKey, cek: Uint8Array) {
|
||||
checkEncCryptoKey(key, alg, 'encrypt')
|
||||
checkKeyLength(alg, key)
|
||||
|
||||
return new Uint8Array(await crypto.subtle.encrypt(subtleAlgorithm(alg), key, cek))
|
||||
}
|
||||
|
||||
export async function decrypt(alg: string, key: types.CryptoKey, encryptedKey: Uint8Array) {
|
||||
checkEncCryptoKey(key, alg, 'decrypt')
|
||||
checkKeyLength(alg, key)
|
||||
|
||||
return new Uint8Array(await crypto.subtle.decrypt(subtleAlgorithm(alg), key, encryptedKey))
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import subtleAlgorithm from './subtle_dsa.js'
|
||||
|
||||
import checkKeyLength from './check_key_length.js'
|
||||
import getSignKey from './get_sign_verify_key.js'
|
||||
|
||||
export default async function sign(alg: string, key: CryptoKey | Uint8Array, data: Uint8Array) {
|
||||
export default async (alg: string, key: types.CryptoKey | Uint8Array, data: Uint8Array) => {
|
||||
const cryptoKey = await getSignKey(alg, key, 'sign')
|
||||
checkKeyLength(alg, cryptoKey)
|
||||
const signature = await crypto.subtle.sign(
|
||||
@@ -1,6 +1,6 @@
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
|
||||
export default function subtleDsa(alg: string, algorithm: KeyAlgorithm | EcKeyAlgorithm) {
|
||||
export default (alg: string, algorithm: KeyAlgorithm | EcKeyAlgorithm) => {
|
||||
const hash = `SHA-${alg.slice(-3)}`
|
||||
switch (alg) {
|
||||
case 'HS256':
|
||||
@@ -22,7 +22,7 @@ export default function subtleDsa(alg: string, algorithm: KeyAlgorithm | EcKeyAl
|
||||
case 'Ed25519':
|
||||
return { name: 'Ed25519' }
|
||||
case 'EdDSA':
|
||||
return { name: algorithm.name }
|
||||
return { name: 'Ed25519' }
|
||||
default:
|
||||
throw new JOSENotSupported(
|
||||
`alg ${alg} is not supported either by JOSE or your javascript runtime`,
|
||||
@@ -1,4 +1,4 @@
|
||||
const validateAlgorithms = (option: string, algorithms?: string[]) => {
|
||||
export default (option: string, algorithms?: string[]) => {
|
||||
if (
|
||||
algorithms !== undefined &&
|
||||
(!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== 'string'))
|
||||
@@ -12,5 +12,3 @@ const validateAlgorithms = (option: string, algorithms?: string[]) => {
|
||||
|
||||
return new Set(algorithms)
|
||||
}
|
||||
|
||||
export default validateAlgorithms
|
||||
|
||||
@@ -6,13 +6,13 @@ interface CritCheckHeader {
|
||||
[propName: string]: unknown
|
||||
}
|
||||
|
||||
function validateCrit(
|
||||
export default (
|
||||
Err: typeof JWEInvalid | typeof JWSInvalid,
|
||||
recognizedDefault: Map<string, boolean>,
|
||||
recognizedOption: { [propName: string]: boolean } | undefined,
|
||||
protectedHeader: CritCheckHeader | undefined,
|
||||
joseHeader: CritCheckHeader,
|
||||
) {
|
||||
) => {
|
||||
if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined) {
|
||||
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected')
|
||||
}
|
||||
@@ -53,5 +53,3 @@ function validateCrit(
|
||||
|
||||
return new Set(protectedHeader.crit)
|
||||
}
|
||||
|
||||
export default validateCrit
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import subtleAlgorithm from './subtle_dsa.js'
|
||||
|
||||
import checkKeyLength from './check_key_length.js'
|
||||
import getVerifyKey from './get_sign_verify_key.js'
|
||||
|
||||
export default async function verify(
|
||||
export default async (
|
||||
alg: string,
|
||||
key: CryptoKey | Uint8Array,
|
||||
key: types.CryptoKey | Uint8Array,
|
||||
signature: Uint8Array,
|
||||
data: Uint8Array,
|
||||
) {
|
||||
) => {
|
||||
const cryptoKey = await getVerifyKey(alg, key, 'verify')
|
||||
checkKeyLength(alg, cryptoKey)
|
||||
const algorithm = subtleAlgorithm(alg, cryptoKey.algorithm)
|
||||
@@ -1,54 +0,0 @@
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import { encoder, concat, uint32be, lengthAndInput, concatKdf } from '../lib/buffer_utils.js'
|
||||
import { checkEncCryptoKey } from '../lib/crypto_key.js'
|
||||
|
||||
export async function deriveKey(
|
||||
publicKey: CryptoKey,
|
||||
privateKey: CryptoKey,
|
||||
algorithm: string,
|
||||
keyLength: number,
|
||||
apu: Uint8Array = new Uint8Array(0),
|
||||
apv: Uint8Array = new Uint8Array(0),
|
||||
) {
|
||||
checkEncCryptoKey(publicKey, 'ECDH')
|
||||
checkEncCryptoKey(privateKey, 'ECDH', 'deriveBits')
|
||||
|
||||
const value = concat(
|
||||
lengthAndInput(encoder.encode(algorithm)),
|
||||
lengthAndInput(apu),
|
||||
lengthAndInput(apv),
|
||||
uint32be(keyLength),
|
||||
)
|
||||
|
||||
let length: number
|
||||
if (publicKey.algorithm.name === 'X25519') {
|
||||
length = 256
|
||||
} else {
|
||||
length =
|
||||
Math.ceil(parseInt((publicKey.algorithm as EcKeyAlgorithm).namedCurve.slice(-3), 10) / 8) << 3
|
||||
}
|
||||
|
||||
const sharedSecret = new Uint8Array(
|
||||
await crypto.subtle.deriveBits(
|
||||
{
|
||||
name: publicKey.algorithm.name,
|
||||
public: publicKey,
|
||||
},
|
||||
privateKey,
|
||||
length,
|
||||
),
|
||||
)
|
||||
|
||||
return concatKdf(sharedSecret, keyLength, value)
|
||||
}
|
||||
|
||||
export async function generateEpk(key: CryptoKey) {
|
||||
return crypto.subtle.generateKey(key.algorithm as EcKeyAlgorithm, true, ['deriveBits'])
|
||||
}
|
||||
|
||||
export function ecdhAllowed(key: CryptoKey) {
|
||||
return (
|
||||
['P-256', 'P-384', 'P-521'].includes((key.algorithm as EcKeyAlgorithm).namedCurve) ||
|
||||
key.algorithm.name === 'X25519'
|
||||
)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import type { FetchFunction } from './interfaces.d.ts'
|
||||
import { JOSEError, JWKSTimeout } from '../util/errors.js'
|
||||
|
||||
type AcceptedRequestOptions = Pick<RequestInit, 'headers'>
|
||||
|
||||
const fetchJwks: FetchFunction = async (
|
||||
url: URL,
|
||||
timeout: number,
|
||||
options: AcceptedRequestOptions,
|
||||
) => {
|
||||
let controller!: AbortController
|
||||
let id!: ReturnType<typeof setTimeout>
|
||||
let timedOut = false
|
||||
if (typeof AbortController === 'function') {
|
||||
controller = new AbortController()
|
||||
id = setTimeout(() => {
|
||||
timedOut = true
|
||||
controller.abort()
|
||||
}, timeout)
|
||||
}
|
||||
|
||||
const response = await fetch(url.href, {
|
||||
signal: controller ? controller.signal : undefined,
|
||||
redirect: 'manual',
|
||||
headers: options.headers,
|
||||
}).catch((err) => {
|
||||
if (timedOut) throw new JWKSTimeout()
|
||||
throw err
|
||||
})
|
||||
|
||||
if (id !== undefined) clearTimeout(id)
|
||||
|
||||
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 default fetchJwks
|
||||
@@ -1,153 +0,0 @@
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
import random from './random.js'
|
||||
import type { GenerateKeyPairOptions } from '../key/generate_key_pair.js'
|
||||
import type { GenerateSecretOptions } from '../key/generate_secret.js'
|
||||
|
||||
export async function generateSecret(alg: string, options?: GenerateSecretOptions) {
|
||||
let length: number
|
||||
let algorithm: AesKeyGenParams | HmacKeyGenParams
|
||||
let keyUsages: KeyUsage[]
|
||||
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 random(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,
|
||||
) as unknown as Promise<CryptoKey>
|
||||
}
|
||||
|
||||
function getModulusLengthOption(options?: GenerateKeyPairOptions) {
|
||||
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: string, options?: GenerateKeyPairOptions) {
|
||||
let algorithm: RsaHashedKeyGenParams | EcKeyGenParams | KeyAlgorithm
|
||||
let keyUsages: KeyUsage[]
|
||||
|
||||
switch (alg) {
|
||||
case 'PS256':
|
||||
case 'PS384':
|
||||
case 'PS512':
|
||||
algorithm = {
|
||||
name: 'RSA-PSS',
|
||||
hash: `SHA-${alg.slice(-3)}`,
|
||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||
modulusLength: getModulusLengthOption(options),
|
||||
}
|
||||
keyUsages = ['sign', 'verify']
|
||||
break
|
||||
case 'RS256':
|
||||
case 'RS384':
|
||||
case 'RS512':
|
||||
algorithm = {
|
||||
name: 'RSASSA-PKCS1-v1_5',
|
||||
hash: `SHA-${alg.slice(-3)}`,
|
||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||
modulusLength: getModulusLengthOption(options),
|
||||
}
|
||||
keyUsages = ['sign', 'verify']
|
||||
break
|
||||
case 'RSA-OAEP':
|
||||
case 'RSA-OAEP-256':
|
||||
case 'RSA-OAEP-384':
|
||||
case 'RSA-OAEP-512':
|
||||
algorithm = {
|
||||
name: 'RSA-OAEP',
|
||||
hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`,
|
||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||
modulusLength: getModulusLengthOption(options),
|
||||
}
|
||||
keyUsages = ['decrypt', 'unwrapKey', 'encrypt', 'wrapKey']
|
||||
break
|
||||
case 'ES256':
|
||||
algorithm = { name: 'ECDSA', namedCurve: 'P-256' }
|
||||
keyUsages = ['sign', 'verify']
|
||||
break
|
||||
case 'ES384':
|
||||
algorithm = { name: 'ECDSA', namedCurve: 'P-384' }
|
||||
keyUsages = ['sign', 'verify']
|
||||
break
|
||||
case 'ES512':
|
||||
algorithm = { name: 'ECDSA', namedCurve: 'P-521' }
|
||||
keyUsages = ['sign', 'verify']
|
||||
break
|
||||
case 'Ed25519':
|
||||
algorithm = { name: 'Ed25519' }
|
||||
keyUsages = ['sign', 'verify']
|
||||
break
|
||||
case 'EdDSA': {
|
||||
keyUsages = ['sign', 'verify']
|
||||
algorithm = { name: 'Ed25519' }
|
||||
break
|
||||
}
|
||||
case 'ECDH-ES':
|
||||
case 'ECDH-ES+A128KW':
|
||||
case 'ECDH-ES+A192KW':
|
||||
case 'ECDH-ES+A256KW': {
|
||||
keyUsages = ['deriveBits']
|
||||
const crv = options?.crv ?? 'P-256'
|
||||
switch (crv) {
|
||||
case 'P-256':
|
||||
case 'P-384':
|
||||
case 'P-521': {
|
||||
algorithm = { name: 'ECDH', namedCurve: crv }
|
||||
break
|
||||
}
|
||||
case 'X25519':
|
||||
algorithm = { name: crv }
|
||||
break
|
||||
default:
|
||||
throw new JOSENotSupported(
|
||||
'Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, and X25519',
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
default:
|
||||
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')
|
||||
}
|
||||
|
||||
return crypto.subtle.generateKey(algorithm, options?.extractable ?? false, keyUsages) as Promise<{
|
||||
publicKey: CryptoKey
|
||||
privateKey: CryptoKey
|
||||
}>
|
||||
}
|
||||
Vendored
-65
@@ -1,65 +0,0 @@
|
||||
import type { JWK, CryptoKey } from '../types.d.ts'
|
||||
import type { PEMImportOptions } from '../key/import.js'
|
||||
|
||||
export interface TimingSafeEqual {
|
||||
(a: Uint8Array, b: Uint8Array): boolean
|
||||
}
|
||||
export interface Pbes2KWEncryptFunction {
|
||||
(
|
||||
alg: string,
|
||||
key: unknown,
|
||||
cek: Uint8Array,
|
||||
p2c?: number,
|
||||
p2s?: Uint8Array,
|
||||
): Promise<{
|
||||
encryptedKey: Uint8Array
|
||||
p2c: number
|
||||
p2s: string
|
||||
}>
|
||||
}
|
||||
export interface Pbes2KWDecryptFunction {
|
||||
(
|
||||
alg: string,
|
||||
key: unknown,
|
||||
encryptedKey: Uint8Array,
|
||||
p2c: number,
|
||||
p2s: Uint8Array,
|
||||
): Promise<Uint8Array>
|
||||
}
|
||||
export interface EncryptFunction {
|
||||
(
|
||||
enc: string,
|
||||
plaintext: Uint8Array,
|
||||
cek: unknown,
|
||||
iv: Uint8Array | undefined,
|
||||
aad: Uint8Array,
|
||||
): Promise<{
|
||||
ciphertext: Uint8Array
|
||||
tag: Uint8Array | undefined
|
||||
iv: Uint8Array | undefined
|
||||
}>
|
||||
}
|
||||
export interface DecryptFunction {
|
||||
(
|
||||
enc: string,
|
||||
cek: unknown,
|
||||
ciphertext: Uint8Array,
|
||||
iv: Uint8Array | undefined,
|
||||
tag: Uint8Array | undefined,
|
||||
additionalData: Uint8Array,
|
||||
): Promise<Uint8Array>
|
||||
}
|
||||
export interface FetchFunction {
|
||||
(url: URL, timeout: number, options?: any): Promise<{ [propName: string]: unknown }>
|
||||
}
|
||||
export interface DigestFunction {
|
||||
(digest: 'sha256' | 'sha384' | 'sha512', data: Uint8Array): Promise<Uint8Array>
|
||||
}
|
||||
export interface PEMImportFunction {
|
||||
(pem: string, alg: string, options?: PEMImportOptions): Promise<CryptoKey>
|
||||
}
|
||||
interface ExportFunction<T> {
|
||||
(key: unknown): Promise<T>
|
||||
}
|
||||
export type JWKExportFunction = ExportFunction<JWK>
|
||||
export type PEMExportFunction = ExportFunction<string>
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { CryptoKey, KeyObject } from '../types.d.ts'
|
||||
|
||||
export function assertCryptoKey(key: unknown): asserts key is CryptoKey {
|
||||
if (!isCryptoKey(key)) {
|
||||
throw new Error('unreachable. report.')
|
||||
}
|
||||
}
|
||||
|
||||
export function isCryptoKey(key: unknown): key is CryptoKey {
|
||||
// @ts-expect-error
|
||||
return key?.[Symbol.toStringTag] === 'CryptoKey'
|
||||
}
|
||||
|
||||
export function isKeyObject<T extends KeyObject = KeyObject>(key: unknown): key is T {
|
||||
// @ts-expect-error
|
||||
return key?.[Symbol.toStringTag] === 'KeyObject'
|
||||
}
|
||||
|
||||
export default (key: unknown): key is CryptoKey | KeyObject => {
|
||||
return isCryptoKey(key) || isKeyObject(key)
|
||||
}
|
||||
|
||||
export const types = ['CryptoKey', 'KeyObject']
|
||||
@@ -1,59 +0,0 @@
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import random from './random.js'
|
||||
import { p2s as concatSalt } from '../lib/buffer_utils.js'
|
||||
import { encode as base64url } from './base64url.js'
|
||||
import { wrap, unwrap } from './aeskw.js'
|
||||
import checkP2s from '../lib/check_p2s.js'
|
||||
import { checkEncCryptoKey } from '../lib/crypto_key.js'
|
||||
|
||||
function getCryptoKey(key: CryptoKey | Uint8Array, alg: string) {
|
||||
if (key instanceof Uint8Array) {
|
||||
return crypto.subtle.importKey('raw', key, 'PBKDF2', false, ['deriveBits'])
|
||||
}
|
||||
|
||||
checkEncCryptoKey(key, alg, 'deriveBits')
|
||||
return key
|
||||
}
|
||||
|
||||
async function deriveKey(p2s: Uint8Array, alg: string, p2c: number, key: CryptoKey | Uint8Array) {
|
||||
checkP2s(p2s)
|
||||
|
||||
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 getCryptoKey(key, alg)
|
||||
|
||||
return new Uint8Array(await crypto.subtle.deriveBits(subtleAlg, cryptoKey, keylen))
|
||||
}
|
||||
|
||||
export async function encrypt(
|
||||
alg: string,
|
||||
key: CryptoKey | Uint8Array,
|
||||
cek: Uint8Array,
|
||||
p2c = 2048,
|
||||
p2s: Uint8Array = random(new Uint8Array(16)),
|
||||
) {
|
||||
const derived = await deriveKey(p2s, alg, p2c, key)
|
||||
|
||||
const encryptedKey = await wrap(alg.slice(-6), derived, cek)
|
||||
|
||||
return { encryptedKey, p2c, p2s: base64url(p2s) }
|
||||
}
|
||||
|
||||
export async function decrypt(
|
||||
alg: string,
|
||||
key: CryptoKey | Uint8Array,
|
||||
encryptedKey: Uint8Array,
|
||||
p2c: number,
|
||||
p2s: Uint8Array,
|
||||
) {
|
||||
const derived = await deriveKey(p2s, alg, p2c, key)
|
||||
|
||||
return unwrap(alg.slice(-6), derived, encryptedKey)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export default crypto.getRandomValues.bind(crypto)
|
||||
@@ -1,18 +0,0 @@
|
||||
import type { CryptoKey } from '../types.d.ts'
|
||||
import subtleAlgorithm from './subtle_rsaes.js'
|
||||
import { checkEncCryptoKey } from '../lib/crypto_key.js'
|
||||
import checkKeyLength from './check_key_length.js'
|
||||
|
||||
export async function encrypt(alg: string, key: CryptoKey, cek: Uint8Array) {
|
||||
checkEncCryptoKey(key, alg, 'encrypt')
|
||||
checkKeyLength(alg, key)
|
||||
|
||||
return new Uint8Array(await crypto.subtle.encrypt(subtleAlgorithm(alg), key, cek))
|
||||
}
|
||||
|
||||
export async function decrypt(alg: string, key: CryptoKey, encryptedKey: Uint8Array) {
|
||||
checkEncCryptoKey(key, alg, 'decrypt')
|
||||
checkKeyLength(alg, key)
|
||||
|
||||
return new Uint8Array(await crypto.subtle.decrypt(subtleAlgorithm(alg), key, encryptedKey))
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export default 'WebCryptoAPI'
|
||||
@@ -1,15 +0,0 @@
|
||||
import { JOSENotSupported } from '../util/errors.js'
|
||||
|
||||
export default function subtleRsaEs(alg: string) {
|
||||
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`,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { TimingSafeEqual } from './interfaces.d.ts'
|
||||
|
||||
const timingSafeEqual: 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')
|
||||
}
|
||||
if (a.length !== b.length) {
|
||||
throw new TypeError('Input buffers must have the same length')
|
||||
}
|
||||
|
||||
const len = a.length
|
||||
let out = 0
|
||||
let i = -1
|
||||
while (++i < len) {
|
||||
out |= a[i] ^ b[i]
|
||||
}
|
||||
return out === 0
|
||||
}
|
||||
|
||||
export default timingSafeEqual
|
||||
Vendored
+11
-96
@@ -1,97 +1,3 @@
|
||||
/**
|
||||
* KeyLike are runtime-specific classes representing asymmetric keys or symmetric secrets. These are
|
||||
* instances of {@link !CryptoKey} and additionally {@link !KeyObject} in Node.js runtime.
|
||||
* {@link !Uint8Array} instances are also accepted as symmetric secret representation only.
|
||||
*
|
||||
* {@link key/import Key Import Functions} can be used to import PEM, or JWK formatted asymmetric
|
||||
* keys and certificates to these runtime-specific representations.
|
||||
*
|
||||
* In Node.js the {@link !Buffer} class is a subclass of {@link !Uint8Array} and so {@link !Buffer} can
|
||||
* be provided for symmetric secrets as well.
|
||||
*
|
||||
* {@link !KeyObject} is a representation of a key/secret available in the Node.js runtime. In
|
||||
* addition to the {@link key/import Key Import Functions} you may use the runtime APIs
|
||||
* {@link !createPublicKey}, {@link !createPrivateKey}, and {@link !createSecretKey} to obtain a
|
||||
* {@link !KeyObject} from your existing key material.
|
||||
*
|
||||
* {@link !CryptoKey} is a representation of a key/secret available in the Browser and
|
||||
* Web-interoperable 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.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* Import a PEM-encoded SPKI Public Key
|
||||
*
|
||||
* ```js
|
||||
* const algorithm = 'ES256'
|
||||
* const spki = `-----BEGIN PUBLIC KEY-----
|
||||
* MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFlHHWfLk0gLBbsLTcuCrbCqoHqmM
|
||||
* YJepMC+Q+Dd6RBmBiA41evUsNMwLeN+PNFqib+xwi9JkJ8qhZkq8Y/IzGg==
|
||||
* -----END PUBLIC KEY-----`
|
||||
* const ecPublicKey = await jose.importSPKI(spki, algorithm)
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* Import SPKI from an X.509 Certificate
|
||||
*
|
||||
* ```js
|
||||
* const algorithm = 'ES256'
|
||||
* const x509 = `-----BEGIN CERTIFICATE-----
|
||||
* MIIBXjCCAQSgAwIBAgIGAXvykuMKMAoGCCqGSM49BAMCMDYxNDAyBgNVBAMMK3Np
|
||||
* QXBNOXpBdk1VaXhXVWVGaGtjZXg1NjJRRzFyQUhXaV96UlFQTVpQaG8wHhcNMjEw
|
||||
* OTE3MDcwNTE3WhcNMjIwNzE0MDcwNTE3WjA2MTQwMgYDVQQDDCtzaUFwTTl6QXZN
|
||||
* VWl4V1VlRmhrY2V4NTYyUUcxckFIV2lfelJRUE1aUGhvMFkwEwYHKoZIzj0CAQYI
|
||||
* KoZIzj0DAQcDQgAE8PbPvCv5D5xBFHEZlBp/q5OEUymq7RIgWIi7tkl9aGSpYE35
|
||||
* UH+kBKDnphJO3odpPZ5gvgKs2nwRWcrDnUjYLDAKBggqhkjOPQQDAgNIADBFAiEA
|
||||
* 1yyMTRe66MhEXID9+uVub7woMkNYd0LhSHwKSPMUUTkCIFQGsfm1ecXOpeGOufAh
|
||||
* v+A1QWZMuTWqYt+uh/YSRNDn
|
||||
* -----END CERTIFICATE-----`
|
||||
* const ecPublicKey = await jose.importX509(x509, algorithm)
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* Import a PEM-encoded PKCS8 Private Key
|
||||
*
|
||||
* ```js
|
||||
* const algorithm = 'ES256'
|
||||
* const pkcs8 = `-----BEGIN PRIVATE KEY-----
|
||||
* MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiyvo0X+VQ0yIrOaN
|
||||
* nlrnUclopnvuuMfoc8HHly3505OhRANCAAQWUcdZ8uTSAsFuwtNy4KtsKqgeqYxg
|
||||
* l6kwL5D4N3pEGYGIDjV69Sw0zAt43480WqJv7HCL0mQnyqFmSrxj8jMa
|
||||
* -----END PRIVATE KEY-----`
|
||||
* const ecPrivateKey = await jose.importPKCS8(pkcs8, algorithm)
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* Import a JSON Web Key (JWK)
|
||||
*
|
||||
* ```js
|
||||
* const ecPublicKey = await jose.importJWK(
|
||||
* {
|
||||
* crv: 'P-256',
|
||||
* kty: 'EC',
|
||||
* x: 'ySK38C1jBdLwDsNWKzzBHqKYEE5Cgv-qjWvorUXk9fw',
|
||||
* y: '_LeQBw07cf5t57Iavn4j-BqJsAD1dpoz8gokd3sBsOo',
|
||||
* },
|
||||
* 'ES256',
|
||||
* )
|
||||
*
|
||||
* const rsaPublicKey = await jose.importJWK(
|
||||
* {
|
||||
* kty: 'RSA',
|
||||
* e: 'AQAB',
|
||||
* n: '12oBZRhCiZFJLcPg59LkZZ9mdhSMTKAQZYq32k_ti5SBB6jerkh-WzOMAO664r_qyLkqHUSp3u5SbXtseZEpN3XPWGKSxjsy-1JyEFTdLSYe6f9gfrmxkUF_7DTpq0gn6rntP05g2-wFW50YO7mosfdslfrTJYWHFhJALabAeYirYD7-9kqq9ebfFMF4sRRELbv9oi36As6Q9B3Qb5_C1rAzqfao_PCsf9EPsTZsVVVkA5qoIAr47lo1ipfiBPxUCCNSdvkmDTYgvvRm6ZoMjFbvOtgyts55fXKdMWv7I9HMD5HwE9uW839PWA514qhbcIsXEYSFMPMV6fnlsiZvQQ',
|
||||
* },
|
||||
* 'PS256',
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
export type KeyLike = CryptoKey | KeyObject
|
||||
|
||||
/** Generic JSON Web Key Parameters. */
|
||||
export interface JWKParameters {
|
||||
/** JWK "kty" (Key Type) Parameter */
|
||||
@@ -767,12 +673,21 @@ export interface JSONWebKeySet {
|
||||
keys: JWK[]
|
||||
}
|
||||
|
||||
export type CryptoRuntime = 'WebCryptoAPI' | 'node:crypto'
|
||||
|
||||
/**
|
||||
* {@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: string
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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 = Extract<
|
||||
Awaited<ReturnType<typeof crypto.subtle.generateKey>>,
|
||||
{ type: string }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as base64url from '../runtime/base64url.js'
|
||||
import * as base64url from '../lib/base64url.js'
|
||||
|
||||
/**
|
||||
* Utility function to encode a string or {@link !Uint8Array} as a base64url string.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { decode as base64url } from './base64url.js'
|
||||
import { decoder } from '../lib/buffer_utils.js'
|
||||
import isObject from '../lib/is_object.js'
|
||||
import type { JWTPayload } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
import { JWTInvalid } from './errors.js'
|
||||
|
||||
/**
|
||||
@@ -22,7 +22,9 @@ import { JWTInvalid } from './errors.js'
|
||||
*
|
||||
* @param jwt JWT token in compact JWS serialization.
|
||||
*/
|
||||
export function decodeJwt<PayloadType = JWTPayload>(jwt: string): PayloadType & JWTPayload {
|
||||
export function decodeJwt<PayloadType = types.JWTPayload>(
|
||||
jwt: string,
|
||||
): PayloadType & types.JWTPayload {
|
||||
if (typeof jwt !== 'string')
|
||||
throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string')
|
||||
|
||||
@@ -46,7 +48,8 @@ export function decodeJwt<PayloadType = JWTPayload>(jwt: string): PayloadType &
|
||||
throw new JWTInvalid('Failed to parse the decoded payload as JSON')
|
||||
}
|
||||
|
||||
if (!isObject<PayloadType & JWTPayload>(result)) throw new JWTInvalid('Invalid JWT Claims Set')
|
||||
if (!isObject<PayloadType & types.JWTPayload>(result))
|
||||
throw new JWTInvalid('Invalid JWT Claims Set')
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { decode as base64url } from './base64url.js'
|
||||
import { decoder } from '../lib/buffer_utils.js'
|
||||
import isObject from '../lib/is_object.js'
|
||||
import type { JWSHeaderParameters, JWEHeaderParameters } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
|
||||
export type ProtectedHeaderParameters = JWSHeaderParameters & JWEHeaderParameters
|
||||
export type ProtectedHeaderParameters = types.JWSHeaderParameters & types.JWEHeaderParameters
|
||||
|
||||
/**
|
||||
* Decodes the Protected Header of a JWE/JWS/JWT token utilizing any JOSE serialization.
|
||||
|
||||
+16
-6
@@ -1,4 +1,4 @@
|
||||
import type { JWTPayload, CryptoKey } from '../types.d.ts'
|
||||
import type * as types from '../types.d.ts'
|
||||
|
||||
/**
|
||||
* A generic Error that all other JOSE specific Error subclasses extend.
|
||||
@@ -74,10 +74,15 @@ export class JWTClaimValidationFailed extends JOSEError {
|
||||
* has however been verified. Claims Set verification happens after the JWS Signature or JWE
|
||||
* Decryption processes.
|
||||
*/
|
||||
payload: JWTPayload
|
||||
payload: types.JWTPayload
|
||||
|
||||
/** @ignore */
|
||||
constructor(message: string, payload: JWTPayload, claim = 'unspecified', reason = 'unspecified') {
|
||||
constructor(
|
||||
message: string,
|
||||
payload: types.JWTPayload,
|
||||
claim = 'unspecified',
|
||||
reason = 'unspecified',
|
||||
) {
|
||||
super(message, { cause: { claim, reason, payload } })
|
||||
this.claim = claim
|
||||
this.reason = reason
|
||||
@@ -126,10 +131,15 @@ export class JWTExpired extends JOSEError implements JWTClaimValidationFailed {
|
||||
* has however been verified. Claims Set verification happens after the JWS Signature or JWE
|
||||
* Decryption processes.
|
||||
*/
|
||||
payload: JWTPayload
|
||||
payload: types.JWTPayload
|
||||
|
||||
/** @ignore */
|
||||
constructor(message: string, payload: JWTPayload, claim = 'unspecified', reason = 'unspecified') {
|
||||
constructor(
|
||||
message: string,
|
||||
payload: types.JWTPayload,
|
||||
claim = 'unspecified',
|
||||
reason = 'unspecified',
|
||||
) {
|
||||
super(message, { cause: { claim, reason, payload } })
|
||||
this.claim = claim
|
||||
this.reason = reason
|
||||
@@ -446,7 +456,7 @@ export class JWKSNoMatchingKey extends JOSEError {
|
||||
*/
|
||||
export class JWKSMultipleMatchingKeys extends JOSEError {
|
||||
/** @ignore */
|
||||
[Symbol.asyncIterator]!: () => AsyncIterableIterator<CryptoKey>
|
||||
[Symbol.asyncIterator]!: () => AsyncIterableIterator<types.CryptoKey>
|
||||
|
||||
/** @ignore */
|
||||
static override code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS'
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ echo "Using $(deno --version | head -1)"
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
deno run --allow-read --allow-net --allow-env --import-map tap/import_map.json --no-npm tap/run-deno.ts
|
||||
deno run --unstable-sloppy-imports --allow-read --allow-net --allow-env --no-npm tap/run-deno.ts
|
||||
|
||||
+1
-12
@@ -1,15 +1,4 @@
|
||||
#!/bin/bash
|
||||
./node_modules/.bin/esbuild \
|
||||
--log-level=warning \
|
||||
--format=esm \
|
||||
--sourcemap \
|
||||
--bundle \
|
||||
--platform=node \
|
||||
--external:electron \
|
||||
--external:#dist \
|
||||
--target=esnext \
|
||||
--outfile=tap/run-electron.js \
|
||||
tap/run-electron.ts
|
||||
|
||||
source .electron_flags.sh
|
||||
electron tap/run-electron.js
|
||||
electron tap/run-electron.ts
|
||||
|
||||
+2
-12
@@ -1,21 +1,11 @@
|
||||
#!/bin/bash
|
||||
./node_modules/.bin/esbuild \
|
||||
--log-level=warning \
|
||||
--format=esm \
|
||||
--sourcemap \
|
||||
--bundle \
|
||||
--platform=node \
|
||||
--external:#dist \
|
||||
--target=esnext \
|
||||
--outfile=tap/run-node.mjs \
|
||||
tap/run-node.ts
|
||||
|
||||
source .node_flags.sh
|
||||
|
||||
node tap/run-node.mjs
|
||||
node tap/run-node.ts
|
||||
WEB_CRYPTO_API=$?
|
||||
|
||||
node tap/run-node.mjs --keys='KeyObject'
|
||||
node tap/run-node.ts --keys='KeyObject'
|
||||
WEB_CRYPTO_API_WITH_KEYOBJECT=$?
|
||||
|
||||
echo ""
|
||||
|
||||
+3
-2
@@ -1,7 +1,6 @@
|
||||
import type QUnit from 'qunit'
|
||||
import * as env from './env.js'
|
||||
import type * as jose from '../src/index.js'
|
||||
import random from './random.js'
|
||||
import * as roundtrip from './encrypt.js'
|
||||
|
||||
export default (
|
||||
@@ -35,7 +34,9 @@ export default (
|
||||
function secretsFor(enc: string) {
|
||||
return [
|
||||
keys.generateSecret(enc, { extractable: true }),
|
||||
random(parseInt(enc.endsWith('GCM') ? enc.slice(1, 4) : enc.slice(-3)) >> 3),
|
||||
crypto.getRandomValues(
|
||||
new Uint8Array(parseInt(enc.endsWith('GCM') ? enc.slice(1, 4) : enc.slice(-3)) >> 3),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import type QUnit from 'qunit'
|
||||
import * as env from './env.js'
|
||||
import type * as jose from '../src/index.js'
|
||||
import random from './random.js'
|
||||
import * as roundtrip from './encrypt.js'
|
||||
|
||||
export default (
|
||||
@@ -35,7 +34,7 @@ export default (
|
||||
function secretsFor(alg: string) {
|
||||
return [
|
||||
keys.generateSecret(alg, { extractable: true }),
|
||||
random(parseInt(alg.slice(1, 4), 10) >> 3),
|
||||
crypto.getRandomValues(new Uint8Array(parseInt(alg.slice(1, 4), 10) >> 3)),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
+3
-4
@@ -1,6 +1,5 @@
|
||||
import type QUnit from 'qunit'
|
||||
import type * as jose from '../src/index.js'
|
||||
import random from './random.js'
|
||||
|
||||
type keyType = Uint8Array | jose.CryptoKey | jose.KeyObject | jose.GenerateKeyPairResult
|
||||
|
||||
@@ -68,12 +67,12 @@ export async function jwe(
|
||||
alg: string,
|
||||
enc: string,
|
||||
secretOrKeyPair: keyType,
|
||||
cleartext = random(),
|
||||
cleartext = crypto.getRandomValues(new Uint8Array(16)),
|
||||
) {
|
||||
// Test Uint8Array, CryptoKey, and KeyObject key inputs
|
||||
{
|
||||
const [eKey, dKey] = await getKeys(secretOrKeyPair, false, keys)
|
||||
const aad = random()
|
||||
const aad = crypto.getRandomValues(new Uint8Array(16))
|
||||
|
||||
const jwe = await new lib.FlattenedEncrypt(cleartext)
|
||||
.setProtectedHeader({ alg, enc })
|
||||
@@ -96,7 +95,7 @@ export async function jwe(
|
||||
// Test JWK key input
|
||||
{
|
||||
const [eKey, dKey] = await getKeys(secretOrKeyPair, true, keys)
|
||||
const aad = random()
|
||||
const aad = crypto.getRandomValues(new Uint8Array(16))
|
||||
|
||||
const jwe = await new lib.FlattenedEncrypt(cleartext)
|
||||
.setProtectedHeader({ alg, enc })
|
||||
|
||||
@@ -4,19 +4,19 @@ import type * as jose from '../src/index.js'
|
||||
export default async (
|
||||
QUnit: QUnit,
|
||||
lib: typeof jose,
|
||||
keys: Pick<typeof jose, 'exportJWK' | 'generateKeyPair' | 'generateSecret' | 'importJWK'>,
|
||||
_lib: Pick<typeof jose, 'exportJWK' | 'generateKeyPair' | 'generateSecret' | 'importJWK'>,
|
||||
) => {
|
||||
const { module, test } = QUnit
|
||||
module('generate_options.ts')
|
||||
|
||||
const isWebCrypto =
|
||||
typeof CryptoKey !== 'undefined' && (await keys.generateSecret('HS256')) instanceof CryptoKey
|
||||
typeof CryptoKey !== 'undefined' && (await lib.generateSecret('HS256')) instanceof CryptoKey
|
||||
|
||||
if (isWebCrypto) {
|
||||
for (const extractable of [undefined, true, false]) {
|
||||
test(`secret CryptoKey extractable: ${extractable ?? 'default (false)'}`, async (t) => {
|
||||
const expected = extractable ?? false
|
||||
const secret = (await keys.generateSecret('HS256', { extractable })) as CryptoKey
|
||||
const secret = (await lib.generateSecret('HS256', { extractable })) as CryptoKey
|
||||
t.equal(secret.extractable, expected)
|
||||
})
|
||||
}
|
||||
@@ -24,7 +24,7 @@ export default async (
|
||||
for (const extractable of [undefined, true, false]) {
|
||||
test(`CryptoKeyPair extractable: ${extractable ?? 'default (false)'}`, async (t) => {
|
||||
const expected = extractable ?? false
|
||||
const kp = (await keys.generateKeyPair('ES256', { extractable })) as CryptoKeyPair
|
||||
const kp = (await lib.generateKeyPair('ES256', { extractable })) as CryptoKeyPair
|
||||
t.equal(kp.privateKey.extractable, expected)
|
||||
t.equal(kp.publicKey.extractable, true)
|
||||
})
|
||||
@@ -34,7 +34,7 @@ export default async (
|
||||
for (const modulusLength of [undefined, 2048, 3072]) {
|
||||
test(`RSA modulusLength ${modulusLength ?? 'default (2048)'}`, async (t) => {
|
||||
const expected = modulusLength ?? 2048
|
||||
const { publicKey } = (await keys.generateKeyPair('RS256', {
|
||||
const { publicKey } = (await lib.generateKeyPair('RS256', {
|
||||
modulusLength,
|
||||
})) as CryptoKeyPair
|
||||
|
||||
|
||||
+5
-3
@@ -1,6 +1,5 @@
|
||||
import type QUnit from 'qunit'
|
||||
import type * as jose from '../src/index.js'
|
||||
import random from './random.js'
|
||||
import * as roundtrip from './sign.js'
|
||||
|
||||
export default (
|
||||
@@ -16,13 +15,16 @@ export default (
|
||||
function digestSizeSecretsFor(alg: string) {
|
||||
return [
|
||||
keys.generateSecret(alg, { extractable: true }),
|
||||
random(parseInt(alg.slice(2, 5), 10) >> 3),
|
||||
crypto.getRandomValues(new Uint8Array(parseInt(alg.slice(2, 5), 10) >> 3)),
|
||||
]
|
||||
}
|
||||
|
||||
function nonDigestSizeSecretFor(alg: string) {
|
||||
const length = parseInt(alg.slice(2, 5), 10) >> 3
|
||||
return [random(length - 1), random(length + 1)]
|
||||
return [
|
||||
crypto.getRandomValues(new Uint8Array(length - 1)),
|
||||
crypto.getRandomValues(new Uint8Array(length + 1)),
|
||||
]
|
||||
}
|
||||
|
||||
for (const alg of algorithms) {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"./noop.js": "./noop.ts",
|
||||
"./pbes2.js": "./pbes2.ts",
|
||||
"./pem.js": "./pem.ts",
|
||||
"./random.js": "./random.ts",
|
||||
"./rsaes.js": "./rsaes.ts",
|
||||
"./run.js": "./run.ts",
|
||||
"./sign.js": "./sign.ts"
|
||||
|
||||
+7
-3
@@ -6,7 +6,7 @@ import type * as jose from '../src/index.js'
|
||||
export default (
|
||||
QUnit: QUnit,
|
||||
lib: typeof jose,
|
||||
keys: Pick<typeof jose, 'exportJWK' | 'generateKeyPair' | 'generateSecret' | 'importJWK'>,
|
||||
_keys: Pick<typeof jose, 'exportJWK' | 'generateKeyPair' | 'generateSecret' | 'importJWK'>,
|
||||
) => {
|
||||
const { module, test } = QUnit
|
||||
module('jwk.ts')
|
||||
@@ -82,12 +82,16 @@ export default (
|
||||
}
|
||||
|
||||
const execute = async (t: typeof QUnit.assert) => {
|
||||
const key = await lib.importJWK({ ...jwk, ext: true }, alg)
|
||||
const key = await lib.importJWK({ ...jwk, ext: true } as jose.JWK, alg)
|
||||
|
||||
const exported = await lib.exportJWK(key)
|
||||
|
||||
for (const prop of [...new Set([...Object.keys(jwk), ...Object.keys(exported)])]) {
|
||||
t.strictEqual(exported[prop], jwk[prop as keyof JsonWebKey], `${prop} mismatch`)
|
||||
t.strictEqual(
|
||||
exported[prop as keyof JsonWebKey],
|
||||
jwk[prop as keyof JsonWebKey],
|
||||
`${prop} mismatch`,
|
||||
)
|
||||
}
|
||||
|
||||
t.ok(1)
|
||||
|
||||
@@ -12,11 +12,15 @@ const stub: Pick<
|
||||
> = {
|
||||
// @ts-expect-error
|
||||
exportJWK(key) {
|
||||
let k: crypto.KeyObject
|
||||
if (key instanceof Uint8Array) {
|
||||
key = crypto.createSecretKey(key)
|
||||
k = crypto.createSecretKey(key)
|
||||
} else {
|
||||
// @ts-expect-error
|
||||
k = key
|
||||
}
|
||||
|
||||
return (key as crypto.KeyObject).export({ format: 'jwk' })
|
||||
return (k as unknown as crypto.KeyObject).export({ format: 'jwk' })
|
||||
},
|
||||
// @ts-expect-error
|
||||
importJWK(jwk) {
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ function normalize(pem: string) {
|
||||
export default (
|
||||
QUnit: QUnit,
|
||||
lib: typeof jose,
|
||||
keys: Pick<typeof jose, 'exportJWK' | 'generateKeyPair' | 'generateSecret' | 'importJWK'>,
|
||||
_keys: Pick<typeof jose, 'exportJWK' | 'generateKeyPair' | 'generateSecret' | 'importJWK'>,
|
||||
) => {
|
||||
const { module, test } = QUnit
|
||||
module('pem.ts')
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
// @ts-ignore
|
||||
const nodeCrypto = await import('crypto').catch(() => {})
|
||||
|
||||
export default (byteLength = 16) => {
|
||||
try {
|
||||
return crypto.getRandomValues(new Uint8Array(byteLength))
|
||||
} catch {
|
||||
return nodeCrypto.randomFillSync(new Uint8Array(byteLength))
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user