-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpbkdf2.js
147 lines (130 loc) · 4.22 KB
/
pbkdf2.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
(() => {
let salt;
let ciphertext;
let iv;
// https://bitcoin.stackexchange.com/questions/52727/byte-array-to-hexadecimal-and-back-again-in-javascript
function toHexString(byteArray) {
return Array.prototype.map.call(byteArray, function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('');
}
function toByteArray(hexString) {
var result = [];
for (var i = 0; i < hexString.length; i += 2) {
result.push(parseInt(hexString.substr(i, 2), 16));
}
return result;
}
/*
Fetch the contents of the "message" textbox, and encode it
in a form we can use for the encrypt operation.
*/
function getMessageEncoding() {
let message = document.querySelector("#pbkdf2-message").value;
let enc = new TextEncoder();
return enc.encode(message);
}
/*
Get some key material to use as input to the deriveKey method.
The key material is a password supplied by the user.
*/
function getKeyMaterial() {
let password = window.prompt("Enter your password");
let enc = new TextEncoder();
return window.crypto.subtle.importKey(
"raw",
enc.encode(password),
{name: "PBKDF2"},
false,
["deriveBits", "deriveKey"]
);
}
/*
Given some key material and some random salt
derive an AES-GCM key using PBKDF2.
*/
function getKey(keyMaterial, salt) {
return window.crypto.subtle.deriveKey(
{
"name": "PBKDF2",
salt: salt,
"iterations": 100000,
"hash": "SHA-256"
},
keyMaterial,
{ "name": "AES-GCM", "length": 256},
true,
[ "encrypt", "decrypt" ]
);
}
/*
Derive a key from a password supplied by the user, and use the key
to encrypt the message.
Update the "ciphertextValue" box with a representation of part of
the ciphertext.
*/
async function encrypt() {
const ciphertextValue = document.querySelector(".pbkdf2 .ciphertext-value");
ciphertextValue.textContent = "";
const decryptedValue = document.querySelector(".pbkdf2 .decrypted-value");
decryptedValue.textContent = "";
let keyMaterial = await getKeyMaterial();
salt = window.crypto.getRandomValues(new Uint8Array(16));
let key = await getKey(keyMaterial, salt);
iv = window.crypto.getRandomValues(new Uint8Array(12));
let encoded = getMessageEncoding();
ciphertext = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: iv
},
key,
encoded
);
let buffer = new Uint8Array(ciphertext, 0, 5);
ciphertextValue.classList.add("fade-in");
ciphertextValue.addEventListener("animationend", () => {
ciphertextValue.classList.remove("fade-in");
});
//ciphertextValue.textContent = `${buffer}...[${ciphertext.byteLength} bytes total]`;
ciphertextValue.textContent = toHexString(buffer);
}
/*
Derive a key from a password supplied by the user, and use the key
to decrypt the ciphertext.
If the ciphertext was decrypted successfully,
update the "decryptedValue" box with the decrypted value.
If there was an error decrypting,
update the "decryptedValue" box with an error message.
*/
async function decrypt() {
const decryptedValue = document.querySelector(".pbkdf2 .decrypted-value");
decryptedValue.textContent = "";
decryptedValue.classList.remove("error");
let keyMaterial = await getKeyMaterial();
let key = await getKey(keyMaterial, salt);
try {
let decrypted = await window.crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: iv
},
key,
ciphertext
);
let dec = new TextDecoder();
decryptedValue.classList.add("fade-in");
decryptedValue.addEventListener("animationend", () => {
decryptedValue.classList.remove("fade-in");
});
decryptedValue.textContent = dec.decode(decrypted);
} catch (e) {
decryptedValue.classList.add("error");
decryptedValue.textContent = "*** Decryption error ***";
}
}
const encryptButton = document.querySelector(".pbkdf2 .encrypt-button");
encryptButton.addEventListener("click", encrypt);
const decryptButton = document.querySelector(".pbkdf2 .decrypt-button");
decryptButton.addEventListener("click", decrypt);
})();