How to Decrypt Credit Card Data, Part II
Customers often ask: How can I decrypt the data coming out of my ID TECH credit-card reader?
The answer: You need to know the algorithm that was used to encrypt the data, and the keythat was used. Then you can decrypt the data using the key.
These days, almost all credit-card data gets encrypted using a one-time-only key, obtained via a special key-management scheme called DUKPT (which stands for Derived Unique Key Per Transaction). It’s important to understand that in the DUKPT world, every transaction has its own key. The key can’t be reused for any other transaction(s); hence, replay attacks are impossible.
The question is: How can you derive a DUKPT key that will unlock a given transaction? The answer is: Generally speaking, you need the Key Serial Number (KSN) for the transaction, plus a special value called the IPEK, or initial key that was injected into the credit card reader. The IPEK, in turn, is derived from a super-secret key (that’s never injected into a card reader) called the BDK (Base Derivation Key). Unlike the BDK, the IPEK is unique to a given physical device. (One BDK can be the source of many unique IPEKs.) If you don’t know the IPEK for your device (and there’s no reason why you would, since the IPEK is never written down anywhere), you can derive it from a KSN and a Base Derivation Key, using the technique described in Part I of this article.
Deriving a session key (sometimes called a working key, or simply “data key”) is actually best thought of as a 3-step process. The steps are:
1. Use the BDK and KSN to derive the IPEK. (See Part I of this article for details on how to do this.)
2. Use the ANSI X9.24 (DUKPT) key-derivation algorithm to derive a basis key, or initial “derived key,” from the KSN and IPEK.
3. Convert the derived key of Step 2 into your choice of Data Key, PIN Key, or MAC Key. (Note that while most credit card readers are set up to use the Data variant key for the transaction session key, some are, in fact, set up to use the PIN variant instead.)
Let’s go ahead and see what’s involved in obtaining the “derived key” (step 2), since this is by far the most painstaking part of the 3-step process. Once we’ve got the derived key, we’ll talk about how to transform it into a Data, PIN, or MAC variant, which is relatively easy.
We’ll be using a fair amount of pseudocode below, but rest assured, you can find complete, working source code (in JavaScript) for all of the following steps in our popular Encrypt/Decrypt Tool. (Try it now, if you haven’t already. It’s a self-contained web page that works in any modern browser.)
Deriving a Key
To derive the basis key from which a Data, PIN, or MAC variant can be created, you need to start with a transaction KSN and an IPEK. Once you have those (again: see Part I of this series), do this:
1. Obtain the bottom (rightmost) 8 bytes of your 10-byte KSN. Discard the top two bytes.
2. Create a BaseKSN variable to hold a masked version of your 8-byte KSN. Obtain the masked version by ANDing the 8-byte KSN of Step 1 with the (hex) value 0xFFFFFFFFFFE00000.
3. Get the counter bits from your original (not masked!) 10-byte KSN by ANDing its bottom three bytes with 0x1FFFFF. (Recall that the bottom 21 bits of a KSN comprise the transaction counter.) We’ll put this in a variable called (what else?) counter
.
4. Copy your 16-byte IPEK into a variable called curKey
.
5. Now we need to set up a loop. Each time through the loop, we are going to inspect the counter bits (starting from the top bit, or 21st bit; on the second pass through the loop, we’ll check the 20th bit; then the 19th bit; and so on). Each time we find a bit that’s turned on, we will OR it into the BaseKSN, then call generateKey()
to update curKey
. The BaseKSN will accumulate bits with each trip through the loop and the curKey value will update with each turned-on counter bit we find.
for (var shiftReg = 0x100000; shiftReg > 0; shiftReg >>= 1) if ( ( shiftReg & counter ) > 0 ) { baseKSN |= shiftReg; curKey = generateKey( curKey, BaseKSN ); }
What does generateKey()
do? Glad you asked! If your programming language supports BigInteger math, the code will look something like this:
function generateKey(key, ksn) { var mask = 0xC0C0C0C000000000C0C0C0C000000000; var maskedKey = mask ^ key; var left = encryptRegister( maskedKey, ksn ); var right = encryptRegister( key, ksn ); return (left << 64) | right; // join left and right together }
Okay. You can see that the 16-byte key is masked, then used to encrypt the 8-byte ksn
value, to get the left half (the left 8 bytes) of a new key. The right half of the new key is a cipher created from the same ksn, but using an unmasked key.
Finally, you need to know what encryptRegister()
looks like. This is it:
// Returns an 8-byte result function encryptRegister(key, ksn) { var CBC = 1; // cipher block chaining enabled var iv = ""; // initial vector var bottom8 = key & 0xFFFFFFFFFFFFFFFF; // bottom 8 bytes var top8 = (key & 0xFFFFFFFFFFFFFFFF0000000000000000) >> 64; // top 8 bytes var bottom8xorKSN = bottom8 ^ ksn; // This will be single-DES because of the 8-byte key: var desEncrypted = des( top8, bottom8xorKSN, true, /* encrypt */ CBC, iv ); var result = bottom8 ^ desEncrypted; return result; // return the 8-byte result }
Note that Cipher Block Chaining is actually meaningless here, because we are encrypting an 8-byte value (one block of data). There is nothing to “chain.” It’s included in the code simply because the encryption routine happens to require a parameter that says yes or no to chaining.
Also note that we are using an 8-byte key to do the encryption. TDES defaults to single-DES when the key is only 8 bytes long. That’s because an 8-byte key would (in triple DES) result in an encrypt/decrypt/encrypt cycle that’s equivalent to doing a single encrypt.
The plain-English explanation of what’s going on is that the routine uses the top 8 bytes of a 16-byte key to encrypt a special value that comes from XORing the bottom 8 bytes of the key with the (8-byte) ksn. The result is a one-way hash of the ksn.
Put it all together, and the result is that the loop from Step 5 above produces a curKey
value that ends up being a basis key from which we can derive Data, PIN, or MAC variants. (The loop in Step 5 is, or should be, part of a function that ends up returning curKey, which is the basis key.)
It’s time now to look at those three “key variant” options in more detail.
Creating Data, PIN, and MAC Key Variants
ANSI X9.24 allows a DUKPT key to take on one of three final forms, called variants. The forms are MAC, PIN, and Data. Let’s defer any discussion of what these various key types are used for in order to concentrate on how they’re created.
The starting point for any of the variants is a DUKPT basis key (the derived key that we called curKey
in Step 5 further above). To get the MAC variant, you simply need to XOR the basis key (the “derived key”) with a special constant:
MACkey = derivedKey ^ 0x000000000000FF00000000000000FF00;
The PIN variant, likewise, is created in similar fashion, but using a different constant:
PINkey = derivedKey ^ 0x00000000000000FF00000000000000FF;
The Data variant requires yet another constant:
Datakey = derivedKey ^ 0x0000000000FF00000000000000FF0000;
For MAC and PIN variants, the XOR operation constitutes the final step in creating the relevant session key. For the Data variant, it’s customary to perform one additional step, involving a one-way hash (to preclude any possibility of someone back-transforming a Data key into a MAC key). In pseudocode:
// left half: var left = des( EDE3KeyExpand( derivedKey ), top8bytes( derivedKey ), true, CBC, iv ); // right half: var right = des( EDE3KeyExpand( derivedKey ), bottom8bytes( derivedKey ), true, CBC, iv ); finalDataKey = (left << 64) | right; // combine halves
In English: First, obtain a 24-byte version of your derived key, by using the EDE3 expansion method. (This simply means copying the first 8 bytes of a 16-byte key onto the tail end of the key, creating a 24-byte key in which the first and last 8 bytes are the same.) Use that key to TDES-encrypt the first 8 bytes of your 16-byte derived key, thereby creating an 8-byte cipher. That’s the left half of the eventual data key. To create the right half, use the same 24-byte key to encrypt the bottom 8 bytes of the derivedKey. Combine the two 8-byte ciphers (left and right pieces), and you’re done.
Known-Good Values
If you’re trying to do this at home, you may want to check your work against some known-good values. So, start with a 16-byte BDK of 0123456789ABCDEFFEDCBA9876543210 (hex), which is the test-key value everyone tends to use. Try a test KSN value of 629949012C0000000003. These two values should allow you to derive an IPEK of D2943CCF80F42E88E23C12D1162FD547. (Refer to Part I of this article if you want to see how to derive the IPEK.)
Starting with the aforementioned IPEK, you should see the following values when deriving a “derived key” (or DUKPT basis key):
On the first trip through the “if” of the KSN-counter loop, your BaseKSN
will be 49012C0000000002 and curKey
will become B58CDA5C7A1E9FF5E7335B988626D01A after generateKey()
.
On the second trip through the “if” of the counter loop, you will have processed both “ON” bits of the counter, and therefore your BaseKSN will be 49012C0000000003 and the resulting curKey will be 841AB7B94ED086EBC2B8A8385DA7DFCA. (Remember, you are ORing counter bits, MSB first, into the BaseKSN. If the counter ends in 0x0F, the BaseKSN will go from 49012C0000000008 to 49012C000000000C to 49012C000000000E to 49012C000000000F as you OR bits successively.)
Your “derived key” will thus be 841AB7B94ED086EBC2B8A8385DA7DFCA.
After XORing the data-variant constant, the derived key will change to 841AB7B94E2F86EBC2B8A8385D58DFCA.
After enciphering the top and bottom halves of the latter value, using an EDE3-expansion key of 841AB7B94E2F86EBC2B8A8385D58DFCA841AB7B94E2F86EB, you should get a final data key of F739AEF595D3877F731782D28BB6AC4F. That is: Using the 24-byte EDE3 key value to encrypt 841AB7B94E2F86EB, you should get a cipher of F739AEF595D3877F, and using the same key to encrypt C2B8A8385D58DFCA, you should get a cipher of 731782D28BB6AC4F. Concatenate the ciphers, and you’re done. You now have a 16-byte key with which you can decrypt data from the transaction whose KSN was 629949012C0000000003.
Example Code: The ID TECH Encrypt/Decrypt Tool
To see full source code for all of the DUKPT key derivation routines discussed here, be sure to download (and inspect the source code of) our HTML-and-JavaScript-based Encrypt/Decrypt Tool, which can calculate IPEKs, derive all 3 DUKPT key variants, encrypt or decrypt data using TDES or AES, and much more. You can use Chrome’s excellent developer-console toolset to step through the Tool’s code in real time, inspect variable values as they change, set breakpoints, etc. It’s a tremendous learning aid. And it’s free! So download the Encrypt/Decrypt Tool, play with it, and tell your friends about it. As far as I know, it’s the only pure-JavaScript DUKPT implementation on the Web.