This is very useful to anyone writing TOTP tools, big thanks to the author.
However, reading the article this section caught my eye:
"As we now know, SHA-1 has some fundamental weaknesses. ... But the TOTP authors disagree and allow a for some different algorithms to be used."
With significant compute resources SHA1 can be broken for some use cases, but I don't think this is one of them. Is HN aware of any practical attacks against TOTP-SHA1?
HMAC-SHA1 is not broken as a pseudorandom function. SHA1 is only broken for collision-resistance for the moment. That doesn't mean it's great, or that you shouldn't upgrade.
Do you think that the technique of using the last 4 bits to choose a further pseudorandom 31 bits from the rest of the hash MIGHT mitigate SOME future weakness as a PRNG? Or do you have confidence it is completely useless? Or neither, of course.
I think it's weird. Either you trust your output to be pseudorandom or you don't. These weird hacks may be fine, but they feel like adding "safety" duct tape onto the wings of a passenger jet.
About the "validity" period: in practice it seems to be more like a rotation period, the code themselves seem to be valid longer than this, that is you can often enter them for quite some time after they get rotated.
And I also often wonder about the OTP aspect, whether there is any protection in TOTP backends for the same TOTP being used twice.
A code may be repeated, but they should not enter a cycle (which would be a real disaster.)
You also need to keep in mind the birthday paradox. Repeated codes will happen with high probability after around sqrt(1,000,000) = 1000 code emissions. https://en.wikipedia.org/wiki/Birthday_problem
I'm so glad this has been created. I've felt for quite some time that TOTP is such a triumph of simplicity and security. It's so simple that if you have your secret key on hand, you can generate your TOTP code with just 30 lines of Python!: https://github.com/susam/mintotp/blob/main/mintotp.py
It's amazing! Its default UX assumes you're basically air gapped. It works based on a minimal shared secret that's totally portable. Conceptually, it's as clear as "hash (your secret+(now()//30)), display hash as decimal, only show the last 6 (ish) digits".
Wrong, what it's missing from passkey is phishing resistance. And you can use passkeys with at least a few independent commercial password managers if you are not happy giving your passkeys to Google & Apple (I work for one such password manager)
Psst: your employer is a vendor benefiting from said lock-in.
Without a means for transfering from one password management resource to another, passkeys create an increasingly significant friction against migrating one's account and artifically reduces competitive pressure on the vendor.
The phrase we use for that anti-consumer phenomenom is "vendor lock-in"
(If your employer has an effective and practical import and export technique for passkeys, I would be delighted to hear so and apologize for the suggestion otherwise. But as far as I've seen, that's not a thing.)
That is table stakes. So, rewind 5 years. Introduce that, remove attestation while you are at it. And make sure we have multiple compatible implementations.
Without it passkeys are a sham. And since that was and is the case I don't see any conceivable future where passkeys benefit anyone but google/apple/ms. For the sake of the web it needs to die.
Being able to export a passkey sounds like a bug, not a feature. I was surprised that the spec was altered to allow hosting in the cloud at all, I thought it was more interesting as a means of keeping secrets in secure enclaves.
The expectation is that for any service you should be able to have multiple passkeys -- including multiple passkeys from different vendors. I'm not sure how that exactly causes lock in?
Exactly, you can just register passkeys from multiple providers if that's a fear. Worst case (i.e. Google nukes your account and you've only set one passkey), you go through the usual email recovery or backup codes process and set a new one.
It would be good to have a FOSS-implementation with TPM as the storage backend for computers and phones. Then people wouldn't need to use keys stored in the cloud.
Assuming you've only set one passkey (you can add multiple passkeys from different passkey managers) it's no more of an inconvenience than if you lost your password. Normal recovery/backup code processes still work.
TOTPs are amazing! I'm currently building a low-power, offline ESP32 device that generates time-based one-time QR codes and displays them on an e-ink screen. I plan to use it for proof of presence.
if you're only doing the signature generation rather than the full protocol (which TOTP leaves entirely up to the user), then the code to generate a passkey signature wouldn't be any longer
> HOTP spec, however, does place a minimum requirement - but no maximum:
>> Implementations MUST extract a 6-digit code at a minimum and possibly 7 and 8-digit code. Depending on security requirements, Digit = 7 or more SHOULD be considered in order to extract a longer HOTP value. RFC 4226 - 5.3. Generating an HOTP Value
Am I bad at reading specs or is there a maximum here? If I say „minimum 6, possibly 7 or 8“, that sounds like 9 and more isn’t allowed.
HOTP requires a minimum of 6 digits (R4 in Section 4), but does not actually set a maximum. The algorithm in the RFC caps at 9-digits per Section E.2 (and my cursory reading of the code). That's not a required limit, but a technical limit of their choices. It also happens that 9-digits probably is about as big as you'd want to go, three 3-digit chunks aren't much harder to remember and type in correctly but past that it'd become increasingly impractical. The "and possibly 7 and 8-digit code" should be taken as descriptive, not prescriptive. The requirements occur earlier in the document and do not set a maximum. If they'd intended 8 as the maximum then they wouldn't have presented an algorithm that goes to 9 digits and would have stated it clearly in the requirements section, or followed up the HOTP RFC with another one clarifying these details.
This is very useful to anyone writing TOTP tools, big thanks to the author.
However, reading the article this section caught my eye:
"As we now know, SHA-1 has some fundamental weaknesses. ... But the TOTP authors disagree and allow a for some different algorithms to be used."
With significant compute resources SHA1 can be broken for some use cases, but I don't think this is one of them. Is HN aware of any practical attacks against TOTP-SHA1?
HMAC-SHA1 is not broken as a pseudorandom function. SHA1 is only broken for collision-resistance for the moment. That doesn't mean it's great, or that you shouldn't upgrade.
Do you think that the technique of using the last 4 bits to choose a further pseudorandom 31 bits from the rest of the hash MIGHT mitigate SOME future weakness as a PRNG? Or do you have confidence it is completely useless? Or neither, of course.
I think it's weird. Either you trust your output to be pseudorandom or you don't. These weird hacks may be fine, but they feel like adding "safety" duct tape onto the wings of a passenger jet.
Given that nobody uses TOTP with Sha256 today I assume there is no real reason to upgrade.
But has anyone actually reaserached this? For all we know, using hma-sha256 in TOTP may actually make it less secure.
About the "validity" period: in practice it seems to be more like a rotation period, the code themselves seem to be valid longer than this, that is you can often enter them for quite some time after they get rotated.
And I also often wonder about the OTP aspect, whether there is any protection in TOTP backends for the same TOTP being used twice.
The validity is up to the service, as described in https://datatracker.ietf.org/doc/html/rfc6238#section-5.2
Because of clock skew and latency, a service might choose to allow the previous and/or next code as being acceptable.
They repeat roughly every year. 1,000,000 periods of 30 seconds is just less than 350 days.
They do not repeat. The totp is calculated with the whole Unix timestamp, not a cyclical counter.
Yes, but there are only 6 digits available. So a million different codes can be generated. A new code is generated every 30 seconds
Rather pleasingly, 30,000,000 seconds is roughly 1 year.
So, assuming a uniform distribution of codes, you would expect to see a repeat after a year.
A code may be repeated, but they should not enter a cycle (which would be a real disaster.)
You also need to keep in mind the birthday paradox. Repeated codes will happen with high probability after around sqrt(1,000,000) = 1000 code emissions. https://en.wikipedia.org/wiki/Birthday_problem
I am not sure whether TOTP can be used two times, but that longer validity aspect already lead to a vulnerability called "Microsoft MFA AuthQuake"
I'm so glad this has been created. I've felt for quite some time that TOTP is such a triumph of simplicity and security. It's so simple that if you have your secret key on hand, you can generate your TOTP code with just 30 lines of Python!: https://github.com/susam/mintotp/blob/main/mintotp.py
It's amazing! Its default UX assumes you're basically air gapped. It works based on a minimal shared secret that's totally portable. Conceptually, it's as clear as "hash (your secret+(now()//30)), display hash as decimal, only show the last 6 (ish) digits".
What a triumph. I love TOTP.
This is meant as a sidelong critique on passkeys.
It doesn't have the killer feature of passkeys though, vendor lock-in. Which is the sole purpose of them.
Wrong, what it's missing from passkey is phishing resistance. And you can use passkeys with at least a few independent commercial password managers if you are not happy giving your passkeys to Google & Apple (I work for one such password manager)
Psst: your employer is a vendor benefiting from said lock-in.
Without a means for transfering from one password management resource to another, passkeys create an increasingly significant friction against migrating one's account and artifically reduces competitive pressure on the vendor.
The phrase we use for that anti-consumer phenomenom is "vendor lock-in"
(If your employer has an effective and practical import and export technique for passkeys, I would be delighted to hear so and apologize for the suggestion otherwise. But as far as I've seen, that's not a thing.)
Passkeys are still very much work in progress but there will be standard ways of transferring passkeys between password managers at least https://www.theverge.com/2024/10/15/24270875/password-manage...
I'm not working specifically on passkeys lately so I don't know exactly what is already available to the public, I'll ask around
That is table stakes. So, rewind 5 years. Introduce that, remove attestation while you are at it. And make sure we have multiple compatible implementations.
Without it passkeys are a sham. And since that was and is the case I don't see any conceivable future where passkeys benefit anyone but google/apple/ms. For the sake of the web it needs to die.
Being able to export a passkey sounds like a bug, not a feature. I was surprised that the spec was altered to allow hosting in the cloud at all, I thought it was more interesting as a means of keeping secrets in secure enclaves.
The expectation is that for any service you should be able to have multiple passkeys -- including multiple passkeys from different vendors. I'm not sure how that exactly causes lock in?
That misses the overhead of having to rotate the passkeys for thousands of websites you've used them on.
Exactly, you can just register passkeys from multiple providers if that's a fear. Worst case (i.e. Google nukes your account and you've only set one passkey), you go through the usual email recovery or backup codes process and set a new one.
It would be good to have a FOSS-implementation with TPM as the storage backend for computers and phones. Then people wouldn't need to use keys stored in the cloud.
Have a look at Keepassxc. It doesn't use a tpm, but supports passkeys and is portable.
This is the sole reason I'm avoiding passkey setup. How big of a worry is it?
Assuming you've only set one passkey (you can add multiple passkeys from different passkey managers) it's no more of an inconvenience than if you lost your password. Normal recovery/backup code processes still work.
TOTPs are amazing! I'm currently building a low-power, offline ESP32 device that generates time-based one-time QR codes and displays them on an e-ink screen. I plan to use it for proof of presence.
What's your approach for keeping the clock in sync?
I use a GPS module that turns off once it has retrieved the time and location.
if you're only doing the signature generation rather than the full protocol (which TOTP leaves entirely up to the user), then the code to generate a passkey signature wouldn't be any longer
Yeah, I have implemented it in PHP and Lua as well, cannot check the LOC right now, but it is around ~30 as well, if not less, at least in PHP.
It’s definitely a fairly trivial implementation, here’s one of a few dozen lines of (not very clean) Swift: https://github.com/JustAman62/ovault/blob/7ee38b7e5c2666cd34...
The simplicity of the implementation makes a test suite even more valuable, as it’s really quite hard to figure out if your implementation is correct.
As part of writing the test suite, I also wrote an implementation in JavaScript. It uses the Web Crypto API rather than external libraries.
https://shkspr.mobi/blog/2025/03/using-the-web-crypto-api-to...
> HOTP spec, however, does place a minimum requirement - but no maximum:
>> Implementations MUST extract a 6-digit code at a minimum and possibly 7 and 8-digit code. Depending on security requirements, Digit = 7 or more SHOULD be considered in order to extract a longer HOTP value. RFC 4226 - 5.3. Generating an HOTP Value
Am I bad at reading specs or is there a maximum here? If I say „minimum 6, possibly 7 or 8“, that sounds like 9 and more isn’t allowed.
HOTP requires a minimum of 6 digits (R4 in Section 4), but does not actually set a maximum. The algorithm in the RFC caps at 9-digits per Section E.2 (and my cursory reading of the code). That's not a required limit, but a technical limit of their choices. It also happens that 9-digits probably is about as big as you'd want to go, three 3-digit chunks aren't much harder to remember and type in correctly but past that it'd become increasingly impractical. The "and possibly 7 and 8-digit code" should be taken as descriptive, not prescriptive. The requirements occur earlier in the document and do not set a maximum. If they'd intended 8 as the maximum then they wouldn't have presented an algorithm that goes to 9 digits and would have stated it clearly in the requirements section, or followed up the HOTP RFC with another one clarifying these details.
https://datatracker.ietf.org/doc/html/rfc4226