Categories
Uncategorized

eduMFA/privacyIdea - convert TOTP secrets

eduMFA (privacyIDEA) only accepts hex‑encoded TOTP secret keys; this post shows why, and how to convert between hex and Base32 with ready‑to‑use Python scripts.

Disclaimer: This is the first (and for now only) post written with AI assistance (but based on internal knowledge base). It was modified, extended and cleaned up, but may still contain AI related hallucinations.

Excerpt: eduMFA (a fork of privacyIDEA) only accepts hex‑encoded TOTP secret keys; this post shows why, and how to convert between hex and Base32 with ready‑to‑use Python scripts.

Introduction

Two‑factor authentication (2FA) is now a baseline security requirement for schools, enterprises, and public services. eduMFA, the identity‑aware MFA solution built on privacyIDEA, enforces a strict input format for TOTP (Time‑Based One‑Time Password) secret keys: hex encoding. If you try to enroll a Base32 key – a format most authenticator apps display – you’ll see an enrollment error. This post explains the reasoning behind the hex requirement, walks through the pitfalls of mismatched encodings, and provides three tiny Python utilities that converts the discussed formats.

Why eduMFA Demands Hex‑Encoded Secrets

The upstream project privacyIDEA stores TOTP seed values as raw binary data, eduMFA does the same. When exporting or importing via the REST API, the binary blob is represented as a hex‑encoded string. This design choice eliminates ambiguities caused by Base32 padding (=) or line‑breaks that some apps inject.

“eduMFA / privacyIDEA expect hex encoded TOTP secret key (and rejects other formats)”

If you feed a Base32 string directly, the server cannot decode it to the underlying binary seed, and the enrollment call fails with a 400 error. Converting your secret to hex first guarantees compatibility across all API clients, CLI tools, and the web UI.

Converting Between Hex, Base32, and Base64

Below are three self‑contained Python 3 scripts that perform the needed transformations. They purposefully avoid external dependencies beyond the Python standard library, making them ideal for quick use on Linux, macOS, or Windows PowerShell.

ScriptInputOutputTypical Use
TOTP-b32tohex.pyBase32 secretHex stringWhen you have a QR‑generated Base32 secret and need the hex version for eduMFA
TOTP-hex2b32.pyHex secretBase32 stringBase32 is used by KeepassXC for example. You may also need it for QR code generation.
TOTP-converter.pyHex secretBase64 + Base32 + hex reconstructionsFull‑cycle sanity check

1. Base32 → Hex (TOTP-b32tohex.py)

#!/usr/bin/env python3
import base64
import sys

# First argument is the Base32 string (no padding required)
b32 = sys.argv[1]

# Decode Base32 to raw bytes, then show hex representation
bin_out32 = base64.b32decode(b32)
print(f'{bin_out32.hex()}')

Run it like:

linux # ./TOTP-b32tohex.py MHNLU4ELS7XN44SNBGARKJBOFYBG25OW
61daba708b97eede724d098115242e2e026d75d6

The output hex string can be fed directly to eduMFA’s enrollment API.

2. Hex → Base32 (TOTP-hex2b32.py)

#!/usr/bin/env python3
import codecs
import base64
import sys

hex_in = sys.argv[1]
bin_in = codecs.decode(hex_in, 'hex')
b32 = base64.b32encode(bin_in).decode()
print(f'{b32}')

Example:

linux # ./TOTP-hex2b32.py 61daba708b97eede724d098115242e2e026d75d6 MHNLU4ELS7XN44SNBGARKJBOFYBG25OW

You can now generate a QR code for a user who prefers Base32‑based apps like Google Authenticator.

3. Full‑Cycle Converter (TOTP-converter.py)

#!/usr/bin/env python3
import codecs
import base64
import sys

hex_in = sys.argv[1]                 # Hex secret supplied by the admin
bin_in = codecs.decode(hex_in, 'hex')

# Encode to Base64 (standard RFC 4648)
b64 = base64.b64encode(bin_in).decode()
print(f'base64 = {b64}')

# Encode to Base32
b32 = base64.b32encode(bin_in).decode()
print(f'base32 = {b32}')

# Decode back to verify integrity
bin_out32 = base64.b32decode(b32)
print(f'bin (from base32) = {bin_out32}')
bin_out64 = base64.b64decode(b64)
print(f'bin (from base64) = {bin_out64}')

# Show hex again for confirmation
print(f'hex (from base32) = {bin_out32.hex()}')
print(f'hex (from base64) = {bin_out64.hex()}')

The script prints all representations and confirms that round‑tripping does not corrupt the secret. This is useful during migration projects or automated provisioning pipelines.

Generating QR codes

As QR codes were mentioned above, here’s how to create a QR code for your favorite TOTP app. Make sure to replace “username” and the second (!) “Issuer” with desired values:

linux # sudo apt install qrencode
linux # qrencode -t PNG -o totp_qr.png -s 10 "otpauth://totp/Issuer:username?secret=MHNLU4ELS7XN44SNBGARKJBOFYBG25OW&issuer=Issuer"

Here’s the resulting PNG image:

Troubleshooting Common Errors

SymptomLikely CauseFix
Invalid secret format error from eduMFASecret contains non‑hex characters or padding (=)Verify input with grep -E '^[0-9a-f]+$' or regenerate using the script
QR code shows garbled charactersUsed Base64 instead of Base32 for the QR payloadUse TOTP-hex2b32.py to produce a proper Base32 string
Enrollment succeeds but login failsSecret mismatch between server and authenticatorRe‑run TOTP-converter.py and compare the hex output from both sides

Leave a Reply

Your email address will not be published. Required fields are marked *