unjwt 0.5.16 → 0.5.18
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 +12 -28
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +4 -5
- package/dist/jwe.d.mts +2 -2
- package/dist/jwe.mjs +3 -4
- package/dist/jwk.d.mts +2 -2
- package/dist/jwk.mjs +2 -2
- package/dist/jws.d.mts +2 -2
- package/dist/jws.mjs +3 -4
- package/dist/shared/{unjwt.D1MC63Yb.mjs → unjwt.BEyYvaE4.mjs} +602 -48
- package/dist/shared/{unjwt.OzI0aGB_.d.mts → unjwt.Ca4-7Myr.d.mts} +22 -10
- package/dist/shared/{unjwt.BkUfaQRR.d.mts → unjwt.CcoSLC0q.d.mts} +25 -8
- package/dist/shared/{unjwt.u3h40Nr-.mjs → unjwt.DLpR-j1d.mjs} +2 -3
- package/dist/shared/{unjwt.CbxAdgsb.d.mts → unjwt.DMz8YzKh.d.mts} +29 -2
- package/dist/shared/{unjwt.DUVOOOvO.d.mts → unjwt.DNIO0cRI.d.mts} +1 -1
- package/dist/shared/unjwt.VeTDOWHW.mjs +247 -0
- package/dist/shared/{unjwt.r8lbwWSJ.d.mts → unjwt.XLRRd_3f.d.mts} +4 -2
- package/dist/shared/{unjwt.CRHfsi7v.mjs → unjwt.tM5bsBSH.mjs} +1 -1
- package/dist/utils.d.mts +2 -2
- package/dist/utils.mjs +1 -1
- package/package.json +3 -3
- package/dist/shared/unjwt.BZ3B0Ak8.mjs +0 -395
- package/dist/shared/unjwt.BpusjhPR.mjs +0 -101
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { k as isCryptoKey, p as concatUint8Arrays, d as base64UrlEncode, q as base64Decode, u as base64Encode, t as textEncoder, s as sanitizeObject, w as assertCryptoKey, a as isJWK, e as base64UrlDecode, x as isAsymmetricJWK, r as randomBytes, m as isJWKSet } from './unjwt.tM5bsBSH.mjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* This is a fork of Jose library's utility functions.
|
|
@@ -762,6 +762,65 @@ const fromX509 = (pem, alg, options) => {
|
|
|
762
762
|
return fromSPKI(spki, alg, options);
|
|
763
763
|
};
|
|
764
764
|
|
|
765
|
+
function lengthAndInput(input) {
|
|
766
|
+
return concatUint8Arrays(uint32be(input.length), input);
|
|
767
|
+
}
|
|
768
|
+
async function concatKdf(secret, bits, value) {
|
|
769
|
+
const iterations = Math.ceil((bits >> 3) / 32);
|
|
770
|
+
const res = new Uint8Array(iterations * 32);
|
|
771
|
+
for (let iter = 0; iter < iterations; iter++) {
|
|
772
|
+
const buf = new Uint8Array(4 + secret.length + value.length);
|
|
773
|
+
buf.set(uint32be(iter + 1));
|
|
774
|
+
buf.set(secret, 4);
|
|
775
|
+
buf.set(value, 4 + secret.length);
|
|
776
|
+
res.set(await digest("sha256", buf), iter * 32);
|
|
777
|
+
}
|
|
778
|
+
return res.slice(0, bits >> 3);
|
|
779
|
+
}
|
|
780
|
+
async function deriveECDHESKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) {
|
|
781
|
+
checkEncCryptoKey(publicKey, "ECDH");
|
|
782
|
+
checkEncCryptoKey(privateKey, "ECDH", "deriveBits");
|
|
783
|
+
const value = concatUint8Arrays(
|
|
784
|
+
lengthAndInput(textEncoder.encode(algorithm)),
|
|
785
|
+
lengthAndInput(apu),
|
|
786
|
+
lengthAndInput(apv),
|
|
787
|
+
uint32be(keyLength)
|
|
788
|
+
);
|
|
789
|
+
const length = publicKey.algorithm.name === "X25519" ? 256 : Math.ceil(
|
|
790
|
+
Number.parseInt(
|
|
791
|
+
publicKey.algorithm.namedCurve.slice(-3),
|
|
792
|
+
10
|
|
793
|
+
) / 8
|
|
794
|
+
) << 3;
|
|
795
|
+
const sharedSecret = new Uint8Array(
|
|
796
|
+
await crypto.subtle.deriveBits(
|
|
797
|
+
{
|
|
798
|
+
name: publicKey.algorithm.name,
|
|
799
|
+
public: publicKey
|
|
800
|
+
},
|
|
801
|
+
privateKey,
|
|
802
|
+
length
|
|
803
|
+
)
|
|
804
|
+
);
|
|
805
|
+
return concatKdf(sharedSecret, keyLength, value);
|
|
806
|
+
}
|
|
807
|
+
function allowed(key) {
|
|
808
|
+
switch (key.algorithm.namedCurve) {
|
|
809
|
+
case "P-256":
|
|
810
|
+
case "P-384":
|
|
811
|
+
case "P-521": {
|
|
812
|
+
return true;
|
|
813
|
+
}
|
|
814
|
+
default: {
|
|
815
|
+
return key.algorithm.name === "X25519";
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
async function digest(algorithm, data) {
|
|
820
|
+
const subtleDigest = `SHA-${algorithm.slice(-3)}`;
|
|
821
|
+
return new Uint8Array(await crypto.subtle.digest(subtleDigest, data));
|
|
822
|
+
}
|
|
823
|
+
|
|
765
824
|
/**
|
|
766
825
|
* This is a fork of Jose library's utility functions.
|
|
767
826
|
* @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/jwt_to_key.ts
|
|
@@ -923,6 +982,134 @@ async function keyToJWK(key) {
|
|
|
923
982
|
return jwk;
|
|
924
983
|
}
|
|
925
984
|
|
|
985
|
+
/**
|
|
986
|
+
* This is a fork of Jose library's utility functions.
|
|
987
|
+
* @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/sign.ts
|
|
988
|
+
* @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/verify.ts
|
|
989
|
+
* @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
|
|
990
|
+
*/
|
|
991
|
+
async function sign(alg, key, data) {
|
|
992
|
+
const cryptoKey = await getSignVerifyKey(alg, key, "sign");
|
|
993
|
+
checkKeyLength(alg, cryptoKey);
|
|
994
|
+
const signature = await crypto.subtle.sign(
|
|
995
|
+
subtleAlgorithm$1(alg, cryptoKey.algorithm),
|
|
996
|
+
cryptoKey,
|
|
997
|
+
data
|
|
998
|
+
);
|
|
999
|
+
return new Uint8Array(signature);
|
|
1000
|
+
}
|
|
1001
|
+
async function verify(alg, key, signature, data) {
|
|
1002
|
+
const cryptoKey = await getSignVerifyKey(alg, key, "verify");
|
|
1003
|
+
checkKeyLength(alg, cryptoKey);
|
|
1004
|
+
const algorithm = subtleAlgorithm$1(alg, cryptoKey.algorithm);
|
|
1005
|
+
try {
|
|
1006
|
+
return await crypto.subtle.verify(algorithm, cryptoKey, signature, data);
|
|
1007
|
+
} catch {
|
|
1008
|
+
return false;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
function checkKeyLength(alg, key) {
|
|
1012
|
+
if (alg.startsWith("RS") || alg.startsWith("PS")) {
|
|
1013
|
+
const { modulusLength } = key.algorithm;
|
|
1014
|
+
if (typeof modulusLength !== "number" || modulusLength < 2048) {
|
|
1015
|
+
throw new TypeError(
|
|
1016
|
+
`${alg} requires key modulusLength to be 2048 bits or larger`
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
async function getSignVerifyKey(alg, key, usage) {
|
|
1022
|
+
if (key instanceof Uint8Array) {
|
|
1023
|
+
if (!alg.startsWith("HS")) {
|
|
1024
|
+
throw new TypeError(
|
|
1025
|
+
`Key must be ${key} of type: CryptoKey or JSON Web Key`
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
return crypto.subtle.importKey(
|
|
1029
|
+
"raw",
|
|
1030
|
+
key,
|
|
1031
|
+
{ hash: `SHA-${alg.slice(-3)}`, name: "HMAC" },
|
|
1032
|
+
false,
|
|
1033
|
+
[usage]
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
checkSigCryptoKey(key, alg, usage);
|
|
1037
|
+
return key;
|
|
1038
|
+
}
|
|
1039
|
+
function subtleAlgorithm$1(alg, algorithm) {
|
|
1040
|
+
const hash = `SHA-${alg.slice(-3)}`;
|
|
1041
|
+
switch (alg) {
|
|
1042
|
+
case "HS256":
|
|
1043
|
+
case "HS384":
|
|
1044
|
+
case "HS512": {
|
|
1045
|
+
return { hash, name: "HMAC" };
|
|
1046
|
+
}
|
|
1047
|
+
case "PS256":
|
|
1048
|
+
case "PS384":
|
|
1049
|
+
case "PS512": {
|
|
1050
|
+
return {
|
|
1051
|
+
hash,
|
|
1052
|
+
name: "RSA-PSS",
|
|
1053
|
+
saltLength: Number.parseInt(alg.slice(-3), 10) >> 3
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
case "RS256":
|
|
1057
|
+
case "RS384":
|
|
1058
|
+
case "RS512": {
|
|
1059
|
+
return { hash, name: "RSASSA-PKCS1-v1_5" };
|
|
1060
|
+
}
|
|
1061
|
+
case "ES256":
|
|
1062
|
+
case "ES384":
|
|
1063
|
+
case "ES512": {
|
|
1064
|
+
return {
|
|
1065
|
+
hash,
|
|
1066
|
+
name: "ECDSA",
|
|
1067
|
+
namedCurve: algorithm.namedCurve
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
case "Ed25519":
|
|
1071
|
+
// Fall through
|
|
1072
|
+
case "EdDSA": {
|
|
1073
|
+
return { name: "Ed25519" };
|
|
1074
|
+
}
|
|
1075
|
+
default: {
|
|
1076
|
+
throw new Error(
|
|
1077
|
+
`alg ${alg} is not supported either by JOSE or your javascript runtime`
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
const subtleAlgorithm = (alg) => {
|
|
1084
|
+
switch (alg) {
|
|
1085
|
+
case "RSA-OAEP":
|
|
1086
|
+
case "RSA-OAEP-256":
|
|
1087
|
+
case "RSA-OAEP-384":
|
|
1088
|
+
case "RSA-OAEP-512": {
|
|
1089
|
+
return "RSA-OAEP";
|
|
1090
|
+
}
|
|
1091
|
+
default: {
|
|
1092
|
+
throw new Error(
|
|
1093
|
+
`alg ${alg} is not supported either by JOSE or your javascript runtime`
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
async function encryptRSAES(alg, key, cek) {
|
|
1099
|
+
checkEncCryptoKey(key, alg, "encrypt");
|
|
1100
|
+
checkKeyLength(alg, key);
|
|
1101
|
+
return new Uint8Array(
|
|
1102
|
+
await crypto.subtle.encrypt(subtleAlgorithm(alg), key, cek)
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
async function decryptRSAES(alg, key, encryptedKey) {
|
|
1106
|
+
checkEncCryptoKey(key, alg, "decrypt");
|
|
1107
|
+
checkKeyLength(alg, key);
|
|
1108
|
+
return new Uint8Array(
|
|
1109
|
+
await crypto.subtle.decrypt(subtleAlgorithm(alg), key, encryptedKey)
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
926
1113
|
/**
|
|
927
1114
|
* This is a fork of Jose library's utility functions.
|
|
928
1115
|
* @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/pbes2kw.ts
|
|
@@ -1026,6 +1213,224 @@ async function _unwrap(alg, key, encryptedKey) {
|
|
|
1026
1213
|
return new Uint8Array(await crypto.subtle.exportKey("raw", cryptoKeyCek));
|
|
1027
1214
|
}
|
|
1028
1215
|
|
|
1216
|
+
/**
|
|
1217
|
+
* This is a fork of Jose library's utility functions.
|
|
1218
|
+
* @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/encrypt_key_management.ts
|
|
1219
|
+
* @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
|
|
1220
|
+
*/
|
|
1221
|
+
const PUBLIC_EPK_FIELDS = /* @__PURE__ */ new Set([
|
|
1222
|
+
"kty",
|
|
1223
|
+
"crv",
|
|
1224
|
+
"x",
|
|
1225
|
+
"y",
|
|
1226
|
+
"kid",
|
|
1227
|
+
"use",
|
|
1228
|
+
"key_ops",
|
|
1229
|
+
"alg",
|
|
1230
|
+
"x5c",
|
|
1231
|
+
"x5t",
|
|
1232
|
+
"x5t#S256",
|
|
1233
|
+
"x5u"
|
|
1234
|
+
]);
|
|
1235
|
+
function toPublicEpkJwk(jwk) {
|
|
1236
|
+
const filtered = {};
|
|
1237
|
+
for (const [key, value] of Object.entries(jwk)) {
|
|
1238
|
+
if (value !== void 0 && PUBLIC_EPK_FIELDS.has(key)) {
|
|
1239
|
+
filtered[key] = value;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
const { kty, crv, x, y } = filtered;
|
|
1243
|
+
if (typeof kty !== "string") {
|
|
1244
|
+
throw new TypeError(
|
|
1245
|
+
'ECDH-ES ephemeral key JWK must include the "kty" parameter.'
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
1248
|
+
if (typeof crv !== "string") {
|
|
1249
|
+
throw new TypeError(
|
|
1250
|
+
'ECDH-ES ephemeral key JWK must include the "crv" parameter.'
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
if (typeof x !== "string") {
|
|
1254
|
+
throw new TypeError(
|
|
1255
|
+
'ECDH-ES ephemeral key JWK must include the "x" coordinate.'
|
|
1256
|
+
);
|
|
1257
|
+
}
|
|
1258
|
+
if (kty === "EC" && typeof y !== "string") {
|
|
1259
|
+
throw new TypeError(
|
|
1260
|
+
'ECDH-ES EC ephemeral key JWK must include both "x" and "y" coordinates.'
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
const publicJwk = {
|
|
1264
|
+
...filtered,
|
|
1265
|
+
kty,
|
|
1266
|
+
crv,
|
|
1267
|
+
x
|
|
1268
|
+
};
|
|
1269
|
+
if (typeof y === "string") {
|
|
1270
|
+
publicJwk.y = y;
|
|
1271
|
+
} else {
|
|
1272
|
+
delete publicJwk.y;
|
|
1273
|
+
}
|
|
1274
|
+
return sanitizeObject(publicJwk);
|
|
1275
|
+
}
|
|
1276
|
+
async function ensurePrivateKey(alg, value) {
|
|
1277
|
+
const normalized = await normalizeKey(value, alg);
|
|
1278
|
+
if (!(normalized instanceof CryptoKey)) {
|
|
1279
|
+
throw new TypeError(
|
|
1280
|
+
"ECDH-ES ephemeral private key must be a CryptoKey or convertible JWK."
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
if (normalized.type !== "private") {
|
|
1284
|
+
throw new TypeError(
|
|
1285
|
+
'ECDH-ES ephemeral private key must have type "private".'
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
return normalized;
|
|
1289
|
+
}
|
|
1290
|
+
async function exportPublicJwkFrom(alg, value) {
|
|
1291
|
+
if (isCryptoKey(value)) {
|
|
1292
|
+
try {
|
|
1293
|
+
return toPublicEpkJwk(await keyToJWK(value));
|
|
1294
|
+
} catch (error_) {
|
|
1295
|
+
throw new TypeError(
|
|
1296
|
+
"ECDH-ES ephemeral CryptoKey must be extractable to export as JWK.",
|
|
1297
|
+
{ cause: error_ instanceof Error ? error_ : void 0 }
|
|
1298
|
+
);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
if (isJWK(value) && isAsymmetricJWK(value) && value.kty === "EC") {
|
|
1302
|
+
return toPublicEpkJwk(value);
|
|
1303
|
+
}
|
|
1304
|
+
const normalized = await normalizeKey(value, alg);
|
|
1305
|
+
if (!(normalized instanceof CryptoKey)) {
|
|
1306
|
+
throw new TypeError(
|
|
1307
|
+
"ECDH-ES ephemeral public key must be a CryptoKey or convertible JWK."
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
return exportPublicJwkFrom(alg, normalized);
|
|
1311
|
+
}
|
|
1312
|
+
async function encryptKey(alg, enc, key, providedCek, providedParameters = {}) {
|
|
1313
|
+
let encryptedKey;
|
|
1314
|
+
let parameters = {};
|
|
1315
|
+
let cek;
|
|
1316
|
+
switch (alg) {
|
|
1317
|
+
case "dir": {
|
|
1318
|
+
cek = key;
|
|
1319
|
+
break;
|
|
1320
|
+
}
|
|
1321
|
+
case "ECDH-ES":
|
|
1322
|
+
case "ECDH-ES+A128KW":
|
|
1323
|
+
case "ECDH-ES+A192KW":
|
|
1324
|
+
case "ECDH-ES+A256KW": {
|
|
1325
|
+
assertCryptoKey(key);
|
|
1326
|
+
if (!allowed(key)) {
|
|
1327
|
+
throw new Error(
|
|
1328
|
+
"ECDH with the provided key is not allowed or not supported by your javascript runtime"
|
|
1329
|
+
);
|
|
1330
|
+
}
|
|
1331
|
+
const { apu, apv, epk: providedEpk, epkPrivateKey } = providedParameters;
|
|
1332
|
+
if (providedEpk && !epkPrivateKey) {
|
|
1333
|
+
throw new TypeError(
|
|
1334
|
+
"ECDH-ES custom ephemeral key material must include the matching private key."
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
let ephemeralPrivateKey;
|
|
1338
|
+
let epkHeader;
|
|
1339
|
+
if (epkPrivateKey) {
|
|
1340
|
+
ephemeralPrivateKey = await ensurePrivateKey(alg, epkPrivateKey);
|
|
1341
|
+
const publicSource = providedEpk ?? epkPrivateKey;
|
|
1342
|
+
epkHeader = await exportPublicJwkFrom(alg, publicSource);
|
|
1343
|
+
} else {
|
|
1344
|
+
if (providedEpk) {
|
|
1345
|
+
throw new TypeError(
|
|
1346
|
+
"ECDH-ES custom ephemeral public key requires a matching private key."
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
const generated = await crypto.subtle.generateKey(
|
|
1350
|
+
key.algorithm,
|
|
1351
|
+
true,
|
|
1352
|
+
["deriveBits"]
|
|
1353
|
+
);
|
|
1354
|
+
ephemeralPrivateKey = generated.privateKey;
|
|
1355
|
+
epkHeader = await exportPublicJwkFrom(alg, generated.publicKey);
|
|
1356
|
+
}
|
|
1357
|
+
const sharedSecret = await deriveECDHESKey(
|
|
1358
|
+
key,
|
|
1359
|
+
ephemeralPrivateKey,
|
|
1360
|
+
alg === "ECDH-ES" ? enc : alg,
|
|
1361
|
+
alg === "ECDH-ES" ? bitLengthCEK(enc) : Number.parseInt(alg.slice(-5, -2), 10),
|
|
1362
|
+
apu,
|
|
1363
|
+
apv
|
|
1364
|
+
);
|
|
1365
|
+
parameters = { epk: epkHeader };
|
|
1366
|
+
if (apu) parameters.apu = base64UrlEncode(apu);
|
|
1367
|
+
if (apv) parameters.apv = base64UrlEncode(apv);
|
|
1368
|
+
if (alg === "ECDH-ES") {
|
|
1369
|
+
cek = sharedSecret;
|
|
1370
|
+
break;
|
|
1371
|
+
}
|
|
1372
|
+
cek = providedCek || generateCEK(enc);
|
|
1373
|
+
const kwAlg = alg.slice(-6);
|
|
1374
|
+
encryptedKey = (await wrap(kwAlg, sharedSecret, cek)).encryptedKey;
|
|
1375
|
+
break;
|
|
1376
|
+
}
|
|
1377
|
+
case "RSA-OAEP":
|
|
1378
|
+
case "RSA-OAEP-256":
|
|
1379
|
+
case "RSA-OAEP-384":
|
|
1380
|
+
case "RSA-OAEP-512": {
|
|
1381
|
+
cek = providedCek || generateCEK(enc);
|
|
1382
|
+
assertCryptoKey(key);
|
|
1383
|
+
encryptedKey = await encryptRSAES(alg, key, cek);
|
|
1384
|
+
break;
|
|
1385
|
+
}
|
|
1386
|
+
case "PBES2-HS256+A128KW":
|
|
1387
|
+
case "PBES2-HS384+A192KW":
|
|
1388
|
+
case "PBES2-HS512+A256KW": {
|
|
1389
|
+
cek = providedCek || generateCEK(enc);
|
|
1390
|
+
const { p2c, p2s } = providedParameters;
|
|
1391
|
+
({ encryptedKey, ...parameters } = await wrap(alg, key, cek, p2c, p2s));
|
|
1392
|
+
break;
|
|
1393
|
+
}
|
|
1394
|
+
case "A128KW":
|
|
1395
|
+
case "A192KW":
|
|
1396
|
+
case "A256KW": {
|
|
1397
|
+
cek = providedCek || generateCEK(enc);
|
|
1398
|
+
encryptedKey = (await wrap(alg, key, cek)).encryptedKey;
|
|
1399
|
+
break;
|
|
1400
|
+
}
|
|
1401
|
+
case "A128GCMKW":
|
|
1402
|
+
case "A192GCMKW":
|
|
1403
|
+
case "A256GCMKW": {
|
|
1404
|
+
cek = providedCek || generateCEK(enc);
|
|
1405
|
+
const { iv } = providedParameters;
|
|
1406
|
+
({ encryptedKey, ...parameters } = await encryptIV(alg, key, cek, iv));
|
|
1407
|
+
break;
|
|
1408
|
+
}
|
|
1409
|
+
default: {
|
|
1410
|
+
throw new Error(
|
|
1411
|
+
'Invalid or unsupported "alg" (JWE Algorithm) header value'
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
return { cek, encryptedKey, parameters };
|
|
1416
|
+
}
|
|
1417
|
+
async function normalizeKey(key, alg) {
|
|
1418
|
+
if (key instanceof Uint8Array) {
|
|
1419
|
+
return key;
|
|
1420
|
+
}
|
|
1421
|
+
if (isCryptoKey(key)) {
|
|
1422
|
+
return key;
|
|
1423
|
+
}
|
|
1424
|
+
if (isJWK(key)) {
|
|
1425
|
+
const safeJwk = sanitizeObject(key);
|
|
1426
|
+
if ("k" in safeJwk && safeJwk.k) {
|
|
1427
|
+
return base64UrlDecode(safeJwk.k, false);
|
|
1428
|
+
}
|
|
1429
|
+
return jwkTokey({ ...safeJwk, alg });
|
|
1430
|
+
}
|
|
1431
|
+
throw new Error("unreachable");
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1029
1434
|
async function generateKey(alg, options = {}) {
|
|
1030
1435
|
const exportToJWK = options.toJWK !== void 0 && options.toJWK !== false;
|
|
1031
1436
|
const defaultExtractable = options.extractable !== false;
|
|
@@ -1052,19 +1457,39 @@ async function generateKey(alg, options = {}) {
|
|
|
1052
1457
|
...additionalKeyParams
|
|
1053
1458
|
} = typeof options.toJWK === "object" ? options.toJWK : {};
|
|
1054
1459
|
if (key instanceof CryptoKey) {
|
|
1055
|
-
return exportKey(key, {
|
|
1460
|
+
return exportKey(key, {
|
|
1461
|
+
...additionalKeyParams,
|
|
1462
|
+
key_ops: keyUsages,
|
|
1463
|
+
alg
|
|
1464
|
+
});
|
|
1056
1465
|
} else {
|
|
1057
1466
|
const [publicKey, privateKey] = await Promise.all([
|
|
1058
1467
|
exportKey(key.publicKey, { ...additionalKeyParams, alg }),
|
|
1059
|
-
exportKey(key.privateKey, {
|
|
1468
|
+
exportKey(key.privateKey, {
|
|
1469
|
+
...additionalKeyParams,
|
|
1470
|
+
key_ops: keyUsages,
|
|
1471
|
+
alg
|
|
1472
|
+
})
|
|
1060
1473
|
]);
|
|
1061
1474
|
return { privateKey, publicKey };
|
|
1062
1475
|
}
|
|
1063
1476
|
}
|
|
1064
1477
|
return key;
|
|
1065
1478
|
}
|
|
1479
|
+
async function generateJWK(alg, jwkParams, options = {}) {
|
|
1480
|
+
const {
|
|
1481
|
+
// @ts-expect-error destructuring just to avoid passing it down
|
|
1482
|
+
toJWK: _,
|
|
1483
|
+
...opts
|
|
1484
|
+
} = options;
|
|
1485
|
+
return generateKey(alg, {
|
|
1486
|
+
...opts,
|
|
1487
|
+
toJWK: jwkParams || true
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1066
1490
|
async function deriveKeyFromPassword(password, alg, options) {
|
|
1067
|
-
const { salt, iterations,
|
|
1491
|
+
const { salt, iterations, extractable, keyUsage } = options;
|
|
1492
|
+
const exportToJWK = options.toJWK !== void 0 && options.toJWK !== false;
|
|
1068
1493
|
if (!(salt instanceof Uint8Array) || salt.length < 8) {
|
|
1069
1494
|
throw new Error("PBES2 Salt Input (salt) must be 8 or more octets");
|
|
1070
1495
|
}
|
|
@@ -1083,7 +1508,7 @@ async function deriveKeyFromPassword(password, alg, options) {
|
|
|
1083
1508
|
const wrappingAlg = alg.slice(-6);
|
|
1084
1509
|
const defaultUsages = ["wrapKey", "unwrapKey"];
|
|
1085
1510
|
const finalUsages = keyUsage ?? defaultUsages;
|
|
1086
|
-
const finalExtractable =
|
|
1511
|
+
const finalExtractable = exportToJWK ? true : extractable === true;
|
|
1087
1512
|
const derivedKey = await crypto.subtle.importKey(
|
|
1088
1513
|
"raw",
|
|
1089
1514
|
derivedBytes,
|
|
@@ -1091,12 +1516,30 @@ async function deriveKeyFromPassword(password, alg, options) {
|
|
|
1091
1516
|
finalExtractable,
|
|
1092
1517
|
finalUsages
|
|
1093
1518
|
);
|
|
1094
|
-
if (
|
|
1519
|
+
if (exportToJWK) {
|
|
1095
1520
|
const jwk = await keyToJWK(derivedKey);
|
|
1096
|
-
|
|
1521
|
+
const {
|
|
1522
|
+
alg: _a,
|
|
1523
|
+
kty: _k,
|
|
1524
|
+
key_ops: _ko,
|
|
1525
|
+
ext: _e,
|
|
1526
|
+
...additionalKeyParams
|
|
1527
|
+
} = typeof options.toJWK === "object" ? options.toJWK : {};
|
|
1528
|
+
return { ...additionalKeyParams, ...jwk, alg: wrappingAlg, kty: "oct" };
|
|
1097
1529
|
}
|
|
1098
1530
|
return derivedKey;
|
|
1099
1531
|
}
|
|
1532
|
+
async function deriveJWKFromPassword(password, alg, options, jwkParams) {
|
|
1533
|
+
const {
|
|
1534
|
+
// @ts-expect-error destructuring just to avoid passing it down
|
|
1535
|
+
toJWK: _,
|
|
1536
|
+
...opts
|
|
1537
|
+
} = options;
|
|
1538
|
+
return deriveKeyFromPassword(password, alg, {
|
|
1539
|
+
...opts,
|
|
1540
|
+
toJWK: jwkParams || true
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1100
1543
|
async function importKey(key, alg) {
|
|
1101
1544
|
if (typeof key === "string") {
|
|
1102
1545
|
key = textEncoder.encode(key);
|
|
@@ -1287,22 +1730,33 @@ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
|
|
|
1287
1730
|
case "RSA-OAEP-256":
|
|
1288
1731
|
case "RSA-OAEP-384":
|
|
1289
1732
|
case "RSA-OAEP-512": {
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1733
|
+
if (!(importedUnwrappingKey instanceof CryptoKey)) {
|
|
1734
|
+
throw new TypeError(
|
|
1735
|
+
"RSA-OAEP requires the unwrapping key to be provided as a CryptoKey"
|
|
1736
|
+
);
|
|
1737
|
+
}
|
|
1738
|
+
unwrappedCekBytes = await decryptRSAES(
|
|
1739
|
+
alg,
|
|
1293
1740
|
importedUnwrappingKey,
|
|
1294
|
-
|
|
1295
|
-
options.unwrappedKeyAlgorithm || { name: "AES-GCM" },
|
|
1296
|
-
// Fallback
|
|
1297
|
-
defaultExtractable,
|
|
1298
|
-
options.keyUsage || ["encrypt", "decrypt"]
|
|
1299
|
-
// Default usages
|
|
1741
|
+
wrappedKey
|
|
1300
1742
|
);
|
|
1301
|
-
if (returnAs)
|
|
1302
|
-
|
|
1303
|
-
|
|
1743
|
+
if (!returnAs) {
|
|
1744
|
+
break;
|
|
1745
|
+
}
|
|
1746
|
+
const inferredAlgorithm = options.unwrappedKeyAlgorithm || inferAesImportAlgorithm(options.enc, unwrappedCekBytes.length);
|
|
1747
|
+
if (!inferredAlgorithm) {
|
|
1748
|
+
throw new Error(
|
|
1749
|
+
'Unable to infer algorithm for RSA-OAEP unwrapped key. Provide "unwrappedKeyAlgorithm" in options.'
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1752
|
+
const keyUsages = options.keyUsage || ["encrypt", "decrypt"];
|
|
1753
|
+
return await crypto.subtle.importKey(
|
|
1754
|
+
"raw",
|
|
1755
|
+
unwrappedCekBytes,
|
|
1756
|
+
inferredAlgorithm,
|
|
1757
|
+
defaultExtractable,
|
|
1758
|
+
keyUsages
|
|
1304
1759
|
);
|
|
1305
|
-
break;
|
|
1306
1760
|
}
|
|
1307
1761
|
case "ECDH-ES":
|
|
1308
1762
|
case "ECDH-ES+A128KW":
|
|
@@ -1311,7 +1765,49 @@ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
|
|
|
1311
1765
|
if (!options.epk) {
|
|
1312
1766
|
throw new Error("ECDH-ES requires 'epk' (Ephemeral Public Key) option");
|
|
1313
1767
|
}
|
|
1314
|
-
|
|
1768
|
+
if (!(importedUnwrappingKey instanceof CryptoKey)) {
|
|
1769
|
+
throw new TypeError(
|
|
1770
|
+
"ECDH-ES requires the unwrapping key to be a CryptoKey"
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
if (!allowed(importedUnwrappingKey)) {
|
|
1774
|
+
throw new Error(
|
|
1775
|
+
"ECDH with the provided key is not allowed or not supported"
|
|
1776
|
+
);
|
|
1777
|
+
}
|
|
1778
|
+
const epkCandidate = options.epk instanceof CryptoKey ? options.epk : await normalizeKey(options.epk, alg);
|
|
1779
|
+
if (!(epkCandidate instanceof CryptoKey)) {
|
|
1780
|
+
throw new TypeError("Failed to normalize ECDH ephemeral public key");
|
|
1781
|
+
}
|
|
1782
|
+
const apuBytes = typeof options.apu === "string" ? base64UrlDecode(options.apu, false) : options.apu ?? new Uint8Array(0);
|
|
1783
|
+
const apvBytes = typeof options.apv === "string" ? base64UrlDecode(options.apv, false) : options.apv ?? new Uint8Array(0);
|
|
1784
|
+
const infoAlg = alg === "ECDH-ES" ? options.enc : alg;
|
|
1785
|
+
if (!infoAlg) {
|
|
1786
|
+
throw new Error(
|
|
1787
|
+
"ECDH-ES requires content encryption algorithm ('enc') to derive the shared secret"
|
|
1788
|
+
);
|
|
1789
|
+
}
|
|
1790
|
+
const keyLength = alg === "ECDH-ES" ? bitLengthCEK(infoAlg) : Number.parseInt(alg.slice(-5, -2), 10);
|
|
1791
|
+
const sharedSecret = await deriveECDHESKey(
|
|
1792
|
+
epkCandidate,
|
|
1793
|
+
importedUnwrappingKey,
|
|
1794
|
+
infoAlg,
|
|
1795
|
+
keyLength,
|
|
1796
|
+
apuBytes,
|
|
1797
|
+
apvBytes
|
|
1798
|
+
);
|
|
1799
|
+
if (alg === "ECDH-ES") {
|
|
1800
|
+
unwrappedCekBytes = sharedSecret;
|
|
1801
|
+
} else {
|
|
1802
|
+
const kwAlg = alg.slice(-6);
|
|
1803
|
+
if (!(wrappedKey instanceof Uint8Array) || wrappedKey.length === 0) {
|
|
1804
|
+
throw new Error(
|
|
1805
|
+
"ECDH-ES key agreement with key wrapping requires an encrypted key"
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
unwrappedCekBytes = await unwrap(kwAlg, sharedSecret, wrappedKey);
|
|
1809
|
+
}
|
|
1810
|
+
break;
|
|
1315
1811
|
}
|
|
1316
1812
|
default: {
|
|
1317
1813
|
throw new Error(`Unsupported key unwrapping algorithm: ${alg}`);
|
|
@@ -1483,28 +1979,6 @@ function getGenerateKeyParams(alg, options) {
|
|
|
1483
1979
|
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
1484
1980
|
break;
|
|
1485
1981
|
}
|
|
1486
|
-
// ECDSA Signatures
|
|
1487
|
-
case "ES256": {
|
|
1488
|
-
algorithm = { name: "ECDSA", namedCurve: "P-256" };
|
|
1489
|
-
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
1490
|
-
break;
|
|
1491
|
-
}
|
|
1492
|
-
case "ES384": {
|
|
1493
|
-
algorithm = { name: "ECDSA", namedCurve: "P-384" };
|
|
1494
|
-
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
1495
|
-
break;
|
|
1496
|
-
}
|
|
1497
|
-
case "ES512": {
|
|
1498
|
-
algorithm = { name: "ECDSA", namedCurve: "P-521" };
|
|
1499
|
-
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
1500
|
-
break;
|
|
1501
|
-
}
|
|
1502
|
-
case "Ed25519":
|
|
1503
|
-
case "EdDSA": {
|
|
1504
|
-
algorithm = { name: "Ed25519" };
|
|
1505
|
-
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
1506
|
-
break;
|
|
1507
|
-
}
|
|
1508
1982
|
// RSA Encryption
|
|
1509
1983
|
case "RSA-OAEP":
|
|
1510
1984
|
case "RSA-OAEP-256":
|
|
@@ -1542,10 +2016,70 @@ function getGenerateKeyParams(alg, options) {
|
|
|
1542
2016
|
keyUsages = defaultKeyUsage ?? ["encrypt", "decrypt"];
|
|
1543
2017
|
break;
|
|
1544
2018
|
}
|
|
1545
|
-
//
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
2019
|
+
// ECDSA Signatures
|
|
2020
|
+
case "ES256": {
|
|
2021
|
+
algorithm = { name: "ECDSA", namedCurve: "P-256" };
|
|
2022
|
+
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
2023
|
+
break;
|
|
2024
|
+
}
|
|
2025
|
+
case "ES384": {
|
|
2026
|
+
algorithm = { name: "ECDSA", namedCurve: "P-384" };
|
|
2027
|
+
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
2028
|
+
break;
|
|
2029
|
+
}
|
|
2030
|
+
case "ES512": {
|
|
2031
|
+
algorithm = { name: "ECDSA", namedCurve: "P-521" };
|
|
2032
|
+
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
2033
|
+
break;
|
|
2034
|
+
}
|
|
2035
|
+
case "Ed25519": {
|
|
2036
|
+
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
2037
|
+
algorithm = { name: "Ed25519" };
|
|
2038
|
+
break;
|
|
2039
|
+
}
|
|
2040
|
+
case "EdDSA": {
|
|
2041
|
+
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
2042
|
+
const namedCurve = options?.namedCurve ?? "Ed25519";
|
|
2043
|
+
switch (namedCurve) {
|
|
2044
|
+
case "Ed25519":
|
|
2045
|
+
case "Ed448": {
|
|
2046
|
+
algorithm = { name: namedCurve };
|
|
2047
|
+
break;
|
|
2048
|
+
}
|
|
2049
|
+
default: {
|
|
2050
|
+
throw new Error(
|
|
2051
|
+
"Unsupported namedCurve provided. Supported values are: Ed25519 and Ed448"
|
|
2052
|
+
);
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
break;
|
|
2056
|
+
}
|
|
2057
|
+
// ECDSA Signatures
|
|
2058
|
+
case "ECDH-ES":
|
|
2059
|
+
case "ECDH-ES+A128KW":
|
|
2060
|
+
case "ECDH-ES+A192KW":
|
|
2061
|
+
case "ECDH-ES+A256KW": {
|
|
2062
|
+
keyUsages = ["deriveBits"];
|
|
2063
|
+
const namedCurve = options?.namedCurve ?? "P-256";
|
|
2064
|
+
switch (namedCurve) {
|
|
2065
|
+
case "P-256":
|
|
2066
|
+
case "P-384":
|
|
2067
|
+
case "P-521": {
|
|
2068
|
+
algorithm = { name: "ECDH", namedCurve };
|
|
2069
|
+
break;
|
|
2070
|
+
}
|
|
2071
|
+
case "X25519": {
|
|
2072
|
+
algorithm = { name: "X25519" };
|
|
2073
|
+
break;
|
|
2074
|
+
}
|
|
2075
|
+
default: {
|
|
2076
|
+
throw new Error(
|
|
2077
|
+
"Unsupported namedCurve provided. Supported values are: P-256, P-384, P-521 and X25519"
|
|
2078
|
+
);
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
break;
|
|
2082
|
+
}
|
|
1549
2083
|
default: {
|
|
1550
2084
|
throw new Error(
|
|
1551
2085
|
`Unsupported or invalid algorithm for key generation: ${alg}`
|
|
@@ -1554,12 +2088,32 @@ function getGenerateKeyParams(alg, options) {
|
|
|
1554
2088
|
}
|
|
1555
2089
|
return { algorithm, keyUsages };
|
|
1556
2090
|
}
|
|
2091
|
+
function inferAesImportAlgorithm(enc, cekLengthBytes) {
|
|
2092
|
+
const bitLength = cekLengthBytes << 3;
|
|
2093
|
+
if (enc) {
|
|
2094
|
+
if (enc.includes("GCM")) {
|
|
2095
|
+
return {
|
|
2096
|
+
name: "AES-GCM",
|
|
2097
|
+
length: Number.parseInt(enc.slice(1, 4), 10)
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
if (enc.includes("CBC-HS")) {
|
|
2101
|
+
return void 0;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
if (bitLength === 128 || bitLength === 192 || bitLength === 256) {
|
|
2105
|
+
return { name: "AES-GCM", length: bitLength };
|
|
2106
|
+
}
|
|
2107
|
+
return void 0;
|
|
2108
|
+
}
|
|
1557
2109
|
|
|
1558
2110
|
const jwk = {
|
|
1559
2111
|
__proto__: null,
|
|
2112
|
+
deriveJWKFromPassword: deriveJWKFromPassword,
|
|
1560
2113
|
deriveKeyFromPassword: deriveKeyFromPassword,
|
|
1561
2114
|
exportJWKToPEM: exportJWKToPEM,
|
|
1562
2115
|
exportKey: exportKey,
|
|
2116
|
+
generateJWK: generateJWK,
|
|
1563
2117
|
generateKey: generateKey,
|
|
1564
2118
|
getJWKFromSet: getJWKFromSet,
|
|
1565
2119
|
importJWKFromPEM: importJWKFromPEM,
|
|
@@ -1568,4 +2122,4 @@ const jwk = {
|
|
|
1568
2122
|
wrapKey: wrapKey
|
|
1569
2123
|
};
|
|
1570
2124
|
|
|
1571
|
-
export {
|
|
2125
|
+
export { encrypt as a, getJWKFromSet as b, generateKey as c, decrypt as d, encryptKey as e, generateJWK as f, generateIV as g, deriveKeyFromPassword as h, importKey as i, jwk as j, deriveJWKFromPassword as k, exportKey as l, importJWKFromPEM as m, exportJWKToPEM as n, sign as s, unwrapKey as u, verify as v, wrapKey as w };
|