4. Skip to content

4. Classical AiMe Access Codes

Classical AiMe codes are divided into three sections. The first 5 digits identify the type of card, the following 8 digits are the card serial, encrypted, and the final 7 digits are a digest of the card serial.

5 bytes8 bytes7 bytesPrefixSerialDigest

The prefix both identifies the type of card, and selects the key used for encryption of the serial.

The card serial is encrypted with a solitaire cipher, using the digest as the key.

Computing the digest is done as follows:

  • Zero-prefix the serial number to 8 characters
  • Compute the MD5 of the serial
  • Shuffle the serial, using each nibble of the key as an index (ie data[i]=digest[key_nibbles[i]])
  • Treating the 128-bit value as a single little endian number, XOR every 23 bits together (counting from LSB, such that the final operation will be performed on only 13 bits, which will include the MSB)
  • Zero-prefix the result of the operation to 7 characters

The following python snippet implements this behavior with some nasty bit-string hacks. A less succinct but potentially nicer implementation would use bit shifts instead.

import hashlib

def digest(serial: int, key: str):
    real_digest = hashlib.md5(str(serial).zfill(8).encode()).digest()
    digest = bytes(real_digest[int(key[i], 16)] for i in range(16))

    bitstring = "".join(bin(i)[2:].zfill(8)[::-1] for i in digest)[::-1].zfill(6 * 23)
    computed = 0
    while bitstring:
        work = int(bitstring[:23], 2)
        computed ^= work
        bitstring = bitstring[23:]

    return str(computed).zfill(7)