-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
executable file
·152 lines (131 loc) · 5.22 KB
/
script.js
File metadata and controls
executable file
·152 lines (131 loc) · 5.22 KB
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
// Event listener for "Paste Key"
document.getElementById('pasteKeyBtn').addEventListener('click', async () => {
try {
const clipboardText = await navigator.clipboard.readText();
if (!clipboardText) {
alert('Clipboard is empty or contains invalid text.');
return;
}
document.getElementById('keyInput').value = clipboardText;
} catch (error) {
console.error('Error accessing clipboard:', error);
alert('Clipboard access failed. Please paste the key manually.');
}
});
// Event listener for "Generate TOTP"
document.getElementById('generateBtn').addEventListener('click', () => {
const key = document.getElementById('keyInput').value.trim().replace(/\s+/g, ''); // Remove spaces
if (!key) {
alert('Please enter a valid Base32-encoded key.');
return;
}
try {
startTOTPUpdate(key);
} catch (error) {
console.error('Error generating TOTP:', error);
alert('Invalid Base32-encoded key. Please check your input.');
}
});
// Event listener for "Copy" button
document.getElementById('copyBtn').addEventListener('click', async () => {
const totpDisplay = document.getElementById('totpDisplay').textContent;
if (!totpDisplay || totpDisplay === "TOTP will appear here") {
alert('No TOTP available to copy.');
return;
}
try {
await navigator.clipboard.writeText(totpDisplay);
} catch (error) {
console.error('Failed to copy TOTP:', error);
alert('Copy failed. Please try manually.');
}
});
let totpInterval;
let countdownInterval;
function startTOTPUpdate(secretKey) {
const radius = 45; // Circle radius
const circumference = 2 * Math.PI * radius;
const circle = document.querySelectorAll('.progress-ring__circle')[1];
circle.style.strokeDasharray = `${circumference} ${circumference}`; // Set circle circumference
const updateToken = async () => {
const currentTime = Date.now();
const remainingTime = 30 - Math.floor((currentTime / 1000) % 30); // Calculate remaining time
// Generate and display the TOTP
const totp = await generateTOTP(secretKey);
document.getElementById('totpDisplay').textContent = totp;
// Show the Copy button
document.getElementById('copyBtn').style.display = 'block';
// Reset circular progress bar
const offset = (1 - remainingTime / 30) * circumference;
circle.style.strokeDashoffset = offset;
// Clear previous countdown interval
if (countdownInterval) clearInterval(countdownInterval);
// Start real-time countdown
let countdownValue = remainingTime;
document.getElementById('countdown').textContent = countdownValue;
countdownInterval = setInterval(() => {
countdownValue--;
document.getElementById('countdown').textContent = countdownValue;
// Update progress bar dynamically
const newOffset = (1 - countdownValue / 30) * circumference;
circle.style.strokeDashoffset = newOffset;
if (countdownValue <= 0) {
clearInterval(countdownInterval);
}
}, 1000);
// Schedule the next TOTP update
if (totpInterval) clearTimeout(totpInterval);
totpInterval = setTimeout(updateToken, remainingTime * 1000);
};
updateToken();
}
async function generateTOTP(secretKeyBase32) {
const secretKey = base32Decode(secretKeyBase32);
const timeStep = Math.floor(Date.now() / 30000);
const hmac = await generateHMAC(secretKey, timeStep);
const offset = hmac[hmac.length - 1] & 0x0f;
const binaryCode = ((hmac[offset] & 0x7f) << 24) |
((hmac[offset + 1] & 0xff) << 16) |
((hmac[offset + 2] & 0xff) << 8) |
(hmac[offset + 3] & 0xff);
const totp = (binaryCode % 1000000).toString().padStart(6, '0');
// Show the Copy button and set the TOTP text
document.getElementById('copyBtn').style.display = 'block';
return totp;
}
async function generateHMAC(secretKey, timeStep) {
const msg = new Uint8Array(8);
for (let i = 0; i < 8; i++) {
msg[7 - i] = timeStep & 0xff;
timeStep >>= 8;
}
const cryptoKey = await crypto.subtle.importKey(
'raw',
secretKey,
{ name: 'HMAC', hash: { name: 'SHA-1' } },
false,
['sign']
);
const signature = await crypto.subtle.sign('HMAC', cryptoKey, msg);
return new Uint8Array(signature);
}
function base32Decode(base32) {
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
const normalizedBase32 = base32.toUpperCase().replace(/=+$/, ''); // Remove padding if present
const bits = [];
for (const char of normalizedBase32) {
const val = alphabet.indexOf(char);
if (val === -1) {
throw new Error('Invalid Base32 character in key.');
}
bits.push(...val.toString(2).padStart(5, '0').split('').map(Number));
}
const bytes = [];
for (let i = 0; i < bits.length; i += 8) {
const byte = bits.slice(i, i + 8);
if (byte.length === 8) {
bytes.push(parseInt(byte.join(''), 2));
}
}
return new Uint8Array(bytes);
}