Modern cryptography on the NES


Super Tilt Bro. is an online game. You can even make an account to be placed on the official ranking, to do that you simply enter your login/password and... the technical trouble begins!

Nice little credentials that need to be protected with 8-bit power!

We are on 2024, hackers do not operate on Commodore 64 anymore, the game needs to protect you against today's threats. In this article we will go into the depths of the cyber-war by introducing how your passwords are typically protected, and the choices made for Super Tilt Bro. Spoiler, SHA-256 is at the heart of all that, and its implementation on the 6502 CPU will be discussed.

Protecting your password

There are a lot of ways to protect passwords. The minimum expected nowadays is that the password never transits unencrypted on the network, and is not stored on the server. A typical website will only send it over HTTPS, which is an encrypted protocol, and store a salted hash on the server. The salted hash is black magic: when you send your password the server can verify it is the good one by comparing it to the hash, but with just the hash no one can compute your password back. Ultimately your password is protected against a hacker that sees you internet connection thanks to HTTPS, also, a hacker gaining access to the server doesn't get your password thanks to salted hashes.


This is how tiny websites protect your data. Big ones can use more complex schemes.

This is cool, now we have an 8-bit CPU and two kilobytes of RAM, can we do all this? Yes, and no. HTTPS in itself is pretty CPU intensive, and very convoluted as it evolved over 30 years supporting multiple generations of encryption schemes. It would be a terrible fit with our beloved entertainment system. Modern hash algorithms are designed with 32-bit CPUs in mind but are very compact, definitely doable.

Anyway, before choosing a technical solution we need to understand what we want to protect, and against whom. There is no ultimate security, one need to choose our fights. Preventing a player connecting to another's account is the first consideration but knowing that Super Tilt Bro. is a small game with a wholesome community, basic security for that should be good enough. Note that the community can change over time, and anti account stealing measures must have a way to evolve if necessary. The second and most important point is to protect player passwords against theft. People reuse passwords, it is bad but a fact. If a hacker with access to a 2024 computer watches your internet connection or gain access to the server, they should not be able to find your password. You may lose more than your gaming account even if you use your credit card number as a password (I know you do it!)

OK, account-stealing and more importantly password-stealing are our priorities. Considering that players will not go as far as eavesdropping each other's internet or hack the server to steal accounts, the most important part is the salted hash part. Making the password secret to a hacker seeing only the salted hash, even with access to modern computational power. To achieve this level of protection, the game computes itself the hash of the password, salted by the login and sends that salted hash to the server once to obtain a login token. The password itself never leaves the NES. The login token is a simple number that can be placed in any message and identifies the logged session, avoiding sending the salted hash in every message, and simplifying server-side authentication. The hash algorithm is our main defense against the dark armies of the web, a state-of-the-art algorithm is necessary, Super Tilt Bro. uses SHA-256 which is unbroken and performs efficiently enough on the 6502 CPU.


The login protocol for Super Tilt Bro.

There would have been other alternatives, without citing them all here are some that can come to mind, and why it was not chosen. The Wi-Fi chip could handle HTTPS, partially, it performs the encryption but no server authentication. A challenge could be used instead of sending the hash, it would have prevented an attacker to log in knowing only the salted hash at the cost of a bit more complex login step, it is definitely an improvement if stealing accounts becomes a problem. Two-factor authentication, it is to be added over the login protocol, adding it can be achieved without changing the choices made above.

Let's discuss the SHA-256 hash algorithm

Hash algorithms have many fascinating properties. The one of interest in our case is that it can take data of any length and transform it in a way that it is not possible to come back. In mathematical terms, this is a `h` function so that `h(m) -> x` and no way to discover `m` by knowing only `x`. Super Tilt Bro. places the login/password as `m` and transmits only `x` for authentication purpose.

SHA-256 operates on big endian 32-bit words. It requires 8 variables storing the final hashed value, 64 mathematically important constants, and an array of 64 variable words where most operations take place. This is 136 words, or 544 bytes, almost half of which are constants that can be stored in ROM. This is a very low memory footprint by today standards, and fits nicely in the NES limited RAM.

The message to be hashed is padded to be at least 64 bytes long, then is processed by successive chunks of 64 bytes. Each chunk passes through 64 rounds of pseudo-random bit shifting and logical operations, effectively producing a random-looking value in the 8 variables composing the hash. That is many iterations of multiple 32-bit operations, and definitely a big task for the 6502 CPU. Thankfully we can leverage our specific case to alleviate that.


