{% extends "base.html" %} {% block title %}Card IDs{% endblock %} {% block body %}
Fair. My intent with these pages is to describe things in enough detail that they should be simple to implement yourself, but this is one of those things that's quite easy to just drop in some pre-made code for. My local implementation is in python, so that's all you're getting :). As a free bonus, have some test cases too. It's not great code by any stretch, and it liberally uses assertions rather than proper exceptions, but it should be a good enough starting point for your own version.
{% highlight 'python' %} import binascii from Crypto.Cipher import DES3 KEY = b"" # Check the DES section for this _KEY = bytes(i * 2 for i in KEY) # Preprocess the key ALPHABET = "0123456789ABCDEFGHJKLMNPRSTUWXYZ" def enc_des(uid): cipher = DES3.new(_KEY, DES3.MODE_CBC, iv=b'\0' * 8) return cipher.encrypt(uid) def dec_des(uid): cipher = DES3.new(_KEY, DES3.MODE_CBC, iv=b'\0' * 8) return cipher.decrypt(uid) def checksum(data): chk = sum(data[i] * (i % 3 + 1) for i in range(15)) while chk > 31: chk = (chk >> 5) + (chk & 31) return chk def pack_5(data): data = "".join(f"{i:05b}" for i in data) if len(data) % 8 != 0: data += "0" * (8 - (len(data) % 8)) return bytes(int(data[i:i+8], 2) for i in range(0, len(data), 8)) def unpack_5(data): data = "".join(f"{i:08b}" for i in data) if len(data) % 5 != 0: data += "0" * (5 - (len(data) % 5)) return bytes(int(data[i:i+5], 2) for i in range(0, len(data), 5)) def to_konami_id(uid): assert len(uid) == 16, "UID must be 16 bytes" if uid.upper().startswith("E004"): card_type = 1 elif uid.upper().startswith("0"): card_type = 2 else: raise ValueError("Invalid UID prefix") kid = binascii.unhexlify(uid) assert len(kid) == 8, "ID must be 8 bytes" out = bytearray(unpack_5(enc_des(kid[::-1]))[:13]) + b'\0\0\0' out[0] ^= card_type out[13] = 1 for i in range(1, 14): out[i] ^= out[i - 1] out[14] = card_type out[15] = checksum(out) return "".join(ALPHABET[i] for i in out) def to_uid(konami_id): if konami_id[14] == "1": card_type = 1 elif konami_id[14] == "2": card_type = 2 else: raise ValueError("Invalid ID") assert len(konami_id) == 16, f"ID must be 16 characters" assert all(i in ALPHABET for i in konami_id), "ID contains invalid characters" card = [ALPHABET.index(i) for i in konami_id] assert card[11] % 2 == card[12] % 2, "Parity check failed" assert card[13] == card[12] ^ 1, "Card invalid" assert card[15] == checksum(card), "Checksum failed" for i in range(13, 0, -1): card[i] ^= card[i - 1] card[0] ^= card_type card_id = dec_des(pack_5(card[:13])[:8])[::-1] card_id = binascii.hexlify(card_id).decode().upper() if card_type == 1: assert card_id[:4] == "E004", "Invalid card type" elif card_type == 2: assert card_id[0] == "0", "Invalid card type" return card_id if __name__ == "__main__": assert to_konami_id("0000000000000000") == "007TUT8XJNSSPN2P", "To KID failed" assert to_uid("007TUT8XJNSSPN2P") == "0000000000000000", "From KID failed" assert to_uid(to_konami_id("000000100200F000")) == "000000100200F000", "Roundtrip failed" {% endhighlight %}e-Amusement cards use 16 digit IDs. KONAMI IDs are also 16 digits. Are they related? Yes! In fact, KONAMI IDs are derived from the ID stored on the e-Amusement card.
KONAMI IDs have an alphabet of 0123456789ABCDEFGHJKLMNPRSTUWXYZ
(note that IOQV
are
absent), whereas e-A IDs (yeah I'm not typing that out every time) have an alphabet of
0123456789ABCDEF
(hex). It stands to reason then that there's additional information present in
KONAMI IDs, as they are the same length, but can hold a greater density of information. That intuition would be
correct.
Let's take a look at the format of KONAMI IDs. The first step before we can do anything is to convert it from a string to a series of integers. Each byte is replaced with its index in the alphabet, giving us 16 values ranging from 0 through 31. These bytes has the following meanings:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
e-Amusement ID | Check byte | Card type | Checksum |
Due to how IDs are constructed, there are a number of checks we can perform to validate an ID:
[11] % 2 == [12] % 2
[13] == [12] ^ 1
[15] == checksum([0..14])
0
and magnetic strip cards start with E004
.
[14] == 1
(magnetic stripe) or [14] == 2
(FeliCa)To decrypt a KONAMI ID, at a high level we must:
As we'll see in the next section, card IDs have an XOR pass performed. This is what allows for the above encoding check, but we must remove it before we can begin decoding. A line of code speaks a thousand words, so have three:
for i from 13 to 1 inclusive:
card[i] ^= card[i - 1]
card[0] ^= card_type
The values in the e-Amusement ID
field above will all maximally be 31
(0b11111
) therefore before we perform the decryption step we first densly pack these 5-bit
integers. That is, 0b11111 0b00000 0b11111
would be packed to 0b11111000 0b00111110
.
This value will be 8 bytes long. We can now decrypt it, reverse it, and convert to hex.
In most languages, implementing a packer can be done one of two ways. The approach chosen by Bemani is to first create a table of every individual bit (each stored in a whole byte!), then iterate through the bits ORing them together. This is a simple but wasteful implementation. The other approach is to use a buffer byte and slowly shift values in, tracking how many bits are stored in the buffer byte, and performing different actions depending on how many bits this is. For both packing and unpacking this requires three cases. It's somewhat more complex, but less wasteful in terms of memory usage. Pick your poison I suppose.
Haha well you see we can actually cheat and use string manipulation. Wasteful? Incredibly. Efficient? Not at all. Quick and easy? Yup!
{% highlight "python" %}def pack_5(data): data = "".join(f"{i:05b}" for i in data) if len(data) % 8 != 0: data += "0" * (8 - (len(data) % 8)) return bytes(int(data[i:i+8], 2) for i in range(0, len(data), 8)) def unpack_5(data): data = "".join(f"{i:08b}" for i in data) if len(data) % 5 != 0: data += "0" * (5 - (len(data) % 5)) return bytes(int(data[i:i+5], 2) for i in range(0, len(data), 5)){% endhighlight %}
If your language of choice allows this, and you don't care for efficiency, this can be a great time-saver towards get something working. Truth be told my local implementation originally used the Bemani method (it was a line-for-line port, after all), switched to the second method, then I opted for this hacky string method in the name of code clarity.
This is mostly the above process, in reverse, but we need to make sure to populate some of the extra check bytes.
Before we start, we need to make sure we have a valid card! FeliCa cards (type 2
) will begin with a
single null nibble, and magnetic stripe cards (type 1
) with the word E004
. We then
parse the entire ID as a hex string, giving us an 8-byte value.
This value is reversed, and encrypted. After encryption, we need to unpack it from it's 5-packed format. This is the same process as unpacking, but reversed. The unpacked data can be ambiguous in length. It's 13 bytes. If your unpacker produces a 14th byte, it'll be null and can be discarded.
We pad the 13 bytes with 3 extra null bytes, then apply our checks to the ID:
card[0] ^= card_type
card[13] = 1
for i from 0 to 13 inclusive:
card[i + 1] ^= card[i]
card[14] = card_type
card[15] = checksum(card)
This leaves us with 16 values ranging from 0 to 31, which we apply as indecies to our alphabet to produce the final ID.
As if the encryption and XOR wasn't enough, card IDs also contain a checksum to make absolutely sure the card is valid. I could explain in words how the checksum works, but that's probably not very useful. Have a pseudocode snippet instead:
checksum(bytes):
chk = 0
for i from 0 to 14 inclusive:
chk += bytes[i] * (i % 3 + 1)
while chk > 31:
chk = (chk >> 5) + (chk & 31)
return chk
For whatever reason, Bemani decided that IDs should be encrypted. Thankfully however they used triple DES, which
almost certainly has an existing implementation in your language of choice. It is triple DES, in CBC mode, with
a totally null IV
. The key is quite easy to find if you hit the right binaries with
strings
. Alternatively, check the source of this page. The key
contains characters that are all within the ASCII range. Before we can use it with DES, the value of every byte
needs doubled. This was presumably done to give the values more range, but I sincerely doubt it adds any
additional security.
Curiosity is a great thing. Unfortunately, this is code that is implement within the game specific DLL files.
If you happen to have SDXV 4 in front of you too, head over over to soundvoltex.dll:0x1027316f
and you should see everything you need.
As part of breaking down how this all works, I produced a more or less line-for-line Python port of the game code, for testing, validation, etc.. It's not especially pretty, but should give you an idea of how it works under the hood. One interesting observation is that it looks like the initial and final permutation steps were inlined. It's also possible that they did the whole thing with macros rather than inline functions. Either way, my python port didn't do any cleaning up, because we can just use a DES library.
{% highlight "python" %}DES_KEYMAP = [ [0x02080008, 0x02082000, 0x00002008, 0x00000000, 0x02002000, 0x00080008, 0x02080000, 0x02082008, 0x00000008, 0x02000000, 0x00082000, 0x00002008, 0x00082008, 0x02002008, 0x02000008, 0x02080000, 0x00002000, 0x00082008, 0x00080008, 0x02002000, 0x02082008, 0x02000008, 0x00000000, 0x00082000, 0x02000000, 0x00080000, 0x02002008, 0x02080008, 0x00080000, 0x00002000, 0x02082000, 0x00000008, 0x00080000, 0x00002000, 0x02000008, 0x02082008, 0x00002008, 0x02000000, 0x00000000, 0x00082000, 0x02080008, 0x02002008, 0x02002000, 0x00080008, 0x02082000, 0x00000008, 0x00080008, 0x02002000, 0x02082008, 0x00080000, 0x02080000, 0x02000008, 0x00082000, 0x00002008, 0x02002008, 0x02080000, 0x00000008, 0x02082000, 0x00082008, 0x00000000, 0x02000000, 0x02080008, 0x00002000, 0x00082008], [0x08000004, 0x00020004, 0x00000000, 0x08020200, 0x00020004, 0x00000200, 0x08000204, 0x00020000, 0x00000204, 0x08020204, 0x00020200, 0x08000000, 0x08000200, 0x08000004, 0x08020000, 0x00020204, 0x00020000, 0x08000204, 0x08020004, 0x00000000, 0x00000200, 0x00000004, 0x08020200, 0x08020004, 0x08020204, 0x08020000, 0x08000000, 0x00000204, 0x00000004, 0x00020200, 0x00020204, 0x08000200, 0x00000204, 0x08000000, 0x08000200, 0x00020204, 0x08020200, 0x00020004, 0x00000000, 0x08000200, 0x08000000, 0x00000200, 0x08020004, 0x00020000, 0x00020004, 0x08020204, 0x00020200, 0x00000004, 0x08020204, 0x00020200, 0x00020000, 0x08000204, 0x08000004, 0x08020000, 0x00020204, 0x00000000, 0x00000200, 0x08000004, 0x08000204, 0x08020200, 0x08020000, 0x00000204, 0x00000004, 0x08020004], [0x80040100, 0x01000100, 0x80000000, 0x81040100, 0x00000000, 0x01040000, 0x81000100, 0x80040000, 0x01040100, 0x81000000, 0x01000000, 0x80000100, 0x81000000, 0x80040100, 0x00040000, 0x01000000, 0x81040000, 0x00040100, 0x00000100, 0x80000000, 0x00040100, 0x81000100, 0x01040000, 0x00000100, 0x80000100, 0x00000000, 0x80040000, 0x01040100, 0x01000100, 0x81040000, 0x81040100, 0x00040000, 0x81040000, 0x80000100, 0x00040000, 0x81000000, 0x00040100, 0x01000100, 0x80000000, 0x01040000, 0x81000100, 0x00000000, 0x00000100, 0x80040000, 0x00000000, 0x81040000, 0x01040100, 0x00000100, 0x01000000, 0x81040100, 0x80040100, 0x00040000, 0x81040100, 0x80000000, 0x01000100, 0x80040100, 0x80040000, 0x00040100, 0x01040000, 0x81000100, 0x80000100, 0x01000000, 0x81000000, 0x01040100], [0x04010801, 0x00000000, 0x00010800, 0x04010000, 0x04000001, 0x00000801, 0x04000800, 0x00010800, 0x00000800, 0x04010001, 0x00000001, 0x04000800, 0x00010001, 0x04010800, 0x04010000, 0x00000001, 0x00010000, 0x04000801, 0x04010001, 0x00000800, 0x00010801, 0x04000000, 0x00000000, 0x00010001, 0x04000801, 0x00010801, 0x04010800, 0x04000001, 0x04000000, 0x00010000, 0x00000801, 0x04010801, 0x00010001, 0x04010800, 0x04000800, 0x00010801, 0x04010801, 0x00010001, 0x04000001, 0x00000000, 0x04000000, 0x00000801, 0x00010000, 0x04010001, 0x00000800, 0x04000000, 0x00010801, 0x04000801, 0x04010800, 0x00000800, 0x00000000, 0x04000001, 0x00000001, 0x04010801, 0x00010800, 0x04010000, 0x04010001, 0x00010000, 0x00000801, 0x04000800, 0x04000801, 0x00000001, 0x04010000, 0x00010800], [0x00000400, 0x00000020, 0x00100020, 0x40100000, 0x40100420, 0x40000400, 0x00000420, 0x00000000, 0x00100000, 0x40100020, 0x40000020, 0x00100400, 0x40000000, 0x00100420, 0x00100400, 0x40000020, 0x40100020, 0x00000400, 0x40000400, 0x40100420, 0x00000000, 0x00100020, 0x40100000, 0x00000420, 0x40100400, 0x40000420, 0x00100420, 0x40000000, 0x40000420, 0x40100400, 0x00000020, 0x00100000, 0x40000420, 0x00100400, 0x40100400, 0x40000020, 0x00000400, 0x00000020, 0x00100000, 0x40100400, 0x40100020, 0x40000420, 0x00000420, 0x00000000, 0x00000020, 0x40100000, 0x40000000, 0x00100020, 0x00000000, 0x40100020, 0x00100020, 0x00000420, 0x40000020, 0x00000400, 0x40100420, 0x00100000, 0x00100420, 0x40000000, 0x40000400, 0x40100420, 0x40100000, 0x00100420, 0x00100400, 0x40000400], [0x00800000, 0x00001000, 0x00000040, 0x00801042, 0x00801002, 0x00800040, 0x00001042, 0x00801000, 0x00001000, 0x00000002, 0x00800002, 0x00001040, 0x00800042, 0x00801002, 0x00801040, 0x00000000, 0x00001040, 0x00800000, 0x00001002, 0x00000042, 0x00800040, 0x00001042, 0x00000000, 0x00800002, 0x00000002, 0x00800042, 0x00801042, 0x00001002, 0x00801000, 0x00000040, 0x00000042, 0x00801040, 0x00801040, 0x00800042, 0x00001002, 0x00801000, 0x00001000, 0x00000002, 0x00800002, 0x00800040, 0x00800000, 0x00001040, 0x00801042, 0x00000000, 0x00001042, 0x00800000, 0x00000040, 0x00001002, 0x00800042, 0x00000040, 0x00000000, 0x00801042, 0x00801002, 0x00801040, 0x00000042, 0x00001000, 0x00001040, 0x00801002, 0x00800040, 0x00000042, 0x00000002, 0x00001042, 0x00801000, 0x00800002], [0x10400000, 0x00404010, 0x00000010, 0x10400010, 0x10004000, 0x00400000, 0x10400010, 0x00004010, 0x00400010, 0x00004000, 0x00404000, 0x10000000, 0x10404010, 0x10000010, 0x10000000, 0x10404000, 0x00000000, 0x10004000, 0x00404010, 0x00000010, 0x10000010, 0x10404010, 0x00004000, 0x10400000, 0x10404000, 0x00400010, 0x10004010, 0x00404000, 0x00004010, 0x00000000, 0x00400000, 0x10004010, 0x00404010, 0x00000010, 0x10000000, 0x00004000, 0x10000010, 0x10004000, 0x00404000, 0x10400010, 0x00000000, 0x00404010, 0x00004010, 0x10404000, 0x10004000, 0x00400000, 0x10404010, 0x10000000, 0x10004010, 0x10400000, 0x00400000, 0x10404010, 0x00004000, 0x00400010, 0x10400010, 0x00004010, 0x00400010, 0x00000000, 0x10404000, 0x10000010, 0x10400000, 0x10004010, 0x00000010, 0x00404000], [0x00208080, 0x00008000, 0x20200000, 0x20208080, 0x00200000, 0x20008080, 0x20008000, 0x20200000, 0x20008080, 0x00208080, 0x00208000, 0x20000080, 0x20200080, 0x00200000, 0x00000000, 0x20008000, 0x00008000, 0x20000000, 0x00200080, 0x00008080, 0x20208080, 0x00208000, 0x20000080, 0x00200080, 0x20000000, 0x00000080, 0x00008080, 0x20208000, 0x00000080, 0x20200080, 0x20208000, 0x00000000, 0x00000000, 0x20208080, 0x00200080, 0x20008000, 0x00208080, 0x00008000, 0x20000080, 0x00200080, 0x20208000, 0x00000080, 0x00008080, 0x20200000, 0x20008080, 0x20000000, 0x20200000, 0x00208000, 0x20208080, 0x00008080, 0x00208000, 0x20200080, 0x00200000, 0x20000080, 0x20008000, 0x00000000, 0x00008000, 0x00200000, 0x20200080, 0x00208080, 0x20000000, 0x20208000, 0x00000080, 0x20008080] ] DES_ROTORS = [0x22, 0x0D, 0x05, 0x2E, 0x2F, 0x12, 0x20, 0x29, 0x0B, 0x35, 0x21, 0x14, 0x0E, 0x24, 0x1E, 0x18, 0x31, 0x02, 0x0F, 0x25, 0x2A, 0x32, 0x00, 0x15, 0x26, 0x30, 0x06, 0x1A, 0x27, 0x04, 0x34, 0x19, 0x0C, 0x1B, 0x1F, 0x28, 0x01, 0x11, 0x1C, 0x1D, 0x17, 0x33, 0x23, 0x07, 0x03, 0x16, 0x09, 0x2B, 0x29, 0x14, 0x0C, 0x35, 0x36, 0x19, 0x27, 0x30, 0x12, 0x1F, 0x28, 0x1B, 0x15, 0x2B, 0x25, 0x00, 0x01, 0x09, 0x16, 0x2C, 0x31, 0x02, 0x07, 0x1C, 0x2D, 0x37, 0x0D, 0x21, 0x2E, 0x0B, 0x06, 0x20, 0x13, 0x22, 0x26, 0x2F, 0x08, 0x18, 0x23, 0x24, 0x1E, 0x03, 0x2A, 0x0E, 0x0A, 0x1D, 0x10, 0x32, 0x37, 0x22, 0x1A, 0x26, 0x0B, 0x27, 0x35, 0x05, 0x20, 0x2D, 0x36, 0x29, 0x23, 0x02, 0x33, 0x0E, 0x0F, 0x17, 0x24, 0x03, 0x08, 0x10, 0x15, 0x2A, 0x06, 0x0C, 0x1B, 0x2F, 0x1F, 0x19, 0x14, 0x2E, 0x21, 0x30, 0x34, 0x04, 0x16, 0x07, 0x31, 0x32, 0x2C, 0x11, 0x01, 0x1C, 0x18, 0x2B, 0x1E, 0x09, 0x0C, 0x30, 0x28, 0x34, 0x19, 0x35, 0x26, 0x13, 0x2E, 0x06, 0x0B, 0x37, 0x31, 0x10, 0x0A, 0x1C, 0x1D, 0x25, 0x32, 0x11, 0x16, 0x1E, 0x23, 0x01, 0x14, 0x1A, 0x29, 0x04, 0x2D, 0x27, 0x22, 0x1F, 0x2F, 0x05, 0x0D, 0x12, 0x24, 0x15, 0x08, 0x09, 0x03, 0x00, 0x0F, 0x2A, 0x07, 0x02, 0x2C, 0x17, 0x1A, 0x05, 0x36, 0x0D, 0x27, 0x26, 0x34, 0x21, 0x1F, 0x14, 0x19, 0x0C, 0x08, 0x1E, 0x18, 0x2A, 0x2B, 0x33, 0x09, 0x00, 0x24, 0x2C, 0x31, 0x0F, 0x22, 0x28, 0x37, 0x12, 0x06, 0x35, 0x30, 0x2D, 0x04, 0x13, 0x1B, 0x20, 0x32, 0x23, 0x16, 0x17, 0x11, 0x0E, 0x1D, 0x01, 0x15, 0x10, 0x03, 0x25, 0x28, 0x13, 0x0B, 0x1B, 0x35, 0x34, 0x0D, 0x2F, 0x2D, 0x22, 0x27, 0x1A, 0x16, 0x2C, 0x07, 0x01, 0x02, 0x0A, 0x17, 0x0E, 0x32, 0x03, 0x08, 0x1D, 0x30, 0x36, 0x0C, 0x20, 0x14, 0x26, 0x05, 0x06, 0x12, 0x21, 0x29, 0x2E, 0x09, 0x31, 0x24, 0x25, 0x00, 0x1C, 0x2B, 0x0F, 0x23, 0x1E, 0x11, 0x33, 0x36, 0x21, 0x19, 0x29, 0x26, 0x0D, 0x1B, 0x04, 0x06, 0x30, 0x35, 0x28, 0x24, 0x03, 0x15, 0x0F, 0x10, 0x18, 0x25, 0x1C, 0x09, 0x11, 0x16, 0x2B, 0x05, 0x0B, 0x1A, 0x2E, 0x22, 0x34, 0x13, 0x14, 0x20, 0x2F, 0x37, 0x1F, 0x17, 0x08, 0x32, 0x33, 0x0E, 0x2A, 0x02, 0x1D, 0x31, 0x2C, 0x00, 0x0A, 0x0B, 0x2F, 0x27, 0x37, 0x34, 0x1B, 0x29, 0x12, 0x14, 0x05, 0x26, 0x36, 0x32, 0x11, 0x23, 0x1D, 0x1E, 0x07, 0x33, 0x2A, 0x17, 0x00, 0x24, 0x02, 0x13, 0x19, 0x28, 0x1F, 0x30, 0x0D, 0x21, 0x22, 0x2E, 0x04, 0x0C, 0x2D, 0x25, 0x16, 0x09, 0x0A, 0x1C, 0x01, 0x10, 0x2B, 0x08, 0x03, 0x0E, 0x18, 0x12, 0x36, 0x2E, 0x05, 0x06, 0x22, 0x30, 0x19, 0x1B, 0x0C, 0x2D, 0x04, 0x02, 0x18, 0x2A, 0x24, 0x25, 0x0E, 0x03, 0x31, 0x1E, 0x07, 0x2B, 0x09, 0x1A, 0x20, 0x2F, 0x26, 0x37, 0x14, 0x28, 0x29, 0x35, 0x0B, 0x13, 0x34, 0x2C, 0x1D, 0x10, 0x11, 0x23, 0x08, 0x17, 0x32, 0x0F, 0x0A, 0x15, 0x00, 0x20, 0x0B, 0x1F, 0x13, 0x14, 0x30, 0x05, 0x27, 0x29, 0x1A, 0x06, 0x12, 0x10, 0x07, 0x01, 0x32, 0x33, 0x1C, 0x11, 0x08, 0x2C, 0x15, 0x02, 0x17, 0x28, 0x2E, 0x04, 0x34, 0x0C, 0x22, 0x36, 0x37, 0x26, 0x19, 0x21, 0x0D, 0x03, 0x2B, 0x1E, 0x00, 0x31, 0x16, 0x25, 0x09, 0x1D, 0x18, 0x23, 0x0E, 0x2E, 0x19, 0x2D, 0x21, 0x22, 0x05, 0x13, 0x35, 0x37, 0x28, 0x14, 0x20, 0x1E, 0x15, 0x0F, 0x09, 0x0A, 0x2A, 0x00, 0x16, 0x03, 0x23, 0x10, 0x25, 0x36, 0x1F, 0x12, 0x0D, 0x1A, 0x30, 0x0B, 0x0C, 0x34, 0x27, 0x2F, 0x1B, 0x11, 0x02, 0x2C, 0x0E, 0x08, 0x24, 0x33, 0x17, 0x2B, 0x07, 0x31, 0x1C, 0x1F, 0x27, 0x06, 0x2F, 0x30, 0x13, 0x21, 0x26, 0x0C, 0x36, 0x22, 0x2E, 0x2C, 0x23, 0x1D, 0x17, 0x18, 0x01, 0x0E, 0x24, 0x11, 0x31, 0x1E, 0x33, 0x0B, 0x2D, 0x20, 0x1B, 0x28, 0x05, 0x19, 0x1A, 0x0D, 0x35, 0x04, 0x29, 0x00, 0x10, 0x03, 0x1C, 0x16, 0x32, 0x0A, 0x25, 0x02, 0x15, 0x08, 0x2A, 0x2D, 0x35, 0x14, 0x04, 0x05, 0x21, 0x2F, 0x34, 0x1A, 0x0B, 0x30, 0x1F, 0x03, 0x31, 0x2B, 0x25, 0x07, 0x0F, 0x1C, 0x32, 0x00, 0x08, 0x2C, 0x0A, 0x19, 0x06, 0x2E, 0x29, 0x36, 0x13, 0x27, 0x28, 0x1B, 0x26, 0x12, 0x37, 0x0E, 0x1E, 0x11, 0x2A, 0x24, 0x09, 0x18, 0x33, 0x10, 0x23, 0x16, 0x01, 0x06, 0x26, 0x22, 0x12, 0x13, 0x2F, 0x04, 0x0D, 0x28, 0x19, 0x05, 0x2D, 0x11, 0x08, 0x02, 0x33, 0x15, 0x1D, 0x2A, 0x09, 0x0E, 0x16, 0x03, 0x18, 0x27, 0x14, 0x1F, 0x37, 0x0B, 0x21, 0x35, 0x36, 0x29, 0x34, 0x20, 0x0C, 0x1C, 0x2C, 0x00, 0x01, 0x32, 0x17, 0x07, 0x0A, 0x1E, 0x31, 0x24, 0x0F, 0x14, 0x34, 0x30, 0x20, 0x21, 0x04, 0x12, 0x1B, 0x36, 0x27, 0x13, 0x06, 0x00, 0x16, 0x10, 0x0A, 0x23, 0x2B, 0x01, 0x17, 0x1C, 0x24, 0x11, 0x07, 0x35, 0x22, 0x2D, 0x0C, 0x19, 0x2F, 0x26, 0x0B, 0x37, 0x0D, 0x2E, 0x1A, 0x2A, 0x03, 0x0E, 0x0F, 0x09, 0x25, 0x15, 0x18, 0x2C, 0x08, 0x32, 0x1D, 0x1B, 0x06, 0x37, 0x27, 0x28, 0x0B, 0x19, 0x22, 0x04, 0x2E, 0x1A, 0x0D, 0x07, 0x1D, 0x17, 0x11, 0x2A, 0x32, 0x08, 0x1E, 0x23, 0x2B, 0x18, 0x0E, 0x1F, 0x29, 0x34, 0x13, 0x20, 0x36, 0x2D, 0x12, 0x05, 0x14, 0x35, 0x21, 0x31, 0x0A, 0x15, 0x16, 0x10, 0x2C, 0x1C, 0x00, 0x33, 0x0F, 0x02, 0x24] byte_102D4AA0 = [0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x03, 0x02, 0x01, 0x00, 0x1F, 0x1E] KEY_DATA = [0] * 96 def roll_right(arg2, arg3): return (arg2 << 32 - arg3 | (arg2 >> arg3)) & 0xffffffff def des_small_flips_encrypt(key, output, uid): v3 = uid[0] | (uid[1] << 8) | (uid[2] << 16) | (uid[3] << 24) v0 = uid[4] | (uid[5] << 8) | (uid[6] << 16) | (uid[7] << 24) v4 = (16 * ((v3 ^ (v0 >> 4)) & 0xF0F0F0F)) ^ v0 v5 = (v3 ^ (v0 >> 4)) & 0xF0F0F0F ^ v3 v6 = (v4 ^ (v5 >> 16)) & 0xffff v7 = (v6 << 16) ^ v5 v8 = v6 ^ v4 v9 = (v7 ^ (v8 >> 2)) & 0x33333333 v10 = (4 * v9) ^ v8 v11 = v9 ^ v7 v12 = (v10 ^ (v11 >> 8)) & 0xFF00FF v13 = (v12 << 8) ^ v11 v14 = roll_right(v12 ^ v10, 1) v15 = (v13 ^ v14) & 0x55555555 v16 = v15 ^ v14 v17 = roll_right(v15 ^ v13, 1) for i in range(0, 32, 4): v19 = roll_right(v17 ^ key[i + 1], 28) v16 ^= (0 ^ DES_KEYMAP[0][((v17 ^ key[i]) >> 26) & 0x3f] ^ DES_KEYMAP[1][((v17 ^ key[i]) >> 18) & 0x3f] ^ DES_KEYMAP[2][(((v17 ^ key[i]) >> 8) >> 2) & 0x3f] ^ DES_KEYMAP[3][((v17 ^ key[i]) >> 2) & 0x3f] ^ DES_KEYMAP[4][(v19 >> 26) & 0x3f] ^ DES_KEYMAP[5][(v19 >> 18) & 0x3f] ^ DES_KEYMAP[6][(v19 >> 10) & 0x3f] ^ DES_KEYMAP[7][(v19 >> 2) & 0x3f] ) v20 = roll_right(v16 ^ key[i + 3], 28) v17 ^= (0 ^ DES_KEYMAP[0][((v16 ^ key[i + 2]) >> 26) & 0x3f] ^ DES_KEYMAP[1][((v16 ^ key[i + 2]) >> 18) & 0x3F] ^ DES_KEYMAP[2][(((v16 ^ key[i + 2]) >> 8) >> 2) & 0x3f] ^ DES_KEYMAP[3][((v16 ^ key[i + 2]) >> 2) & 0x3F] ^ DES_KEYMAP[4][(v20 >> 26) & 0x3f] ^ DES_KEYMAP[5][(v20 >> 18) & 0x3F] ^ DES_KEYMAP[6][(v20 >> 10) & 0x3f] ^ DES_KEYMAP[7][(v20 >> 2) & 0x3f] ) v21 = roll_right(v16, 31) v22 = (v17 ^ v21) & 0x55555555 v23 = v22 ^ v21 v24 = roll_right(v22 ^ v17, 31) v25 = (v24 ^ (v23 >> 8)) & 0xFF00FF v26 = (v25 << 8) ^ v23 v27 = v25 ^ v24 v28 = (v26 ^ ((v25 ^ v24) >> 2)) & 0x33333333 v29 = (4 * v28) ^ v27 v30 = v28 ^ v26 v31 = (v29 ^ (v30 >> 16)) & 0xffff v32 = ((v31 << 16) & 0xffffffff) ^ v30 v33 = v31 ^ v29 v34 = (v32 ^ (v33 >> 4)) & 0xF0F0F0F v35 = (v34 << 4) ^ v33 v34 = v34 ^ v32 output[0] = v34 & 0xff output[1] = (v34 >> 8) & 0xff output[2] = (v34 >> 16) & 0xff output[3] = (v34 >> 24) & 0xff output[4] = v35 & 0xff output[5] = (v35 >> 8) & 0xff output[6] = (v35 >> 16) & 0xff output[7] = (v35 >> 24) & 0xff def des_small_flips_decrypt(key, output, uid): v3 = uid[0] | (uid[1] << 8) | (uid[2] << 16) | (uid[3] << 24) v0 = uid[4] | (uid[5] << 8) | (uid[6] << 16) | (uid[7] << 24) v4 = (16 * ((v3 ^ (v0 >> 4)) & 0xF0F0F0F)) ^ v0 v5 = (v3 ^ (v0 >> 4)) & 0xF0F0F0F ^ v3 v6 = (v4 ^ (v5 >> 16)) & 0xffff v7 = (v6 << 16) ^ v5 v8 = v6 ^ v4 v9 = (v7 ^ (v8 >> 2)) & 0x33333333 v10 = (4 * v9) ^ v8 v11 = v9 ^ v7 v12 = (v10 ^ (v11 >> 8)) & 0xFF00FF v13 = (v12 << 8) ^ v11 v14 = roll_right(v12 ^ v10, 1) v15 = (v13 ^ v14) & 0x55555555 v16 = v15 ^ v14 v17 = roll_right(v15 ^ v13, 1) for i in range(0, 32, 4): v19 = roll_right(v17 ^ key[(31 - i)], 28) v16 ^= (0 ^ DES_KEYMAP[0][((v17 ^ key[(30 - i)]) >> 26) & 0x3f] ^ DES_KEYMAP[1][((v17 ^ key[(30 - i)]) >> 18) & 0x3F] ^ DES_KEYMAP[2][(((v17 ^ key[(30 - i)]) >> 8) >> 2) & 0x3f] ^ DES_KEYMAP[3][((v17 ^ key[(30 - i)]) >> 2) & 0x3F] ^ DES_KEYMAP[4][(v19 >> 26) & 0x3f] ^ DES_KEYMAP[5][(v19 >> 18) & 0x3F] ^ DES_KEYMAP[6][(v19 >> 10) & 0x3f] ^ DES_KEYMAP[7][(v19 >> 2) & 0x3f] ) v20 = roll_right(v16 ^ key[(29 - i)], 28) v17 ^= (0 ^ DES_KEYMAP[0][((v16 ^ key[(28 - i)]) >> 26) & 0x3f] ^ DES_KEYMAP[1][((v16 ^ key[(28 - i)]) >> 18) & 0x3F] ^ DES_KEYMAP[2][(((v16 ^ key[(28 - i)]) >> 8) >> 2) & 0x3f] ^ DES_KEYMAP[3][((v16 ^ key[(28 - i)]) >> 2) & 0x3F] ^ DES_KEYMAP[4][(v20 >> 26) & 0x3f] ^ DES_KEYMAP[5][(v20 >> 18) & 0x3f] ^ DES_KEYMAP[6][(v20 >> 10) & 0x3f] ^ DES_KEYMAP[7][(v20 >> 2) & 0x3f] ) v21 = roll_right(v16, 31) v22 = (v17 ^ v21) & 0x55555555 v23 = v22 ^ v21 v24 = roll_right(v22 ^ v17, 31) v25 = (v24 ^ (v23 >> 8)) & 0xFF00FF v26 = (v25 << 8) ^ v23 v27 = v25 ^ v24 v28 = (v26 ^ ((v25 ^ v24) >> 2)) & 0x33333333 v29 = (4 * v28) ^ v27 v30 = v28 ^ v26 v31 = (v29 ^ (v30 >> 16)) & 0xffff v32 = (v31 << 16) ^ v30 v33 = v31 ^ v29 v34 = (v32 ^ (v33 >> 4)) & 0xF0F0F0F v35 = (v34 << 4) ^ v33 v34 = v34 ^ v32 output[0] = v34 & 0xff output[1] = (v34 >> 8) & 0xff output[2] = (v34 >> 16) & 0xff output[3] = (v34 >> 24) & 0xff output[4] = v35 & 0xff output[5] = (v35 >> 8) & 0xff output[6] = (v35 >> 16) & 0xff output[7] = (v35 >> 24) & 0xff def des3_encryption(key, kid_out, uid): des_small_flips_encrypt(key, kid_out, uid) des_small_flips_decrypt(key[32:], kid_out, kid_out) des_small_flips_encrypt(key[64:], kid_out, kid_out) def des_setkey(key, offset, out): b1 = bytearray(56); for i in range(8): v3 = out[i] for j in range(7): b1[-7 * i - j + 55] = (v3 >> (j + 1)) & 1 for i in range(32): v5 = 0 for j in range(24): v5 |= b1[DES_ROTORS[24 * i + j]] << byte_102D4AA0[24 * (i & 1) + j] key[offset + i] = v5 def des3_setkey(key, out): for i in range(3): des_setkey(key, 32 * i, out[8 * i:]) def load_key(key): key_data = bytearray(24) for i in range(24): key_data[i] = 2 * key[i % len(key)] des3_setkey(KEY_DATA, key_data){% endhighlight %}