OAUTH3.crypto - defer calls to asynchronously loaded methods.

This commit is contained in:
John Shaver 2017-11-13 13:09:58 -08:00
parent c77d280b00
commit a4b7833989
1 changed files with 48 additions and 8 deletions

View File

@ -8,6 +8,9 @@
OAUTH3.crypto.core = require('./oauth3.node.crypto'); OAUTH3.crypto.core = require('./oauth3.node.crypto');
} catch (error) { } catch (error) {
OAUTH3.crypto.core = {}; OAUTH3.crypto.core = {};
OAUTH3.crypto.core.ready = false;
var finishBeforeReady = [];
var deferedCalls = [];
// We don't currently have a fallback method for this function, so we assign // We don't currently have a fallback method for this function, so we assign
// it directly to the core object instead of the webCrypto object. // it directly to the core object instead of the webCrypto object.
@ -17,10 +20,31 @@
}; };
var webCrypto = {}; var webCrypto = {};
var deferCryptoCall = function(name) {
return function() {
var args = arguments;
return new OAUTH3.PromiseA(function(resolve, reject) {
deferedCalls.push(function(){
try {
webCrypto[name].apply(webCrypto, args)
.then(function(result){
resolve(result);
});
} catch(e) {
reject(e);
}
});
});
};
};
OAUTH3.crypto.core.sha256 = deferCryptoCall("sha256");
webCrypto.sha256 = function (buf) { webCrypto.sha256 = function (buf) {
return OAUTH3._browser.window.crypto.subtle.digest({name: 'SHA-256'}, buf); return OAUTH3._browser.window.crypto.subtle.digest({name: 'SHA-256'}, buf);
}; };
OAUTH3.crypto.core.pbkdf2 = deferCryptoCall("pbkdf2");
webCrypto.pbkdf2 = function (password, salt) { webCrypto.pbkdf2 = function (password, salt) {
return OAUTH3._browser.window.crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(password), {name: 'PBKDF2'}, false, ['deriveKey']) return OAUTH3._browser.window.crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(password), {name: 'PBKDF2'}, false, ['deriveKey'])
.then(function (key) { .then(function (key) {
@ -32,12 +56,15 @@
}); });
}; };
OAUTH3.crypto.core.encrypt = deferCryptoCall("encrypt");
webCrypto.encrypt = function (rawKey, iv, data) { webCrypto.encrypt = function (rawKey, iv, data) {
return OAUTH3._browser.window.crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['encrypt']) return OAUTH3._browser.window.crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['encrypt'])
.then(function (key) { .then(function (key) {
return OAUTH3._browser.window.crypto.subtle.encrypt({name: 'AES-GCM', iv: iv}, key, data); return OAUTH3._browser.window.crypto.subtle.encrypt({name: 'AES-GCM', iv: iv}, key, data);
}); });
}; };
OAUTH3.crypto.core.decrypt = deferCryptoCall("decrypt");
webCrypto.decrypt = function (rawKey, iv, data) { webCrypto.decrypt = function (rawKey, iv, data) {
return OAUTH3._browser.window.crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['decrypt']) return OAUTH3._browser.window.crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['decrypt'])
.then(function (key) { .then(function (key) {
@ -45,6 +72,7 @@
}); });
}; };
OAUTH3.crypto.core.genEcdsaKeyPair = deferCryptoCall("genEcdsaKeyPair");
webCrypto.genEcdsaKeyPair = function () { webCrypto.genEcdsaKeyPair = function () {
return OAUTH3._browser.window.crypto.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, ['sign', 'verify']) return OAUTH3._browser.window.crypto.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, ['sign', 'verify'])
.then(function (keyPair) { .then(function (keyPair) {
@ -57,6 +85,7 @@
}); });
}; };
OAUTH3.crypto.core.sign = deferCryptoCall("sign");
webCrypto.sign = function (jwk, msg) { webCrypto.sign = function (jwk, msg) {
return OAUTH3._browser.window.crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['sign']) return OAUTH3._browser.window.crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['sign'])
.then(function (key) { .then(function (key) {
@ -66,6 +95,8 @@
return new Uint8Array(sig); return new Uint8Array(sig);
}); });
}; };
OAUTH3.crypto.core.verify = deferCryptoCall("verify");
webCrypto.verify = function (jwk, msg, signature) { webCrypto.verify = function (jwk, msg, signature) {
// If the JWK has properties that should only exist on the private key or is missing // If the JWK has properties that should only exist on the private key or is missing
// "verify" in the key_ops, importing in as a public key won't work. // "verify" in the key_ops, importing in as a public key won't work.
@ -103,18 +134,19 @@
return prom; return prom;
}; };
function checkException(name, func) { function checkException(name, func) {
OAUTH3.PromiseA.resolve().then(func) return OAUTH3.PromiseA.resolve().then(func)
.then(function () { .then(function () {
OAUTH3.crypto.core[name] = webCrypto[name]; OAUTH3.crypto.core[name] = webCrypto[name];
}, function (err) { }, function (err) {
console.warn('error with WebCrypto', name, '- using fallback', err); console.warn('error with WebCrypto', name, '- using fallback', err);
loadFallback().then(function () { return loadFallback().then(function () {
OAUTH3.crypto.core[name] = OAUTH3_crypto_fallback[name]; OAUTH3.crypto.core[name] = OAUTH3_crypto_fallback[name];
}); });
}); });
} }
function checkResult(name, expected, func) { function checkResult(name, expected, func) {
checkException(name, function () {
finishBeforeReady.push(checkException(name, function () {
return func() return func()
.then(function (result) { .then(function (result) {
if (typeof expected === typeof result) { if (typeof expected === typeof result) {
@ -127,7 +159,7 @@
throw new Error("result ("+result+") doesn't match expectation ("+expected+")"); throw new Error("result ("+result+") doesn't match expectation ("+expected+")");
} }
}); });
}); }));
} }
var zeroBuf = new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]); var zeroBuf = new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
@ -159,12 +191,20 @@
return webCrypto.verify(jwk, dataBuf, sig); return webCrypto.verify(jwk, dataBuf, sig);
}); });
// The results of these functions are less predictable, so we can't check their return value. // The results of these functions are less predictable, so we can't check their return value.
checkException('genEcdsaKeyPair', function () { finishBeforeReady.push(checkException('genEcdsaKeyPair', function () {
return webCrypto.genEcdsaKeyPair(); return webCrypto.genEcdsaKeyPair();
}); }));
checkException('sign', function () { finishBeforeReady.push(checkException('sign', function () {
return webCrypto.sign(jwk, dataBuf); return webCrypto.sign(jwk, dataBuf);
}); }));
OAUTH3.PromiseA.all(finishBeforeReady)
.then(function(results) {
OAUTH3.crypto.core.ready = true;
deferedCalls.forEach(function(request) {
request();
});
});
} }
checkWebCrypto(); checkWebCrypto();
} }