ATM Pin encryption using 3DES

Introduction

Most modern ATM’s use a Triple Des algorithm to encrypt the pin and send it to a host server for processing. Once the host system receives the pin, it does a translation of the pin from one encryption key to another, and sends it to a bank. In this post I will attempt to explain the process and how it is done in the real world.

Overview of the Triple Data Encryption Standard

What we all call Triple DES is EDE (encrypt, decrypt, encrypt). The way that it works is that you take three 56-bit keys, and encrypt with K1, decrypt with K2 and encrypt with K3. There are two-key and three-key versions. Think of the two-key version as merely one where K1=K3. Note that if K1=K2=K3, then Triple DES is really Single DES.

Triple DES was created back when DES was getting a bit weaker than people were comfortable with. As a result, they wanted an easy way to get more strength. In a system dependent on DES, making a composite function out of multiple DESes is likely to be easier than bolting in a new cipher and sidesteps the political issue of arguing that the new cipher is better than DES.

As it turns out, when you compose a cipher into a new one, you can’t use a double enciphering. There is a class of attacks called meet-in-the-middle attacks, in which you encrypt from one end, decrypt from the other, and start looking for collisions (things that give you the same answer). With sufficient memory, Double DES (or any other cipher) would only be twice as strong as the base cipher — or one bit more in strength.

There’s more to it. If the cipher forms a group, then encrypting twice with two keys is equivalent to encrypting once with some key. Now, it’s not trivial to know what that other key is, but it means that a brute-force attack would find that third key as it tried all possible single-keys. So if the cipher’s a group, then multiple-ciphering is merely a waste of time.

Applying this encryption in Python is trivial as there are plenty of tested libraries that can provide the functionality like pyDes and Crypto :

import os
from Crypto.Cipher import DES3

def encrypt_file(in_filename, out_filename, chunk_size, key, iv):
    des3 = DES3.new(key, DES3.MODE_CFB, iv)

    with open(in_filename, 'r') as in_file:
        with open(out_filename, 'w') as out_file:
            while True:
                chunk = in_file.read(chunk_size)
                if len(chunk) == 0:
                    break
                elif len(chunk) % 16 != 0:
                    chunk += ' ' * (16 - len(chunk) % 16)
                out_file.write(des3.encrypt(chunk))

def decrypt_file(in_filename, out_filename, chunk_size, key, iv):
    des3 = DES3.new(key, DES3.MODE_CFB, iv)

    with open(in_filename, 'r') as in_file:
        with open(out_filename, 'w') as out_file:
            while True:
                chunk = in_file.read(chunk_size)
                if len(chunk) == 0:
                    break
                out_file.write(des3.decrypt(chunk))

 

ATM Internals and how they calculate the keys

When you have an ATM, you typically need to provide it with a set of encryption keys from your host, or HSM. These keys are clear text keys and it’s not encrypted in any way. Your host will link them to your terminal number, and when the ATM encrypts the pin; the host will know what keys are used so it can decrypt / translate them to the bank. The clear keys are never stored by the host, only the LMK encrypted keys.

Lets assume your host provides the following keys to you as the ‘ATM Encryption Key’ :

Clear component A: 67C4 A719 1ADA FD08 6432 CE0D D638 4AB
Key check value: 20D40B
Clear component B: 8A89 6D4C 4625 5E2A 1A75 2002 07A7 D35E
Key check value: 4EC801
Combined Check Value: 2B547D

Now typically you would enter the clear components into the ATM, as Encryption keys, and the ATM will combine them (Basically XOR Them) and derive the check value. If the check value match, then all is good.

What happens at your host end is the following:

Your host will also combine the keys and encrypt them under the LMK (Local Master Key). It will then use this key to encrypt all other keys that are sent to the ATM.

Now the ATM have a Terminal Master Key that it can use to decrypt all keys that are sent to it from the host.

ATM Configuration Request (Key Exchange)

Now when an ATM starts up, the first thing it does it send a configuration request to the host. This request is to get the Third key used in Triple DES.  The Host will generate a random Terminal Pin Key and encrypt it under the Terminal Master Key (TMK). Since the ATM has the Terminal Master Key, it can decrypt the encrypted TPK, and use all 3 Keys now for the Triple DES operations. (it actually uses 2)

The Host would generally execute the A0 Thales command to get this key. He would store the key in the key database to do the decryption / translation later.

 

Pin Encryption / Decryption

When a ATM gets ready to transmit a transaction it does the 3DES operation on the Pin only. the cypher text is now transmitted to the host. The host never knows the pin code, and only does a translation of the pin from the terminal keys to the bank keys.

The Host will have the following:

(ZPK) Zone Pin Key – from the Bank during Host to Bank Key exchange

(TPK) Terminal Pin Key – from Terminal using Terminal Configuration Request

(PAN) Account Number –  from Transaction transmitted.

