UNPKG

hono

Version:

Web framework built on Web Standards

188 lines (187 loc) 5.06 kB
// src/utils/jwt/jws.ts import { getRuntimeKey } from "../../helper/adapter/index.js"; import { decodeBase64 } from "../encode.js"; import { CryptoKeyUsage, JwtAlgorithmNotImplemented } from "./types.js"; import { utf8Encoder } from "./utf8.js"; 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") { throw new Error(`unexpected non private key: CryptoKey.type is ${key.type}`); } return key; } const usages = [CryptoKeyUsage.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")) { const privateKey = await crypto.subtle.importKey("pkcs8", pemToBinary(key), alg, true, [ CryptoKeyUsage.Sign ]); key = await exportPublicJwkFrom(privateKey); } const usages = [CryptoKeyUsage.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: [CryptoKeyUsage.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) { const runtime = getRuntimeKey(); if (runtime === "node" && !!crypto.webcrypto) { return key instanceof crypto.webcrypto.CryptoKey; } return key instanceof CryptoKey; } export { signing, verifying };