You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					253 lines
				
				6.3 KiB
			
		
		
			
		
	
	
					253 lines
				
				6.3 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								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),
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 |