mirror of
https://github.com/bitwarden/help
synced 2025-12-06 00:03:30 +00:00
182 lines
6.7 KiB
HTML
182 lines
6.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
|
|
<title>bitwarden crypto</title>
|
|
|
|
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
|
|
rel="stylesheet">
|
|
<link href="//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700,300italic,400italic,600italic"
|
|
rel="stylesheet">
|
|
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
|
|
rel="stylesheet">
|
|
|
|
<script src="//unpkg.com/vue"></script>
|
|
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
|
<!--[if lt IE 9]>
|
|
<script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
|
<script src="//oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
|
|
<![endif]-->
|
|
</head>
|
|
<body>
|
|
<div class="container" id="app">
|
|
<h1>Key Derivation</h1>
|
|
<form>
|
|
<div class="form-group">
|
|
<label for="email">Email</label>
|
|
<input type="text" id="email" class="form-control" v-model="email">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="masterPassword">Master Password</label>
|
|
<input type="password" id="masterPassword" class="form-control" v-model="masterPassword">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="pbkdf2Iterations">PBKDF2 Client Iterations</label>
|
|
<input type="number" id="pbkdf2Iterations" class="form-control" v-model="pbkdf2Iterations">
|
|
</div>
|
|
<button type="button" id="deriveKeys" class="btn btn-default"
|
|
v-on:click="deriveKeys">Derive Keys</button>
|
|
</form>
|
|
<hr>
|
|
<h2>Private Key</h2>
|
|
<p>{{key.b64}}</p>
|
|
<h2>Master Password Hash</h2>
|
|
|
|
<h2>Generated Symmetric Key Material</h2>
|
|
|
|
<h3>Encryption Key</h3>
|
|
<h3>MAC Key</h3>
|
|
|
|
<h2>Generated RSA Keypair</h2>
|
|
|
|
<h1>Encryption</h1>
|
|
<form>
|
|
<div class="form-group">
|
|
<label for="plaintext">Plaintext Value</label>
|
|
<input type="text" id="plaintext" class="form-control">
|
|
</div>
|
|
<button type="button" id="encrypt" class="btn btn-default">Encrypt</button>
|
|
</form>
|
|
|
|
<h2>The "Cipher String"</h2>
|
|
</div>
|
|
|
|
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
|
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
|
<script>
|
|
(function(){
|
|
var CipherString = function(type, iv, ct, mac) {
|
|
this.type = type;
|
|
this.iv = iv;
|
|
this.ct = ct;
|
|
this.mac = mac;
|
|
this.string = type + '.' + iv.b64 + '|' + ct.b64;
|
|
if(mac) {
|
|
this.string += ('|' + mac.b64);
|
|
}
|
|
}
|
|
|
|
var ByteData = function(buf) {
|
|
if(!buf) {
|
|
return;
|
|
}
|
|
this.buf = buf;
|
|
this.b64 = toB64(buf);
|
|
}
|
|
|
|
function pbkdf2(password, salt, iterations, length) {
|
|
console.log('pbkdf2');
|
|
|
|
return window.crypto.subtle.importKey('raw', password, { name: 'PBKDF2' },
|
|
false, ['deriveKey', 'deriveBits']).then(function (importedKey) {
|
|
return window.crypto.subtle.deriveKey({
|
|
'name': 'PBKDF2',
|
|
salt: salt,
|
|
iterations: iterations,
|
|
hash: { name: 'SHA-256' }
|
|
}, importedKey, {
|
|
name: 'AES-CBC',
|
|
length: 256
|
|
}, true, ['encrypt', 'decrypt']);
|
|
}).then(function (derivedKey) {
|
|
return window.crypto.subtle.exportKey('raw', derivedKey);
|
|
}).then(function (exportedKey) {
|
|
return new ByteData(exportedKey);
|
|
}).catch(function (err) {
|
|
console.error(err);
|
|
});
|
|
}
|
|
|
|
function fromUtf8(str) {
|
|
var strUtf8 = unescape(encodeURIComponent(str));
|
|
var ab = new Uint8Array(strUtf8.length);
|
|
for (var i = 0; i < strUtf8.length; i++) {
|
|
ab[i] = strUtf8.charCodeAt(i);
|
|
}
|
|
return ab;
|
|
}
|
|
|
|
function toUtf8(buf) {
|
|
var bytes = new Uint8Array(buf);
|
|
var encodedString = String.fromCharCode.apply(null, bytes),
|
|
decodedString = decodeURIComponent(escape(encodedString));
|
|
return decodedString;
|
|
}
|
|
|
|
function fromB64(str) {
|
|
var binary_string = window.atob(str);
|
|
var len = binary_string.length;
|
|
var bytes = new Uint8Array(len);
|
|
for (var i = 0; i < len; i++) {
|
|
bytes[i] = binary_string.charCodeAt(i);
|
|
}
|
|
return bytes.buffer;
|
|
}
|
|
|
|
function toB64(buf) {
|
|
var binary = '';
|
|
var bytes = new Uint8Array(buf);
|
|
var len = bytes.byteLength;
|
|
for (var i = 0; i < len; i++) {
|
|
binary += String.fromCharCode(bytes[i]);
|
|
}
|
|
return window.btoa(binary);
|
|
}
|
|
|
|
new Vue({
|
|
el: '#app',
|
|
data: {
|
|
email: null,
|
|
masterPassword: null,
|
|
pbkdf2Iterations: 5000,
|
|
key: new ByteData(),
|
|
keyHash: null,
|
|
keyMaterial: null,
|
|
keyMaterialEnc: null,
|
|
keyMaterialMac: null,
|
|
publicKey: null,
|
|
privateKey: null,
|
|
plaintext: null,
|
|
cipherString: null,
|
|
decPlaintext: null
|
|
},
|
|
methods: {
|
|
deriveKeys: function () {
|
|
var self = this,
|
|
password = fromUtf8(this.masterPassword),
|
|
salt = fromUtf8(this.email);
|
|
|
|
pbkdf2(password, salt, this.pbkdf2Iterations, 256).then(function(key) {
|
|
self.key = key;
|
|
});
|
|
},
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|