With these values, the Host can translate the pin using a HSM, below is an example of the D4 Command.

 Res = KeyGenerator.TranslatePIN_TDES(TerminalPINKey=self.Crypto["TPK_LMK"], PINEncryptionKey=self.HostKeys["ZPK_LMK"], PINBlock=self.iso.getBit(52), AccountNumber=track2["PAN"][-13:-1])

def get_commandTPKPinBlock(self, TerminalPINKey, PINEncryptionKey, PINBlock, AccountNumber):

 command_code = 'D4'
 KTP = TerminalPINKey
 KPE = PINEncryptionKey
 PinBlock = PINBlock
 PAN = AccountNumber


 message = command_code
 message += KTP
 message += KPE
 message += PinBlock
 message += PAN
 return message
#transmit to HSM

The transaction can now be transmitted to the acquiring bank with the translated pin for processing.

Sometimes the ATM requires a Message Authentication Code, this will be covered in another post.

easy as pie

 

 

Thales Key Exchange Examples and Troubleshooting

Judging from the searches done to locate this blog, it’s clear many of us share the following opinion: although Thales (formerly RACAL) is a market leader with its 7000 and 8000 series of HSM devices, their documentation falls painfully short in two areas: there are NO COMMAND EXAMPLES (!!!) in the manuals (an appalling omission); and the troubleshooting assistance is also distressingly thin. As a result, we had to lean heavily on our local Thales distributor for guidance on how commands really get pieced together. And, man, is it ever esoteric….see my earlier posts regarding the ‘KSN Descriptor’ for evidence of that. Moreover, our distributor provided us with some outstanding troubleshooting support to get us through the all important FA/FB key exhange. So, I’ll post our experience here in hopes that others can benefit from it.

The FA/FB is the command exchange to “Translate a ZPK from ZMK to LMK Encryption.” We had this working internally with some simulated stuff, but once we tried this command ‘for real’ with our Debit/EBT gateway partner , we consistently received parity errors back from our Thales 8000 HSM (i.e., the FB returned with some result code != 0).

There are three possible issues / resolution paths to explore in these situations:

Your switching partner has employed an Atalla HSM and you’ve not taken it into account. When a Thales/RACAL HSM ‘talks’ to an Atalla, your box commands must specify an Atalla Variant.

Your switching partner didn’t specify a Key Scheme in its ZPK creation, and the default is X9.17 (‘X’). If you specify the RACAL Scheme (‘U’) in your ‘FA’ and the ZPK under ZMK provided to the box was created via the X9.17 scheme, you’ll get a parity error.

You created the ZMK cryptogram internally using one key scheme and now are trying to employ it in the FA command specifying (inadvertantly) the other variant scheme.

You want to try to resolve each of these in turn.

The “test solution” path for Item #1 is as follows…

Let’s assume your FA command is constructed like so:

FAU2D775BFD****************FABE0D7CU6C0FDE16D22FF2D95273E3741AF4E187

[NOTE: I’ve obscured the ZMK Cryptogram here for blogging purposes only. The actual value is a 32-position hexidecimal string.]

If you discover that the other side is using an Atalla, you need to specify an “Atalla Variant,” which you do by specifying a ‘1’ at the end of the ‘FA’ command string:

FAU2D775BFD****************FABE0D7CU6C0FDE16D22FF2D95273E3741AF4E1871

The “test solution” path for Item #2 is as follows…

We find some endpoint partners have no idea which Key Scheme (X9.17 or Racal Native) they employed to create their keys. So, you may have to experiment. This FA command string says that the ZMK and ZPK were created using the RACAL native scheme (‘U’):

FAU2D775BFD****************FABE0D7CU6C0FDE16D22FF2D95273E3741AF4E187

To specify that the ZPK was created using the X9.17 scheme, you’d do the following:

FAU2D775BFD****************FABE0D7CX6C0FDE16D22FF2D95273E3741AF4E187

The “test solution” path for Item #3 is as follows…

When you created your ZMK (probably during a key ceremony involving a reconstitution of key parts provided by your endpoint gateway), you specify (or otherwise let default) your ZMK Key Scheme. Again, this can be either the RACAL Native Scheme (‘U’) or X9.17 (‘X’). [I believe the default is X9.17.] For example, if you created the ZMK using the ‘X’ approach, and then submitted an ‘FA’ command that looks like this:

FAU2D775BFD****************FABE0D7CU6C0FDE16D22FF2D95273E3741AF4E187

…it’s gonna fail. You absolutely must maintain consistency from the creation of the ZMK cryptogram through its subsequent usage. So, the command would change to look like this:

FAX2D775BFD****************FABE0D7CU6C0FDE16D22FF2D95273E3741AF4E187

[NOTE: I’m not advocating ‘X’ over ‘U’ here…just showing you an example. In a recent concluded project, we assumed the incoming ZPK was of the RACAL native variety, but it arrived from the remote partner as created under the X9.17 scheme. Thing is, the gateway team had no idea they had done that and could not articulate the difference. So, be prepared to experiment with every permutation of what I’ve described herein before ‘unlocking’ the solution.]