2. Skip to content

2. Amusement IC Access Codes

Amusement IC access codes are divided into three sections.

The first three digits are a company code, the next 12 digits encode a cipher revision number and the card serial, and the final 5 digits are a checksum.

3 bytes12 bytes5 bytesCompanyCodeEncoded dataCRC16

Company Code Company Card
500 SEGA AiMe, limited edition “not for sale” cards
501 SEGA AiMe
510 Bandai Namco Banapass
520 Konami e-Amusement
530 Taito Nesica

The encoding is most easily described with code, so without further ado here is code to both encode and decode access codes:

2.1 Encoding

def crc16(data, bits, initial=0xffff, poly=0x8408):
    v = initial
    for _ in range(bits):
        v = (v >> 1) ^ (poly if ((v ^ data) & 1) else 0)
        data >>= 1
    return v

def encode_access_code(company: int, serial: int, rev: int) -> None | str:
    # Bounds checking
    if company > 999: return None
    if serial > 99999999999: return None
    if rev > 9: return None

    body = f"{rev}{serial:011}"
    crc_i = crc16(int(f"{company:03}{body}", 16), 15 * 4)

    # Extract digits of CRC
    crc = [(crc_i // (10**(4-i))) % 10 for i in range(5)]
    body = [int(body[i]) * 10 + int(body[i + 1]) for i in range(0, 12, 2)]

    # Apply the inner layer of substition
    # This uses table [rev]
    # The lowest nibble contains rev, which is untouched by this substition
    # As such, the first byte only operates on the least siginificant nibble,
    # using an appropriately smaller table.
    # The following boxes are used for this operation
    boxes = ((crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10)
    for n, i in enumerate(body):
        if n == 0:
            # Preserve the higher nibble
            body[n] = (body[n] & 0xf0) | sbtable2[boxes[n]][body[n] & 0x0f]
        else:
            body[n] = sbtable[rev][boxes[n]][i]

    # Apply the outer layer of substition
    # This always uses table [4], and as such can mask rev
    # The following boxes are used for this operation
    boxes = ((crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1])
    for n, i in enumerate(body):
        body[n] = sbtable[4][boxes[n]][i]

    # Reconstruct the digits to form the access code
    body = "".join(f"{i:02}" for i in body)
    return f"{co:03}{body}{crc_i:05}"

2.2 Decoding

# See Encoding for the definition of crc16

class Decoded(NamedTuple):
    rev: int
    serial: int

def decode_access_code(ac: str) -> None | Decoded:
    body = [int(ac[i]) * 10 + int(ac[i + 1]) for i in range(3, 15, 2)]
    crc = [int(i) for i in ac[15:20]]

    # Strip back the outer layer of substitution
    # This always uses table [4]
    # The following boxes are used for this operation
    boxes = ((crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1])
    for n, i in enumerate(body):
        body[n] = ibtable[4][boxes[n]][i]

    # This reveals the table to be used for the inner layer
    rv = body[0] >> 4
    # Strip back the inner layer of substituion
    # Only the lower nibble is used of the first byte, and as such it has a smaller
    # table for operating on single nibbles at a time.
    # The following boxes are used for this operation
    boxes = ((crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10)
    for n, i in enumerate(body):
        if n == 0:
            # Preserve the higher nibble
            body[n] = (body[n] & 0xf0) | ibtable2[boxes[n]][body[n] & 0x0f]
        else:
            body[n] = ibtable[rv][boxes[n]][i]

    serial = "".join(f"{i:02}" for i in body)
    # Validate the CRC before returning the serial number
    if crc16(int(ac[0:3] + serial, 16), 15 * 4) != int(ac[15:20]):
        return None

    return Decoded(int(serial[0]), int(serial[1:]))

2.3 S-Box Tables

The primary S-Box table contains 5 tables of 10 boxes, each 10x10 in dimension (row-major).

The secondary table used for individual nibbles is one table of 10 boxes, each 10x1 in dimension.

2.4 I-Box Tables

The inverse tables can be derived from the S-Box tables with relative triviality. They can alternatively be downloaded pre-computed below.

Generation code:

itable = [[[0 for _ in b] for b in t] for t in stable]
for st, it in zip(stable, itable):
    for sb, ib in zip(st, it):
        for n, i in enumerate(sb):
            ib[i] = n

itable2 = [[0 for _ in b] for b in stable2]
for sb, ib in zip(stable2, itable2):
    for n, i in enumerate(sb):
        ib[i] = n