I maintain a a test-suite for TOTP codes. It contains a bunch of codes which adhere to the specification, some of which stretch it to breaking point, and some that are completely invalid. These codes are a good starting point for checking whether a 2FA / MFA app works correctly.
Proton have release a swish new authenticator app for Android, iOS, Mac, Linux and Windows. Sadly, their open source repository doesn't allow for bug reports so I'm blogging in public instead.
The good news is, the majority of tests pass. It accepts a wide range of acceptable codes and refuses to store most broken ones. There are a few niggles though. None of these are severe security issues, but they probably ought to be fixed.
Very long codes
The maximum number of digits which can be generated by the standard TOTP algorithm is 10. Proton happily scans codes containing 1 - 9 digits, but complains about 10 digit codes. So this fails:
otpauth://totp/issuer%3Aaccount%20name?secret=QWERTYUIOP&digits=10&issuer=issuer&algorithm=SHA1&period=30
 
					The TOTP RFC says:
Basically, the output of the HMAC-SHA-1 calculation is truncated to obtain user-friendly values
But doesn't say how far to truncate.
There's nothing I can see in the spec that prevents an implementer using all 10.
Risk: The user may not be able to store a valid code.
Recommendation: Allow 10 digit codes.
Invalid Secrets
Here we get to yet another deficiency in the TOTP specification. How is a secret defined?
Google says the secret is:
an arbitrary key value encoded in Base32 according to RFC 3548. The padding specified in RFC 3548 section 2.2 is not required and should be omitted.
Whereas Apple says it is:
An arbitrary key value encoded in Base32. Secrets should be at least 160 bits.
Either way, the Base32 alphabet contains only uppercase letters and a few numbers.  What happens if we give it a secret like QWERT!£$%^)*(YUIOP?
 
					Proton Authenticator just accepts it. It stores the full secret but I'm not sure how it generates the code based on it.
Risk: The code may be generated incorrectly.
Recommendation: Warn the user that the secret may be invalid and that a correct 2FA code cannot be guaranteed.
Issuer Mismatch
In this example, the first issuer is example.com but the second issuer is microsoft.com
otpauth://totp/example.com%3Aaccount%20name?secret=QWERTYUIOP&digits=6&issuer=microsoft.com&algorithm=SHA1&period=30
What should the TOTP reader do with this? Proton chooses microsoft.com.
This is something which, again, is inconsistent between major providers.
Google says this parameter is:
Strongly Recommended The issuer parameter is a string value indicating the provider or service this account is associated with, URL-encoded according to RFC 3986. If the issuer parameter is absent, issuer information may be taken from the issuer prefix of the label. If both issuer parameter and issuer label prefix are present, they should be equal.
Apple merely says:
The domain of the site or app. The password manager uses this field to suggest credentials when setting up a new code generator.
Yubico equivocates with
The issuer parameter is recommended, but it can be absent. Also, the issuer parameter and issuer string in label should be equal.
Risk: The code may be displayed with the wrong issuer.
Recommendation: Warn the user that there are multiple issuers. Let them choose which one is correct.
Dealing With Defaults
What should a TOTP app do if there is missing information? Proton does the following:
- If the code has no number set for digits, it defaults to 6
- If the code has no time set for period, it defaults to 30
- If the code has no algorithm, it defaults to SHA1
Risk: Low. The user normally has to confirm with the issuer that the the TOTP code has been correctly stored.
Recommendation: Let the user know that the code has missing data and may not be correct.
Odd Labels
The label allows you to have multiple codes for the same service. For example Big Bank:Personal Account and Big Bank:Family Savings.  The Google spec is slightly confusing:
The issuer prefix and account name should be separated by a literal or url-encoded colon, and optional spaces may precede the account name. Neither issuer nor account name may themselves contain a colon.
What happens if there are spaces before the account name?
otpauth://totp/Spaces:%20%20%20%20%20%20%20%20%20%20%20%20test%40example.com?secret=QWERTYUIOP&digits=6&issuer=&algorithm=SHA1&period=30

Proton strips the spaces (probably wise) but also removes the issuer.
Risk: The user will not know which account the code is for.
Recommendation: Keep the issuer.
Timeline
These aren't particularly high severity bugs, nevertheless I like to give organisations a bit of time to respond.
- 2025-07-31 - Discovered.
- 2025-08-01 - Disclosed via a web form.
- 2025-08-31 - Automatically published.
Next Steps
- If you're a user, please contribute tests or give feedback.
- If you're a developer, please check your app conforms to the specification.
- If you're from a security company - wanna help me write up a proper RFC so this doesn't cause issues in the future?
 
			
			
			
		
One thought on “Some minor bugs in Proton's new Authenticator app”
Valuable work and research. Keep it up.
More comments on Mastodon.