79 lines
2.4 KiB
JavaScript
79 lines
2.4 KiB
JavaScript
(function () {
|
|
const encoder = new TextEncoder();
|
|
const decoder = new TextDecoder();
|
|
const form = document.getElementById('unlockForm');
|
|
const passwordInput = document.getElementById('letterPassword');
|
|
const statusEl = document.getElementById('unlockStatus');
|
|
const payloadEl = document.getElementById('encryptedPayload');
|
|
|
|
function fromBase64(value) {
|
|
const binary = atob(value);
|
|
const bytes = new Uint8Array(binary.length);
|
|
for (let i = 0; i < binary.length; i += 1) {
|
|
bytes[i] = binary.charCodeAt(i);
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
async function deriveKey(password, salt, iterations) {
|
|
const baseKey = await crypto.subtle.importKey(
|
|
'raw',
|
|
encoder.encode(password),
|
|
'PBKDF2',
|
|
false,
|
|
['deriveKey']
|
|
);
|
|
|
|
return crypto.subtle.deriveKey(
|
|
{
|
|
name: 'PBKDF2',
|
|
salt,
|
|
iterations,
|
|
hash: 'SHA-256'
|
|
},
|
|
baseKey,
|
|
{ name: 'AES-GCM', length: 256 },
|
|
false,
|
|
['decrypt']
|
|
);
|
|
}
|
|
|
|
async function unlock(password) {
|
|
const payload = JSON.parse(payloadEl.textContent);
|
|
const salt = fromBase64(payload.salt);
|
|
const iv = fromBase64(payload.iv);
|
|
const ciphertext = fromBase64(payload.ciphertext);
|
|
const key = await deriveKey(password, salt, payload.iterations);
|
|
const plaintext = await crypto.subtle.decrypt(
|
|
{ name: 'AES-GCM', iv },
|
|
key,
|
|
ciphertext
|
|
);
|
|
|
|
return decoder.decode(plaintext);
|
|
}
|
|
|
|
form.addEventListener('submit', async event => {
|
|
event.preventDefault();
|
|
const password = passwordInput.value;
|
|
if (!password) {
|
|
statusEl.textContent = '请输入密码。';
|
|
return;
|
|
}
|
|
|
|
statusEl.textContent = '正在解锁...';
|
|
form.querySelector('button').disabled = true;
|
|
|
|
try {
|
|
const html = await unlock(password);
|
|
document.open();
|
|
document.write(html);
|
|
document.close();
|
|
} catch (error) {
|
|
statusEl.textContent = '密码不正确,或页面密文已损坏。';
|
|
form.querySelector('button').disabled = false;
|
|
passwordInput.select();
|
|
}
|
|
});
|
|
}());
|