Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
MinTOTP – Minimal TOTP generator in 20 lines of Python (github.com/susam)
157 points by susam on Oct 18, 2022 | hide | past | favorite | 51 comments


Kind of offtopic. IPhone has a totp password key store in icloud. It is very unknown feature.


Docs or it didn’t happen.



This is amazing ! Thanks so much!


can't decide if this is genuine excitement or sarcasm. good job, both of you.


bunch of upvotes tells me this is real for everyone.


It’s not sarcasm :)


I'd call this 7 lines rather than 20. The rest is function definition and argument parsing. But really it should be just 1:

  pyotp.TOTP('base32secret3232').now()
Why is this relevant? Because it's inadvisable to write security related software if you're not prepared to take on the full range of possible problems that introducing another security library brings (CVEs, supply chain security, etc.). The adage that doing security right requires doing everything right. Doing security wrong only takes 1 thing.


Active discussion about TOTP underway here: https://news.ycombinator.com/item?id=33245042


This reminds me a lot of my own attempt[0] from a few years back, but packaged as a Python module instead of a standalone script.

[0] https://github.com/grahammitchell/google-authenticator


There's Go and Ruby clones from the issues. This even seems something I could do myself, nice!


It can be done as a bash one liner, so yeah, definitely doable in minimal lines of Python code. More than 50% of this file is “overhead” even.


It can be done with a python 1-liner by that logic!

`mintotp.totp('ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS')`


I meant specifically chaining generic commands, not a purpose built tool.


Can you paste your bash one-liner here?


To be very clear, I don’t use this, but I confirmed it does work and generates the same codes as the linked article. Requires Bash (3+) and doesn’t work as written in Zsh.

https://gist.github.com/jsjohnst/95f34bc1f6ab46fd5c038138f0e...

