466 lines
28 KiB
HTML
466 lines
28 KiB
HTML
{% extends "konami.html" %}
|
|
{% block title %}Card IDs{% endblock %}
|
|
{% block body %}
|
|
<h1>Card ID generation</h1>
|
|
<details>
|
|
<summary>I'm just here for code.</summary>
|
|
<p>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.</p>
|
|
<pre></pre>{% highlight 'python' %}
|
|
import binascii
|
|
from Crypto.Cipher import DES3
|
|
|
|
|
|
KEY = b"?I'llB2c.YouXXXeMeHaYpy!"
|
|
_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 %}</pre>
|
|
</details>
|
|
<p>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.</p>
|
|
<p>KONAMI IDs have an alphabet of <code>0123456789ABCDEFGHJKLMNPRSTUWXYZ</code> (note that <code>IOQV</code> are
|
|
absent), whereas e-A IDs (yeah I'm not typing that out every time) have an alphabet of
|
|
<code>0123456789ABCDEF</code> (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.
|
|
</p>
|
|
<h2 id="konami">Converting KONAMI IDs to e-Amusement IDs</h2>
|
|
<p>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:</p>
|
|
<table class="code">
|
|
<thead>
|
|
<tr>
|
|
<td>0</td>
|
|
<td>1</td>
|
|
<td>2</td>
|
|
<td>3</td>
|
|
<td>4</td>
|
|
<td>5</td>
|
|
<td>6</td>
|
|
<td>7</td>
|
|
<td>8</td>
|
|
<td>9</td>
|
|
<td>10</td>
|
|
<td>11</td>
|
|
<td>12</td>
|
|
<td>13</td>
|
|
<td>14</td>
|
|
<td>15</td>
|
|
</tr>
|
|
</thead>
|
|
<tr>
|
|
<td colspan="13">e-Amusement ID</td>
|
|
<td>Check byte</td>
|
|
<td>Card type</td>
|
|
<td>Checksum</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<p>Due to how IDs are constructed, there are a number of checks we can perform to validate an ID:</p>
|
|
<ul>
|
|
<li>Parity check: <code>[11] % 2 == [12] % 2</code></li>
|
|
<li>Encoding check: <code>[13] == [12] ^ 1</code></li>
|
|
<li>Checksum: <code>[15] == <a href="#checksum">checksum([0..14])</a></code></li>
|
|
<li>Post-decoding, FeliCa cards start with <code>0</code> and magnetic strip cards start with <code>E004</code>.
|
|
</li>
|
|
<li>Card type: <code>[14] == 1</code> (magnetic stripe) or <code>[14] == 2</code> (FeliCa)</li>
|
|
</ul>
|
|
|
|
<p>To decrypt a KONAMI ID, at a high level we must:</p>
|
|
<ul>
|
|
<li>Remove the XOR encoding</li>
|
|
<li>5-pack the ID</li>
|
|
<li>Decrypt the packed ID</li>
|
|
<li>Reverse the bytes</li>
|
|
<li>Convert to upper-case hex</li>
|
|
</ul>
|
|
<p>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:</p>
|
|
<pre><code>for i from 13 to 1 inclusive:
|
|
card[i] ^= card[i - 1]
|
|
card[0] ^= card_type</code></pre>
|
|
<p id="packing">The values in the <code>e-Amusement ID</code> field above will all maximally be 31
|
|
(<code>0b11111</code>) therefore before we perform the decryption step we first densly pack these 5-bit
|
|
integers. That is, <code>0b11111 0b00000 0b11111</code> would be packed to <code>0b11111000 0b00111110</code>.
|
|
This value will be 8 bytes long. We can now <a href="#des">decrypt it</a>, reverse it, and convert to hex.
|
|
</p>
|
|
<details>
|
|
<summary>Implementing 5-bit packing</summary>
|
|
<p>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.
|
|
</p>
|
|
<details>
|
|
<summary>In <i>most</i> languages?</summary>
|
|
<p>Haha well you see we can actually cheat and use string manipulation. Wasteful? Incredibly. Efficient? Not
|
|
at all. Quick and easy? Yup!</p>
|
|
<pre>{% 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 %}</pre>
|
|
<p>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.</p>
|
|
</details>
|
|
</details>
|
|
|
|
<h2 id="eaid">Converting e-Amusement IDs to KONAMI IDs</h2>
|
|
<p>This is mostly the above process, in reverse, but we need to make sure to populate some of the extra check bytes.
|
|
</p>
|
|
<p>Before we start, we need to make sure we have a valid card! FeliCa cards (type <code>2</code>) will begin with a
|
|
single null nibble, and magnetic stripe cards (type <code>1</code>) with the word <code>E004</code>. We then
|
|
parse the entire ID as a hex string, giving us an 8-byte value.</p>
|
|
<p>This value is reversed, and <a href="#des">encrypted</a>. After encryption, we need to unpack it from it's
|
|
5-packed format. This is the same process as <a href="#packing">unpacking</a>, 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.</p>
|
|
<p>We pad the 13 bytes with 3 extra null bytes, then apply our checks to the ID:</p>
|
|
<pre><code>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] = <a href="#checksum">checksum(card)</a></code></pre>
|
|
|
|
<p>This leaves us with 16 values ranging from 0 to 31, which we apply as indecies to our alphabet to produce the
|
|
final ID.</p>
|
|
|
|
<h2 id="checksum">Checksums</h2>
|
|
<p>As if the encryption and XOR wasn't enough, card IDs also contain a checksum to make <i>absolutely</i> 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:</p>
|
|
<pre><code>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</code></pre>
|
|
|
|
<h2 id="des">The DES scheme used</h2>
|
|
<p>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 <code>IV</code>. The encryption key is <code>?I'llB2c.YouXXXeMeHaYpy!</code>. The key consists of
|
|
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.
|
|
</p>
|
|
<details>
|
|
<summary>I'm curious how Bemani implemented this in their own code!</summary>
|
|
<p>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 <code>soundvoltex.dll:0x1027316f</code>
|
|
and you should see everything you need.
|
|
</p>
|
|
<p>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.</p>
|
|
<details>
|
|
<summary>Show me that!</summary>
|
|
<pre>{% 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 %}</pre>
|
|
</details>
|
|
</details>
|
|
{% endblock %} |