diff --git a/crypto.html b/crypto.html index e17e4ccf..4dd686a2 100644 --- a/crypto.html +++ b/crypto.html @@ -106,7 +106,7 @@ (function () { // Constants/Enums - var encTypes = { + const encTypes = { AesCbc256_B64: 0, AesCbc128_HmacSha256_B64: 1, AesCbc256_HmacSha256_B64: 2, @@ -118,80 +118,86 @@ // Classes - var Cipher = function (encType, iv, ct, mac) { - if (!arguments.length) { - this.encType = null; - this.iv = null; - this.ct = null; + class Cipher { + constructor(encType, iv, ct, mac) { + if (!arguments.length) { + this.encType = null; + this.iv = null; + this.ct = null; + this.mac = null; + this.string = null; + return; + } + + this.encType = encType; + this.iv = iv; + this.ct = ct; + this.string = encType + '.' + iv.b64 + '|' + ct.b64; + this.mac = null; - this.string = null; - return; - } - - this.encType = encType; - this.iv = iv; - this.ct = ct; - this.string = encType + '.' + iv.b64 + '|' + ct.b64; - - this.mac = null; - if (mac) { - this.mac = mac; - this.string += ('|' + mac.b64); + if (mac) { + this.mac = mac; + this.string += ('|' + mac.b64); + } } } - var ByteData = function (buf) { - if (!arguments.length) { - this.arr = null; - this.b64 = null; - return; - } + class ByteData { + constructor(buf) { + if (!arguments.length) { + this.arr = null; + this.b64 = null; + return; + } - this.arr = new Uint8Array(buf); - this.b64 = toB64(buf); + this.arr = new Uint8Array(buf); + this.b64 = toB64(buf); + } } - var SymmetricCryptoKey = function (buf) { - if (!arguments.length) { - this.key = new ByteData(); - this.encKey = new ByteData(); - this.macKey = new ByteData(); - return; + class SymmetricCryptoKey { + constructor(buf) { + if (!arguments.length) { + this.key = new ByteData(); + this.encKey = new ByteData(); + this.macKey = new ByteData(); + return; + } + + this.key = new ByteData(buf); + + // First half + let encKey = this.key.arr.slice(0, this.key.arr.length / 2).buffer; + this.encKey = new ByteData(encKey); + + // Second half + let macKey = this.key.arr.slice(this.key.arr.length / 2).buffer; + this.macKey = new ByteData(macKey); } - - this.key = new ByteData(buf); - - // First half - var encKey = this.key.arr.slice(0, this.key.arr.length / 2).buffer; - this.encKey = new ByteData(encKey); - - // Second half - var macKey = this.key.arr.slice(this.key.arr.length / 2).buffer; - this.macKey = new ByteData(macKey); } // Helpers function fromUtf8(str) { - var strUtf8 = unescape(encodeURIComponent(str)), + let strUtf8 = unescape(encodeURIComponent(str)), bytes = new Uint8Array(strUtf8.length); - for (var i = 0; i < strUtf8.length; i++) { + for (let i = 0; i < strUtf8.length; i++) { bytes[i] = strUtf8.charCodeAt(i); } return bytes.buffer; } function toUtf8(buf) { - var bytes = new Uint8Array(buf), + let bytes = new Uint8Array(buf), encodedString = String.fromCharCode.apply(null, bytes), decodedString = decodeURIComponent(escape(encodedString)); return decodedString; } function toB64(buf) { - var binary = '', + let binary = '', bytes = new Uint8Array(buf); - for (var i = 0; i < bytes.byteLength; i++) { + for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]); } return window.btoa(binary); @@ -204,80 +210,80 @@ // Crypto function pbkdf2(password, salt, iterations, length) { - var importAlg = { + let importAlg = { name: 'PBKDF2' }; - var deriveAlg = { + let deriveAlg = { name: 'PBKDF2', salt: salt, iterations: iterations, hash: { name: 'SHA-256' } }; - var aesOptions = { + let aesOptions = { name: 'AES-CBC', length: 256 }; return window.crypto.subtle.importKey('raw', password, importAlg, false, ['deriveKey']) - .then(function (importedKey) { + .then((importedKey) => { return window.crypto.subtle.deriveKey(deriveAlg, importedKey, aesOptions, true, ['encrypt']); - }).then(function (derivedKey) { + }).then((derivedKey) => { return window.crypto.subtle.exportKey('raw', derivedKey); - }).then(function (exportedKey) { + }).then((exportedKey) => { return new ByteData(exportedKey); - }).catch(function (err) { + }).catch((err) => { console.error(err); }); } function aesEncrypt(data, encKey, macKey) { - var keyOptions = { + let keyOptions = { name: 'AES-CBC' }; - var encOptions = { + let encOptions = { name: 'AES-CBC', iv: new Uint8Array(16) }; window.crypto.getRandomValues(encOptions.iv); - var ivData = new ByteData(encOptions.iv.buffer); + let ivData = new ByteData(encOptions.iv.buffer); - var ctData, macData; + let ctData, macData; return window.crypto.subtle.importKey('raw', encKey.arr.buffer, keyOptions, false, ['encrypt']) - .then(function (importedKey) { + .then((importedKey) => { return window.crypto.subtle.encrypt(encOptions, importedKey, data); - }).then(function (encryptedBuffer) { + }).then((encryptedBuffer) => { ctData = new ByteData(encryptedBuffer); if (!macKey) { return null; } - var dataForMac = buildDataForMac(ivData.arr, ctData.arr); + let dataForMac = buildDataForMac(ivData.arr, ctData.arr); return computeMac(dataForMac.buffer, macKey.arr.buffer); - }).then(function (macBuffer) { - var type = encTypes.AesCbc256_B64; + }).then((macBuffer) => { + let type = encTypes.AesCbc256_B64; if (macBuffer) { type = encTypes.AesCbc256_HmacSha256_B64; macData = new ByteData(macBuffer); } return new Cipher(type, ivData, ctData, macData); - }).catch(function (err) { + }).catch((err) => { console.error(err); }); } function aesDecrypt(cipher, encKey, macKey) { - var keyOptions = { + let keyOptions = { name: 'AES-CBC' }; - var decOptions = { + let decOptions = { name: 'AES-CBC', iv: cipher.iv.arr.buffer }; - var checkMacPromise = new Promise(function (resolve) { + let checkMacPromise = new Promise((resolve) => { if (cipher.encType == encTypes.AesCbc256_B64) { resolve(false); return; @@ -289,65 +295,65 @@ }); return checkMacPromise - .then(function (checkMac) { + .then((checkMac) => { if (!checkMac) { return null; } - var dataForMac = buildDataForMac(cipher.iv.arr, cipher.ct.arr); + let dataForMac = buildDataForMac(cipher.iv.arr, cipher.ct.arr); return computeMac(dataForMac.buffer, macKey.arr.buffer) }) - .then(function (macBuffer) { + .then((macBuffer) => { if (!macBuffer) { return true; } return macsEqual(cipher.mac.arr.buffer, macBuffer, macKey.arr.buffer); - }).then(function (macsMatch) { + }).then((macsMatch) => { if (macsMatch === false) { throw 'MAC check failed.'; } return window.crypto.subtle.importKey('raw', encKey.arr.buffer, keyOptions, false, ['decrypt']); - }).then(function (importedKey) { + }).then((importedKey) => { return window.crypto.subtle.decrypt(decOptions, importedKey, cipher.ct.arr.buffer); - }).catch(function (err) { + }).catch((err) => { console.error(err); }); } function computeMac(data, key) { - var alg = { + let alg = { name: 'HMAC', hash: { name: 'SHA-256' } }; return window.crypto.subtle.importKey('raw', key, alg, false, ['sign']) - .then(function (importedKey) { + .then((importedKey) => { return window.crypto.subtle.sign(alg, importedKey, data); }); } function macsEqual(mac1Data, mac2Data, key) { - var alg = { + let alg = { name: 'HMAC', hash: { name: 'SHA-256' } }; - var mac1, importedMacKey; + let mac1, importedMacKey; return window.crypto.subtle.importKey('raw', key, alg, false, ['sign']) - .then(function (importedKey) { + .then((importedKey) => { importedMacKey = importedKey; return window.crypto.subtle.sign(alg, importedMacKey, mac1Data); - }).then(function (mac) { + }).then((mac) => { mac1 = mac; return window.crypto.subtle.sign(alg, importedMacKey, mac2Data); - }).then(function (mac2) { + }).then((mac2) => { if (mac1.byteLength !== mac2.byteLength) { return false; } - var arr1 = new Uint8Array(mac1); - var arr2 = new Uint8Array(mac2); + let arr1 = new Uint8Array(mac1); + let arr2 = new Uint8Array(mac2); - for (var i = 0; i < arr2.length; i++) { + for (let i = 0; i < arr2.length; i++) { if (arr1[i] !== arr2[i]) { return false; } @@ -358,41 +364,41 @@ } function buildDataForMac(ivArr, ctArr) { - var dataForMac = new Uint8Array(ivArr.length + ctArr.length); + let dataForMac = new Uint8Array(ivArr.length + ctArr.length); dataForMac.set(ivArr, 0); dataForMac.set(ctArr, ivArr.length); return dataForMac; } function generateRsaKeypair() { - var rsaOptions = { + let rsaOptions = { name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537 hash: { name: 'SHA-1' } }; - var keypair, publicKey; + let keypair, publicKey; return window.crypto.subtle.generateKey(rsaOptions, true, ['encrypt', 'decrypt']) - .then(function (generatedKey) { + .then((generatedKey) => { keypair = generatedKey; return window.crypto.subtle.exportKey('spki', keypair.publicKey); - }).then(function (exportedKey) { + }).then((exportedKey) => { publicKey = new ByteData(exportedKey); return window.crypto.subtle.exportKey('pkcs8', keypair.privateKey); - }).then(function (exportedKey) { + }).then((exportedKey) => { return { publicKey: publicKey, privateKey: new ByteData(exportedKey) }; - }).catch(function (err) { + }).catch((err) => { console.error(err); }); } // App - var vm = new Vue({ + let vm = new Vue({ el: '#app', data: { email: '', @@ -428,27 +434,27 @@ }, watch: { masterKey: function (newMasterKey) { - var self = this; + let self = this; if (!newMasterKey || !newMasterKey.arr || !self.masterPasswordBuffer) { return new ByteData(); } pbkdf2(newMasterKey.arr.buffer, self.masterPasswordBuffer, 1, 256) - .then(function (masterKeyHash) { + .then((masterKeyHash) => { self.masterKeyHash = masterKeyHash; }); } }, methods: { generateKeys: function () { - var self = this; + let self = this; - var symKey = new Uint8Array(512 / 8); + let symKey = new Uint8Array(512 / 8); window.crypto.getRandomValues(symKey); self.symKey = new SymmetricCryptoKey(symKey); - generateRsaKeypair().then(function (keypair) { + generateRsaKeypair().then((keypair) => { self.publicKey = keypair.publicKey; self.privateKey = keypair.privateKey; }); @@ -456,30 +462,30 @@ } }); - vm.$watch(function () { + vm.$watch(() => { return { masterPassword: vm.masterPasswordBuffer, email: vm.emailBuffer, iterations: vm.pbkdf2Iterations }; - }, function (newVal, oldVal) { + }, (newVal, oldVal) => { if (!newVal.masterPassword || !newVal.email || !newVal.iterations || newVal.iterations < 1) { vm.masterKey = new ByteData(); return; } pbkdf2(newVal.masterPassword, newVal.email, newVal.iterations, 256) - .then(function (masterKey) { + .then((masterKey) => { vm.masterKey = masterKey; }); }); - vm.$watch(function () { + vm.$watch(() => { return { symKey: vm.symKey, secret: vm.secretBuffer }; - }, function (newVal, oldVal) { + }, (newVal, oldVal) => { if (!newVal.symKey || !newVal.secret) { vm.protectedSecret = new Cipher(); vm.unprotectedSecret = ''; @@ -487,50 +493,50 @@ } aesEncrypt(newVal.secret, newVal.symKey.encKey, newVal.symKey.macKey) - .then(function (cipher) { + .then((cipher) => { vm.protectedSecret = cipher; return aesDecrypt(vm.protectedSecret, newVal.symKey.encKey, newVal.symKey.macKey); - }).then(function (secret) { + }).then((secret) => { vm.unprotectedSecret = toUtf8(secret); }); }); - vm.$watch(function () { + vm.$watch(() => { return { masterKey: vm.masterKey, symKey: vm.symKey }; - }, function (newVal, oldVal) { + }, (newVal, oldVal) => { if (!newVal.masterKey || !newVal.masterKey.arr || !newVal.symKey || !newVal.symKey.key) { vm.protectedSymKey = new Cipher(); return; } aesEncrypt(newVal.symKey.key.arr, newVal.masterKey, null) - .then(function (cipher) { + .then((cipher) => { vm.protectedSymKey = cipher; return aesDecrypt(vm.protectedSymKey, newVal.masterKey, null); - }).then(function (unprotectedSymKey) { + }).then((unprotectedSymKey) => { vm.unprotectedSymKey = new ByteData(unprotectedSymKey); }); }); - vm.$watch(function () { + vm.$watch(() => { return { symKey: vm.symKey, privateKey: vm.privateKey }; - }, function (newVal, oldVal) { + }, (newVal, oldVal) => { if (!newVal.symKey || !newVal.symKey.key || !newVal.privateKey || !newVal.privateKey.arr) { vm.protectedPrivateKey = new Cipher(); return; } aesEncrypt(newVal.privateKey.arr, newVal.symKey.encKey, newVal.symKey.macKey) - .then(function (cipher) { + .then((cipher) => { vm.protectedPrivateKey = cipher; return aesDecrypt(vm.protectedPrivateKey, newVal.symKey.encKey, newVal.symKey.macKey); - }).then(function (unprotectedPrivateKey) { + }).then((unprotectedPrivateKey) => { vm.unprotectedPrivateKey = new ByteData(unprotectedPrivateKey); }); });