@@ -0,0 +1,78 @@
|
||||
(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();
|
||||
}
|
||||
});
|
||||
}());
|
||||
Reference in New Issue
Block a user