From 45800a666ee39aca6c06c9037ed24b21eca3d28e Mon Sep 17 00:00:00 2001 From: xyr11 Date: Sun, 13 Oct 2024 06:15:28 +0800 Subject: [PATCH] Rearranged layout and updated js --- index.html | 240 ++++++++++++++++++++++++++--------------------------- 1 file changed, 118 insertions(+), 122 deletions(-) diff --git a/index.html b/index.html index ba9dc33..d37d691 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,11 @@ TOTP Generator -
-
+

Time-based One-time Password Algorithm

-
This page is a modified version of russau's JSFiddle. This website works in the browser and does not save any data. Check the GitHub for more info.
+
This page is a modified version of russau's JSFiddle. This website works in the browser and does not transmit any data. Check the GitHub page for more info.

This page contains a JavaScript implementation of the Time-based One-time Password Algorithm used by Google Authenticator and other OTP apps, described in the TOTP RFC Draft.

There are a lot of OTP applications available (check out Wikipedia's list). TOTP is an open standard, so you can use these apps to create one-time passwords for your own application. Some apps can add an OTP by simply scanning a QR code (more info on the URI format can be found here).

This page implements the same OTP algorithm these apps use – you would use this same algorithm server-side to verify an OTP. Test it by setting the OTP Label and Base32 secret and scanning the QR code in your app. The OTP on your app should be the same as the one at the bottom of this page. (This browser and your app must be synchronized using an internet time source to generate the same OTP codes.)

+

The OTP format used below is based on the OTP Key URI format by Google.

-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- QR Code options -
-
- -
-
- -
-
- -
-
- -
-
- Level: - - - - -
-
- -
-
- -
-
- -
-
- -
-
-
-
-
+ +
+

OTP properties

+
+ +
+
+
+ +
+
+
+ +
+
+
+ QR Code options +
+
+ +
+
+ +
- -
QR Code image
+
- -
- -
+
- -
- - -
+ Level: + + + +
- -
+
- -
- -
+
- +
- -
+
+
+
+ +
+
+

One-time Password

+
+

Updating in s

+

Unix epoch div 30 (padded hex)

+
+
-
-
+

Secret (hex)

+
+ + +
+

HMAC (secret, time)

+
+ +
+

QR Code

+ QR Code image +

+
+
Permalink:
+
+
@@ -194,22 +191,17 @@

Time-based One-time Password Algorithm

} } -function errorMessage (text) { +function errorMessage (text, color = 'red') { // Hide the outputs and show a message - elem('output').style.display = 'none' - elem('noticeMsg').innerHTML = `${text}` + elem('qrcode').style.display = 'none' + elem('noticeMsg').innerHTML = `${text}` } // Initialize QR code generator const qr = new QRious({ element: elem('qrcode') }) const defaultQROptions = { background: 'white', backgroundAlpha: 1, foreground: 'black', foregroundAlpha: 1, level: 'L', mime: 'image/png', padding: null, size: 200 } const customQROptions = {} - -// Function to update the displayed qr code function updateQrCode (property, value) { - // Show qr code in case its hidden - elem('qrcode').style.display = '' - // If the previous value is the same as the given value, you don't need to update the variable if (customQROptions[property] === value) return @@ -220,7 +212,7 @@

Time-based One-time Password Algorithm

qr.set({ ...defaultQROptions, ...customQROptions }) } -const otpFields = {} +let otpFields = {} // Fetch value of OTP fields and put to object function fetchOTP () { const label = elem('label').value @@ -231,17 +223,18 @@

Time-based One-time Password Algorithm

if (otpFields.secret === secret && otpFields.label === label && otpFields.issuer === issuer) return // Add to otpFields variable - otpFields.label = label - otpFields.secret = secret - otpFields.issuer = issuer + otpFields = { label, secret, issuer } // Check if user has given the required values - if (!secret || !label) { - return errorMessage('Fill in the Label and Secret fields.') - } - elem('output').style.display = '' + if (!secret) return errorMessage('Fill in the Secret field.') + + // Reset message box + elem('qrcode').style.display = '' elem('noticeMsg').innerHTML = '' + // Don't show qr code if label is absent + if (!label) errorMessage('Fill in the Label field.', 'yellow') + // Update OTP updateOTP() } @@ -272,10 +265,10 @@

Time-based One-time Password Algorithm

const epoch = Math.round(new Date().getTime() / 1000.0) const time = leftpad(dec2hex(Math.floor(epoch / 30)), 16, '0') // Show it - elem('epoch').innerHTML = `${time}` - elem('secretHex').innerHTML = `${key}` + elem('epoch').innerHTML = `${time}` + elem('secretHex').innerHTML = `${key}` elem('secretHexLength').innerHTML = ((key.length * 4) + ' bits') - elem('secretHex').innerHTML = `${time}` + elem('secretHex').innerHTML = `${time}` // Try hashing the key const shaObj = new jsSHA('SHA-1', 'HEX') @@ -290,7 +283,6 @@

Time-based One-time Password Algorithm

elem('hmac').innerHTML = 'Secret (hex) must be in byte elements' elem('otp').innerHTML = 'Error' elem('qrcode').style.display = 'none' - elem('permalink').value = '' return } console.error(err) @@ -303,9 +295,9 @@

Time-based One-time Password Algorithm

const part3 = hmac.substr(offset * 2 + 8, hmac.length - offset) // Display it elem('hmac').innerHTML = '' - if (part1.length > 0) elem('hmac').innerHTML += `${part1}` + if (part1.length > 0) elem('hmac').innerHTML += `${part1}` elem('hmac').innerHTML += `${part2}` - if (part3.length > 0) elem('hmac').innerHTML += `${part3}` + if (part3.length > 0) elem('hmac').innerHTML += `${part3}` // Compute OTP let otp = (hex2dec(hmac.substr(offset * 2, 8)) & hex2dec('7fffffff')) + '' @@ -320,11 +312,15 @@

Time-based One-time Password Algorithm

updateQrCode('value', keyUri) elem('value').value = keyUri - // Add permalink // Combine all the inputted data - const searchParams = new URLSearchParams({ ...otpFields, ...customQROptions }) + let allInputs = ({ ...otpFields, ...customQROptions }) + // Remove blank fields + allInputs = Object.fromEntries(Object.entries(allInputs).filter(([prop, value]) => value !== '' && value != null)) + const searchParams = new URLSearchParams(allInputs) searchParams.delete('value') - elem('permalink').value = window.location.origin + window.location.pathname + '?' + searchParams.toString() + // Add permalink + const permalink = window.location.origin + window.location.pathname + '?' + searchParams.toString() + elem('permalink').innerHTML = `${permalink}` } // Read query fields / URL params