UNPKG

jose

Version:

Universal 'JSON Web Almost Everything' - JWA, JWS, JWE, JWT, JWK with no dependencies

109 lines (108 loc) 4.4 kB
import { diffieHellman, generateKeyPair as generateKeyPairCb, createPublicKey } from 'crypto'; import { promisify } from 'util'; import * as base64url from './base64url.js'; import getNamedCurve from './get_named_curve.js'; import { encoder, concat, uint32be, lengthAndInput, concatKdf as KDF, } from '../lib/buffer_utils.js'; import digest from './digest.js'; import { JOSENotSupported } from '../util/errors.js'; import { isCryptoKey, getKeyObject } from './webcrypto.js'; const generateKeyPair = promisify(generateKeyPairCb); const concatKdf = KDF.bind(undefined, digest.bind(undefined, 'sha256')); export const deriveKey = async (publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) => { const value = concat(lengthAndInput(encoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength)); if (isCryptoKey(publicKey)) { publicKey = getKeyObject(publicKey); } if (isCryptoKey(privateKey)) { privateKey = getKeyObject(privateKey); } const sharedSecret = diffieHellman({ privateKey, publicKey }); return concatKdf(sharedSecret, keyLength, value); }; export const ephemeralKeyToPublicJWK = function ephemeralKeyToPublicJWK(key) { if (isCryptoKey(key)) { key = getKeyObject(key); } switch (key.asymmetricKeyType) { case 'x25519': case 'x448': { const s = key.asymmetricKeyType === 'x25519' ? 32 : 56; return { crv: key.asymmetricKeyType.toUpperCase(), kty: 'OKP', x: base64url.encode(createPublicKey(key).export({ format: 'der', type: 'spki' }).slice(-s)), }; } case 'ec': { const crv = getNamedCurve(key); const s = crv === 'P-256' ? 64 : crv === 'P-384' ? 96 : 132; const b = key.export({ format: 'der', type: 'pkcs8' }); const x = base64url.encode(b.slice(-s, -s >> 1)); const y = base64url.encode(b.slice(-s >> 1)); return { crv, kty: 'EC', x, y }; } default: throw new JOSENotSupported('unsupported or invalid EPK'); } }; export const generateEpk = async (key) => { if (isCryptoKey(key)) { key = getKeyObject(key); } switch (key.asymmetricKeyType) { case 'x25519': return (await generateKeyPair('x25519')).privateKey; case 'x448': { return (await generateKeyPair('x448')).privateKey; } case 'ec': { const namedCurve = getNamedCurve(key); return (await generateKeyPair('ec', { namedCurve })).privateKey; } default: throw new JOSENotSupported('unsupported or invalid EPK'); } }; export const publicJwkToEphemeralKey = async (jwk) => { let pem; switch (jwk.crv) { case 'P-256': pem = Buffer.concat([ Buffer.from('3059301306072a8648ce3d020106082a8648ce3d03010703420004', 'hex'), base64url.decode(jwk.x), base64url.decode(jwk.y), ]); break; case 'P-384': pem = Buffer.concat([ Buffer.from('3076301006072a8648ce3d020106052b8104002203620004', 'hex'), base64url.decode(jwk.x), base64url.decode(jwk.y), ]); break; case 'P-521': pem = Buffer.concat([ Buffer.from('30819b301006072a8648ce3d020106052b810400230381860004', 'hex'), base64url.decode(jwk.x), base64url.decode(jwk.y), ]); break; case 'X25519': pem = Buffer.concat([ Buffer.from('302a300506032b656e032100', 'hex'), base64url.decode(jwk.x), ]); break; case 'X448': pem = Buffer.concat([ Buffer.from('3042300506032b656f033900', 'hex'), base64url.decode(jwk.x), ]); break; default: throw new JOSENotSupported('unsupported or invalid JWK "crv" (Curve or Subtype of Key Pair) Parameter value'); } return createPublicKey({ format: 'der', key: pem, type: 'spki' }); }; const curves = ['P-256', 'P-384', 'P-521', 'X25519', 'X448']; export const ecdhAllowed = (key) => curves.includes(getNamedCurve(key));