123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- var bufferEqual = require('buffer-equal-constant-time');
- var Buffer = require('safe-buffer').Buffer;
- var crypto = require('crypto');
- var formatEcdsa = require('ecdsa-sig-formatter');
- var util = require('util');
- var MSG_INVALID_ALGORITHM = '"%s" is not a valid algorithm.\n Supported algorithms are:\n "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512" and "none".'
- var MSG_INVALID_SECRET = 'secret must be a string or buffer';
- var MSG_INVALID_VERIFIER_KEY = 'key must be a string or a buffer';
- var MSG_INVALID_SIGNER_KEY = 'key must be a string, a buffer or an object';
- var supportsKeyObjects = typeof crypto.createPublicKey === 'function';
- if (supportsKeyObjects) {
- MSG_INVALID_VERIFIER_KEY += ' or a KeyObject';
- MSG_INVALID_SECRET += 'or a KeyObject';
- }
- function checkIsPublicKey(key) {
- if (Buffer.isBuffer(key)) {
- return;
- }
- if (typeof key === 'string') {
- return;
- }
- if (!supportsKeyObjects) {
- throw typeError(MSG_INVALID_VERIFIER_KEY);
- }
- if (typeof key !== 'object') {
- throw typeError(MSG_INVALID_VERIFIER_KEY);
- }
- if (typeof key.type !== 'string') {
- throw typeError(MSG_INVALID_VERIFIER_KEY);
- }
- if (typeof key.asymmetricKeyType !== 'string') {
- throw typeError(MSG_INVALID_VERIFIER_KEY);
- }
- if (typeof key.export !== 'function') {
- throw typeError(MSG_INVALID_VERIFIER_KEY);
- }
- };
- function checkIsPrivateKey(key) {
- if (Buffer.isBuffer(key)) {
- return;
- }
- if (typeof key === 'string') {
- return;
- }
- if (typeof key === 'object') {
- return;
- }
- throw typeError(MSG_INVALID_SIGNER_KEY);
- };
- function checkIsSecretKey(key) {
- if (Buffer.isBuffer(key)) {
- return;
- }
- if (typeof key === 'string') {
- return key;
- }
- if (!supportsKeyObjects) {
- throw typeError(MSG_INVALID_SECRET);
- }
- if (typeof key !== 'object') {
- throw typeError(MSG_INVALID_SECRET);
- }
- if (key.type !== 'secret') {
- throw typeError(MSG_INVALID_SECRET);
- }
- if (typeof key.export !== 'function') {
- throw typeError(MSG_INVALID_SECRET);
- }
- }
- function fromBase64(base64) {
- return base64
- .replace(/=/g, '')
- .replace(/\+/g, '-')
- .replace(/\//g, '_');
- }
- function toBase64(base64url) {
- base64url = base64url.toString();
- var padding = 4 - base64url.length % 4;
- if (padding !== 4) {
- for (var i = 0; i < padding; ++i) {
- base64url += '=';
- }
- }
- return base64url
- .replace(/\-/g, '+')
- .replace(/_/g, '/');
- }
- function typeError(template) {
- var args = [].slice.call(arguments, 1);
- var errMsg = util.format.bind(util, template).apply(null, args);
- return new TypeError(errMsg);
- }
- function bufferOrString(obj) {
- return Buffer.isBuffer(obj) || typeof obj === 'string';
- }
- function normalizeInput(thing) {
- if (!bufferOrString(thing))
- thing = JSON.stringify(thing);
- return thing;
- }
- function createHmacSigner(bits) {
- return function sign(thing, secret) {
- checkIsSecretKey(secret);
- thing = normalizeInput(thing);
- var hmac = crypto.createHmac('sha' + bits, secret);
- var sig = (hmac.update(thing), hmac.digest('base64'))
- return fromBase64(sig);
- }
- }
- function createHmacVerifier(bits) {
- return function verify(thing, signature, secret) {
- var computedSig = createHmacSigner(bits)(thing, secret);
- return bufferEqual(Buffer.from(signature), Buffer.from(computedSig));
- }
- }
- function createKeySigner(bits) {
- return function sign(thing, privateKey) {
- checkIsPrivateKey(privateKey);
- thing = normalizeInput(thing);
- // Even though we are specifying "RSA" here, this works with ECDSA
- // keys as well.
- var signer = crypto.createSign('RSA-SHA' + bits);
- var sig = (signer.update(thing), signer.sign(privateKey, 'base64'));
- return fromBase64(sig);
- }
- }
- function createKeyVerifier(bits) {
- return function verify(thing, signature, publicKey) {
- checkIsPublicKey(publicKey);
- thing = normalizeInput(thing);
- signature = toBase64(signature);
- var verifier = crypto.createVerify('RSA-SHA' + bits);
- verifier.update(thing);
- return verifier.verify(publicKey, signature, 'base64');
- }
- }
- function createPSSKeySigner(bits) {
- return function sign(thing, privateKey) {
- checkIsPrivateKey(privateKey);
- thing = normalizeInput(thing);
- var signer = crypto.createSign('RSA-SHA' + bits);
- var sig = (signer.update(thing), signer.sign({
- key: privateKey,
- padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
- saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST
- }, 'base64'));
- return fromBase64(sig);
- }
- }
- function createPSSKeyVerifier(bits) {
- return function verify(thing, signature, publicKey) {
- checkIsPublicKey(publicKey);
- thing = normalizeInput(thing);
- signature = toBase64(signature);
- var verifier = crypto.createVerify('RSA-SHA' + bits);
- verifier.update(thing);
- return verifier.verify({
- key: publicKey,
- padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
- saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST
- }, signature, 'base64');
- }
- }
- function createECDSASigner(bits) {
- var inner = createKeySigner(bits);
- return function sign() {
- var signature = inner.apply(null, arguments);
- signature = formatEcdsa.derToJose(signature, 'ES' + bits);
- return signature;
- };
- }
- function createECDSAVerifer(bits) {
- var inner = createKeyVerifier(bits);
- return function verify(thing, signature, publicKey) {
- signature = formatEcdsa.joseToDer(signature, 'ES' + bits).toString('base64');
- var result = inner(thing, signature, publicKey);
- return result;
- };
- }
- function createNoneSigner() {
- return function sign() {
- return '';
- }
- }
- function createNoneVerifier() {
- return function verify(thing, signature) {
- return signature === '';
- }
- }
- module.exports = function jwa(algorithm) {
- var signerFactories = {
- hs: createHmacSigner,
- rs: createKeySigner,
- ps: createPSSKeySigner,
- es: createECDSASigner,
- none: createNoneSigner,
- }
- var verifierFactories = {
- hs: createHmacVerifier,
- rs: createKeyVerifier,
- ps: createPSSKeyVerifier,
- es: createECDSAVerifer,
- none: createNoneVerifier,
- }
- var match = algorithm.match(/^(RS|PS|ES|HS)(256|384|512)$|^(none)$/i);
- if (!match)
- throw typeError(MSG_INVALID_ALGORITHM, algorithm);
- var algo = (match[1] || match[3]).toLowerCase();
- var bits = match[2];
- return {
- sign: signerFactories[algo](bits),
- verify: verifierFactories[algo](bits),
- }
- };
|