index.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. var bufferEqual = require('buffer-equal-constant-time');
  2. var Buffer = require('safe-buffer').Buffer;
  3. var crypto = require('crypto');
  4. var formatEcdsa = require('ecdsa-sig-formatter');
  5. var util = require('util');
  6. 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".'
  7. var MSG_INVALID_SECRET = 'secret must be a string or buffer';
  8. var MSG_INVALID_VERIFIER_KEY = 'key must be a string or a buffer';
  9. var MSG_INVALID_SIGNER_KEY = 'key must be a string, a buffer or an object';
  10. var supportsKeyObjects = typeof crypto.createPublicKey === 'function';
  11. if (supportsKeyObjects) {
  12. MSG_INVALID_VERIFIER_KEY += ' or a KeyObject';
  13. MSG_INVALID_SECRET += 'or a KeyObject';
  14. }
  15. function checkIsPublicKey(key) {
  16. if (Buffer.isBuffer(key)) {
  17. return;
  18. }
  19. if (typeof key === 'string') {
  20. return;
  21. }
  22. if (!supportsKeyObjects) {
  23. throw typeError(MSG_INVALID_VERIFIER_KEY);
  24. }
  25. if (typeof key !== 'object') {
  26. throw typeError(MSG_INVALID_VERIFIER_KEY);
  27. }
  28. if (typeof key.type !== 'string') {
  29. throw typeError(MSG_INVALID_VERIFIER_KEY);
  30. }
  31. if (typeof key.asymmetricKeyType !== 'string') {
  32. throw typeError(MSG_INVALID_VERIFIER_KEY);
  33. }
  34. if (typeof key.export !== 'function') {
  35. throw typeError(MSG_INVALID_VERIFIER_KEY);
  36. }
  37. };
  38. function checkIsPrivateKey(key) {
  39. if (Buffer.isBuffer(key)) {
  40. return;
  41. }
  42. if (typeof key === 'string') {
  43. return;
  44. }
  45. if (typeof key === 'object') {
  46. return;
  47. }
  48. throw typeError(MSG_INVALID_SIGNER_KEY);
  49. };
  50. function checkIsSecretKey(key) {
  51. if (Buffer.isBuffer(key)) {
  52. return;
  53. }
  54. if (typeof key === 'string') {
  55. return key;
  56. }
  57. if (!supportsKeyObjects) {
  58. throw typeError(MSG_INVALID_SECRET);
  59. }
  60. if (typeof key !== 'object') {
  61. throw typeError(MSG_INVALID_SECRET);
  62. }
  63. if (key.type !== 'secret') {
  64. throw typeError(MSG_INVALID_SECRET);
  65. }
  66. if (typeof key.export !== 'function') {
  67. throw typeError(MSG_INVALID_SECRET);
  68. }
  69. }
  70. function fromBase64(base64) {
  71. return base64
  72. .replace(/=/g, '')
  73. .replace(/\+/g, '-')
  74. .replace(/\//g, '_');
  75. }
  76. function toBase64(base64url) {
  77. base64url = base64url.toString();
  78. var padding = 4 - base64url.length % 4;
  79. if (padding !== 4) {
  80. for (var i = 0; i < padding; ++i) {
  81. base64url += '=';
  82. }
  83. }
  84. return base64url
  85. .replace(/\-/g, '+')
  86. .replace(/_/g, '/');
  87. }
  88. function typeError(template) {
  89. var args = [].slice.call(arguments, 1);
  90. var errMsg = util.format.bind(util, template).apply(null, args);
  91. return new TypeError(errMsg);
  92. }
  93. function bufferOrString(obj) {
  94. return Buffer.isBuffer(obj) || typeof obj === 'string';
  95. }
  96. function normalizeInput(thing) {
  97. if (!bufferOrString(thing))
  98. thing = JSON.stringify(thing);
  99. return thing;
  100. }
  101. function createHmacSigner(bits) {
  102. return function sign(thing, secret) {
  103. checkIsSecretKey(secret);
  104. thing = normalizeInput(thing);
  105. var hmac = crypto.createHmac('sha' + bits, secret);
  106. var sig = (hmac.update(thing), hmac.digest('base64'))
  107. return fromBase64(sig);
  108. }
  109. }
  110. function createHmacVerifier(bits) {
  111. return function verify(thing, signature, secret) {
  112. var computedSig = createHmacSigner(bits)(thing, secret);
  113. return bufferEqual(Buffer.from(signature), Buffer.from(computedSig));
  114. }
  115. }
  116. function createKeySigner(bits) {
  117. return function sign(thing, privateKey) {
  118. checkIsPrivateKey(privateKey);
  119. thing = normalizeInput(thing);
  120. // Even though we are specifying "RSA" here, this works with ECDSA
  121. // keys as well.
  122. var signer = crypto.createSign('RSA-SHA' + bits);
  123. var sig = (signer.update(thing), signer.sign(privateKey, 'base64'));
  124. return fromBase64(sig);
  125. }
  126. }
  127. function createKeyVerifier(bits) {
  128. return function verify(thing, signature, publicKey) {
  129. checkIsPublicKey(publicKey);
  130. thing = normalizeInput(thing);
  131. signature = toBase64(signature);
  132. var verifier = crypto.createVerify('RSA-SHA' + bits);
  133. verifier.update(thing);
  134. return verifier.verify(publicKey, signature, 'base64');
  135. }
  136. }
  137. function createPSSKeySigner(bits) {
  138. return function sign(thing, privateKey) {
  139. checkIsPrivateKey(privateKey);
  140. thing = normalizeInput(thing);
  141. var signer = crypto.createSign('RSA-SHA' + bits);
  142. var sig = (signer.update(thing), signer.sign({
  143. key: privateKey,
  144. padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
  145. saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST
  146. }, 'base64'));
  147. return fromBase64(sig);
  148. }
  149. }
  150. function createPSSKeyVerifier(bits) {
  151. return function verify(thing, signature, publicKey) {
  152. checkIsPublicKey(publicKey);
  153. thing = normalizeInput(thing);
  154. signature = toBase64(signature);
  155. var verifier = crypto.createVerify('RSA-SHA' + bits);
  156. verifier.update(thing);
  157. return verifier.verify({
  158. key: publicKey,
  159. padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
  160. saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST
  161. }, signature, 'base64');
  162. }
  163. }
  164. function createECDSASigner(bits) {
  165. var inner = createKeySigner(bits);
  166. return function sign() {
  167. var signature = inner.apply(null, arguments);
  168. signature = formatEcdsa.derToJose(signature, 'ES' + bits);
  169. return signature;
  170. };
  171. }
  172. function createECDSAVerifer(bits) {
  173. var inner = createKeyVerifier(bits);
  174. return function verify(thing, signature, publicKey) {
  175. signature = formatEcdsa.joseToDer(signature, 'ES' + bits).toString('base64');
  176. var result = inner(thing, signature, publicKey);
  177. return result;
  178. };
  179. }
  180. function createNoneSigner() {
  181. return function sign() {
  182. return '';
  183. }
  184. }
  185. function createNoneVerifier() {
  186. return function verify(thing, signature) {
  187. return signature === '';
  188. }
  189. }
  190. module.exports = function jwa(algorithm) {
  191. var signerFactories = {
  192. hs: createHmacSigner,
  193. rs: createKeySigner,
  194. ps: createPSSKeySigner,
  195. es: createECDSASigner,
  196. none: createNoneSigner,
  197. }
  198. var verifierFactories = {
  199. hs: createHmacVerifier,
  200. rs: createKeyVerifier,
  201. ps: createPSSKeyVerifier,
  202. es: createECDSAVerifer,
  203. none: createNoneVerifier,
  204. }
  205. var match = algorithm.match(/^(RS|PS|ES|HS)(256|384|512)$|^(none)$/i);
  206. if (!match)
  207. throw typeError(MSG_INVALID_ALGORITHM, algorithm);
  208. var algo = (match[1] || match[3]).toLowerCase();
  209. var bits = match[2];
  210. return {
  211. sign: signerFactories[algo](bits),
  212. verify: verifierFactories[algo](bits),
  213. }
  214. };