import { decodeBase64 } from "../encode.js"; import { getRuntimeKey } from "../../helper/adapter/index.js"; import { JwtAlgorithmNotImplemented } from "./types.js"; import { utf8Encoder } from "./utf8.js"; //#region src/utils/jwt/jws.ts /** * @module * JSON Web Signature (JWS) * https://datatracker.ietf.org/doc/html/rfc7515 */ async function signing(privateKey, alg, data) { const algorithm = getKeyAlgorithm(alg); const cryptoKey = await importPrivateKey(privateKey, algorithm); return await crypto.subtle.sign(algorithm, cryptoKey, data); } async function verifying(publicKey, alg, signature, data) { const algorithm = getKeyAlgorithm(alg); const cryptoKey = await importPublicKey(publicKey, algorithm); return await crypto.subtle.verify(algorithm, cryptoKey, signature, data); } function pemToBinary(pem) { return decodeBase64(pem.replace(/-+(BEGIN|END).*?-+/g, "").replace(/\s/g, "")); } async function importPrivateKey(key, alg) { if (!crypto.subtle || !crypto.subtle.importKey) throw new Error("`crypto.subtle.importKey` is undefined. JWT auth middleware requires it."); if (isCryptoKey(key)) { if (key.type !== "private" && key.type !== "secret") throw new Error(`unexpected key type: CryptoKey.type is ${key.type}, expected private or secret`); return key; } const usages = ["sign"]; if (typeof key === "object") return await crypto.subtle.importKey("jwk", key, alg, false, usages); if (key.includes("PRIVATE")) return await crypto.subtle.importKey("pkcs8", pemToBinary(key), alg, false, usages); return await crypto.subtle.importKey("raw", utf8Encoder.encode(key), alg, false, usages); } async function importPublicKey(key, alg) { if (!crypto.subtle || !crypto.subtle.importKey) throw new Error("`crypto.subtle.importKey` is undefined. JWT auth middleware requires it."); if (isCryptoKey(key)) { if (key.type === "public" || key.type === "secret") return key; key = await exportPublicJwkFrom(key); } if (typeof key === "string" && key.includes("PRIVATE")) key = await exportPublicJwkFrom(await crypto.subtle.importKey("pkcs8", pemToBinary(key), alg, true, ["sign"])); const usages = ["verify"]; if (typeof key === "object") return await crypto.subtle.importKey("jwk", key, alg, false, usages); if (key.includes("PUBLIC")) return await crypto.subtle.importKey("spki", pemToBinary(key), alg, false, usages); return await crypto.subtle.importKey("raw", utf8Encoder.encode(key), alg, false, usages); } async function exportPublicJwkFrom(privateKey) { if (privateKey.type !== "private") throw new Error(`unexpected key type: ${privateKey.type}`); if (!privateKey.extractable) throw new Error("unexpected private key is unextractable"); const jwk = await crypto.subtle.exportKey("jwk", privateKey); const { kty } = jwk; const { alg, e, n } = jwk; const { crv, x, y } = jwk; return { kty, alg, e, n, crv, x, y, key_ops: ["verify"] }; } function getKeyAlgorithm(name) { switch (name) { case "HS256": return { name: "HMAC", hash: { name: "SHA-256" } }; case "HS384": return { name: "HMAC", hash: { name: "SHA-384" } }; case "HS512": return { name: "HMAC", hash: { name: "SHA-512" } }; case "RS256": return { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } }; case "RS384": return { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-384" } }; case "RS512": return { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-512" } }; case "PS256": return { name: "RSA-PSS", hash: { name: "SHA-256" }, saltLength: 32 }; case "PS384": return { name: "RSA-PSS", hash: { name: "SHA-384" }, saltLength: 48 }; case "PS512": return { name: "RSA-PSS", hash: { name: "SHA-512" }, saltLength: 64 }; case "ES256": return { name: "ECDSA", hash: { name: "SHA-256" }, namedCurve: "P-256" }; case "ES384": return { name: "ECDSA", hash: { name: "SHA-384" }, namedCurve: "P-384" }; case "ES512": return { name: "ECDSA", hash: { name: "SHA-512" }, namedCurve: "P-521" }; case "EdDSA": return { name: "Ed25519", namedCurve: "Ed25519" }; default: throw new JwtAlgorithmNotImplemented(name); } } function isCryptoKey(key) { if (getRuntimeKey() === "node" && !!crypto.webcrypto) return key instanceof crypto.webcrypto.CryptoKey; return key instanceof CryptoKey; } //#endregion export { signing, verifying };