Pure JavaScript (ES5.1) OAuth3 implementation for Browsers and Node.js
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.
 
 

106 lines
3.2 KiB

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