SHA's way of scrambling your message

First, it is OK to be slow to compute the hash. The computation can be spread over multiple frames since it is done once, in the connection menu. That said, it cannot take several seconds. The player should not notice the connection being particularly slow just because of that. There also is a particularity that allows some shortcuts: the message's size is known. The login plus the password take 32 bytes, that's less than one chunk: we can implement pre-known padding and only handle a single chunk. Finally, using an 8-bit CPU we are not bound by the big endian nature of SHA-256 and can bring chaos to the variable and constant tables!

Instead of conforming to the table being 64 words of big endian 32 bits, Super Tilt Bro. rearanges it to 4 tables of 64 bytes. Each table containing a byte of each word. It allows accessing them more naturally in absolute-indexed addressing mode:

; Round constants K
;  Stores 64 32-bit words,
;  k0 is a table of the MSB of each word
;  k3 is a table of the LSB of each word
;
;  Will be indexed, should be aligned on 256 bytes
k0:
.byt $42, $71, $b5, $e9, $39, $59, $92, $ab
.byt $d8, $12, $24, $55, $72, $80, $9b, $c1
.byt $e4, $ef, $0f, $24, $2d, $4a, $5c, $76
.byt $98, $a8, $b0, $bf, $c6, $d5, $06, $14
.byt $27, $2e, $4d, $53, $65, $76, $81, $92
.byt $a2, $a8, $c2, $c7, $d1, $d6, $f4, $10
.byt $19, $1e, $27, $34, $39, $4e, $5b, $68
.byt $74, $78, $84, $8c, $90, $a4, $be, $c6
k1:
.byt $8a, $37, $c0, $b5, $56, $f1, $3f, $1c
.byt $07, $83, $31, $0c, $be, $de, $dc, $9b
.byt $9b, $be, $c1, $0c, $e9, $74, $b0, $f9
.byt $3e, $31, $03, $59, $e0, $a7, $ca, $29
.byt $b7, $1b, $2c, $38, $0a, $6a, $c2, $72
.byt $bf, $1a, $4b, $6c, $92, $99, $0e, $6a
.byt $a4, $37, $48, $b0, $1c, $d8, $9c, $2e
.byt $8f, $a5, $c8, $c7, $be, $50, $f9, $71
k2:
[...] ; 64 other bytes
k3:
[...] ; 64 other bytes

; Now we can access easily all bytes of any word with absolute-indexed addressing ldx #5 lda k0, x ; Equivalent to C: k[5] >> 24 lda k1, x ; Equivalent to C: (k[5] >> 16) & 0xff lda k2, x ; Equivalent to C: (k[5] >> 8) & 0xff lda k3, x ; Equivalent to C: k[5] & 0xff
; That in itself would not have been a big performance enhancer, ; because once X is correctly set on a 32-bit words table we can access bytes with an addition in origin address, ; "lda k+0, x", "lda k+1, x", ... ; But SHA-256 requires looping and computing indexes quite a lot, having a natural value in the register simplifies ; the indexing of an otherwise complex algorithm.

This SHA-256 implementation has not been benchmarked, but the connection feels instantaneous, no need to optimize further. If the performances have met the need of first try, that was not the same for correctness. Some bugs appeared, and a C implementation has been written to be able to compare the internal state at different steps of the algorithm. To ensure accuracy, Super Tilt Bro. now has some unit tests and even a fuzzing test stressing explicitly its SHA-256 implementation.

Conclusion

Super Tilt Bro. uses modern cryptography to protect your password. Do not assume it is enough! Errors can have slipped in, this is just a game, do not trust it to protect a valuable password. Anyway, it was fun to implement SHA-256 on the NES!

If you want to go further in the rabbit hole, you can follow these links:

Wikipedia article on SHA-2 has a great pseudocode for the SHA-256 implementation: https://en.wikipedia.org/wiki/SHA-2.

Another mad man who did the same, this article was my starting point: https://bumbershootsoft.wordpress.com/2019/04/24/implementing-sha-256-on-the-650...

Super Tilt Bro.'s SHA-256 implementation, it is reusable and even expandable to handle multiple chunks: https://github.com/sgadrat/super-tilt-bro/blob/713cae919834535b0c756973c33e2ea2e...

Get Super Tilt Bro. for NES

Download NowName your own price

Comments

Log in with itch.io to leave a comment.

(+1)

Awesome work! Glad to know the extra step is being taken to protect us from malicious intent.