UNPKG

dpop

Version:

DPoP (RFC9449) for JavaScript Runtimes

229 lines 8.33 kB
const encoder = new TextEncoder(); const decoder = new TextDecoder(); function buf(input) { if (typeof input === 'string') { return encoder.encode(input); } return decoder.decode(input); } function checkRsaKeyAlgorithm(algorithm) { if (typeof algorithm.modulusLength !== 'number' || algorithm.modulusLength < 2048) { throw new OperationProcessingError(`${algorithm.name} modulusLength must be at least 2048 bits`); } } function subtleAlgorithm(key) { switch (key.algorithm.name) { case 'ECDSA': return { name: key.algorithm.name, hash: 'SHA-256' }; case 'RSA-PSS': checkRsaKeyAlgorithm(key.algorithm); return { name: key.algorithm.name, saltLength: 256 >> 3, }; case 'RSASSA-PKCS1-v1_5': checkRsaKeyAlgorithm(key.algorithm); return { name: key.algorithm.name }; case 'Ed25519': return { name: key.algorithm.name }; } throw new UnsupportedOperationError(); } async function jwt(header, claimsSet, key) { if (key.usages.includes('sign') === false) { throw new TypeError('private CryptoKey instances used for signing assertions must include "sign" in their "usages"'); } const input = `${b64u(buf(JSON.stringify(header)))}.${b64u(buf(JSON.stringify(claimsSet)))}`; const signature = b64u(await crypto.subtle.sign(subtleAlgorithm(key), key, buf(input))); return `${input}.${signature}`; } let encodeBase64Url; if (Uint8Array.prototype.toBase64) { encodeBase64Url = (input) => { if (input instanceof ArrayBuffer) { input = new Uint8Array(input); } return input.toBase64({ alphabet: 'base64url', omitPadding: true }); }; } else { const CHUNK_SIZE = 0x8000; encodeBase64Url = (input) => { if (input instanceof ArrayBuffer) { input = new Uint8Array(input); } const arr = []; for (let i = 0; i < input.byteLength; i += CHUNK_SIZE) { arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE))); } return btoa(arr.join('')).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); }; } function b64u(input) { return encodeBase64Url(input); } class UnsupportedOperationError extends Error { constructor(message) { var _a; super(message !== null && message !== void 0 ? message : 'operation not supported'); this.name = this.constructor.name; (_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, this, this.constructor); } } class OperationProcessingError extends Error { constructor(message) { var _a; super(message); this.name = this.constructor.name; (_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, this, this.constructor); } } function psAlg(key) { switch (key.algorithm.hash.name) { case 'SHA-256': return 'PS256'; default: throw new UnsupportedOperationError('unsupported RsaHashedKeyAlgorithm hash name'); } } function rsAlg(key) { switch (key.algorithm.hash.name) { case 'SHA-256': return 'RS256'; default: throw new UnsupportedOperationError('unsupported RsaHashedKeyAlgorithm hash name'); } } function esAlg(key) { switch (key.algorithm.namedCurve) { case 'P-256': return 'ES256'; default: throw new UnsupportedOperationError('unsupported EcKeyAlgorithm namedCurve'); } } function determineJWSAlgorithm(key) { switch (key.algorithm.name) { case 'RSA-PSS': return psAlg(key); case 'RSASSA-PKCS1-v1_5': return rsAlg(key); case 'ECDSA': return esAlg(key); case 'Ed25519': return 'Ed25519'; default: throw new UnsupportedOperationError('unsupported CryptoKey algorithm name'); } } function isCryptoKey(key) { return key instanceof CryptoKey; } function isPrivateKey(key) { return isCryptoKey(key) && key.type === 'private'; } function isPublicKey(key) { return isCryptoKey(key) && key.type === 'public'; } function epochTime() { return Math.floor(Date.now() / 1000); } export async function generateProof(keypair, htu, htm, nonce, accessToken, additional) { const privateKey = keypair === null || keypair === void 0 ? void 0 : keypair.privateKey; const publicKey = keypair === null || keypair === void 0 ? void 0 : keypair.publicKey; if (!isPrivateKey(privateKey)) { throw new TypeError('"keypair.privateKey" must be a private CryptoKey'); } if (!isPublicKey(publicKey)) { throw new TypeError('"keypair.publicKey" must be a public CryptoKey'); } if (publicKey.extractable !== true) { throw new TypeError('"keypair.publicKey.extractable" must be true'); } if (typeof htu !== 'string') { throw new TypeError('"htu" must be a string'); } if (typeof htm !== 'string') { throw new TypeError('"htm" must be a string'); } if (nonce !== undefined && typeof nonce !== 'string') { throw new TypeError('"nonce" must be a string or undefined'); } if (accessToken !== undefined && typeof accessToken !== 'string') { throw new TypeError('"accessToken" must be a string or undefined'); } if (additional !== undefined && (typeof additional !== 'object' || additional === null || Array.isArray(additional))) { throw new TypeError('"additional" must be an object'); } return jwt({ alg: determineJWSAlgorithm(privateKey), typ: 'dpop+jwt', jwk: await publicJwk(publicKey), }, Object.assign(Object.assign({}, additional), { iat: epochTime(), jti: crypto.randomUUID(), htm, nonce, htu, ath: accessToken ? b64u(await crypto.subtle.digest('SHA-256', buf(accessToken))) : undefined }), privateKey); } async function publicJwk(key) { const { kty, e, n, x, y, crv } = await crypto.subtle.exportKey('jwk', key); return { kty, crv, e, n, x, y }; } export async function generateKeyPair(alg, options) { var _a; let algorithm; if (typeof alg !== 'string' || alg.length === 0) { throw new TypeError('"alg" must be a non-empty string'); } switch (alg) { case 'PS256': algorithm = { name: 'RSA-PSS', hash: 'SHA-256', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), }; break; case 'RS256': algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), }; break; case 'ES256': algorithm = { name: 'ECDSA', namedCurve: 'P-256' }; break; case 'Ed25519': algorithm = { name: 'Ed25519' }; break; default: throw new UnsupportedOperationError(); } return (crypto.subtle.generateKey(algorithm, (_a = options === null || options === void 0 ? void 0 : options.extractable) !== null && _a !== void 0 ? _a : false, ['sign', 'verify'])); } export async function calculateThumbprint(publicKey) { if (!isPublicKey(publicKey)) { throw new TypeError('"publicKey" must be a public CryptoKey'); } if (publicKey.extractable !== true) { throw new TypeError('"publicKey.extractable" must be true'); } const jwk = await publicJwk(publicKey); let components; switch (jwk.kty) { case 'EC': components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y }; break; case 'OKP': components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x }; break; case 'RSA': components = { e: jwk.e, kty: jwk.kty, n: jwk.n }; break; default: throw new UnsupportedOperationError('unsupported JWK kty'); } return b64u(await crypto.subtle.digest({ name: 'SHA-256' }, buf(JSON.stringify(components)))); } //# sourceMappingURL=index.js.map