
Tiny Toy TOTP Generator in 18 Lines of Python - susam
https://github.com/susam/toytotp
======
ddevault
In practice, clocks are often out of sync enough that you want to give some
leeway and accept some codes on either side of the current code. The
implementation I use for sourcehut is similarly tiny:

[https://git.sr.ht/~sircmpwn/meta.sr.ht/tree/master/metasrht/...](https://git.sr.ht/~sircmpwn/meta.sr.ht/tree/master/metasrht/totp.py)

~~~
susam
Yes, in fact the RFCs for both HOTP and TOTP have a section each dedicated to
resynchronization of counter/clock to account for client and server being out
of sync. Here are the relevant sections:

\- RFC 4226 (HOTP): Section 7.4 (Resynchronization of the Counter):
[https://tools.ietf.org/html/rfc4226#section-7.4](https://tools.ietf.org/html/rfc4226#section-7.4)

\- RFC 6238 (TOTP): Section 6 (Resynchronization):
[https://tools.ietf.org/html/rfc6238#section-6](https://tools.ietf.org/html/rfc6238#section-6)

In case of HOTP, the client's counter could be ahead of the server's counter
if a user requests multiple HOTPs from the client before the user presents the
HOTP to the server. Therefore the server should look ahead according to a
permissible look-ahead window to see if any succeeding HOTP value matches the
HOTP presented by the user or client.

In case of TOTP, there is of course the problem of clock drifts. Therefore the
server should look backward as well as forward by a few time steps to see if
any TOTP backward or forward matches the TOTP presented by the user or client.

~~~
dunham
In the past, when I've implemented TOTP on the server, I've allowed for a
couple of time periods of drift. But I recorded the time period that last
matched, and only allowed subsequent authentication attempts to be strictly
greater than the last successful time period, to prevent replay attacks.

~~~
susam
Yes, RFC 6238 requires this too. Quoting from
[https://tools.ietf.org/html/rfc6238#section-5.2](https://tools.ietf.org/html/rfc6238#section-5.2):

> Note that a prover may send the same OTP inside a given time-step window
> multiple times to a verifier. The verifier MUST NOT accept the second
> attempt of the OTP after the successful validation has been issued for the
> first OTP, which ensures one-time only use of an OTP.

------
gingerlime
Plugging my hotp/totp python library hotpie[0]. Written originally in 2010.
Around 100 lines including comments and tests against the RFC. Available on
Pypi for python 2.x and 3.x.

The beauty of HOTP/TOTP for me is its simplicity. And being able to write a
simple implementation makes it easy to test, debug and be sure that there are
little or no holes.

[0]
[https://github.com/gingerlime/hotpie](https://github.com/gingerlime/hotpie)

~~~
fr33104d
I remember having fun implementing that rfc and having a sort of Google
authenticator on my Linux box besides Android.

[https://github.com/bjornua/totp.py/blob/master/totp.py](https://github.com/bjornua/totp.py/blob/master/totp.py)

------
porterde
Being of a certain age and in the UK, I was disappointed to find this is not
an 18 line Top Of The Pops generator... :)

~~~
ggm
Comes with mandatory "whole lotta love" algorithm and bad lip sync

------
xendo
5 most important lines:

    
    
      import base64
      import hmac
      import struct
      import sys
      import time

~~~
pushpop
I get your point but equally the point of programming languages is to abstract
away the harder stuff. The question is just how much abstraction developers
want between the code and the execution.

I mean, you could make the same claim about one written in C because it
doesn’t include stdio.h. So where do you draw the line? Assembly?

~~~
xendo
My point is that saying that something is written in 18 lines of code when
actually all the heavy lifting is done by the libraries is misleading. That
doesn’t mean I don’t appreciate Python’s expressiveness and rich standard
library.

~~~
icebraining
Yeah, but that comment can be made about literally every software in
existence. Even core language keywords are doing a lot of heavy lifting in the
interpreter.

In Python, the stdlib is effectively part of the language, so as long as
they're not installing third-party libs, I don't see how it can be misleading.

~~~
anamexis
I think you both raise valid points.

This was an interesting thing to look at to see get a quick idea of how TOTP
works.

On the flip side, though, if this we had this hypothetical implementation:

    
    
        import totp
    
        if __name__ == '__main__':
            totp.generate()
    

Well, we probably wouldn't be here discussing it.

~~~
PeterisP
Well, the key part essentially _is_ just like that, it's the single line

    
    
         mac = hmac.new(secret_bytes, counter_bytes, digest).digest()
    

and everything else is just converting data to/from the proper format for this
function.

~~~
anamexis
Well sure, but given that TOTP is itself just formatted HMAC, that "everything
else" of formatting is the essence of TOTP.

Otherwise you probably wouldn't say you're implementing TOTP, you'd say you're
implementing HMAC.

------
shakna
Along with micropython-hmac (MicroPython doesn't include hmac), and the touch
sensor on an ESP32 I threw together an authenticator with the same inspiration
as this project. It seems to work quite well, overall.

~~~
saghul
I haven't used MicroPython in a long time, but it seems to include hmac:
[https://github.com/micropython/micropython-
lib/tree/master/h...](https://github.com/micropython/micropython-
lib/tree/master/hmac) right?

~~~
shakna
micropython-lib packages aren't included in the base MicroPython (well, not
the main one. The Pycopy fork does), and are published on PyPI under
micropython-x. You've found the source for the package I named.

~~~
saghul
Ah, didn't know that, TIL!

------
daneel_w
Here is one in 4 lines:
[https://github.com/stolendata/totp/blob/master/totp.php#L42-...](https://github.com/stolendata/totp/blob/master/totp.php#L42-L45)

------
yuri91
The RFCs for HOTP[0] and TOTP[1] are very readable.

I built a small cli utility to manage my TOTP logins easily using them as
reference.

[0] [https://tools.ietf.org/html/rfc4226](https://tools.ietf.org/html/rfc4226)

[1] [https://tools.ietf.org/html/rfc6238](https://tools.ietf.org/html/rfc6238)

------
ianai
Am I the only one who found the use of acronyms here a little annoying? Like,
use the whole word expansion at least once before introducing the acronym.
That should be standard practice regardless of discipline and audience.

~~~
tptacek
Which acronym? Expanding "TOTP" isn't going to help anyone; what you really
want is the short sentence that simply explains what TOTP is.

~~~
fwip
I didn't know what it meant, and looked it up. "Time-based One Time Password"
explains a hell of a lot more that "TOTP." From just those 4 words I figured
out we were talking about the kind of thing that powers 2-factor auth
implementations where get a time-based one-time code.

Before I looked it up, I thought it was somehow related to NTP.

~~~
tptacek
My point is that "time based one-time password" doesn't really tell you
anything about what TOTP is; what you want is the phrase "the protocol code-
based 2FA applications like Google Authenticator use", after which you don't
care anymore about the stupid name the protocol has.

It's a little bit like expanding "transport control protocol". I mean, sure, I
guess it's marginally better than the acronym TCP? But really, that's not the
clarification you want to provide to someone who doesn't know what TCP is.

Also, obviously, if you don't know what TOTP is, you might just not be the
audience for the article. Remember that most people don't write their personal
blogs (or, in this case, their Github personal projects) specifically for an
audience of Hacker News people, even though HN sometimes makes it seem that
way.

------
Thorrez
>However, doing so defeats the purpose of two-factor authentication (2FA).

There are still some 2FA benefits from this. The biggest one is for people who
reuse passwords. Some websites might put you into a higher security tier if
you have 2FA. Also if you have to choose between SMS vs this, this has some
benefits, for example it can't be hijacked by social engineering your phone
provider.

Also it says regular TOTP protects you from keyloggers. That's not fully true,
because the keylogger could steal your TOTP code as you enter it into the
website. If the keylogger is realtime, it could log into your account before
you're able to. Or if you assume there's malware on your computer, it could
steal your cookies, or perform whatever account actions it wants directly on
your computer.

~~~
susam
The README does not say that TOTP protects us from keyloggers. I was either
not clear in writing that paragraph or you may have misundertood that
paragraph. Here's what the README says,

> If your desktop/laptop device is compromised, then both authentication
> factors would be compromised. The attacker can steal the first
> authentication factor that only you should know (e.g., password) by running
> a key logger on the compromised device. The attacker can also steal the
> second authentication factor that only you should have ...

What I mean here is that generating the TOTP on the same system that we would
use to log into a website with 2FA defeats the purpose of 2FA because the
attacker can steal the TOTP secret key in addition to stealing the password
(keylogger being one way to steal the password).

~~~
eikenberry
I think the grandparent is just trying to say that while it defeats one of the
purposes behind 2fa, it doesn't defeat all of them. Security is all about
trade-offs and defense in depth and 2fa is still valuable in this sense even
with the 2fa secret stored on your laptop. That is, it trades off some
security for convenience but is still more secure than not having 2fa.

~~~
susam
I agree. I think it is acceptable to trade some security for convenience and
in this case the convenient solution is still more secure than not having 2FA
at all. However, the fact that some security is being traded for convenience
should be documented in the README, otherwise one can criticize that the
README is promoting a less secure usage of TOTP.

------
varshithr
If we don't have to follow PEP8, how many lines can we condense it to? I am
going to try.

------
_def
Readability is more important than your ego.

------
tedk-42
MFA - what you have.

I personally prefer this golang library to generate my OTP codes
[https://github.com/pquerna/otp](https://github.com/pquerna/otp) as it's much
faster than running a python script.

My personal computer is the 'what I have' to do the second factor of
authentication for most sites via a shell script, followed by xclip (linux) or
pbcopy (mac). I prefer this over browser extensions using javascript to paste
in your OTP code for you.

~~~
icebraining
If you want raw speed, oathtool is hard to beat. Only 132KB to load to memory,
compared to the megabytes of the typical Go binary. But unless you're playing
an FPS where you have to input TOTP codes to shoot, this script is probably
fast enough (80ms on my old laptop).

~~~
userbinator
_Only 132KB to load to memory_

It's amusing to see people think 100KB+ is small, when the whole TOTP
algorithm, including SHA1 (probably the biggest part), and the input/output
conversion, likely needs only a few KB of code. With the exception of SHA1,
everything else is doable in dozens of bytes.

~~~
icebraining
True, but I'm just talking about the sizes of the binaries generated by the
default settings of common compilers (gcc, in the case of oathtool), for
comparing orders of magnitude. I'm sure there's plenty of fat you could trim
by simply tuning the compilation a bit.

(Oathtool also supports SHA2, since it may be used by other implementations,
per the RFC. )

