-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathindex.html
308 lines (282 loc) · 17.1 KB
/
index.html
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>PeerAuth</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
@media (max-width: 767px) {
.qr-column {
flex: 0 0 100%;
max-width: 100%;
}
}
</style>
<style>
.language-switcher {
position: absolute;
top: 20px;
right: 20px;
}
</style>
</head>
<body class="container mt-5">
<div class="language-switcher">
<select id="languageSelect" class="form-select">
<option value="en">English</option>
<option value="de">Deutsch</option>
<option value="zh-CN">简体中文</option>
</select>
</div>
<h1>PeerAuth</h1>
<h2 data-i18n="whatIsThis">What is this?</h2>
<p data-i18n="intro1">
Machine learning has become more and more powerful, to the point where a bad actor can take a photo and a voice
recording of someone you know, and forge a complete video recording. See the "OmniHuman-1" model developed by ByteDance:<br>
<ul>
<li><a href="https://x.com/ai_for_success/status/1886685232952435133" data-i18n="discussionLink">discussion on X</a></li>
<li><a href="https://omnihuman-lab.github.io/" data-i18n="paperLink">ByteDance's paper</a></li>
</ul>
</p>
<p data-i18n="intro2">Bad actors can now digitally impersonate someone you love, and trick you into doing things like paying a ransom.</p>
<p data-i18n="solutionDesc">To mitigate that risk, I have developed this simple solution where you can setup a unique time-based one-time passcode (TOTP) between any pair of persons.</p>
<p data-i18n="howItWorks">This is how it works:</p>
<ol>
<li data-i18n="step1"></li>
<li data-i18n="step2"></li>
<li data-i18n="step3"></li>
<li data-i18n="step4"></li>
<li data-i18n="step5"></li>
</ol>
<p data-i18n="securityNote">Note that this depends on both Alice's and Bob's phones being secure. If somebody steals Bob's phone and manages to bypass the fingerprint or PIN or facial recognition of Bob's phone, then all bets are off.</p>
<p><a href="https://news.ycombinator.com/item?id=42942854" target="_blank"><span data-i18n="hn">Discussion on Hacker News</span></a></p>
<p><a href="https://github.com/ksze/PeerAuth" target="_blank"><span data-i18n="srccode">Source code of this page on GitHub</span></a></p>
<div class="mb-3">
<label for="name1" class="form-label" data-i18n="person1">Person 1</label>
<input type="text" class="form-control" id="name1" required>
</div>
<div class="mb-3">
<label for="name2" class="form-label" data-i18n="person2">Person 2</label>
<input type="text" class="form-control" id="name2" required>
</div>
<button id="generateBtn" class="btn btn-primary mb-4" disabled data-i18n="generate">Generate</button>
<div id="qrSection" style="display: none;">
<p>
<span data-i18n="base32secret"></span><br>
<span id="secret" class="font-monospace"></span>
</p>
<div class="row justify-content-between gx-0">
<div class="col-12 col-md-3 gy-5 text-center qr-column">
<p><span id="label1"></span><span data-i18n="pleaseScan">, please scan the QR code below:</span></p>
<div id="qrcode1" class="d-flex justify-content-center"></div>
</div>
<div class="col-12 col-md-3 gy-5 text-center qr-column">
<p><span id="label2"></span><span data-i18n="pleaseScan">, please scan the QR code below:</span></p>
<div id="qrcode2" class="d-flex justify-content-center"></div>
</div>
</div>
<div class="mt-4">
<h4 data-i18n="verificationOTP">Verification OTP:</h4>
<p id="otpDisplay1" class="h2"></p>
<p><span data-i18n="expiresIn">Expires in </span><span id="countdown">30</span><span data-i18n="seconds"> seconds</span></p>
</div>
</div>
<!-- Libraries -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/otpauth/9.3.6/otpauth.umd.min.js"></script>
<script>
// Language configuration
const translations = {
'en': {
'whatIsThis': 'What is this?',
'intro1': 'Machine learning has become more and more powerful, to the point where a bad actor can take a photo and a voice recording of someone you know, and forge a complete video recording. See the "OmniHuman-1" model developed by ByteDance:',
'discussionLink': 'discussion on X',
'paperLink': 'ByteDance\'s paper',
'intro2': 'Bad actors can now digitally impersonate someone you love, and trick you into doing things like paying a ransom.',
'solutionDesc': 'To mitigate that risk, I have developed this simple solution where you can setup a unique time-based one-time passcode (TOTP) between any pair of persons.',
'howItWorks': 'This is how it works:',
'step1': 'Two people, Person A and Person B, sit in front of the same computer and open this page;',
'step2': 'They input their respective names (e.g. Alice and Bob) onto the same page, and click "Generate";',
'step3': 'The page will generate two TOTP QR codes, one for Alice and one for Bob;',
'step4': 'Alice and Bob scan the respective QR code into a TOTP mobile app (such as Authy or Google Authenticator) on their respective mobile phones;',
'step5': 'In the future, when Alice speaks with Bob over the phone or over video call, and wants to verify the identity of Bob, Alice asks Bob to provide the 6-digit TOTP code from the mobile app. If the code matches what Alice has on her own phone, then Alice has more confidence that she is speaking with the real Bob.',
'securityNote': 'Note that this depends on both Alice\'s and Bob\'s phones being secure. If somebody steals Bob\'s phone and manages to bypass the fingerprint or PIN or facial recognition of Bob\'s phone, then all bets are off.',
'person1': 'Person A',
'person2': 'Person B',
'generate': 'Generate',
'verificationOTP': 'Verification OTP:',
'expiresIn': 'Expires in ',
'seconds': ' seconds',
'pleaseScan': ', please scan the QR code below:',
'hn': 'Discussion on Hacker News',
'srccode': 'Source code of this page on GitHub',
'base32secret': 'If you are not able to scan the QR codes below, here is the TOTP secret key encoded as base32, for manual entry into authenticator app:',
},
'zh-CN': {
'whatIsThis': '这是什么?',
'intro1': '机器学习技术日益强大,甚至能让不法分子利用一张照片和一段录音,伪造出完整的视频记录。例如,字节跳动开发的“OmniHuman-1”模型就能实现这一点:',
'discussionLink': 'X上的讨论',
'paperLink': '字节跳动的论文',
'intro2': '如今,这些恶意行为者能够数字化地冒充你所爱的人,诱骗你做出诸如支付赎金等行为。',
'solutionDesc': '为了降低这种风险,我开发了一个简单的解决方案,让任何两个人之间可以设置一个基于时间的独特一次性密码(TOTP)。',
'howItWorks': '工作原理如下:',
'step1': '两个人,甲方和乙方,坐在同一台电脑前打开本页面;',
'step2': '两人在同一页面上输入各自的名字(例如小明和小红),然后点击 "生成";',
'step3': '页面将生成两个TOTP二维码,分别对应小明和小红;',
'step4': '小明和小红用各自的手机验证器应用(如 Authy、微软身份验证器、腾讯身份验证器)扫描对应的二维码;',
'step5': '将来,当小明通过电话或视频与小红交谈,并想验证小红的身份时,小明可以要求小红提供手机应用中的6位数TOTP代码。如果代码与小明自己手机上的匹配,那么小明就能更有信心确认自己正在与真正的小红交谈。',
'securityNote': '请注意这需要小明和小红的手机都保持安全。如果有人窃取了小红的手机,并成功绕过了指纹、PIN码或面部识别等安全措施,那么这一保护机制将失效。',
'person1': '甲方',
'person2': '乙方',
'generate': '生成',
'verificationOTP': '验证码:',
'expiresIn': '剩余时间 ',
'seconds': ' 秒',
'pleaseScan': ',请扫描以下的二维码:',
'hn': 'Hacker News 上的讨论',
'srccode': '本页于 GitHub 上的源代码',
'base32secret': '假如你无法扫描下面的二维码,你还可以手动输入这个以 base32 编码的 TOTP 密钥到验证器应用里:',
},
'de': {
'whatIsThis': 'Was ist das hier?',
'intro1': 'Maschinelles Lernen ist immer besser geworden so dass es einem kriminellen oder andersartig bösartigem Menschen reicht ein Foto und eine Tonaufnahme einer dir bekannten Person zu nehmen um daraus eine gefälschte Videoaufnahme zu fabrizieren. Siehe dazu auch das "OmniHuman-1"-Modell, das von ByteDance entwickelt wurde:',
'discussionLink': 'Diskussion auf X',
'paperLink': 'ByteDances Veröffentlichung',
'intro2': 'Kriminelle oder anderweitig feindlich gesinnte Menschen können nun von dir geliebte Menschen imitieren und dich übertölpeln und zum Beispiel zur Zahlung von Lösegeld zu bringen.',
'solutionDesc': 'Um diese Gefahr abzuschwächen habe ich diese einfache Methode entwickelt bei der ein einzigartiger zeitbasierter Einmalcode (TOTP) zwischen zwei Personen eingerichtet wird.',
'howItWorks': 'Und so funktioniert es:',
'step1': 'Zwei Menschen, Person A und Person B, sitzen vor dem selben Computer und öffnen diese Webseite;',
'step2': 'Sie beide geben ihre Namen (z.B. Alice und Bob) in die entsprechenden Namensfelder ein und klicken auf "Erzeugen";',
'step3': 'Die Seite erzeugt nun zwei TOTP QR-Codes: einen für Alice und einen für Bob;',
'step4': 'Alice und Bob scannen nun je den angegebenen Code mit ihrer Zweifaktorapp (z.B. Authy oder Google Authenticator);',
'step5': 'In zukünftigen Telefonaten oder Videoanrufen könnte Alice nun, um sich der Identität von Bob zu versichern, diesen bitten den aktuellen 6-stelligen Einmalcode vorzulesen. Wenn der Einmalcode dem entspricht was Alice auch in ihrer Zweifaktorapp sieht, kann sie etwas mehr vertrauen darin haben dass sie tatsächlich Bob vor sich hat.',
'securityNote': 'Bitte beachten: dieser Mechanismus vertraut darauf dass die Telefone von Alice und Bob sicher sind. Falls es jemand anderem gelingt Bobs Telefon zu entwenden und die Pin, den Fingerabdruckt oder die Gesichtserkennung zu überwinden, kann diese Person auch die Einmalcodes verwenden.',
'person1': 'Person A',
'person2': 'Person B',
'generate': 'Erzeugen',
'verificationOTP': 'Einmalcode zur Überprüfung:',
'expiresIn': 'Wechselt in ',
'seconds': ' Sekunden',
'pleaseScan': ', bitte Scanne diesen QR Code ein:',
'hn': 'Diskussion auf Hacker News (Englisch)',
'srccode': 'Der Quellcode dieser Seite auf GitHub',
'base32secret': 'Falls ihr die QR Codes nicht einscannen könnt, ist hier der geheime Schlüssel als Base32 für die manuelle Eingabe in die Zweifaktorapp:',
},
};
// Function to detect user's preferred language
function getPreferredLanguage() {
const supportedLanguages = ['en', 'de', 'zh-CN'];
const browserLanguages = navigator.languages || [navigator.language];
for (let lang of browserLanguages) {
// Check exact match
if (supportedLanguages.includes(lang)) {
return lang;
}
// Check language prefix (e.g., 'zh' for 'zh-CN')
const langPrefix = lang.split('-')[0];
if (supportedLanguages.includes(langPrefix)) {
return langPrefix;
}
}
return 'en'; // Default to English
}
function updateContent(lang) {
document.querySelectorAll('[data-i18n]').forEach(element => {
const key = element.dataset.i18n;
if (translations[lang][key]) {
if (element.tagName === 'INPUT' || element.tagName === 'SELECT') {
element.placeholder = translations[lang][key];
} else {
element.innerHTML = translations[lang][key];
}
}
});
}
// Initialize with user's preferred language
const preferredLang = getPreferredLanguage();
updateContent(preferredLang);
document.getElementById('languageSelect').addEventListener('change', (e) => {
updateContent(e.target.value);
});
</script>
<script>
// Original script with localization updates
const inputs = document.querySelectorAll('input');
const generateBtn = document.getElementById('generateBtn');
let totp1 = null;
let totp2 = null;
let interval = null;
inputs.forEach(input => {
input.addEventListener('input', () => {
generateBtn.disabled = !(
document.getElementById('name1').value &&
document.getElementById('name2').value
);
});
});
generateBtn.addEventListener('click', () => {
if (interval !== null) { clearInterval(interval); }
const name1 = document.getElementById('name1').value;
const name2 = document.getElementById('name2').value;
const lang = document.getElementById('languageSelect').value;
const secret = new OTPAuth.Secret({ size: 20 });
const algorithm = 'SHA1';
const issuer = 'PeerAuth';
const digits = 6;
const period = 30;
totp1 = new OTPAuth.TOTP({
issuer,
label: encodeURIComponent(name2),
algorithm,
digits,
period,
secret,
});
totp2 = new OTPAuth.TOTP({
issuer,
label: encodeURIComponent(name1),
algorithm,
digits,
period,
secret,
});
function updateOtpDisplay() {
if (!totp1) return;
const now = Math.floor(Date.now() / 1000);
const remaining = period - (now % period);
document.getElementById('otpDisplay1').textContent = totp1.generate();
document.getElementById('countdown').textContent = remaining;
}
// Update QR code labels with translations
document.getElementById('label1').textContent = name1;
document.getElementById('label2').textContent = name2;
document.getElementById('secret').textContent = secret.base32;
document.getElementById('qrSection').style.display = 'block';
const qrcode1node = document.getElementById('qrcode1');
const qrcode2node = document.getElementById('qrcode2');
for (node of [qrcode1node, qrcode2node]) {
while (node.lastElementChild) {
node.removeChild(node.lastElementChild);
}
}
new QRCode(document.getElementById('qrcode1'), {
text: totp1.toString(),
width: 200,
height: 200,
correctLevel: QRCode.CorrectLevel.H
});
new QRCode(document.getElementById('qrcode2'), {
text: totp2.toString(),
width: 200,
height: 200,
correctLevel: QRCode.CorrectLevel.H
});
// Smooth scroll to qrcode1 with animation
document.getElementById('qrSection').scrollIntoView({behavior: 'smooth', block: 'start', inline: 'nearest'});
updateOtpDisplay();
interval = setInterval(updateOtpDisplay, 1000);
});
</script>
</body>
</html>