changed the API for most of the crypto functions

thus far I don't think anyone uses those functions so this should be safe
This commit is contained in:
tigerbot 2017-07-28 13:02:36 -06:00
parent 28dbf9ab23
commit 5a5488f504
2 changed files with 75 additions and 74 deletions

View File

@ -207,12 +207,12 @@
} }
, jwt: { , jwt: {
// decode only (no verification) // decode only (no verification)
decode: function (str) { decode: function (token, opts) {
// 'abc.qrs.xyz' // 'abc.qrs.xyz'
// [ 'abc', 'qrs', 'xyz' ] // [ 'abc', 'qrs', 'xyz' ]
// {} // {}
var parts = str.split(/\./g); var parts = token.split(/\./g);
var err; var err;
if (parts.length !== 3) { if (parts.length !== 3) {
err = new Error("Invalid JWT: required 3 '.' separated components not "+parts.length); err = new Error("Invalid JWT: required 3 '.' separated components not "+parts.length);
@ -220,14 +220,55 @@
throw err; throw err;
} }
return JSON.parse(OAUTH3._base64.decodeUrlSafe(parts[1])); if (!opts || !opts.complete) {
return JSON.parse(OAUTH3._base64.decodeUrlSafe(parts[1]));
}
return {
header: JSON.parse(OAUTH3._base64.decodeUrlSafe(parts[0]))
, payload: JSON.parse(OAUTH3._base64.decodeUrlSafe(parts[1]))
};
} }
, verify: function (jwk, token) { , verify: function (token, jwk) {
if (!OAUTH3.crypto) {
return OAUTH3.PromiseA.reject(new Error("OAuth3 crypto library unavailable"));
}
jwk = jwk.publicKey || jwk;
var parts = token.split(/\./g); var parts = token.split(/\./g);
var data = OAUTH3._binStr.binStrToBuffer(parts.slice(0, 2).join('.')); var data = OAUTH3._binStr.binStrToBuffer(parts.slice(0, 2).join('.'));
var signature = OAUTH3._base64.urlSafeToBuffer(parts[2]); var signature = OAUTH3._base64.urlSafeToBuffer(parts[2]);
return OAUTH3.crypto.core.verify(jwk, data, signature); return OAUTH3.crypto.core.verify(jwk, data, signature).then(function () {
return OAUTH3.jwt.decode(token);
});
}
, sign: function (payload, jwk) {
if (!OAUTH3.crypto) {
return OAUTH3.PromiseA.reject(new Error("OAuth3 crypto library unavailable"));
}
jwk = jwk.privateKey || jwk;
var prom;
if (jwk.kid) {
prom = OAUTH3.PromiseA.resolve(jwk.kid);
} else {
prom = OAUTH3.crypto.thumbprintJwk(jwk);
}
return prom.then(function (kid) {
// Currently the crypto part of the OAuth3 library only supports ES256
var header = {type: 'JWT', alg: 'ES256', kid: kid};
var input = [
OAUTH3._base64.encodeUrlSafe(JSON.stringify(header, null))
, OAUTH3._base64.encodeUrlSafe(JSON.stringify(payload, null))
].join('.');
return OAUTH3.crypto.core.sign(jwk, OAUTH3._binStr.binStrToBuffer(input))
.then(OAUTH3._base64.bufferToUrlSafe)
.then(function (signature) {
return input + '.' + signature;
});
});
} }
, freshness: function (tokenMeta, staletime, now) { , freshness: function (tokenMeta, staletime, now) {
// If the token doesn't expire then it's always fresh. // If the token doesn't expire then it's always fresh.

View File

@ -1,5 +1,5 @@
;(function (exports) { ;(function (exports) {
'use strict'; 'use strict';
var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3; var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3;
@ -82,6 +82,7 @@
}; };
function checkWebCrypto() { function checkWebCrypto() {
/* global OAUTH3_crypto_fallback */
var loadFallback = function() { var loadFallback = function() {
var prom; var prom;
loadFallback = function () { return prom; }; loadFallback = function () { return prom; };
@ -102,11 +103,10 @@
return prom; return prom;
}; };
function checkException(name, func) { function checkException(name, func) {
new OAUTH3.PromiseA(function (resolve) { resolve(func()); }) OAUTH3.PromiseA.resolve().then(func)
.then(function () { .then(function () {
OAUTH3.crypto.core[name] = webCrypto[name]; OAUTH3.crypto.core[name] = webCrypto[name];
}) }, function (err) {
.catch(function (err) {
console.warn('error with WebCrypto', name, '- using fallback', err); console.warn('error with WebCrypto', name, '- using fallback', err);
loadFallback().then(function () { loadFallback().then(function () {
OAUTH3.crypto.core[name] = OAUTH3_crypto_fallback[name]; OAUTH3.crypto.core[name] = OAUTH3_crypto_fallback[name];
@ -195,101 +195,61 @@
.then(OAUTH3._base64.bufferToUrlSafe); .then(OAUTH3._base64.bufferToUrlSafe);
}; };
OAUTH3.crypto._createKey = function (ppid) { OAUTH3.crypto.createKeyPair = function () {
var saltProm = OAUTH3.crypto.core.randomBytes(16); // TODO: maybe support other types of key pairs, not just ECDSA P-256
var kekProm = saltProm.then(function (salt) { return OAUTH3.crypto.core.genEcdsaKeyPair().then(function (keyPair) {
return OAUTH3.crypto.core.pbkdf2(ppid, salt);
});
var ecdsaProm = OAUTH3.crypto.core.genEcdsaKeyPair()
.then(function (keyPair) {
return OAUTH3.crypto.thumbprintJwk(keyPair.publicKey).then(function (kid) { return OAUTH3.crypto.thumbprintJwk(keyPair.publicKey).then(function (kid) {
keyPair.privateKey.alg = keyPair.publicKey.alg = 'ES256'; keyPair.privateKey.alg = keyPair.publicKey.alg = 'ES256';
keyPair.privateKey.kid = keyPair.publicKey.kid = kid; keyPair.privateKey.kid = keyPair.publicKey.kid = kid;
return keyPair; return keyPair;
}); });
}); });
};
OAUTH3.crypto.encryptKeyPair = function (keyPair, password) {
var saltProm = OAUTH3.crypto.core.randomBytes(16);
var kekProm = saltProm.then(function (salt) {
return OAUTH3.crypto.core.pbkdf2(password, salt);
});
return OAUTH3.PromiseA.all([ return OAUTH3.PromiseA.all([
kekProm kekProm
, ecdsaProm
, saltProm , saltProm
, OAUTH3.crypto.core.randomBytes(16)
, OAUTH3.crypto.core.randomBytes(12) , OAUTH3.crypto.core.randomBytes(12)
, OAUTH3.crypto.core.randomBytes(12) , ]).then(function (results) {
]).then(function (results) {
var kek = results[0]; var kek = results[0];
var keyPair = results[1]; var salt = results[1];
var salt = results[2]; var ecdsaIv = results[2];
var userSecret = results[3];
var ecdsaIv = results[4];
var secretIv = results[5];
return OAUTH3.PromiseA.all([ var privKeyBuf = OAUTH3._binStr.binStrToBuffer(JSON.stringify(keyPair.privateKey));
OAUTH3.crypto.core.encrypt(kek, ecdsaIv, OAUTH3._binStr.binStrToBuffer(JSON.stringify(keyPair.privateKey))) return OAUTH3.crypto.core.encrypt(kek, ecdsaIv, privKeyBuf).then(function (encrypted) {
, OAUTH3.crypto.core.encrypt(kek, secretIv, userSecret)
])
.then(function (encrypted) {
return { return {
publicKey: keyPair.publicKey publicKey: keyPair.publicKey
, privateKey: OAUTH3._base64.bufferToUrlSafe(encrypted[0]) , privateKey: OAUTH3._base64.bufferToUrlSafe(encrypted)
, userSecret: OAUTH3._base64.bufferToUrlSafe(encrypted[1])
, salt: OAUTH3._base64.bufferToUrlSafe(salt) , salt: OAUTH3._base64.bufferToUrlSafe(salt)
, ecdsaIv: OAUTH3._base64.bufferToUrlSafe(ecdsaIv) , ecdsaIv: OAUTH3._base64.bufferToUrlSafe(ecdsaIv)
, secretIv: OAUTH3._base64.bufferToUrlSafe(secretIv) , };
};
}); });
}); });
}; };
OAUTH3.crypto._decryptKey = function (ppid, storedObj) { OAUTH3.crypto.decryptKeyPair = function (storedObj, password) {
var salt = OAUTH3._base64.urlSafeToBuffer(storedObj.salt); var salt = OAUTH3._base64.urlSafeToBuffer(storedObj.salt);
var encJwk = OAUTH3._base64.urlSafeToBuffer(storedObj.privateKey); var encJwk = OAUTH3._base64.urlSafeToBuffer(storedObj.privateKey);
var iv = OAUTH3._base64.urlSafeToBuffer(storedObj.ecdsaIv); var iv = OAUTH3._base64.urlSafeToBuffer(storedObj.ecdsaIv);
return OAUTH3.crypto.core.pbkdf2(ppid, salt) return OAUTH3.crypto.core.pbkdf2(password, salt)
.then(function (key) { .then(function (key) {
return OAUTH3.crypto.core.decrypt(key, iv, encJwk); return OAUTH3.crypto.core.decrypt(key, iv, encJwk);
}) })
.then(OAUTH3._binStr.bufferToBinStr) .then(OAUTH3._binStr.bufferToBinStr)
.then(JSON.parse); .then(JSON.parse)
}; .then(function (privateKey) {
return {
OAUTH3.crypto._getKey = function (ppid) { privateKey: privateKey
return OAUTH3.crypto.core.sha256(OAUTH3._binStr.binStrToBuffer(ppid)) , publicKey: storedObj.publicKey
.then(function (hash) { , };
var name = 'kek-' + OAUTH3._base64.bufferToUrlSafe(hash);
var promise;
if (window.localStorage.getItem(name) === null) {
promise = OAUTH3.crypto._createKey(ppid).then(function (key) {
window.localStorage.setItem(name, JSON.stringify(key));
return key;
});
} else {
promise = OAUTH3.PromiseA.resolve(JSON.parse(window.localStorage.getItem(name)));
}
return promise.then(function (storedObj) {
return OAUTH3.crypto._decryptKey(ppid, storedObj);
}); });
});
};
OAUTH3.crypto._signPayload = function (payload) {
return OAUTH3.crypto._getKey('some PPID').then(function (key) {
var header = {type: 'JWT', alg: key.alg, kid: key.kid};
var input = [
OAUTH3._base64.encodeUrlSafe(JSON.stringify(header, null))
, OAUTH3._base64.encodeUrlSafe(JSON.stringify(payload, null))
].join('.');
return OAUTH3.crypto.core.sign(key, OAUTH3._binStr.binStrToBuffer(input))
.then(OAUTH3._base64.bufferToUrlSafe)
.then(function (signature) {
return input + '.' + signature;
});
});
}; };
}('undefined' !== typeof exports ? exports : window)); }('undefined' !== typeof exports ? exports : window));