————

   read -r -p 'Secret Key: ' secret_key; dgst=$(printf '%016X' $(($(date -u '+%s') / ${TOTP_PERIOD_SECONDS:=30})) | xxd -r -p | openssl dgst -sha1 -mac hmac -macopt "hexkey:$(printf $secret_key | tr '[:lower:]' '[:upper:]' | base32 -d | xxd -p)"); offset=$(( 2 * 16#${dgst: -1} )); token=$(( ( 16#${dgst:offset:8} & 0x7fffffff ) % 10**${TOTP_DIGITS:=6} )); printf "%0${TOTP_DIGITS}d\n" "$token"


  oathtool --base32 --totp VHR2WYCQPNXIFB2SPLIAE2AZHQ


Which part of this involves bash? This is an "oathtool one-liner" if anything.


The other "bash" one-liner references a bunch of tools that are not part of bash or even a POSIX environment. FWIW I would rather use oathtool than the other monstrosity.


> The other "bash" one-liner references a bunch of tools that are not part of bash or even a POSIX environment.

You do understand that there is a commonly accepted definition of a bash one-liner and that the one I shared fits that definition spot on, right? Further, there is a bunch of very Bash specific code in it that doesn't work in most other Posix compliant shells, so your scare quotes feel even more like a failed trolling attempt.

> FWIW I would rather use oathtool than the other monstrosity.

So would any sane person. The point wasn't what you should use, it was what is possible, which if you had read comments up the thread chain and tried to understand the context rather than making it a pissing contest about which tool is better when that wasn't even the question at hand.


> You do understand that there is a commonly accepted definition of a bash one-liner and that the one I shared fits that definition spot on, right?

We both disagree with you. What would that definition be?

The one-liner in question doesn't have any of bash's syntax in it. You could pass those words to execve() directly, without any shell, and it would still work. Calling it a line of bash code is just wrong.

Similarly, would you say this is a PHP one-liner? You can run it through PHP, but it contains no PHP syntax:

    <h1>My web page</h1>


@jsjohnst is talking about his one-liner[0] that has lots of bash syntax, not the GP's one-liner[1] that only calls some external program.

[0]: https://news.ycombinator.com/item?id=33254917

[1]: https://news.ycombinator.com/item?id=33249562


I had decided to back away, so thank you for pointing that out! There’s not just Bash syntax in mine, but rather advanced Bash that many terminal users wouldn’t recognize, so I was confused wth GP was on about until you pointed out he was probably referring to the other post which seemingly was trolling.


> We both disagree with you. What would that definition be?

A “bash one-liner” as in common use is a chain of commands using any combination of pipes, subshells, variables, tests, loops, etc.

The distinction between a “bash one-liner” and a “bash script” is that extraneous white space is removed and generally speaking should still be readable.

Admittedly mine is pushing the limits of readability do to the advanced bash syntax, but I still feel it qualifies.

Also, just because my reply was threaded two levels below yours doesn’t mean I was replying to you. I had upvoted you in fact as I actually agree with you re: oathtool example. The party I was replying to actually doesn’t agree with you and thinks that the oathtool example is a “bash one-liner” and that because I call other basic Unix commands mines not, which is just silliness.


I'm not making fun of your bash, a lot of bash one-liners are, essentially, tongue-in-cheek and not intended for daily use.

What I take issue with is the arbitrary point made by the post I replied to that using oathtool is somehow not "bash one-liney", whereas your attempt is somehow "bash one-liney".

Could I make a bash one-liner that uses a rube goldberg machine of echo, netcat, dd, ed etc. to make a HTTP request? Yeah I could, or I could just use curl and it's also still a valid "bash" one-liner.


> Could I make a bash one-liner that uses a rube goldberg machine of echo, netcat, dd, ed etc. to make a HTTP request? Yeah I could

Can you paste your bash one-liner here?

As previously, I’m interested in seeing your Rube Goldberg machine, and mainly for adoration, not practical use. `curl`, as previously, would not qualify.


The basics is something like `printf "GET /\r\n" | nc neverssl.com 80` (interestingly this is considered a HTTP 0.9 request, which HTTP 1.0 compliant servers need to handle).

Add in few useless pipes to cat and dd, and you got yourself a Rube Goldberg machine.


> interestingly this is considered a HTTP 0.9 request, which HTTP 1.0 compliant servers need to handle

For GP, the reason this is an HTTP/0.9 request is because it doesn’t specify a version number on the GET line (HTTP/1.0 would require that to be “GET / HTTP/1.0\r\n\r\n”). HTTP/0.9 was the “one-line” version of HTTP before headers and such were added. Not sure if @22c intended the pun there, but if so, we’ll played!


`curl` is a command

`curl | awk …` is a “shell one-liner” (albeit very basic)

Throw in some $(()), ${var:##}, etc and it’s a great example of a “bash one-liner”


I don't see any fluff myself. Where's the overhead?


It’s not “fluff”, it’s overhead. The real work there is done in about 4 lines (which could be compacted to two and still be somewhat readable). The rest as someone else already said, imports, helper functions (there’s no technical need for totp to just be a wrapper around hotp unless you want to expose hotp or be illustrative), function overhead, etc.

That said, TOTP shouldn’t be treated as code golf, especially in production code. The point is the protocol is trivially simple to implement not that it’s X lines in Y language.


Ah, okay. It doesn't appear overly golfed to me. It felt like the point of publishing it was probably similar to what you're saying. That the TOTP algorithm isn't terribly complicated.


It's not golfed at all, it's more or less where you get when you implement TOTP by hand from the RFCs.

Except for the final reduction, which in the RFC is implemented via a mod, and the base32 decoding, which is not part of either HOTP or TOTP RFCs, but is instead part of the ad hoc "key uri" format. Which is a funny spec because it was defined for Google Authenticator yet AFAIK GA uses none of the parametrisation so that's only useful if you specifically want to exclude GA users.


Wrapper functions, imports, input handling, etc.


Huge thanks to author. I use mintotp pretty much always I need to get token from "pass" stored secret.


Huh, have you heard of the popular pass(1) extension pass-opt[1]?

It allows one to get OTP secrets via a simple:

pass otp secretname # Add -c for clipboard, as usual

And because the OTP side is just a otpauth:// URL on a line of the GPG file, we can still use the rest of the secret for other things:

pass secretname -c

Real smooth!

[1]: https://github.com/tadfisher/pass-otp


Shameless plug of my TOTP in '4' lines of PL/pgSQL: https://gist.github.com/bwbroersma/676d0de32263ed554584ab132...


Hats off to the author for providing 482 lines of documentation (sloc) to accompany it!


Tangentially, I wrote a Python generator for the Duo TOTP app which draws from local iOS backups: https://nathancahill.com/duo-cli


Not pictured: the wall of incomprehensible math code implementing SHA1. Most languages do not provide such implementations by default so I struggle to really call this only 20 lines.


The hmac can be forgiven, in my opinion. But the ~30 lines of code for the Python stdlib's Base32-decoder, which would inflate the line count by 150%, is harder to forgive.

b32-decoding can be made compact, of course: https://github.com/stolendata/totp/blob/master/totp.php#L16-...


Base32? The hard part is to implement QR code scanning across a range of different cameras, lighting conditions and shaky hands. Not to mention melting your own sand to make silicon.


Don't forget creating the rock which you then weather into sand.

Frankly TFA should work a bit harder on this apple pie, this is just lazy.


What other library functions you consider as "cheating"?


In my opinion the Base32-decoder is the main "cheat" of this TOTP implementation. See my comment a few lines up.


The base32 decoding isn’t even part of TOTP.


It's not part of the RFC, but it is part of TOTP as we know it because of conventions that stuck.


Anything above BIOS level. /kidding


Fair assessment, but the crypto community has that one high standard of "don't roll your own crypto" unless you have to or it's a learning project.

Now I'm interested in a minimal SHA-1 implementation!



Woohoo! Thanks for that susam - I can now use this on my Pi Zero portable computer project: the missing piece! The GPG option is sneakily good also.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: