4. Skip to content

4. Access Codes

Access Codes are unique identifiers for AiMe cards. They can be divided into two categories:

  • Old-style codes, starting with a 0
  • Amusement IC codes, starting with a 5

I have seen reference to cards starting with 3, but never seen such a card.

4.1 Old-style codes

Old-style codes follow a simple and predictable formula. 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.

Possible prefixes are listed in the following table:

Prefix Card type Card key
0102 9 MJ4 Evo Mobile 89F56CD1B3EA7402
0103 1 MJ4 Evo Card 471B3E8629D0CA5F
0103 2 Diva Card 5C9AB3E816DF4027
0103 3 Diva Mobile C4E81B3A5920F67D
0103 4 MJ4 Evo Pro Mahjong Player E5FA60281B3D479C
0103 5 AiMe Mobile 5CD3E81B9024F67A
0103 6 AiMe Card A1B3E86CF02974D5
0103 7 Crows Mobile 0000000000000000
0103 8 Crows Card 0000000000000000
0104 0 Sengoku Card 817FA56CB3E0249D
0104 8 Reserved 1B3097D65E82A4FC
0104 9 Reserved 81B7AD9C03EF4652
0105 0 Reserved 84DC50F1B3E276A9
0105 1 Reserved 57C3E81BF4269AD0
0105 2 Reserved 706F81B3E4259CDA
0105 3 Reserved E8179645DB3FC02A
0105 4 Reserved 82DAF451B3E076C9
0105 5 Reserved 54FE81B302DC9A67
0105 6 Reserved 65D81B3E902F4C7A
0105 7 Reserved FD81B3E4A627C095

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)

4.2 Amusement IC codes

Unfortunately these are largely a black-box. I’ll update this section if I ever figure anything else out, but I don’t expect to.