unjwt 0.5.15 → 0.5.17
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 +2 -10
- 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.A5jxFsjh.mjs} +578 -44
- package/dist/shared/{unjwt.BeWVYyaf.d.mts → unjwt.BQUETRUD.d.mts} +67 -8
- package/dist/shared/{unjwt.u3h40Nr-.mjs → unjwt.Bui8we0H.mjs} +2 -3
- package/dist/shared/{unjwt.BsgIQHVD.d.mts → unjwt.ClaeEBcy.d.mts} +21 -9
- package/dist/shared/unjwt.D5J5ziZr.mjs +247 -0
- package/dist/shared/{unjwt.C6b_GyXm.d.mts → unjwt.Djt8pPsc.d.mts} +17 -2
- package/dist/shared/{unjwt.CfL4cArP.d.mts → unjwt.DuLUtSpD.d.mts} +3 -3
- package/dist/shared/{unjwt.Bw4pzdE0.d.mts → unjwt.fumhLslo.d.mts} +46 -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,17 +1457,36 @@ 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
1491
|
const { salt, iterations, toJWK, extractable, keyUsage } = options;
|
|
1068
1492
|
if (!(salt instanceof Uint8Array) || salt.length < 8) {
|
|
@@ -1287,22 +1711,33 @@ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
|
|
|
1287
1711
|
case "RSA-OAEP-256":
|
|
1288
1712
|
case "RSA-OAEP-384":
|
|
1289
1713
|
case "RSA-OAEP-512": {
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1714
|
+
if (!(importedUnwrappingKey instanceof CryptoKey)) {
|
|
1715
|
+
throw new TypeError(
|
|
1716
|
+
"RSA-OAEP requires the unwrapping key to be provided as a CryptoKey"
|
|
1717
|
+
);
|
|
1718
|
+
}
|
|
1719
|
+
unwrappedCekBytes = await decryptRSAES(
|
|
1720
|
+
alg,
|
|
1293
1721
|
importedUnwrappingKey,
|
|
1294
|
-
|
|
1295
|
-
options.unwrappedKeyAlgorithm || { name: "AES-GCM" },
|
|
1296
|
-
// Fallback
|
|
1297
|
-
defaultExtractable,
|
|
1298
|
-
options.keyUsage || ["encrypt", "decrypt"]
|
|
1299
|
-
// Default usages
|
|
1722
|
+
wrappedKey
|
|
1300
1723
|
);
|
|
1301
|
-
if (returnAs)
|
|
1302
|
-
|
|
1303
|
-
|
|
1724
|
+
if (!returnAs) {
|
|
1725
|
+
break;
|
|
1726
|
+
}
|
|
1727
|
+
const inferredAlgorithm = options.unwrappedKeyAlgorithm || inferAesImportAlgorithm(options.enc, unwrappedCekBytes.length);
|
|
1728
|
+
if (!inferredAlgorithm) {
|
|
1729
|
+
throw new Error(
|
|
1730
|
+
'Unable to infer algorithm for RSA-OAEP unwrapped key. Provide "unwrappedKeyAlgorithm" in options.'
|
|
1731
|
+
);
|
|
1732
|
+
}
|
|
1733
|
+
const keyUsages = options.keyUsage || ["encrypt", "decrypt"];
|
|
1734
|
+
return await crypto.subtle.importKey(
|
|
1735
|
+
"raw",
|
|
1736
|
+
unwrappedCekBytes,
|
|
1737
|
+
inferredAlgorithm,
|
|
1738
|
+
defaultExtractable,
|
|
1739
|
+
keyUsages
|
|
1304
1740
|
);
|
|
1305
|
-
break;
|
|
1306
1741
|
}
|
|
1307
1742
|
case "ECDH-ES":
|
|
1308
1743
|
case "ECDH-ES+A128KW":
|
|
@@ -1311,7 +1746,49 @@ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
|
|
|
1311
1746
|
if (!options.epk) {
|
|
1312
1747
|
throw new Error("ECDH-ES requires 'epk' (Ephemeral Public Key) option");
|
|
1313
1748
|
}
|
|
1314
|
-
|
|
1749
|
+
if (!(importedUnwrappingKey instanceof CryptoKey)) {
|
|
1750
|
+
throw new TypeError(
|
|
1751
|
+
"ECDH-ES requires the unwrapping key to be a CryptoKey"
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
if (!allowed(importedUnwrappingKey)) {
|
|
1755
|
+
throw new Error(
|
|
1756
|
+
"ECDH with the provided key is not allowed or not supported"
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
const epkCandidate = options.epk instanceof CryptoKey ? options.epk : await normalizeKey(options.epk, alg);
|
|
1760
|
+
if (!(epkCandidate instanceof CryptoKey)) {
|
|
1761
|
+
throw new TypeError("Failed to normalize ECDH ephemeral public key");
|
|
1762
|
+
}
|
|
1763
|
+
const apuBytes = typeof options.apu === "string" ? base64UrlDecode(options.apu, false) : options.apu ?? new Uint8Array(0);
|
|
1764
|
+
const apvBytes = typeof options.apv === "string" ? base64UrlDecode(options.apv, false) : options.apv ?? new Uint8Array(0);
|
|
1765
|
+
const infoAlg = alg === "ECDH-ES" ? options.enc : alg;
|
|
1766
|
+
if (!infoAlg) {
|
|
1767
|
+
throw new Error(
|
|
1768
|
+
"ECDH-ES requires content encryption algorithm ('enc') to derive the shared secret"
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
const keyLength = alg === "ECDH-ES" ? bitLengthCEK(infoAlg) : Number.parseInt(alg.slice(-5, -2), 10);
|
|
1772
|
+
const sharedSecret = await deriveECDHESKey(
|
|
1773
|
+
epkCandidate,
|
|
1774
|
+
importedUnwrappingKey,
|
|
1775
|
+
infoAlg,
|
|
1776
|
+
keyLength,
|
|
1777
|
+
apuBytes,
|
|
1778
|
+
apvBytes
|
|
1779
|
+
);
|
|
1780
|
+
if (alg === "ECDH-ES") {
|
|
1781
|
+
unwrappedCekBytes = sharedSecret;
|
|
1782
|
+
} else {
|
|
1783
|
+
const kwAlg = alg.slice(-6);
|
|
1784
|
+
if (!(wrappedKey instanceof Uint8Array) || wrappedKey.length === 0) {
|
|
1785
|
+
throw new Error(
|
|
1786
|
+
"ECDH-ES key agreement with key wrapping requires an encrypted key"
|
|
1787
|
+
);
|
|
1788
|
+
}
|
|
1789
|
+
unwrappedCekBytes = await unwrap(kwAlg, sharedSecret, wrappedKey);
|
|
1790
|
+
}
|
|
1791
|
+
break;
|
|
1315
1792
|
}
|
|
1316
1793
|
default: {
|
|
1317
1794
|
throw new Error(`Unsupported key unwrapping algorithm: ${alg}`);
|
|
@@ -1483,28 +1960,6 @@ function getGenerateKeyParams(alg, options) {
|
|
|
1483
1960
|
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
1484
1961
|
break;
|
|
1485
1962
|
}
|
|
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
1963
|
// RSA Encryption
|
|
1509
1964
|
case "RSA-OAEP":
|
|
1510
1965
|
case "RSA-OAEP-256":
|
|
@@ -1542,10 +1997,70 @@ function getGenerateKeyParams(alg, options) {
|
|
|
1542
1997
|
keyUsages = defaultKeyUsage ?? ["encrypt", "decrypt"];
|
|
1543
1998
|
break;
|
|
1544
1999
|
}
|
|
1545
|
-
//
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
2000
|
+
// ECDSA Signatures
|
|
2001
|
+
case "ES256": {
|
|
2002
|
+
algorithm = { name: "ECDSA", namedCurve: "P-256" };
|
|
2003
|
+
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
2004
|
+
break;
|
|
2005
|
+
}
|
|
2006
|
+
case "ES384": {
|
|
2007
|
+
algorithm = { name: "ECDSA", namedCurve: "P-384" };
|
|
2008
|
+
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
2009
|
+
break;
|
|
2010
|
+
}
|
|
2011
|
+
case "ES512": {
|
|
2012
|
+
algorithm = { name: "ECDSA", namedCurve: "P-521" };
|
|
2013
|
+
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
2014
|
+
break;
|
|
2015
|
+
}
|
|
2016
|
+
case "Ed25519": {
|
|
2017
|
+
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
2018
|
+
algorithm = { name: "Ed25519" };
|
|
2019
|
+
break;
|
|
2020
|
+
}
|
|
2021
|
+
case "EdDSA": {
|
|
2022
|
+
keyUsages = defaultKeyUsage ?? ["sign", "verify"];
|
|
2023
|
+
const namedCurve = options?.namedCurve ?? "Ed25519";
|
|
2024
|
+
switch (namedCurve) {
|
|
2025
|
+
case "Ed25519":
|
|
2026
|
+
case "Ed448": {
|
|
2027
|
+
algorithm = { name: namedCurve };
|
|
2028
|
+
break;
|
|
2029
|
+
}
|
|
2030
|
+
default: {
|
|
2031
|
+
throw new Error(
|
|
2032
|
+
"Unsupported namedCurve provided. Supported values are: Ed25519 and Ed448"
|
|
2033
|
+
);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
break;
|
|
2037
|
+
}
|
|
2038
|
+
// ECDSA Signatures
|
|
2039
|
+
case "ECDH-ES":
|
|
2040
|
+
case "ECDH-ES+A128KW":
|
|
2041
|
+
case "ECDH-ES+A192KW":
|
|
2042
|
+
case "ECDH-ES+A256KW": {
|
|
2043
|
+
keyUsages = ["deriveBits"];
|
|
2044
|
+
const namedCurve = options?.namedCurve ?? "P-256";
|
|
2045
|
+
switch (namedCurve) {
|
|
2046
|
+
case "P-256":
|
|
2047
|
+
case "P-384":
|
|
2048
|
+
case "P-521": {
|
|
2049
|
+
algorithm = { name: "ECDH", namedCurve };
|
|
2050
|
+
break;
|
|
2051
|
+
}
|
|
2052
|
+
case "X25519": {
|
|
2053
|
+
algorithm = { name: "X25519" };
|
|
2054
|
+
break;
|
|
2055
|
+
}
|
|
2056
|
+
default: {
|
|
2057
|
+
throw new Error(
|
|
2058
|
+
"Unsupported namedCurve provided. Supported values are: P-256, P-384, P-521 and X25519"
|
|
2059
|
+
);
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
break;
|
|
2063
|
+
}
|
|
1549
2064
|
default: {
|
|
1550
2065
|
throw new Error(
|
|
1551
2066
|
`Unsupported or invalid algorithm for key generation: ${alg}`
|
|
@@ -1554,12 +2069,31 @@ function getGenerateKeyParams(alg, options) {
|
|
|
1554
2069
|
}
|
|
1555
2070
|
return { algorithm, keyUsages };
|
|
1556
2071
|
}
|
|
2072
|
+
function inferAesImportAlgorithm(enc, cekLengthBytes) {
|
|
2073
|
+
const bitLength = cekLengthBytes << 3;
|
|
2074
|
+
if (enc) {
|
|
2075
|
+
if (enc.includes("GCM")) {
|
|
2076
|
+
return {
|
|
2077
|
+
name: "AES-GCM",
|
|
2078
|
+
length: Number.parseInt(enc.slice(1, 4), 10)
|
|
2079
|
+
};
|
|
2080
|
+
}
|
|
2081
|
+
if (enc.includes("CBC-HS")) {
|
|
2082
|
+
return void 0;
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
if (bitLength === 128 || bitLength === 192 || bitLength === 256) {
|
|
2086
|
+
return { name: "AES-GCM", length: bitLength };
|
|
2087
|
+
}
|
|
2088
|
+
return void 0;
|
|
2089
|
+
}
|
|
1557
2090
|
|
|
1558
2091
|
const jwk = {
|
|
1559
2092
|
__proto__: null,
|
|
1560
2093
|
deriveKeyFromPassword: deriveKeyFromPassword,
|
|
1561
2094
|
exportJWKToPEM: exportJWKToPEM,
|
|
1562
2095
|
exportKey: exportKey,
|
|
2096
|
+
generateJWK: generateJWK,
|
|
1563
2097
|
generateKey: generateKey,
|
|
1564
2098
|
getJWKFromSet: getJWKFromSet,
|
|
1565
2099
|
importJWKFromPEM: importJWKFromPEM,
|
|
@@ -1568,4 +2102,4 @@ const jwk = {
|
|
|
1568
2102
|
wrapKey: wrapKey
|
|
1569
2103
|
};
|
|
1570
2104
|
|
|
1571
|
-
export {
|
|
2105
|
+
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, exportKey as k, importJWKFromPEM as l, exportJWKToPEM as m, sign as s, unwrapKey as u, verify as v, wrapKey as w };
|