107 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			107 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | ;(function () { | ||
|  | 'use strict'; | ||
|  | 
 | ||
|  |   var crypto = require('crypto'); | ||
|  |   var OAUTH3 = require('./oauth3.core.js').OAUTH3; | ||
|  |   var ec = require('elliptic').ec('p256'); | ||
|  | 
 | ||
|  |   function randomBytes(size) { | ||
|  |     return new OAUTH3.PromiseA(function (resolve, reject) { | ||
|  |       crypto.randomBytes(size, function (err, buf) { | ||
|  |         if (err) { | ||
|  |           reject(err); | ||
|  |         } else { | ||
|  |           resolve(buf); | ||
|  |         } | ||
|  |       }); | ||
|  |     }); | ||
|  |   } | ||
|  | 
 | ||
|  |   function sha256(buf) { | ||
|  |     return crypto.createHash('sha256').update(buf).digest(); | ||
|  |   } | ||
|  | 
 | ||
|  |   function pbkdf2(password, salt) { | ||
|  |     // Derived AES key is 128 bit, and the function takes a size in bytes.
 | ||
|  |     return crypto.pbkdf2Sync(password, Buffer(salt), 8192, 16, 'sha256'); | ||
|  |   } | ||
|  | 
 | ||
|  |   function encrypt(key, iv, data) { | ||
|  |     var cipher = crypto.createCipheriv('aes-128-gcm', Buffer(key), Buffer(iv)); | ||
|  | 
 | ||
|  |     return Buffer.concat([cipher.update(Buffer(data)), cipher.final(), cipher.getAuthTag()]); | ||
|  |   } | ||
|  | 
 | ||
|  |   function decrypt(key, iv, data) { | ||
|  |     var decipher = crypto.createDecipheriv('aes-128-gcm', Buffer(key), Buffer(iv)); | ||
|  | 
 | ||
|  |     decipher.setAuthTag(Buffer(data.slice(-16))); | ||
|  |     return Buffer.concat([decipher.update(Buffer(data.slice(0, -16))), decipher.final()]); | ||
|  |   } | ||
|  | 
 | ||
|  |   function bnToBuffer(bn, size) { | ||
|  |     var buf = bn.toArrayLike(Buffer); | ||
|  | 
 | ||
|  |     if (!size || buf.length === size) { | ||
|  |       return buf; | ||
|  |     } else if (buf.length < size) { | ||
|  |       return Buffer.concat([Buffer(size-buf.length).fill(0), buf]); | ||
|  |     } else if (buf.length > size) { | ||
|  |       throw new Error('EC signature number bigger than expected'); | ||
|  |     } | ||
|  |     throw new Error('invalid size "'+size+'" converting BigNumber to Buffer'); | ||
|  |   } | ||
|  |   function bnToB64(bn) { | ||
|  |     var b64 = bnToBuffer(bn).toString('base64'); | ||
|  |     return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=*$/, ''); | ||
|  |   } | ||
|  |   function genEcdsaKeyPair() { | ||
|  |     var key = ec.genKeyPair(); | ||
|  |     var pubJwk = { | ||
|  |       key_ops: ['verify'] | ||
|  |     , kty: 'EC' | ||
|  |     , crv: 'P-256' | ||
|  |     , x: bnToB64(key.getPublic().getX()) | ||
|  |     , y: bnToB64(key.getPublic().getY()) | ||
|  |     }; | ||
|  | 
 | ||
|  |     var privJwk = JSON.parse(JSON.stringify(pubJwk)); | ||
|  |     privJwk.key_ops = ['sign']; | ||
|  |     privJwk.d = bnToB64(key.getPrivate()); | ||
|  | 
 | ||
|  |     return {privateKey: privJwk, publicKey: pubJwk}; | ||
|  |   } | ||
|  | 
 | ||
|  |   function sign(jwk, msg) { | ||
|  |     var key = ec.keyFromPrivate(Buffer(jwk.d, 'base64')); | ||
|  |     var sig = key.sign(sha256(msg)); | ||
|  |     return Buffer.concat([bnToBuffer(sig.r, 32), bnToBuffer(sig.s, 32)]); | ||
|  |   } | ||
|  | 
 | ||
|  |   function verify(jwk, msg, signature) { | ||
|  |     var key = ec.keyFromPublic({x: Buffer(jwk.x, 'base64'), y: Buffer(jwk.y, 'base64')}); | ||
|  |     var sig = { | ||
|  |       r: Buffer(signature.slice(0, signature.length/2)) | ||
|  |     , s: Buffer(signature.slice(signature.length/2)) | ||
|  |     }; | ||
|  |     return key.verify(sha256(msg), sig); | ||
|  |   } | ||
|  | 
 | ||
|  |   function promiseWrap(func) { | ||
|  |     return function() { | ||
|  |       var args = arguments; | ||
|  |       return new OAUTH3.PromiseA(function (resolve) { | ||
|  |         resolve(func.apply(null, args)); | ||
|  |       }); | ||
|  |     }; | ||
|  |   } | ||
|  |   exports.sha256  = promiseWrap(sha256); | ||
|  |   exports.pbkdf2  = promiseWrap(pbkdf2); | ||
|  |   exports.encrypt = promiseWrap(encrypt); | ||
|  |   exports.decrypt = promiseWrap(decrypt); | ||
|  |   exports.sign    = promiseWrap(sign); | ||
|  |   exports.verify  = promiseWrap(verify); | ||
|  |   exports.genEcdsaKeyPair = promiseWrap(genEcdsaKeyPair); | ||
|  |   exports.randomBytes = randomBytes; | ||
|  | }()); |