1
0
mirror of https://github.com/bitwarden/help synced 2025-12-12 22:33:16 +00:00

crypto updates

This commit is contained in:
Kyle Spearrin
2017-10-27 14:27:48 -04:00
parent 1b5b60528a
commit 59a7e07ce5

View File

@@ -53,24 +53,28 @@
</div> </div>
</form> </form>
<h2>User Key</h2> <h2>Master Key</h2>
<pre>{{userKey.b64}}</pre> <pre>{{masterKey.b64}}</pre>
<h2>Master Password Hash</h2> <h2>Master Password Hash</h2>
<pre>{{userKeyHash.b64}}</pre> <pre>{{masterKeyHash.b64}}</pre>
<h2>Generated Symmetric Key Material</h2> <h2>Generated Symmetric Key</h2>
<pre>{{key.key.b64}}</pre> <pre>{{symKey.key.b64}}</pre>
<h3>Encryption Key</h3> <h3>Encryption Key</h3>
<pre>{{key.encKey.b64}}</pre> <pre>{{symKey.encKey.b64}}</pre>
<h3>MAC Key</h3> <h3>MAC Key</h3>
<pre>{{key.macKey.b64}}</pre> <pre>{{symKey.macKey.b64}}</pre>
<h3>Protected Symmetric Key</h3>
<pre>{{protectedSymKey.string}}</pre>
<h2>Generated RSA Keypair</h2> <h2>Generated RSA Keypair</h2>
<h3>Public Key</h3> <h3>Public Key</h3>
<pre>{{publicKey.b64}}</pre> <pre>{{publicKey.b64}}</pre>
<h3>Private Key</h3> <h3>Private Key</h3>
<pre>{{privateKey.b64}}</pre> <pre>{{privateKey.b64}}</pre>
<h3>Protected Private Key</h3>
<pre>{{protectedPrivateKey.string}}</pre>
<button type="button" id="deriveKeys" class="btn btn-primary" v-on:click="generateKeys"> <button type="button" id="deriveKeys" class="btn btn-primary" v-on:click="generateKeys">
<i class="fa fa-refresh"></i> Regenerate Keys <i class="fa fa-refresh"></i> Regenerate Keys
@@ -82,16 +86,16 @@
<form> <form>
<div class="form-group"> <div class="form-group">
<label for="plaintext">Plaintext Value</label> <label for="secret">Secret Value</label>
<textarea id="plaintext" class="form-control" v-model="plaintext"></textarea> <textarea id="secret" class="form-control" v-model="secret"></textarea>
</div> </div>
</form> </form>
<h2>The "Cipher String"</h2> <h2>The "Cipher String"</h2>
<pre>{{cipher.string}}</pre> <pre>{{protectedSecret.string}}</pre>
<h2>Decrypt</h2> <h2>Decrypt</h2>
<textarea class="form-control" v-model="decPlaintext" readonly></textarea> <textarea class="form-control" v-model="unprotectedSecret" readonly></textarea>
</div> </div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
@@ -102,7 +106,7 @@
(function () { (function () {
// Constants/Enums // Constants/Enums
var encType = { var encTypes = {
AesCbc256_B64: 0, AesCbc256_B64: 0,
AesCbc128_HmacSha256_B64: 1, AesCbc128_HmacSha256_B64: 1,
AesCbc256_HmacSha256_B64: 2, AesCbc256_HmacSha256_B64: 2,
@@ -238,7 +242,7 @@
}); });
} }
function aesEncrypt(data, key) { function aesEncrypt(data, encKey, macKey) {
var keyOptions = { var keyOptions = {
name: 'AES-CBC' name: 'AES-CBC'
}; };
@@ -248,25 +252,32 @@
iv: new Uint8Array(16) iv: new Uint8Array(16)
}; };
window.crypto.getRandomValues(encOptions.iv); window.crypto.getRandomValues(encOptions.iv);
var ivData = new ByteData(encOptions.iv.buffer);
var ivData, ctData, macData; var ctData, macData;
return window.crypto.subtle.importKey('raw', key.encKey.arr.buffer, keyOptions, false, ['encrypt']) return window.crypto.subtle.importKey('raw', encKey.arr.buffer, keyOptions, false, ['encrypt'])
.then(function (importedKey) { .then(function (importedKey) {
return window.crypto.subtle.encrypt(encOptions, importedKey, data); return window.crypto.subtle.encrypt(encOptions, importedKey, data);
}).then(function (encryptedBuffer) { }).then(function (encryptedBuffer) {
ivData = new ByteData(encOptions.iv.buffer);
ctData = new ByteData(encryptedBuffer); ctData = new ByteData(encryptedBuffer);
if (!macKey) {
return null;
}
var dataForMac = buildDataForMac(ivData.arr, ctData.arr); var dataForMac = buildDataForMac(ivData.arr, ctData.arr);
return computeMac(dataForMac.buffer, key.macKey.arr.buffer); return computeMac(dataForMac.buffer, macKey.arr.buffer);
}).then(function (macBuffer) { }).then(function (macBuffer) {
macData = new ByteData(macBuffer); var type = encTypes.AesCbc256_B64;
return new Cipher(encType.AesCbc256_HmacSha256_B64, ivData, ctData, macData); if (macBuffer) {
type = encTypes.AesCbc256_HmacSha256_B64;
macData = new ByteData(macBuffer);
}
return new Cipher(type, ivData, ctData, macData);
}).catch(function (err) { }).catch(function (err) {
console.error(err); console.error(err);
}); });
} }
function aesDecrypt(cipher, key) { function aesDecrypt(cipher, encKey, macKey) {
var keyOptions = { var keyOptions = {
name: 'AES-CBC' name: 'AES-CBC'
}; };
@@ -276,15 +287,33 @@
iv: cipher.iv.arr.buffer iv: cipher.iv.arr.buffer
}; };
var dataForMac = buildDataForMac(cipher.iv.arr, cipher.ct.arr); return new Promise(function (resolve) {
return computeMac(dataForMac.buffer, key.macKey.arr.buffer) if (cipher.encType == encTypes.AesCbc256_B64) {
resolve(false);
return;
}
if (!macKey) {
throw 'MAC key not provided.';
}
resolve(true);
})
.then(function (checkMac) {
if (!checkMac) {
return null;
}
var dataForMac = buildDataForMac(cipher.iv.arr, cipher.ct.arr);
return computeMac(dataForMac.buffer, macKey.arr.buffer)
})
.then(function (macBuffer) { .then(function (macBuffer) {
return macsEqual(cipher.mac.arr.buffer, macBuffer, key.macKey.arr.buffer); if (!macBuffer) {
return true;
}
return macsEqual(cipher.mac.arr.buffer, macBuffer, macKey.arr.buffer);
}).then(function (macsMatch) { }).then(function (macsMatch) {
if (macsMatch === false) { if (macsMatch === false) {
throw 'MAC check failed.'; throw 'MAC check failed.';
} }
return window.crypto.subtle.importKey('raw', key.encKey.arr.buffer, keyOptions, false, ['decrypt']); return window.crypto.subtle.importKey('raw', encKey.arr.buffer, keyOptions, false, ['decrypt']);
}).then(function (importedKey) { }).then(function (importedKey) {
return window.crypto.subtle.decrypt(decOptions, importedKey, cipher.ct.arr.buffer); return window.crypto.subtle.decrypt(decOptions, importedKey, cipher.ct.arr.buffer);
}).catch(function (err) { }).catch(function (err) {
@@ -377,14 +406,22 @@
email: '', email: '',
masterPassword: '', masterPassword: '',
pbkdf2Iterations: 5000, pbkdf2Iterations: 5000,
userKey: new ByteData(),
userKeyHash: new ByteData(), masterKey: new ByteData(),
key: new SymmetricCryptoKey(), masterKeyHash: new ByteData(),
symKey: new SymmetricCryptoKey(),
protectedSymKey: new Cipher(),
unprotectedSymKey: new ByteData(),
publicKey: new ByteData(), publicKey: new ByteData(),
privateKey: new ByteData(), privateKey: new ByteData(),
plaintext: '', protectedPrivateKey: new Cipher(),
cipher: new Cipher(), unprotectedPrivateKey: new ByteData(),
decPlaintext: ''
secret: '',
protectedSecret: new Cipher(),
unprotectedSecret: ''
}, },
computed: { computed: {
masterPasswordBuffer: function () { masterPasswordBuffer: function () {
@@ -393,21 +430,21 @@
emailBuffer: function () { emailBuffer: function () {
return this.email ? fromUtf8(this.email) : null; return this.email ? fromUtf8(this.email) : null;
}, },
plaintextBuffer: function () { secretBuffer: function () {
return this.plaintext ? fromUtf8(this.plaintext) : null; return this.secret ? fromUtf8(this.secret) : null;
} }
}, },
watch: { watch: {
userKey: function (newUserKey) { masterKey: function (newMasterKey) {
var self = this; var self = this;
if (!newUserKey || !newUserKey.arr || !self.masterPasswordBuffer) { if (!newMasterKey || !newMasterKey.arr || !self.masterPasswordBuffer) {
return new ByteData(); return new ByteData();
} }
pbkdf2(newUserKey.arr.buffer, self.masterPasswordBuffer, 1, 256) pbkdf2(newMasterKey.arr.buffer, self.masterPasswordBuffer, 1, 256)
.then(function (userKeyHash) { .then(function (masterKeyHash) {
self.userKeyHash = userKeyHash; self.masterKeyHash = masterKeyHash;
}); });
} }
}, },
@@ -415,9 +452,9 @@
generateKeys: function () { generateKeys: function () {
var self = this; var self = this;
var key = new Uint8Array(512 / 8); var symKey = new Uint8Array(512 / 8);
window.crypto.getRandomValues(key); window.crypto.getRandomValues(symKey);
self.key = new SymmetricCryptoKey(key); self.symKey = new SymmetricCryptoKey(symKey);
generateRsaKeypair().then(function (keypair) { generateRsaKeypair().then(function (keypair) {
self.publicKey = keypair.publicKey; self.publicKey = keypair.publicKey;
@@ -435,44 +472,84 @@
}; };
}, function (newVal, oldVal) { }, function (newVal, oldVal) {
if (!newVal.masterPassword || !newVal.email || !newVal.iterations || newVal.iterations < 1) { if (!newVal.masterPassword || !newVal.email || !newVal.iterations || newVal.iterations < 1) {
vm.userKey = new ByteData(); vm.masterKey = new ByteData();
return; return;
} }
pbkdf2(newVal.masterPassword, newVal.email, newVal.iterations, 256) pbkdf2(newVal.masterPassword, newVal.email, newVal.iterations, 256)
.then(function (userKey) { .then(function (masterKey) {
vm.userKey = userKey; vm.masterKey = masterKey;
}); });
}); });
vm.$watch(function () { vm.$watch(function () {
return { return {
key: vm.key, symKey: vm.symKey,
plaintext: vm.plaintextBuffer secret: vm.secretBuffer
}; };
}, function (newVal, oldVal) { }, function (newVal, oldVal) {
if (!newVal.key || !newVal.plaintext) { if (!newVal.symKey || !newVal.secret) {
vm.cipher = new Cipher(); vm.protectedSecret = new Cipher();
vm.decPlaintext = ''; vm.unprotectedSecret = '';
return; return;
} }
aesEncrypt(newVal.plaintext, newVal.key) aesEncrypt(newVal.secret, newVal.symKey.encKey, newVal.symKey.macKey)
.then(function (cipher) { .then(function (cipher) {
vm.cipher = cipher; vm.protectedSecret = cipher;
return aesDecrypt(cipher, newVal.key); return aesDecrypt(vm.protectedSecret, newVal.symKey.encKey, newVal.symKey.macKey);
}).then(function (plaintext) { }).then(function (secret) {
vm.decPlaintext = toUtf8(plaintext); vm.unprotectedSecret = toUtf8(secret);
}); });
}); });
// Generate initial set of keys vm.$watch(function () {
vm.generateKeys(); return {
masterKey: vm.masterKey,
symKey: vm.symKey
};
}, function (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) {
vm.protectedSymKey = cipher;
return aesDecrypt(vm.protectedSymKey, newVal.masterKey, null);
}).then(function (unprotectedSymKey) {
vm.unprotectedSymKey = new ByteData(unprotectedSymKey);
});
});
vm.$watch(function () {
return {
symKey: vm.symKey,
privateKey: vm.privateKey
};
}, function (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) {
vm.protectedPrivateKey = cipher;
return aesDecrypt(vm.protectedPrivateKey, newVal.symKey.encKey, newVal.symKey.macKey);
}).then(function (unprotectedPrivateKey) {
vm.unprotectedPrivateKey = new ByteData(unprotectedPrivateKey);
});
});
// Set default values // Set default values
vm.email = 'user@example.com'; vm.email = 'user@example.com';
vm.masterPassword = 'password123'; vm.masterPassword = 'password123';
vm.plaintext = 'This is a secret.'; vm.secret = 'This is a secret.';
// Generate initial set of keys
vm.generateKeys();
})(); })();
</script> </script>
</body> </body>