1 parent
1e7c84f91f
commit
1111372dc9
4 files changed
+375
-37
No files matched your search
@@ -0,0 +1,2 @@
|
||||
.protected-backups/
|
||||
page-passwords*.json
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}());
|
||||
@@ -1,37 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>多彩场景信函样例</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../css/style.css">
|
||||
<link rel="stylesheet" href="../css/notice-banner.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="letter-container theme-campus">
|
||||
|
||||
<h2 class="salutation">亲爱的王子玉:</h2>
|
||||
|
||||
<div class="content">
|
||||
<p>好久不见!最近过得怎么样?</p>
|
||||
<p>转眼间夏天就要到了,还记得那时候我们一起在操场上挥洒汗水、在教室里畅谈未来的日子吗?那段时光,真的就像昨天刚发生一样。</p>
|
||||
<p>这次致信,是想告诉你一个好消息。下周六我要举办一个久违的聚会,希望你一定要来!哪怕只是聊聊天,喝杯茶,也是极好的。时光易逝,友情常驻。愿你每天都充满活力,事事顺心!</p>
|
||||
</div>
|
||||
|
||||
<div class="closing">
|
||||
<p class="wish-prefix">此致</p>
|
||||
<p class="wish-suffix">敬礼!</p>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p class="signature">[您的署名]</p>
|
||||
<p class="date">2026年3月25日</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script defer src="../js/notice-banner.js?v=20260614"></script>
|
||||
</body>
|
||||
</html>
|
||||
Loaded 3 of 4 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user