unjwt 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +445 -190
- package/dist/index.d.mts +6 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.mjs +5 -5
- package/dist/jwe.d.mts +3 -3
- package/dist/jwe.d.ts +3 -3
- package/dist/jwe.mjs +4 -3
- package/dist/jwk.d.mts +2 -2
- package/dist/jwk.d.ts +2 -2
- package/dist/jwk.mjs +2 -2
- package/dist/jws.d.mts +3 -3
- package/dist/jws.d.ts +3 -3
- package/dist/jws.mjs +4 -4
- package/dist/shared/unjwt.1gbuWbc6.mjs +1507 -0
- package/dist/shared/unjwt.B0XDPf8a.d.ts +189 -0
- package/dist/shared/unjwt.B4fFiy1c.d.mts +52 -0
- package/dist/shared/unjwt.BG_R52ey.d.mts +100 -0
- package/dist/shared/unjwt.BISJ5yt1.d.ts +52 -0
- package/dist/shared/unjwt.BxqF-n1S.mjs +167 -0
- package/dist/shared/unjwt.C9Xhi7hE.mjs +101 -0
- package/dist/shared/unjwt.CVKbKOFy.d.ts +100 -0
- package/dist/shared/unjwt.Cd9nTEWg.d.mts +52 -0
- package/dist/shared/unjwt.Ce72NmIz.mjs +453 -0
- package/dist/shared/unjwt.CxhGjEAX.d.mts +202 -0
- package/dist/shared/unjwt.CxhGjEAX.d.ts +202 -0
- package/dist/shared/unjwt.D1J1DelP.d.mts +198 -0
- package/dist/shared/unjwt.DHdDAH0L.mjs +83 -0
- package/dist/shared/unjwt.DQIYSlHj.d.mts +189 -0
- package/dist/shared/unjwt.DqmNvXMd.d.ts +52 -0
- package/dist/shared/unjwt.WvWb2zNM.d.ts +198 -0
- package/dist/utils.d.mts +2 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.mjs +1 -0
- package/package.json +8 -4
- package/dist/shared/unjwt.4uzJv0cA.d.mts +0 -110
- package/dist/shared/unjwt.A7HKFx3n.d.mts +0 -52
- package/dist/shared/unjwt.BdjvpIUY.d.mts +0 -79
- package/dist/shared/unjwt.BmjmfFWo.d.ts +0 -79
- package/dist/shared/unjwt.C9ELsKeK.mjs +0 -120
- package/dist/shared/unjwt.CVUxLNoy.d.ts +0 -52
- package/dist/shared/unjwt.ChP7VhqI.d.ts +0 -110
- package/dist/shared/unjwt.DD0p18Gd.mjs +0 -81
- package/dist/shared/unjwt.DGsIvgyn.d.ts +0 -106
- package/dist/shared/unjwt.DPTwlVpK.mjs +0 -28
- package/dist/shared/unjwt.Dr-mxtq9.d.mts +0 -106
- package/dist/shared/unjwt.KOd0vXyx.mjs +0 -90
- package/dist/shared/unjwt._GivNhs2.mjs +0 -213
- package/dist/shared/unjwt.zb2wBNCJ.d.mts +0 -82
- package/dist/shared/unjwt.zb2wBNCJ.d.ts +0 -82
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { n as JWK_HMAC, o as JWK_RSA_SIGN, p as JWK_RSA_PSS, q as JWK_ECDSA, m as JWK } from './unjwt.CxhGjEAX.js';
|
|
2
|
+
import { J as JoseHeaderParameters, a as JWTClaims } from './unjwt.BISJ5yt1.js';
|
|
3
|
+
|
|
4
|
+
type JWS_SIGN_EXTRA = "Ed25519" | "EdDSA";
|
|
5
|
+
/** JWS Signing Algorithm Identifier. */
|
|
6
|
+
type JWSAlgorithm = JWK_HMAC | JWK_RSA_SIGN | JWK_RSA_PSS | JWK_ECDSA | JWS_SIGN_EXTRA;
|
|
7
|
+
/** Recognized JWS Header Parameters, any other Header Members may also be present. */
|
|
8
|
+
interface JWSHeaderParameters extends JoseHeaderParameters {
|
|
9
|
+
/** JWS "alg" (Algorithm) Header Parameter */
|
|
10
|
+
alg?: JWSAlgorithm;
|
|
11
|
+
/**
|
|
12
|
+
* This JWS Extension Header Parameter modifies the JWS Payload representation and the JWS Signing
|
|
13
|
+
* Input computation as per {@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}.
|
|
14
|
+
* If `false`, the payload is used directly without Base64URL encoding.
|
|
15
|
+
* Defaults to `true`.
|
|
16
|
+
*/
|
|
17
|
+
b64?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** JWS Protected Header */
|
|
20
|
+
interface JWSProtectedHeader extends JWSHeaderParameters {
|
|
21
|
+
alg: JWSAlgorithm;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* JWS (JSON Web Signature) options for signing
|
|
25
|
+
*/
|
|
26
|
+
interface JWSSignOptions {
|
|
27
|
+
/** The JWS Algorithm to use. Must be provided. */
|
|
28
|
+
alg?: JWSAlgorithm;
|
|
29
|
+
/**
|
|
30
|
+
* Additional protected header parameters. `alg` is automatically included.
|
|
31
|
+
* `typ` defaults to "JWT" if not provided.
|
|
32
|
+
*/
|
|
33
|
+
protectedHeader?: JWSHeaderParameters;
|
|
34
|
+
}
|
|
35
|
+
/** Result of JWS verification */
|
|
36
|
+
interface JWSVerifyResult<T = JWTClaims | Uint8Array | string> {
|
|
37
|
+
/** The decoded and verified payload. */
|
|
38
|
+
payload: T;
|
|
39
|
+
/** The JWS Protected Header. */
|
|
40
|
+
protectedHeader: JWSProtectedHeader;
|
|
41
|
+
}
|
|
42
|
+
/** Options for JWS verification */
|
|
43
|
+
interface JWSVerifyOptions {
|
|
44
|
+
/** List of allowed algorithms. If provided, the JWS `alg` must be in this list. */
|
|
45
|
+
algorithms?: JWSAlgorithm[];
|
|
46
|
+
/** List of critical header parameters that must be understood and processed. */
|
|
47
|
+
critical?: string[];
|
|
48
|
+
/** If true, forces the payload to be returned as a Uint8Array, otherwise type is inferred. */
|
|
49
|
+
forceUint8Array?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates a JWS (JSON Web Signature) in Compact Serialization format.
|
|
54
|
+
*
|
|
55
|
+
* @param payload The payload to sign. Can be a string, Uint8Array, or an object (which will be JSON stringified).
|
|
56
|
+
* @param key The signing key (CryptoKey, JWK, or raw symmetric key as Uint8Array).
|
|
57
|
+
* @param options Signing options, including the algorithm (`alg`) and protected header parameters.
|
|
58
|
+
* @returns A Promise resolving to the JWS Compact Serialization string.
|
|
59
|
+
*/
|
|
60
|
+
declare function sign(payload: JWTClaims, key: JWK | Uint8Array, options?: JWSSignOptions): Promise<string>;
|
|
61
|
+
declare function sign(payload: string | Uint8Array | Record<string, any>, key: JWK | Uint8Array, options?: JWSSignOptions): Promise<string>;
|
|
62
|
+
declare function sign(payload: JWTClaims, key: CryptoKey, options: JWSSignOptions & {
|
|
63
|
+
alg: JWSAlgorithm;
|
|
64
|
+
}): Promise<string>;
|
|
65
|
+
declare function sign(payload: string | Uint8Array | Record<string, any>, key: CryptoKey, options: JWSSignOptions & {
|
|
66
|
+
alg: JWSAlgorithm;
|
|
67
|
+
}): Promise<string>;
|
|
68
|
+
declare function sign(payload: string | Uint8Array | Record<string, any>, key: CryptoKey | JWK | Uint8Array, options: JWSSignOptions & {
|
|
69
|
+
alg: JWSAlgorithm;
|
|
70
|
+
}): Promise<string>;
|
|
71
|
+
type KeyLookupFunction = (header: JWSProtectedHeader) => CryptoKey | JWK | Uint8Array | Promise<CryptoKey | JWK | Uint8Array>;
|
|
72
|
+
/**
|
|
73
|
+
* Verifies a JWS (JSON Web Signature) in Compact Serialization format.
|
|
74
|
+
*
|
|
75
|
+
* @param jws The JWS Compact Serialization string.
|
|
76
|
+
* @param key The verification key (CryptoKey, JWK, raw symmetric key as Uint8Array, or a function resolving the key).
|
|
77
|
+
* @param options Verification options, such as allowed algorithms.
|
|
78
|
+
* @returns A Promise resolving to an object containing the verified payload and protected header.
|
|
79
|
+
* @throws If the JWS is invalid, signature verification fails, or options are not met.
|
|
80
|
+
*/
|
|
81
|
+
declare function verify<T = JWTClaims | Uint8Array | string>(jws: string, key: CryptoKey | JWK | Uint8Array | KeyLookupFunction, options?: JWSVerifyOptions): Promise<JWSVerifyResult<T>>;
|
|
82
|
+
declare function verify(jws: string, key: CryptoKey | JWK | Uint8Array | KeyLookupFunction, options: JWSVerifyOptions & {
|
|
83
|
+
forceUint8Array: true;
|
|
84
|
+
}): Promise<JWSVerifyResult<Uint8Array>>;
|
|
85
|
+
|
|
86
|
+
type jws_JWSAlgorithm = JWSAlgorithm;
|
|
87
|
+
type jws_JWSHeaderParameters = JWSHeaderParameters;
|
|
88
|
+
type jws_JWSProtectedHeader = JWSProtectedHeader;
|
|
89
|
+
type jws_JWSSignOptions = JWSSignOptions;
|
|
90
|
+
type jws_JWSVerifyOptions = JWSVerifyOptions;
|
|
91
|
+
type jws_JWSVerifyResult<T = JWTClaims | Uint8Array | string> = JWSVerifyResult<T>;
|
|
92
|
+
declare const jws_sign: typeof sign;
|
|
93
|
+
declare const jws_verify: typeof verify;
|
|
94
|
+
declare namespace jws {
|
|
95
|
+
export { jws_sign as sign, jws_verify as verify };
|
|
96
|
+
export type { jws_JWSAlgorithm as JWSAlgorithm, jws_JWSHeaderParameters as JWSHeaderParameters, jws_JWSProtectedHeader as JWSProtectedHeader, jws_JWSSignOptions as JWSSignOptions, jws_JWSVerifyOptions as JWSVerifyOptions, jws_JWSVerifyResult as JWSVerifyResult };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { jws as j, sign as s, verify as v };
|
|
100
|
+
export type { JWSAlgorithm as J, JWSHeaderParameters as a, JWSProtectedHeader as b, JWSSignOptions as c, JWSVerifyResult as d, JWSVerifyOptions as e };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { i as JWK_RSA_Public } from './unjwt.CxhGjEAX.mjs';
|
|
2
|
+
|
|
3
|
+
/** Header Parameters common to JWE and JWS */
|
|
4
|
+
interface JoseHeaderParameters {
|
|
5
|
+
/** "kid" (Key ID) Header Parameter */
|
|
6
|
+
kid?: string;
|
|
7
|
+
/** "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter */
|
|
8
|
+
x5t?: string;
|
|
9
|
+
/** "x5c" (X.509 Certificate Chain) Header Parameter */
|
|
10
|
+
x5c?: string[];
|
|
11
|
+
/** "x5u" (X.509 URL) Header Parameter */
|
|
12
|
+
x5u?: string;
|
|
13
|
+
/** "jku" (JWK Set URL) Header Parameter */
|
|
14
|
+
jku?: string;
|
|
15
|
+
/** "jwk" (JSON Web Key) Header Parameter */
|
|
16
|
+
jwk?: JWK_RSA_Public;
|
|
17
|
+
/** "typ" (Type) Header Parameter */
|
|
18
|
+
typ?: string;
|
|
19
|
+
/** "cty" (Content Type) Header Parameter */
|
|
20
|
+
cty?: string;
|
|
21
|
+
/** "crit" (Critical) Header Parameter */
|
|
22
|
+
crit?: string[];
|
|
23
|
+
/** Any other JWS/JWE Header member. */
|
|
24
|
+
[propName: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
interface JWTClaims {
|
|
27
|
+
/** "iss" (Issuer) Claim */
|
|
28
|
+
iss?: string;
|
|
29
|
+
/** "sub" (Subject) Claim */
|
|
30
|
+
sub?: string;
|
|
31
|
+
/** "aud" (Audience) Claim */
|
|
32
|
+
aud?: string | string[];
|
|
33
|
+
/** "exp" (Expiration Time) Claim */
|
|
34
|
+
exp?: number;
|
|
35
|
+
/** "nbf" (Not Before) Claim */
|
|
36
|
+
nbf?: number;
|
|
37
|
+
/** "iat" (Issued At) Claim */
|
|
38
|
+
iat?: number;
|
|
39
|
+
/** "jti" (JWT ID) Claim */
|
|
40
|
+
jti?: string;
|
|
41
|
+
/** Any other JWT member. */
|
|
42
|
+
[propName: string]: unknown;
|
|
43
|
+
}
|
|
44
|
+
interface JWT<T = JWTClaims> {
|
|
45
|
+
header: JoseHeaderParameters & {
|
|
46
|
+
/** "alg" (Algorithm) Header Parameter */
|
|
47
|
+
alg: string;
|
|
48
|
+
};
|
|
49
|
+
claims: T;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type { JoseHeaderParameters as J, JWTClaims as a, JWT as b };
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import { a as checkEncCryptoKey, u as uint32be, g as generateCEK, e as encryptIV, w as wrap, k as keyToJWK, b as bitLengthCEK, d as jwkTokey, i as importKey, f as generateIV, h as encrypt$1, l as unwrapKey, m as decrypt$1 } from './unjwt.1gbuWbc6.mjs';
|
|
2
|
+
import { e as concatUint8Arrays, t as textEncoder, f as assertCryptoKey, b as base64UrlEncode, g as isCryptoKey, a as isJWK, d as base64UrlDecode, r as randomBytes, c as textDecoder } from './unjwt.DHdDAH0L.mjs';
|
|
3
|
+
import { c as checkKeyLength } from './unjwt.C9Xhi7hE.mjs';
|
|
4
|
+
|
|
5
|
+
function lengthAndInput(input) {
|
|
6
|
+
return concatUint8Arrays(uint32be(input.length), input);
|
|
7
|
+
}
|
|
8
|
+
async function concatKdf(secret, bits, value) {
|
|
9
|
+
const iterations = Math.ceil((bits >> 3) / 32);
|
|
10
|
+
const res = new Uint8Array(iterations * 32);
|
|
11
|
+
for (let iter = 0; iter < iterations; iter++) {
|
|
12
|
+
const buf = new Uint8Array(4 + secret.length + value.length);
|
|
13
|
+
buf.set(uint32be(iter + 1));
|
|
14
|
+
buf.set(secret, 4);
|
|
15
|
+
buf.set(value, 4 + secret.length);
|
|
16
|
+
res.set(await digest("sha256", buf), iter * 32);
|
|
17
|
+
}
|
|
18
|
+
return res.slice(0, bits >> 3);
|
|
19
|
+
}
|
|
20
|
+
async function deriveECDHESKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) {
|
|
21
|
+
checkEncCryptoKey(publicKey, "ECDH");
|
|
22
|
+
checkEncCryptoKey(privateKey, "ECDH", "deriveBits");
|
|
23
|
+
const value = concatUint8Arrays(
|
|
24
|
+
lengthAndInput(textEncoder.encode(algorithm)),
|
|
25
|
+
lengthAndInput(apu),
|
|
26
|
+
lengthAndInput(apv),
|
|
27
|
+
uint32be(keyLength)
|
|
28
|
+
);
|
|
29
|
+
const length = publicKey.algorithm.name === "X25519" ? 256 : Math.ceil(
|
|
30
|
+
Number.parseInt(
|
|
31
|
+
publicKey.algorithm.namedCurve.slice(-3),
|
|
32
|
+
10
|
|
33
|
+
) / 8
|
|
34
|
+
) << 3;
|
|
35
|
+
const sharedSecret = new Uint8Array(
|
|
36
|
+
await crypto.subtle.deriveBits(
|
|
37
|
+
{
|
|
38
|
+
name: publicKey.algorithm.name,
|
|
39
|
+
public: publicKey
|
|
40
|
+
},
|
|
41
|
+
privateKey,
|
|
42
|
+
length
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
return concatKdf(sharedSecret, keyLength, value);
|
|
46
|
+
}
|
|
47
|
+
function allowed(key) {
|
|
48
|
+
switch (key.algorithm.namedCurve) {
|
|
49
|
+
case "P-256":
|
|
50
|
+
case "P-384":
|
|
51
|
+
case "P-521": {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
default: {
|
|
55
|
+
return key.algorithm.name === "X25519";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function digest(algorithm, data) {
|
|
60
|
+
const subtleDigest = `SHA-${algorithm.slice(-3)}`;
|
|
61
|
+
return new Uint8Array(await crypto.subtle.digest(subtleDigest, data));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const subtleAlgorithm = (alg) => {
|
|
65
|
+
switch (alg) {
|
|
66
|
+
case "RSA-OAEP":
|
|
67
|
+
case "RSA-OAEP-256":
|
|
68
|
+
case "RSA-OAEP-384":
|
|
69
|
+
case "RSA-OAEP-512": {
|
|
70
|
+
return "RSA-OAEP";
|
|
71
|
+
}
|
|
72
|
+
default: {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`alg ${alg} is not supported either by JOSE or your javascript runtime`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
async function encryptRSAES(alg, key, cek) {
|
|
80
|
+
checkEncCryptoKey(key, alg, "encrypt");
|
|
81
|
+
checkKeyLength(alg, key);
|
|
82
|
+
return new Uint8Array(
|
|
83
|
+
await crypto.subtle.encrypt(subtleAlgorithm(alg), key, cek)
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* This is a fork of Jose library's utility functions.
|
|
89
|
+
* @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/encrypt_key_management.ts
|
|
90
|
+
* @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
|
|
91
|
+
*/
|
|
92
|
+
async function encryptKey(alg, enc, key, providedCek, providedParameters = {}) {
|
|
93
|
+
let encryptedKey;
|
|
94
|
+
let parameters = {};
|
|
95
|
+
let cek;
|
|
96
|
+
switch (alg) {
|
|
97
|
+
case "dir": {
|
|
98
|
+
cek = key;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
case "ECDH-ES":
|
|
102
|
+
case "ECDH-ES+A128KW":
|
|
103
|
+
case "ECDH-ES+A192KW":
|
|
104
|
+
case "ECDH-ES+A256KW": {
|
|
105
|
+
assertCryptoKey(key);
|
|
106
|
+
if (!allowed(key)) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
"ECDH with the provided key is not allowed or not supported by your javascript runtime"
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const { apu, apv } = providedParameters;
|
|
112
|
+
const ephemeralKey = providedParameters.epk ? await normalizeKey(providedParameters.epk, alg) : (await crypto.subtle.generateKey(
|
|
113
|
+
key.algorithm,
|
|
114
|
+
true,
|
|
115
|
+
["deriveBits"]
|
|
116
|
+
)).privateKey;
|
|
117
|
+
const { x, y, crv, kty } = await keyToJWK(ephemeralKey);
|
|
118
|
+
const sharedSecret = await deriveECDHESKey(
|
|
119
|
+
key,
|
|
120
|
+
ephemeralKey,
|
|
121
|
+
alg === "ECDH-ES" ? enc : alg,
|
|
122
|
+
alg === "ECDH-ES" ? bitLengthCEK(enc) : Number.parseInt(alg.slice(-5, -2), 10),
|
|
123
|
+
apu,
|
|
124
|
+
apv
|
|
125
|
+
);
|
|
126
|
+
parameters = { epk: { x, y, crv, kty } };
|
|
127
|
+
if (apu) parameters.apu = base64UrlEncode(apu);
|
|
128
|
+
if (apv) parameters.apv = base64UrlEncode(apv);
|
|
129
|
+
if (alg === "ECDH-ES") {
|
|
130
|
+
cek = sharedSecret;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
cek = providedCek || generateCEK(enc);
|
|
134
|
+
const kwAlg = alg.slice(-6);
|
|
135
|
+
encryptedKey = (await wrap(kwAlg, sharedSecret, cek)).encryptedKey;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
case "RSA-OAEP":
|
|
139
|
+
case "RSA-OAEP-256":
|
|
140
|
+
case "RSA-OAEP-384":
|
|
141
|
+
case "RSA-OAEP-512": {
|
|
142
|
+
cek = providedCek || generateCEK(enc);
|
|
143
|
+
assertCryptoKey(key);
|
|
144
|
+
encryptedKey = await encryptRSAES(alg, key, cek);
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case "PBES2-HS256+A128KW":
|
|
148
|
+
case "PBES2-HS384+A192KW":
|
|
149
|
+
case "PBES2-HS512+A256KW": {
|
|
150
|
+
cek = providedCek || generateCEK(enc);
|
|
151
|
+
const { p2c, p2s } = providedParameters;
|
|
152
|
+
({ encryptedKey, ...parameters } = await wrap(alg, key, cek, p2c, p2s));
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case "A128KW":
|
|
156
|
+
case "A192KW":
|
|
157
|
+
case "A256KW": {
|
|
158
|
+
cek = providedCek || generateCEK(enc);
|
|
159
|
+
encryptedKey = (await wrap(alg, key, cek)).encryptedKey;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
case "A128GCMKW":
|
|
163
|
+
case "A192GCMKW":
|
|
164
|
+
case "A256GCMKW": {
|
|
165
|
+
cek = providedCek || generateCEK(enc);
|
|
166
|
+
const { iv } = providedParameters;
|
|
167
|
+
({ encryptedKey, ...parameters } = await encryptIV(alg, key, cek, iv));
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
default: {
|
|
171
|
+
throw new Error(
|
|
172
|
+
'Invalid or unsupported "alg" (JWE Algorithm) header value'
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return { cek, encryptedKey, parameters };
|
|
177
|
+
}
|
|
178
|
+
async function normalizeKey(key, alg) {
|
|
179
|
+
if (key instanceof Uint8Array) {
|
|
180
|
+
return key;
|
|
181
|
+
}
|
|
182
|
+
if (isCryptoKey(key)) {
|
|
183
|
+
return key;
|
|
184
|
+
}
|
|
185
|
+
if (isJWK(key)) {
|
|
186
|
+
if ("k" in key && key.k) {
|
|
187
|
+
return base64UrlDecode(key.k, false);
|
|
188
|
+
}
|
|
189
|
+
return jwkTokey({ ...key, alg });
|
|
190
|
+
}
|
|
191
|
+
throw new Error("unreachable");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function encrypt(payload, key, options = {}) {
|
|
195
|
+
const {
|
|
196
|
+
protectedHeader: additionalProtectedHeader,
|
|
197
|
+
cek: providedCek,
|
|
198
|
+
contentEncryptionIV: providedContentIV,
|
|
199
|
+
keyManagementIV,
|
|
200
|
+
p2s = randomBytes(16),
|
|
201
|
+
p2c = 2048,
|
|
202
|
+
ecdhPartyUInfo,
|
|
203
|
+
ecdhPartyVInfo
|
|
204
|
+
} = options;
|
|
205
|
+
let { alg, enc } = options;
|
|
206
|
+
if (!alg) {
|
|
207
|
+
if (typeof key === "string" || key instanceof Uint8Array) {
|
|
208
|
+
alg = "PBES2-HS256+A128KW";
|
|
209
|
+
} else if (isJWK(key)) {
|
|
210
|
+
alg = key.alg;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (!alg) {
|
|
214
|
+
throw new TypeError(
|
|
215
|
+
'JWE "alg" (Key Management Algorithm) must be provided in options or inferable from the key'
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if (!enc) {
|
|
219
|
+
enc = isJWK(key) && "enc" in key ? key.enc : "A128GCM";
|
|
220
|
+
}
|
|
221
|
+
const plaintextBytes = getPlaintextBytes(payload);
|
|
222
|
+
const jweKeyManagementParams = {};
|
|
223
|
+
if (keyManagementIV) jweKeyManagementParams.iv = keyManagementIV;
|
|
224
|
+
if (p2s) jweKeyManagementParams.p2s = p2s;
|
|
225
|
+
if (p2c) jweKeyManagementParams.p2c = p2c;
|
|
226
|
+
if (ecdhPartyUInfo) jweKeyManagementParams.apu = ecdhPartyUInfo;
|
|
227
|
+
if (ecdhPartyVInfo) jweKeyManagementParams.apv = ecdhPartyVInfo;
|
|
228
|
+
if (additionalProtectedHeader?.epk instanceof CryptoKey) {
|
|
229
|
+
jweKeyManagementParams.epk = additionalProtectedHeader.epk;
|
|
230
|
+
}
|
|
231
|
+
const wrappingKeyMaterial = await importKey(key, alg);
|
|
232
|
+
const {
|
|
233
|
+
cek: finalCek,
|
|
234
|
+
// This is the CEK (CryptoKey | Uint8Array) to be used for content encryption
|
|
235
|
+
encryptedKey: jweEncryptedKey,
|
|
236
|
+
// This is the JWE Encrypted Key (Uint8Array | undefined)
|
|
237
|
+
parameters: keyManagementHeaderParams
|
|
238
|
+
// JWE header params from key encryption (e.g., epk, p2s, iv, tag)
|
|
239
|
+
} = await encryptKey(
|
|
240
|
+
alg,
|
|
241
|
+
enc,
|
|
242
|
+
wrappingKeyMaterial,
|
|
243
|
+
providedCek,
|
|
244
|
+
jweKeyManagementParams
|
|
245
|
+
);
|
|
246
|
+
const baseProtectedHeader = { ...additionalProtectedHeader };
|
|
247
|
+
delete baseProtectedHeader.alg;
|
|
248
|
+
delete baseProtectedHeader.enc;
|
|
249
|
+
const jweProtectedHeader = {
|
|
250
|
+
...baseProtectedHeader,
|
|
251
|
+
...keyManagementHeaderParams,
|
|
252
|
+
alg,
|
|
253
|
+
enc
|
|
254
|
+
};
|
|
255
|
+
if (jweProtectedHeader.typ === void 0 && typeof payload === "object" && !(payload instanceof Uint8Array)) {
|
|
256
|
+
jweProtectedHeader.typ = "JWT";
|
|
257
|
+
}
|
|
258
|
+
if (jweProtectedHeader.typ === "JWT" && typeof payload === "object" && !(payload instanceof Uint8Array)) {
|
|
259
|
+
jweProtectedHeader.cty ||= "json";
|
|
260
|
+
}
|
|
261
|
+
const protectedHeaderSerialized = JSON.stringify(jweProtectedHeader);
|
|
262
|
+
const protectedHeaderEncoded = base64UrlEncode(protectedHeaderSerialized);
|
|
263
|
+
const aadBytes = textEncoder.encode(protectedHeaderEncoded);
|
|
264
|
+
const contentIVBytes = providedContentIV ?? generateIV(enc);
|
|
265
|
+
const {
|
|
266
|
+
ciphertext: contentCiphertext,
|
|
267
|
+
tag: contentAuthTag,
|
|
268
|
+
iv: actualContentIV
|
|
269
|
+
} = await encrypt$1(
|
|
270
|
+
enc,
|
|
271
|
+
plaintextBytes,
|
|
272
|
+
finalCek,
|
|
273
|
+
contentIVBytes,
|
|
274
|
+
aadBytes
|
|
275
|
+
);
|
|
276
|
+
if (!actualContentIV) {
|
|
277
|
+
throw new Error("Content encryption IV was not generated or returned.");
|
|
278
|
+
}
|
|
279
|
+
if (!contentAuthTag) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
"Content encryption auth tag was not generated or returned."
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
const encryptedKeyEncoded = jweEncryptedKey ? base64UrlEncode(jweEncryptedKey) : "";
|
|
285
|
+
const jweParts = [
|
|
286
|
+
protectedHeaderEncoded,
|
|
287
|
+
encryptedKeyEncoded,
|
|
288
|
+
base64UrlEncode(actualContentIV),
|
|
289
|
+
base64UrlEncode(contentCiphertext),
|
|
290
|
+
base64UrlEncode(contentAuthTag)
|
|
291
|
+
];
|
|
292
|
+
return jweParts.join(".");
|
|
293
|
+
}
|
|
294
|
+
async function decrypt(jwe, key, options) {
|
|
295
|
+
const parts = jwe.split(".");
|
|
296
|
+
if (parts.length !== 5) {
|
|
297
|
+
throw new Error(
|
|
298
|
+
"Invalid JWE: Must contain five sections (RFC7516, section-3)."
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
const [
|
|
302
|
+
protectedHeaderEncoded,
|
|
303
|
+
encryptedKeyEncoded,
|
|
304
|
+
ivEncoded,
|
|
305
|
+
ciphertextEncoded,
|
|
306
|
+
authTagEncoded
|
|
307
|
+
] = parts;
|
|
308
|
+
let protectedHeader;
|
|
309
|
+
try {
|
|
310
|
+
const protectedHeaderJson = base64UrlDecode(protectedHeaderEncoded);
|
|
311
|
+
protectedHeader = JSON.parse(protectedHeaderJson);
|
|
312
|
+
} catch (error_) {
|
|
313
|
+
throw new Error(
|
|
314
|
+
`Invalid JWE: Protected header is not valid Base64URL or JSON (${error_ instanceof Error ? error_.message : error_})`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
if (!protectedHeader || typeof protectedHeader !== "object" || !protectedHeader.alg || !protectedHeader.enc) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
'Invalid JWE: Protected header must be an object with "alg" and "enc" properties.'
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
const alg = protectedHeader.alg;
|
|
323
|
+
const enc = protectedHeader.enc;
|
|
324
|
+
if (options?.algorithms && !options.algorithms.includes(alg)) {
|
|
325
|
+
throw new Error(`Key management algorithm not allowed: ${alg}`);
|
|
326
|
+
}
|
|
327
|
+
if (options?.encryptionAlgorithms && !options.encryptionAlgorithms.includes(enc)) {
|
|
328
|
+
throw new Error(`Content encryption algorithm not allowed: ${enc}`);
|
|
329
|
+
}
|
|
330
|
+
const resolvedKeyMaterial = typeof key === "function" ? await key(protectedHeader) : key;
|
|
331
|
+
const unwrappingKey = await importKey(resolvedKeyMaterial, alg);
|
|
332
|
+
const encryptedKeyBytes = base64UrlDecode(encryptedKeyEncoded, false);
|
|
333
|
+
const contentIVBytes = base64UrlDecode(ivEncoded, false);
|
|
334
|
+
const contentAuthTagBytes = base64UrlDecode(authTagEncoded, false);
|
|
335
|
+
const ciphertextBytes = base64UrlDecode(ciphertextEncoded, false);
|
|
336
|
+
const unwrapKeyOpts = {
|
|
337
|
+
iv: protectedHeader.iv,
|
|
338
|
+
tag: protectedHeader.tag,
|
|
339
|
+
p2s: protectedHeader.p2s,
|
|
340
|
+
p2c: protectedHeader.p2c,
|
|
341
|
+
epk: protectedHeader.epk,
|
|
342
|
+
apu: protectedHeader.apu,
|
|
343
|
+
apv: protectedHeader.apv,
|
|
344
|
+
unwrappedKeyAlgorithm: options?.unwrappedKeyAlgorithm,
|
|
345
|
+
keyUsage: options?.keyUsage,
|
|
346
|
+
extractable: options?.extractable,
|
|
347
|
+
returnAs: false
|
|
348
|
+
// Returning CEK as Uint8Array
|
|
349
|
+
};
|
|
350
|
+
const cekBytes = await unwrapKey(
|
|
351
|
+
alg,
|
|
352
|
+
encryptedKeyBytes,
|
|
353
|
+
unwrappingKey,
|
|
354
|
+
unwrapKeyOpts
|
|
355
|
+
);
|
|
356
|
+
const aadBytes = textEncoder.encode(protectedHeaderEncoded);
|
|
357
|
+
const plaintextBytes = await decrypt$1(
|
|
358
|
+
enc,
|
|
359
|
+
cekBytes,
|
|
360
|
+
ciphertextBytes,
|
|
361
|
+
contentIVBytes,
|
|
362
|
+
contentAuthTagBytes,
|
|
363
|
+
aadBytes
|
|
364
|
+
);
|
|
365
|
+
let payload;
|
|
366
|
+
if (options?.forceUint8Array) {
|
|
367
|
+
payload = plaintextBytes;
|
|
368
|
+
} else {
|
|
369
|
+
const decodedString = textDecoder.decode(plaintextBytes);
|
|
370
|
+
const cty = protectedHeader.cty?.toLowerCase();
|
|
371
|
+
const isJsonOutput = protectedHeader.typ === "JWT" || cty === "json" || cty === "application/json" || cty && cty.endsWith("+json");
|
|
372
|
+
if (isJsonOutput) {
|
|
373
|
+
if (decodedString.startsWith("{") && decodedString.endsWith("}") || decodedString.startsWith("[") && decodedString.endsWith("]")) {
|
|
374
|
+
try {
|
|
375
|
+
payload = JSON.parse(decodedString);
|
|
376
|
+
} catch {
|
|
377
|
+
payload = decodedString;
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
payload = decodedString;
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
payload = decodedString;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (options?.critical && protectedHeader.crit) {
|
|
387
|
+
const understoodParams = /* @__PURE__ */ new Set([
|
|
388
|
+
...options.critical || [],
|
|
389
|
+
// JWE specific standard headers:
|
|
390
|
+
"alg",
|
|
391
|
+
"enc",
|
|
392
|
+
"typ",
|
|
393
|
+
"cty",
|
|
394
|
+
"kid",
|
|
395
|
+
"jwk",
|
|
396
|
+
"jku",
|
|
397
|
+
"x5c",
|
|
398
|
+
"x5t",
|
|
399
|
+
"x5u",
|
|
400
|
+
"iv",
|
|
401
|
+
"tag",
|
|
402
|
+
"p2s",
|
|
403
|
+
"p2c",
|
|
404
|
+
"epk",
|
|
405
|
+
"apu",
|
|
406
|
+
"apv"
|
|
407
|
+
]);
|
|
408
|
+
for (const critParam of protectedHeader.crit) {
|
|
409
|
+
if (!Object.prototype.hasOwnProperty.call(protectedHeader, critParam)) {
|
|
410
|
+
throw new Error(
|
|
411
|
+
`Critical header parameter "${critParam}" listed in "crit" but not present in the protected header.`
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
if (!understoodParams.has(critParam)) {
|
|
415
|
+
throw new Error(
|
|
416
|
+
`Critical header parameter not understood: ${critParam}`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
} else if (!options?.critical && protectedHeader.crit) {
|
|
421
|
+
throw new Error(
|
|
422
|
+
`Unprocessed critical header parameters: ${protectedHeader.crit.join(", ")}`
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
payload,
|
|
427
|
+
protectedHeader,
|
|
428
|
+
cek: cekBytes,
|
|
429
|
+
aad: aadBytes
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
function getPlaintextBytes(payload) {
|
|
433
|
+
if (payload instanceof Uint8Array) {
|
|
434
|
+
return payload;
|
|
435
|
+
}
|
|
436
|
+
if (typeof payload === "string") {
|
|
437
|
+
return textEncoder.encode(payload);
|
|
438
|
+
}
|
|
439
|
+
if (typeof payload === "object" && payload !== null) {
|
|
440
|
+
return textEncoder.encode(JSON.stringify(payload));
|
|
441
|
+
}
|
|
442
|
+
throw new TypeError(
|
|
443
|
+
"Plaintext must be a string, Uint8Array, or a JSON-serializable object."
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const jwe = {
|
|
448
|
+
__proto__: null,
|
|
449
|
+
decrypt: decrypt,
|
|
450
|
+
encrypt: encrypt
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
export { decrypt as d, encrypt as e, jwe as j };
|