|  |  |  |  | 'use strict' | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // Inspired by node-cookie-signature
 | 
					
						
							|  |  |  |  | // https://github.com/tj/node-cookie-signature
 | 
					
						
							|  |  |  |  | //
 | 
					
						
							|  |  |  |  | // The MIT License
 | 
					
						
							|  |  |  |  | // Copyright (c) 2012–2022 LearnBoost <tj@learnboost.com> and other contributors;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const crypto = require('crypto') | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const base64PaddingRE = /=/g | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | function Signer (secrets, algorithm = 'sha256') { | 
					
						
							|  |  |  |  |   if (!(this instanceof Signer)) { | 
					
						
							|  |  |  |  |     return new Signer(secrets, algorithm) | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   this.secrets = Array.isArray(secrets) ? secrets : [secrets] | 
					
						
							|  |  |  |  |   this.signingKey = this.secrets[0] | 
					
						
							|  |  |  |  |   this.algorithm = algorithm | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   validateSecrets(this.secrets) | 
					
						
							|  |  |  |  |   validateAlgorithm(this.algorithm) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | function validateSecrets (secrets) { | 
					
						
							|  |  |  |  |   for (const secret of secrets) { | 
					
						
							|  |  |  |  |     if (typeof secret !== 'string') { | 
					
						
							|  |  |  |  |       throw new TypeError('Secret key must be a string.') | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | function validateAlgorithm (algorithm) { | 
					
						
							|  |  |  |  |   // validate that the algorithm is supported by the node runtime
 | 
					
						
							|  |  |  |  |   try { | 
					
						
							|  |  |  |  |     crypto.createHmac(algorithm, 'dummyHmac') | 
					
						
							|  |  |  |  |   } catch (e) { | 
					
						
							|  |  |  |  |     throw new TypeError(`Algorithm ${algorithm} not supported.`) | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | function _sign (value, secret, algorithm) { | 
					
						
							|  |  |  |  |   if (typeof value !== 'string') { | 
					
						
							|  |  |  |  |     throw new TypeError('Cookie value must be provided as a string.') | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   return value + '.' + crypto | 
					
						
							|  |  |  |  |     .createHmac(algorithm, secret) | 
					
						
							|  |  |  |  |     .update(value) | 
					
						
							|  |  |  |  |     .digest('base64') | 
					
						
							|  |  |  |  |     // remove base64 padding (=) as it has special meaning in cookies
 | 
					
						
							|  |  |  |  |     .replace(base64PaddingRE, '') | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | function _unsign (signedValue, secrets, algorithm) { | 
					
						
							|  |  |  |  |   if (typeof signedValue !== 'string') { | 
					
						
							|  |  |  |  |     throw new TypeError('Signed cookie string must be provided.') | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   const value = signedValue.slice(0, signedValue.lastIndexOf('.')) | 
					
						
							|  |  |  |  |   const actual = Buffer.from(signedValue.slice(signedValue.lastIndexOf('.') + 1)) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   for (const secret of secrets) { | 
					
						
							|  |  |  |  |     const expected = Buffer.from(crypto | 
					
						
							|  |  |  |  |       .createHmac(algorithm, secret) | 
					
						
							|  |  |  |  |       .update(value) | 
					
						
							|  |  |  |  |       .digest('base64') | 
					
						
							|  |  |  |  |       // remove base64 padding (=) as it has special meaning in cookies
 | 
					
						
							|  |  |  |  |       .replace(base64PaddingRE, '')) | 
					
						
							|  |  |  |  |     if ( | 
					
						
							|  |  |  |  |       expected.length === actual.length && | 
					
						
							|  |  |  |  |       crypto.timingSafeEqual(expected, actual) | 
					
						
							|  |  |  |  |     ) { | 
					
						
							|  |  |  |  |       return { | 
					
						
							|  |  |  |  |         valid: true, | 
					
						
							|  |  |  |  |         renew: secret !== secrets[0], | 
					
						
							|  |  |  |  |         value | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return { | 
					
						
							|  |  |  |  |     valid: false, | 
					
						
							|  |  |  |  |     renew: false, | 
					
						
							|  |  |  |  |     value: null | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Signer.prototype.sign = function (value) { | 
					
						
							|  |  |  |  |   return _sign(value, this.signingKey, this.algorithm) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Signer.prototype.unsign = function (signedValue) { | 
					
						
							|  |  |  |  |   return _unsign(signedValue, this.secrets, this.algorithm) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | function sign (value, secret, algorithm = 'sha256') { | 
					
						
							|  |  |  |  |   const secrets = Array.isArray(secret) ? secret : [secret] | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   validateSecrets(secrets) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return _sign(value, secrets[0], algorithm) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | function unsign (signedValue, secret, algorithm = 'sha256') { | 
					
						
							|  |  |  |  |   const secrets = Array.isArray(secret) ? secret : [secret] | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   validateSecrets(secrets) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return _unsign(signedValue, secrets, algorithm) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | module.exports = Signer | 
					
						
							|  |  |  |  | module.exports.signerFactory = Signer | 
					
						
							|  |  |  |  | module.exports.Signer = Signer | 
					
						
							|  |  |  |  | module.exports.sign = sign | 
					
						
							|  |  |  |  | module.exports.unsign = unsign |