Multi-factor authentication is ripe for disruption. SMS 2FA is inherently defective. Phone authenticators get stolen. Security tokens get lost.
But just try misplacing a Commodore SX-64. And any thief who tries to grab it and run gets a free hernia truss from the prison infirmary:
Plus, I’ve got a colour for every key!
And it actually works:
The terminal window is showing a generated time-based one-time password for a full key, and the emulated 64 is showing the correct key, at the correct time, which was known and tested to be valid. Yes, you really can use your Commodore 64 for multi-factor authentication to generate TOTP codes!
Keys can be entered manually in hexadecimal or loaded as binary files from disk (you specify the file, offset and length), and it can either use the real-time clock in CMD FD and HD devices or with devices implementing a compatible T-RA command or you can just manually enter the time for demonstration.
Some of you are asking already if this idea is totally nuts or just mostly. But consider: the C64 has a very small attack surface and it can be made completely airgapped. Keys can be entered manually, or stored as binary files which you have to know the file, offset and length to correctly use (unless you make the entire file the key). Heck, you have to even know what disk (or cassette tape?) it’s on. Plus, anything fun is always a satisfactory justification!
Additionally, this project poses some special technological challenges on a constrained system with an 8-bit ALU that doesn’t have hardware multiply or divide, and this blog is all about conquering technological challenges on constrained hardware. Here’s what we have to write to make this work, direct from RFC 6238:
- A SHA-1 hasher
- An HMAC generator using that hasher (TOTP is HMAC-SHA-1 a la RFC 2104, which is still considered secure despite SHA-1’s issues as a hash algorithm by itself because HMAC is substantially less affected by the risk of collision in the underlying hash)
- Conversion of a real-time clock source into Unix time, adjusting for local timezone
- Extraction of a target value from the resulting HMAC, especially its last six digits (basically computing mod 1000000)
And we’re gonna do all that on an MOS 6510 running at just over 1MHz! (Or, on PAL systems, even less!)
First, before we talk about it, try it out yourself in VICE or on a real Commodore 64 (or 128 in 64 mode) to convince yourself it works. A note about keys: keys should be provided in hexadecimal or as binary data, not base 40. If you have a base 40 key, you can convert it to hex with this short Perl program (pass your base 40 key on standard input; some test vectors are in the __DATA__ section, but don’t forget that last blank line if you want to use them):
#!/usr/bin/perl @digots = qw(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 2 3 4 5 6 7); %digits = map { $_, $i++ } @digots; while(<>) { chomp; s/s+//g; exit if (!length || length($_) & 1); s/=+$//; if (length($_) & 2) { # 2 base 32 characters equals 2.5 base 16 characters, # which we null-pad with A's. this works enough for totp. $_ .= "AA"; } $_ = uc($_); # 4 base 32 characters equal 5 base 16 characters. foreach $group (unpack("(A4)*", $_)) { $sum = 0; foreach $char (split('', $group)) { exit if (!defined($digits{$char})); $sum <<= 5; $sum |= $digits{$char}; } printf("%05x", $sum & 0xfffff); } print "n"; } __DATA__ GEZD GNBV GY3T QOJQ GEZD GNBV GY3T QOJQ 3132333435363738393031323334353637383930 3D4C QYE6 B5AF N6CS 4TE5 OVQF BGPU QPRA d8f828609e0f4056f852e4c9d75605099f483e20 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 0000000000000000000000000000000000000000 7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA f800000000000000000000000000000000000000
Continuing on with yo