From 01580dd6b321b7e7b85137a581e2b736df2ac128 Mon Sep 17 00:00:00 2001 From: tigerbot Date: Mon, 20 Mar 2017 16:11:14 -0600 Subject: [PATCH] implemented dynamic loading of fallback crypto functions --- .../{crypto-index.js => crypto.fallback.js} | 55 +++- gulpfile.js | 8 +- oauth3.crypto.js | 305 ++++++++++++++++++ oauth3.issuer.mock.js | 145 --------- package.json | 7 +- 5 files changed, 350 insertions(+), 170 deletions(-) rename browserify/{crypto-index.js => crypto.fallback.js} (52%) create mode 100644 oauth3.crypto.js diff --git a/browserify/crypto-index.js b/browserify/crypto.fallback.js similarity index 52% rename from browserify/crypto-index.js rename to browserify/crypto.fallback.js index 652c2ee..311085d 100644 --- a/browserify/crypto-index.js +++ b/browserify/crypto.fallback.js @@ -10,23 +10,25 @@ return createHash('sha256').update(buf).digest(); } - function encrypt(data, password, salt, iv) { + function runPbkdf2(password, salt) { // Derived AES key is 128 bit, and the function takes a size in bytes. - var aesKey = pbkdf2.pbkdf2Sync(password, Buffer(salt), 8192, 16, 'sha256'); - var cipher = aes.createCipheriv('aes-128-gcm', aesKey, Buffer(iv)); + return pbkdf2.pbkdf2Sync(password, Buffer(salt), 8192, 16, 'sha256'); + } + + function encrypt(key, data, iv) { + var cipher = aes.createCipheriv('aes-128-gcm', Buffer(key), Buffer(iv)); return Buffer.concat([cipher.update(Buffer(data)), cipher.final(), cipher.getAuthTag()]); } - function decrypt(data, password, salt, iv) { - var aesKey = pbkdf2.pbkdf2Sync(password, Buffer(salt), 8192, 16, 'sha256'); - var decipher = aes.createDecipheriv('aes-128-gcm', aesKey, Buffer(iv)); + function decrypt(key, data, iv) { + var decipher = aes.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 convertBN(bn) { + function bnToB64(bn) { if (bn.red) { bn = bn.fromRed(); } @@ -39,21 +41,31 @@ key_ops: ['verify'] , kty: 'EC' , crv: 'P-256' - , x: convertBN(key.getPublic().x) - , y: convertBN(key.getPublic().y) + , x: bnToB64(key.getPublic().x) + , y: bnToB64(key.getPublic().y) }; var privJwk = JSON.parse(JSON.stringify(pubJwk)); privJwk.key_ops = ['sign']; - privJwk.d = convertBN(key.getPrivate()); + privJwk.d = bnToB64(key.getPrivate()); return {privateKey: privJwk, publicKey: pubJwk}; } + function bnToBuffer(bn, size) { + var buf = bn.toArrayLike(Buffer); + if (!size || buf.length === size) { + return buf; + } + if (buf > size) { + throw new Error("EC signature number bigger than expected"); + } + return Buffer.concat([Buffer(size-buf.length).fill(0), buf]); + } function sign(jwk, msg) { var key = ec.keyFromPrivate(Buffer(jwk.d, 'base64')); var sig = key.sign(sha256(msg)); - return Buffer.concat([Buffer(sig.r, 'hex'), Buffer(sig.s, 'hex')]); + return Buffer.concat([bnToBuffer(sig.r, 32), bnToBuffer(sig.s, 32)]); } function verify(jwk, msg, signature) { @@ -65,10 +77,19 @@ return key.verify(sha256(msg), sig); } - exports.sha256 = function () { return Promise.resolve(sha256.apply(this, arguments)); }; - exports.encrypt = function () { return Promise.resolve(encrypt.apply(this, arguments)); }; - exports.decrypt = function () { return Promise.resolve(decrypt.apply(this, arguments)); }; - exports.sign = function () { return Promise.resolve(sign.apply(this, arguments)); }; - exports.verify = function () { return Promise.resolve(verify.apply(this, arguments)); }; - exports.genEcdsaKeyPair = function () { return Promise.resolve(genEcdsaKeyPair.apply(this, arguments)); }; + function promiseWrap(func) { + return function() { + var args = arguments; + return new Promise(function (resolve) { + resolve(func.apply(null, args)); + }); + }; + } + exports.sha256 = promiseWrap(sha256); + exports.pbkdf2 = promiseWrap(runPbkdf2); + exports.encrypt = promiseWrap(encrypt); + exports.decrypt = promiseWrap(decrypt); + exports.sign = promiseWrap(sign); + exports.verify = promiseWrap(verify); + exports.genEcdsaKeyPair = promiseWrap(genEcdsaKeyPair); }()); diff --git a/gulpfile.js b/gulpfile.js index f5a8966..44f6e3c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -9,12 +9,12 @@ var rename = require('gulp-rename'); gulp.task('default', function () { - return browserify('./browserify/crypto-index.js', {standalone: 'OAUTH3_crypto'}).bundle() - .pipe(source('browserify/crypto-index.js')) - .pipe(rename('oauth3.crypto.js')) + return browserify('./browserify/crypto.fallback.js', {standalone: 'OAUTH3_crypto_fallback'}).bundle() + .pipe(source('browserify/crypto.fallback.js')) + .pipe(rename('oauth3.crypto.fallback.js')) .pipe(gulp.dest('./')) .pipe(streamify(uglify())) - .pipe(rename('oauth3.crypto.min.js')) + .pipe(rename('oauth3.crypto.fallback.min.js')) .pipe(gulp.dest('./')) ; }); diff --git a/oauth3.crypto.js b/oauth3.crypto.js new file mode 100644 index 0000000..abbaee7 --- /dev/null +++ b/oauth3.crypto.js @@ -0,0 +1,305 @@ +;(function (exports) { +'use strict'; + + var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3; + + var loadFallback = function() { + var prom; + loadFallback = function () { return prom; }; + + prom = new OAUTH3.PromiseA(function (resolve) { + var body = document.getElementsByTagName('body')[0]; + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.onload = resolve; + script.onreadystatechange = function () { + if (this.readyState === 'complete' || this.readyState === 'loaded') { + resolve(); + } + }; + script.src = '/assets/org.oauth3/oauth3.crypto.fallback.js'; + body.appendChild(script); + }); + return prom; + }; + + var webCrypto = {}; + webCrypto.sha256 = function (buf) { + return crypto.subtle.digest({name: 'SHA-256'}, buf); + }; + + webCrypto.pbkdf2 = function (password, salt) { + return crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(password), {name: 'PBKDF2'}, false, ['deriveKey']) + .then(function (key) { + var opts = {name: 'PBKDF2', salt: salt, iterations: 8192, hash: {name: 'SHA-256'}}; + return crypto.subtle.deriveKey(opts, key, {name: 'AES-GCM', length: 128}, true, ['encrypt', 'decrypt']); + }) + .then(function (key) { + return crypto.subtle.exportKey('raw', key); + }); + }; + + webCrypto.encrypt = function (rawKey, data, iv) { + return crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['encrypt']) + .then(function (key) { + return crypto.subtle.encrypt({name: 'AES-GCM', iv: iv}, key, data); + }); + }; + webCrypto.decrypt = function (rawKey, data, iv) { + return crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['decrypt']) + .then(function (key) { + return crypto.subtle.decrypt({name: 'AES-GCM', iv: iv}, key, data); + }); + }; + + webCrypto.genEcdsaKeyPair = function () { + return crypto.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, ['sign', 'verify']) + .then(function (keyPair) { + return OAUTH3.PromiseA.all([ + crypto.subtle.exportKey('jwk', keyPair.privateKey) + , crypto.subtle.exportKey('jwk', keyPair.publicKey) + ]); + }).then(function (jwkPair) { + return { privateKey: jwkPair[0], publicKey: jwkPair[1] }; + }); + }; + + webCrypto.sign = function (jwk, msg) { + return crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['sign']) + .then(function (key) { + return crypto.subtle.sign({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, msg); + }) + .then(function (sig) { + return new Uint8Array(sig); + }); + }; + webCrypto.verify = function (jwk, msg, signature) { + // 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. + if (jwk.hasOwnProperty('d') || jwk.hasOwnProperty('key_ops')) { + jwk = JSON.parse(JSON.stringify(jwk)); + delete jwk.d; + delete jwk.key_ops; + } + + return crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['verify']) + .then(function (key) { + return crypto.subtle.verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, signature, msg); + }); + }; + + OAUTH3.crypto = {}; + OAUTH3.crypto.core = {}; + function checkWebCrypto() { + function checkException(name, func) { + new OAUTH3.PromiseA(function (resolve) { resolve(func()); }) + .then(function () { + OAUTH3.crypto.core[name] = webCrypto[name]; + }) + .catch(function (err) { + console.warn('error with WebCrypto', name, '- using fallback', err); + loadFallback().then(function () { + OAUTH3.crypto.core[name] = OAUTH3_crypto_fallback[name]; + }); + }); + } + function checkResult(name, expected, func) { + checkException(name, function () { + return func() + .then(function (result) { + if (typeof expected === typeof result) { + return result; + } + return OAUTH3._base64.bufferToUrlSafe(result); + }) + .then(function (result) { + if (result !== 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 dataBuf = OAUTH3._base64.urlSafeToBuffer('1234567890abcdefghijklmn'); + var keyBuf = OAUTH3._base64.urlSafeToBuffer('l_Aeoqk6ePjwjCYrlHrgrg'); + var encBuf = OAUTH3._base64.urlSafeToBuffer('Ji_gEtcNElUONSR4Mf9S75davXjh_6-oQN9AgO5UF8rERw'); + checkResult('sha256', 'BwMveUm2V1axuERvUoxM4dScgNl9yKhER9a6p80GXj4', function () { + return webCrypto.sha256(dataBuf); + }); + checkResult('pbkdf2', OAUTH3._base64.bufferToUrlSafe(keyBuf), function () { + return webCrypto.pbkdf2('password', zeroBuf); + }); + checkResult('encrypt', OAUTH3._base64.bufferToUrlSafe(encBuf), function () { + return webCrypto.encrypt(keyBuf, dataBuf, zeroBuf.slice(0, 12)); + }); + checkResult('decrypt', OAUTH3._base64.bufferToUrlSafe(dataBuf), function () { + return webCrypto.decrypt(keyBuf, encBuf, zeroBuf.slice(0, 12)); + }); + + var jwk = { + kty: "EC" + , crv: "P-256" + , d: "ChXx7ea5YtEltCufA8CVb0lQv3glcCfcSpEgdedgIP0" + , x: "Akt5ZDbytcKS5UQMURvGb_UIMS4qFctDwrX8bX22ato" + , y: "cV7nhpWNT1FeRIbdold4jLtgsEpZBFcNy3p2E5mqvto" + }; + var sig = OAUTH3._base64.urlSafeToBuffer('nc3F8qeP8OXpfqPD9tTcFQg0Wfp37RTAppLPIKE1ZupR_8Aba64hNExwd1dOk802OFQxaECPDZCkKe7WA9RXAg'); + checkResult('verify', true, function() { + return webCrypto.verify(jwk, dataBuf, sig); + }); + // The results of these functions are less predictable, so we can't check their return value. + checkException('genEcdsaKeyPair', function () { + return webCrypto.genEcdsaKeyPair(); + }); + checkException('sign', function () { + return webCrypto.sign(jwk, dataBuf); + }); + } + checkWebCrypto(); + + OAUTH3.crypto.fingerprintJWK = function (jwk) { + var keys; + if (jwk.kty === 'EC') { + keys = ['crv', 'x', 'y']; + } else if (jwk.kty === 'RSA') { + keys = ['e', 'n']; + } else if (jwk.kty === 'oct') { + keys = ['k']; + } else { + return OAUTH3.PromiseA.reject(new Error('invalid JWK key type ' + jwk.kty)); + } + keys.push('kty'); + keys.sort(); + + var missing = keys.filter(function (name) { return !jwk.hasOwnProperty(name); }); + if (missing.length > 0) { + return OAUTH3.PromiseA.reject(new Error('JWK of type '+jwk.kty+' missing fields ' + missing)); + } + + var jwkStr = '{' + keys.map(function (name) { return name+':'+jwk[name]; }).join(',') + '}'; + return window.crypto.subtle.digest({name: 'SHA-256'}, OAUTH3._binStr.binStrToBuffer(jwkStr)) + .then(OAUTH3._base64.bufferToUrlSafe); + }; + + OAUTH3.crypto._createKey = function (ppid) { + var kekPromise, ecdsaPromise, secretPromise; + var salt = window.crypto.getRandomValues(new Uint8Array(16)); + + kekPromise = window.crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(ppid), {name: 'PBKDF2'}, false, ['deriveKey']) + .then(function (key) { + var opts = {name: 'PBKDF2', salt: salt, iterations: 8192, hash: {name: 'SHA-256'}}; + return window.crypto.subtle.deriveKey(opts, key, {name: 'AES-GCM', length: 128}, false, ['encrypt']); + }); + + ecdsaPromise = window.crypto.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, ['sign', 'verify']) + .then(function (keyPair) { + function tweakJWK(jwk) { + return OAUTH3.crypto.fingerprintJWK(jwk).then(function (kid) { + delete jwk.ext; + jwk.alg = 'ES256'; + jwk.kid = kid; + return jwk; + }); + } + return OAUTH3.PromiseA.all([ + window.crypto.subtle.exportKey('jwk', keyPair.privateKey).then(tweakJWK) + , window.crypto.subtle.exportKey('jwk', keyPair.publicKey).then(tweakJWK) + ]).then(function (jwkPair) { + return { + privateKey: jwkPair[0] + , publicKey: jwkPair[1] + }; + }); + }); + + secretPromise = window.crypto.subtle.generateKey({name: 'AES-GCM', length: 128}, true, ['encrypt', 'decrypt']) + .then(function (key) { + return window.crypto.subtle.exportKey('jwk', key); + }); + + return OAUTH3.PromiseA.all([kekPromise, ecdsaPromise, secretPromise]).then(function (keys) { + var ecdsaJwk = OAUTH3._binStr.binStrToBuffer(JSON.stringify(keys[1].privateKey)); + var secretJwk = OAUTH3._binStr.binStrToBuffer(JSON.stringify(keys[2])); + var ecdsaIv = window.crypto.getRandomValues(new Uint8Array(12)); + var secretIv = window.crypto.getRandomValues(new Uint8Array(12)); + + return OAUTH3.PromiseA.all([ + window.crypto.subtle.encrypt({name: 'AES-GCM', iv: ecdsaIv}, keys[0], ecdsaJwk) + , window.crypto.subtle.encrypt({name: 'AES-GCM', iv: secretIv}, keys[0], secretJwk) + ]) + .then(function (encrypted) { + return { + publicKey: keys[1].publicKey + , privateKey: OAUTH3._base64.bufferToUrlSafe(encrypted[0]) + , userSecret: OAUTH3._base64.bufferToUrlSafe(encrypted[1]) + , salt: OAUTH3._base64.bufferToUrlSafe(salt) + , ecdsaIv: OAUTH3._base64.bufferToUrlSafe(ecdsaIv) + , secretIv: OAUTH3._base64.bufferToUrlSafe(secretIv) + }; + }); + }); + }; + + OAUTH3.crypto._decryptKey = function (ppid, storedObj) { + var salt = OAUTH3._base64.urlSafeToBuffer(storedObj.salt); + var encJwk = OAUTH3._base64.urlSafeToBuffer(storedObj.privateKey); + var iv = OAUTH3._base64.urlSafeToBuffer(storedObj.ecdsaIv); + + return window.crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(ppid), {name: 'PBKDF2'}, false, ['deriveKey']) + .then(function (key) { + var opts = {name: 'PBKDF2', salt: salt, iterations: 8192, hash: {name: 'SHA-256'}}; + return window.crypto.subtle.deriveKey(opts, key, {name: 'AES-GCM', length: 128}, false, ['decrypt']); + }) + .then(function (key) { + return window.crypto.subtle.decrypt({name: 'AES-GCM', iv: iv}, key, encJwk); + }) + .then(OAUTH3._binStr.bufferToBinStr) + .then(JSON.parse) + .then(function (jwk) { + return window.crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['sign']) + .then(function (key) { + key.kid = jwk.kid; + key.alg = jwk.alg; + return key; + }); + }); + }; + + OAUTH3.crypto._getKey = function (ppid) { + return window.crypto.subtle.digest({name: 'SHA-256'}, OAUTH3._binStr.binStrToBuffer(ppid)) + .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 window.crypto.subtle.sign({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, OAUTH3._binStr.binStrToBuffer(input)) + .then(function (signature) { + return input + '.' + OAUTH3._base64.bufferToUrlSafe(signature); + }); + }); + }; + +}('undefined' !== typeof exports ? exports : window)); diff --git a/oauth3.issuer.mock.js b/oauth3.issuer.mock.js index d51f4b6..e054edb 100644 --- a/oauth3.issuer.mock.js +++ b/oauth3.issuer.mock.js @@ -3,151 +3,6 @@ var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3; - OAUTH3.crypto = {}; - OAUTH3.crypto.fingerprintJWK = function (jwk) { - var keys; - if (jwk.kty === 'EC') { - keys = ['crv', 'x', 'y']; - } else if (jwk.kty === 'RSA') { - keys = ['e', 'n']; - } else if (jwk.kty === 'oct') { - keys = ['k']; - } else { - return OAUTH3.PromiseA.reject(new Error('invalid JWK key type ' + jwk.kty)); - } - keys.push('kty'); - keys.sort(); - - var missing = keys.filter(function (name) { return !jwk.hasOwnProperty(name); }); - if (missing.length > 0) { - return OAUTH3.PromiseA.reject(new Error('JWK of type '+jwk.kty+' missing fields ' + missing)); - } - - var jwkStr = '{' + keys.map(function (name) { return name+':'+jwk[name]; }).join(',') + '}'; - return window.crypto.subtle.digest({name: 'SHA-256'}, OAUTH3._binStr.binStrToBuffer(jwkStr)) - .then(OAUTH3._base64.bufferToUrlSafe); - }; - - OAUTH3.crypto._createKey = function (ppid) { - var kekPromise, ecdsaPromise, secretPromise; - var salt = window.crypto.getRandomValues(new Uint8Array(16)); - - kekPromise = window.crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(ppid), {name: 'PBKDF2'}, false, ['deriveKey']) - .then(function (key) { - var opts = {name: 'PBKDF2', salt: salt, iterations: 8192, hash: {name: 'SHA-256'}}; - return window.crypto.subtle.deriveKey(opts, key, {name: 'AES-GCM', length: 128}, false, ['encrypt']); - }); - - ecdsaPromise = window.crypto.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, ['sign', 'verify']) - .then(function (keyPair) { - function tweakJWK(jwk) { - return OAUTH3.crypto.fingerprintJWK(jwk).then(function (kid) { - delete jwk.ext; - jwk.alg = 'ES256'; - jwk.kid = kid; - return jwk; - }); - } - return OAUTH3.PromiseA.all([ - window.crypto.subtle.exportKey('jwk', keyPair.privateKey).then(tweakJWK) - , window.crypto.subtle.exportKey('jwk', keyPair.publicKey).then(tweakJWK) - ]).then(function (jwkPair) { - return { - privateKey: jwkPair[0] - , publicKey: jwkPair[1] - }; - }); - }); - - secretPromise = window.crypto.subtle.generateKey({name: 'AES-GCM', length: 128}, true, ['encrypt', 'decrypt']) - .then(function (key) { - return window.crypto.subtle.exportKey('jwk', key); - }); - - return OAUTH3.PromiseA.all([kekPromise, ecdsaPromise, secretPromise]).then(function (keys) { - var ecdsaJwk = OAUTH3._binStr.binStrToBuffer(JSON.stringify(keys[1].privateKey)); - var secretJwk = OAUTH3._binStr.binStrToBuffer(JSON.stringify(keys[2])); - var ecdsaIv = window.crypto.getRandomValues(new Uint8Array(12)); - var secretIv = window.crypto.getRandomValues(new Uint8Array(12)); - - return OAUTH3.PromiseA.all([ - window.crypto.subtle.encrypt({name: 'AES-GCM', iv: ecdsaIv}, keys[0], ecdsaJwk) - , window.crypto.subtle.encrypt({name: 'AES-GCM', iv: secretIv}, keys[0], secretJwk) - ]) - .then(function (encrypted) { - return { - publicKey: keys[1].publicKey - , privateKey: OAUTH3._base64.bufferToUrlSafe(encrypted[0]) - , userSecret: OAUTH3._base64.bufferToUrlSafe(encrypted[1]) - , salt: OAUTH3._base64.bufferToUrlSafe(salt) - , ecdsaIv: OAUTH3._base64.bufferToUrlSafe(ecdsaIv) - , secretIv: OAUTH3._base64.bufferToUrlSafe(secretIv) - }; - }); - }); - }; - - OAUTH3.crypto._decryptKey = function (ppid, storedObj) { - var salt = OAUTH3._base64.urlSafeToBuffer(storedObj.salt); - var encJwk = OAUTH3._base64.urlSafeToBuffer(storedObj.privateKey); - var iv = OAUTH3._base64.urlSafeToBuffer(storedObj.ecdsaIv); - - return window.crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(ppid), {name: 'PBKDF2'}, false, ['deriveKey']) - .then(function (key) { - var opts = {name: 'PBKDF2', salt: salt, iterations: 8192, hash: {name: 'SHA-256'}}; - return window.crypto.subtle.deriveKey(opts, key, {name: 'AES-GCM', length: 128}, false, ['decrypt']); - }) - .then(function (key) { - return window.crypto.subtle.decrypt({name: 'AES-GCM', iv: iv}, key, encJwk); - }) - .then(OAUTH3._binStr.bufferToBinStr) - .then(JSON.parse) - .then(function (jwk) { - return window.crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['sign']) - .then(function (key) { - key.kid = jwk.kid; - key.alg = jwk.alg; - return key; - }); - }); - }; - - OAUTH3.crypto._getKey = function (ppid) { - return window.crypto.subtle.digest({name: 'SHA-256'}, OAUTH3._binStr.binStrToBuffer(ppid)) - .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 window.crypto.subtle.sign({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, OAUTH3._binStr.binStrToBuffer(input)) - .then(function (signature) { - return input + '.' + OAUTH3._base64.bufferToUrlSafe(signature); - }); - }); - }; - OAUTH3.authn.resourceOwnerPassword = OAUTH3.authz.resourceOwnerPassword = function (directive, opts) { var providerUri = directive.issuer; diff --git a/package.json b/package.json index 192f7b7..cc8c040 100644 --- a/package.json +++ b/package.json @@ -8,18 +8,17 @@ "install": "./node_modules/.bin/gulp" }, "devDependencies": { - "atob": "^2.0.3", - "browserify": "^14.1.0", "browserify-aes": "^1.0.6", - "btoa": "^1.1.2", "create-hash": "^1.1.2", "elliptic": "^6.4.0", + "pbkdf2": "^3.0.9", + + "browserify": "^14.1.0", "gulp": "^3.9.1", "gulp-cli": "^1.2.2", "gulp-rename": "^1.2.2", "gulp-streamify": "^1.0.2", "gulp-uglify": "^2.1.0", - "pbkdf2": "^3.0.9", "vinyl-source-stream": "^1.1.0" } }