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;
 | |
| }());
 |