mirror of
https://github.com/bitwarden/help
synced 2026-01-10 12:33:33 +00:00
es2015 syntax
This commit is contained in:
230
crypto.html
230
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);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user