Dynamic Key Exchange Models

Dynamic Key Exchange Models

I’ve had a number of people ask me recently about how to implement Dynamic Key Exchange models.  Specifically, I’m talking here about ISO8583-based financial payment gateways.  This post pertains to situations where you’re acting either as the Card Issuer (in which case you’re receiving payment transaction requests from the gateway) or the transaction acquirer (in which case you’re sending payment transaction requests to the gateway in order that they route it for appropriate authorization decisioning).

There’s some terminology to square away first:

Local Master Key (‘LMK’) – This is the key you store in the HSM in order to encrypt and do software-based storage of the current Working Keys (and Base Derivation Keys if you’re using DUKPT).  Also called the Master File Key (‘MFK’)

Zone PIN Key (ZPK’) – The ZPK is what’s used to encrypt the PIN blocks that traverse the wires between institutions.  Also referred to as the ‘Working Key.’  This is the key that the Dynamic Key Exchange is acting upon.  You’re obligated to change the Working Key at agreed-upon intervals (I typically advocate every 12 hours).

Zone Master Key (‘ZMK’) –  Think of the ZMK as the key transportation vehicle.  It’s the key that the two parties use to encrypt and exchange new ZPKs.  This key is established via a key ceremony.  You keep a copy of the ZMK encrypted under the LMK in a file somewhere (you’ll see how it’s used here further down this post).  Also called the Key Exchange Key (‘KEK’).

  1. From the moment you start planning discussions with the gateway, establish RIGHT AWAY that you want field-by-field level specifics of how the Dynamic Key Exchange is to be performed.  It’ll be within the context of some  Network Message Exchange (e.g., 0800/0810), but that’s not granular enough – you need to know the thing down to the field-content level.
  2. Scour the documentation you’ve been provided to see if those details are in there.  I’ve done two different gateway projects recently, and in both cases the Key Exchange details were notably absent from the doc.  But, that doc exists somewhere within the gateway institution.  Track it down.  Get your hands on it.
  3. Knowledge of the Key Exchange model is – by design – not widespread throughout the gateway provider’s project personnel.  Insist on getting their expert in on at least one of the planning calls.  Make note of this person’s name and contact details.  Establish that information channel.  This is a critically important point to your success.

At a high level, there are two models:

  • You request a new ZPK from the gateway, and they provide it in the response.  [I call this the ‘Pull‘ model (for obvious reasons – you pull the key from them).]
  • The gateway sends you a new ZPK and you respond with a message indicating success or failure.  [This, by contrast is the ‘Push‘ model.]

Your implementation will be one of those.

Now, I’ll provide two examples, one push, one pull.

The sequence of events is:

  1. The gateway sends us a new ZPK (under ZMK) in an 0800 (MTI) Network Request.
  2. We obtain the ZMK (under LMK) from our files.
  3. We use the cryptograms from Steps 1 and 2 to create the appropriate command to the Key-Up (here, a ’12’)
  4. We get the response from the Key-Up (the ’13’) and validate that the Check Digits match those provided by the Issuer.
  5. Assuming the check in Step 4 is okay, we store the result (the ZPK under LMK) as the new Working Key.
  6. We send an 0810 (MTI) Network Response back to the Issuer (Note that Field 39 on our response is ’00’ – indicating success).

There’s so much detail here worthy of comment.  I’ll touch on a few things (these are the types of detail you want to bring to the surface in your reviews):

  • This gateway uses ‘162’ in Field 70 to tip to us that it’s a Key Exchange in play.
  • Note how we have to pluck the incoming cryptogram out of the esoteric morass of Field 123.
  • We have to construct an equally cryptic Field 123 on our response.


Here is the pull model:

  1. We request a new key from the Gateway in an 0800.
  2. The new key (ZPK under ZMK) comes back in an 0810.
  3. We fire off an ‘FA’ to the Thales 8000.
  4. We get the ‘FB’ back and validate the check digits.
  5. If okay, we store the result (the ZPK under LMK) as the new Working Key.

Now, since we’re the initiator here we have to have a way to determine when to trigger the exchange request.  We do that through a channel Logon Manager.

You get the idea, I hope!  Nail down all those details in order to maximize your chances of success.  Otherwise, feel free to beat your head against a wall, because that’s what will happen if you don’t get this information.


Doing PIN Translation with DUKPT

On PIN-enabled Debit/EBT transactions sent in from an acquirer’s point-of-sale location, your payment switch application must perform a PIN translation, typically transforming an incoming DUKPT PIN block from the POS device-initiated request into a outgoing Triple DES-encrypted PIN block that makes use of an established Zone PIN Key (“ZPK”) which would have been previously established via a dynamic key exchange with your Debit/EBT gateway provider.

[The remainder of this example assumes you’re using a Thales (formerly Racal) Hardware Security Module (“HSM”)….] Using strict Thales parlance, this variant of a PIN translation request is a request to “translate a PIN from *BDK encryption to interchange key encryption.”  This topic is covered in Section 27.2 (page 2-185) of the Thales reference document entitled “Host Security Module RG7000 Programmer’s Manual” (Thales reference number 1270A514 Issue 5). The CI/CJ exchange should be handled as follows: — CI — Message header – You can use as you see fit. Value is echoed back in CJ.  Note that the length is constant and must be configured in HSM by administrator. Command code – CI BDK – The Base Derivation Key “in play” for this transaction.  In my installations we’ve set this up as follows…

  • Selected the “1A + 32H” option, where the ‘1A’ value should be set to ‘U’
  • Configured such that the first six positions of the KSN represent the “key name” of the BDK injected into the PIN Pad at the transaction origination point (an acquirer can use a number of BDKs in their terminal population).

ZPK – Your current ZPK Cryptogram (obtained dynamically via a key exchange with your Debit/EBT  gateway partner) and stored under your Local Master Key (“LMK”).  In my installations, we’ve used the “1A + 32H” option, where the ‘1A’ value should be set to ‘U’. KSN Descriptor – This value is a bit esoteric and refers directly to the make-up of the KSN which follows.  So to understand the descriptor, it’s first necessary to talk a bit about the KSN (the next field in the CI command layout).  Here’s a typical KSN implementation where the acquirer has chosen a 16-position scheme:

  • Positions 1 – 6: The name of the BDK injected into this device
  • Positions 7 – 11:  The device ID
  • Positions 12 – 16: The transaction counter

[Note that the KSN implementation has to be in synch between the PIN pad and your host-side implementations in order for this to work.] The ‘rules’ for a KSN construction are as follows (reading from left to right in the KSN): a. The ‘base derivation key identifier,’ which is mandatory and five to nine (Hex) positions in length. b. A ‘sub-key identifier,’ which Thales says is ‘optional’ but in practice is ‘reserved for future use’ (and therefore always set to zero). c. A ‘device identifier’ (mandatory), which is two to five Hex digits in length. d. A ‘transaction counter’ (mandatory) which essentially is the part “left over”. So, in the example here, the client with a 6, 0, 5, 5 implementation. With this information in hand, the KSN Descriptor (a three-position value) is better described as XYZ, where: X = base derivation key identifier length Y = sub-key identifier key length (will be zero) Z = device identifier length So, in this context, the ‘605’ submitted in my example is better visualized. ‘605’ says that the 16-digit KSN consists of a 6-position BDK ID, a 0-position sub-key, a 5-position device ID, **AND** (what’s remaining basically) a 5-position transaction counter. [NOTE: Remember that this post applies *specifically* to the Thales/RACAL implementation of PIN translation] Now, with this informatation in hand, we can introduce the next field, the KSN itself… KSN – Using the layout from the descriptor, a typical KSN at this acquirer might be 123456000A8001D4 where: ‘123456’ is the BDK indentifier; ‘000A8’ is the Device ID; and ‘001D4’ is the transaction counter. The BDK name embedded in a particular KSN string must find a match within your BDK cryptogram list (which you need to keep loaded into your payment switch’s encryption database).  If a match is not found in the encryption database, then set your Internal Result Code to “Invalid BDK” and end the transaction.  If found, the value you retrieve goes into the BDK field (as described above). Source encrypted block – The PIN block plucked from the POS device request (this is a 16H value; no ‘1A’ indicator is required). Destination PIN block – In my installations, we typically use ANSI format, so we set this to ‘01’ to signify ANSI format code Account Number – Right-most 12 positions of the PAN excluding the check digit Typically, that is the END of the required CI request message (remainder of the fields in the Thales spec are not mandatory). — CJ — Message header – Echoed back from CI usage. Response code – CJ Error Code – Only ‘00’ should be accepted as an exchange that “worked.” PIN length – Although this field is not used to build the 0200 message formatted for your Debit/EBT gateway, a value like ‘04’ or ‘05’ here are a pretty good indication that the translation occurred successfully. Encrypted PIN – The PIN block that will be used to build the 0200 message formatted for your Debit/EBT gateway (this is a 16H value; no ‘1A’ indicator is required). Destination PIN block – Echoed back from the device as ’01’ format code Typically, that is the END of the response message (remainder of list in the vendor spec would only be present if they were provided in the CI command request)

AS2805 Standards for EFT

Australia Standards 2805 (AS2805) is the standard for Electronic Funds Transfer (EFT) and Payments in Australia and New Zealand. AS2805 is also used for some implementations in South Africa and SE Asian.

AS2805 is owned by Australia Standards and was developed by various voluntary working groups within Committee IT/5. The implementation of AS2805 standards across all industries is clearly defined by the Australian Payments Clearing Association (APCA) as part of the Consumer Electronic Clearing System (CECS) and detailed in the CECS Manual.

Contrary to popular belief AS2805 is not a rename of the ISO8583 standard in the Australia Standards numbering system, as is the case with most international standards.

ISO8583 was first published in 1987, while AS2805 was published two years earlier in 1985, after a lengthy period of draft and review in Australia, New Zealand and South Africa. ISO8583 consists of three (3) parts:

  • Part 1: Messages, Data Elements and Code Values
  • Part 2: Application and Registration Procedures for Institution Identification Codes (IIC)
  • Part 3: Maintenance Procedures for Messages, Data Elements and Code Values

All three (3) parts of ISO8583 are concentrated on only message formats between devices (EFTPOS and ATM) and an acquiring host. ISO8583 can be seen as a small subset of the AS2805 standard and there is no clear guide for uniform implementation as is the case with CECS. AS2805 on the other hand consist of at least thirty three (33) separate published parts and covers general EFT topics such as:

  • Card Management & Authorisation
  • Card Detail Updating
  • PIN Management
  • Key Management and Security
  • Message Authentication
  • Privacy and Data Encryption
  • Communications
  • Message Structure between Devices and Acquiring Host
  • Message Structure between Hosts
  • File Transfers

The thirty three (33) AS2805 standards published so far are the following:

2805.1 Part 1: Communications
2805.2 Part 2: Message Structure, format and content
2805.3.1 Part 3.1: PIN Management and Security – General
2805.3.2 Part 3.2: PIN Management and Security – Offline
2805.4.1 Part 4.1: Message Authentication – Mechanisms Using a Block Cipher
2805.4.2 Part 4.2: Message Authentication – Mechanisms Using a Hash Function
2805.5.1 Part 5.1: Ciphers – Data Encipherment Algorithm 1 (DEA 1)
2805.5.2 Part 5.2: Ciphers – Modes of Operation for an n-bit block cipher algorithm
2805.5.3 Part 5.3: Ciphers – Data Encipherment Algorithm 2 (DEA 2)
2805.5.4 Part 5.4: Ciphers – Data Encipherment Algorithm 3 (DEA 3) & related techniques
2805.6.1.1 Part 6.1.1: Key Management – Principles
2805.6.1.2 Part 6.1.2: Key Management – Symmetric Ciphers, their Key Management & Life Cycle
2805.6.1.4 Part 6.1.4: Key Management – Asymmetric Cryptosystems – Key Management & Life Cycle
2805.6.2 Part 6.2: Key Management – Transaction keys
2805.6.3 Part 6.3: Key Management – Session Keys – Node to Node
2805.6.4 Part 6.4: Key Management – Session Keys – Terminal to Acquirer
2805.6.5.1 Part 6.5.1: Key Management – TCU Initialisation – Principles
2805.6.5.2 Part 6.5.2: Key Management – TCU Initialisation – Symmetric
2805.6.5.3 Part 6.5.3: Key Management – TCU Initialisation – Asymmetric
2805.6.6 Part 6.6: Key Management – Session Keys – Node to Node with KEK Replacement
2805.9 Part 9: Privacy of Communications
2805.10.1 Part 10.1: File Transfer Integrity Validation
2805.10.2 Part 10.2: Secure File Transfer (Retail)
2805.11 Part 11: Card Parameter Table
2805.12.1 Part 12.1: Message Content – Structure and Format
2805.12.2 Part 12.2: Message Content – Codes
2805.12.3 Part 12.3: Message Content – Maintenance of Codes
2805.13.1 Part 13.1: Secure Hash Functions – General
2805.13.2 Part 13.2: Secure Hash Functions – MD5
2805.13.3 Part 13.3: Secure Hash Functions – SHA-1
2805.14.1 Part 14.1: Secure Cryptographic Devices (Retail) – Concepts, Requirements and Evaluation Methods
2805.14.2 Part 14.2: Secure Cryptographic Devices (Retail) – Security Compliance Checklist for Devices used in Financial Transactions
2805.16 Part 16: Merchant Category Codes

The AS2805 standard also provides three (3) published Handbooks related to the AS2805 standard:

HB 127 EFT – Implementing Message Content Standards – Conversion Handbook
HB 128 EFT – Implementing Message Content Standards – Terminal Handbook
HB 129 EFT – Implementing Message Content Standards – Interchange Handbook

There are a number of guideline white papers available to assist the implementation of EFT related functionality such as:

  • Card Management & Production
  • EFTPOS/POS Software Management
  • EFTPOS and POS Product Management
  • Software and Configuration File Downloading
  • Retail Electronic Data Exchange (EDT) that covers price downloads, ordering and statistics
  • Retail Automation
  • Terminal Management
  • Merchant Management
  • Cashier Management
  • Fraud Monitoring and Management

Trace your ATM Transactions


Generally when entrepreneurs decide to become ATM deployers, they do not have sufficient knowledge about ATM protocols and specifications. This is not needed as there are switching providers that can switch their ATM’s  transactions and provide them with adequate reporting.

Following this approach generally leaves them with a massive gap in terms of managing their terminals and merchants  correctly. ATM switching providers have the ability to decode the terminal status messages in real time and determine for example is a terminal has ran out of cash, if hardware is busy failing or even if a processing bank has gone offline.

This allows switching providers to have the upper hand over the smaller ATM deployers.

In this post I will show you how to develop a middleware where this pro active approach can be followed even with small ATM deployers. You will be able to see live cash levels of your terminals and monitor ATM transactions.


ATM Languages

ATM’s  and EFTPOS devices speak different languages and each terminal manufacturer might have their own custom implementation. Terminal developers  normally follow a few types of language implementations: Triton / NDC+ / AS2805 / ISO8583. As all of these will run on TCP using some sort of control protocol (VISAII / ACK Controlled) we need to decode the control elements of the protocol as well.

I will be using the Standard Triton Protocol over VISAII to demonstrate the ability to trace transactional protocols.

Most ATM’s have an TCP/IP setting that will enable you to point the ATM to an IP Address and port. Writing an Server component to listen to this is of course as easy as pie.

Conversion to ASCII

It is an easy approach to write a server application and listen on a post for incoming transactions but we will quickly notice that these message are encoded. This encoding it an easy hurdle (as long as there is no SSL component on the ATM)

StringToAscii Class – The readable encoding

The first class I’m going to create is an class that can convert encoded strings to readable ascii, this need the be based on the control characters specified in your protocol specification.

Each Hex encoded string needs to be mapped to the format you require. (<ETX> / <STX>) and a simple function can provide the conversion.

controls_dic = {
 0: '<NUL>', 1: '<SOH>', 2: '<STX>', 3: '<ETX>', 4: '<EOT>', 5: '<ENQ>', 6: '<ACK>',
 7: '<BEL>', 8: '<BS>' , 9: '<HT>' , 10: '<LF>' , 11: '<VT>' , 12: '<FF>' , 13: '<CR>' ,
14: '<SO>' , 15: '<SI>' , 16: '<DLE>', 17: '<DC1>', 18: '<DC2>', 19: '<DC3>',
20: '<DC4>', 21: '<NAK>', 22: '<SYN>', 23: '<ETB>', 24: '<CAN>', 25: '<EM>',
26: '<SUB>', 27: '<ESC>', 28: '<FS>' , 29: '<GS>' , 30: '<RS>' , 31: '<US>'

def Str2Ascii(s):
 r = ''
 for c in s:
 if ord(c) < 32:
 r = r + controls_dic[ord(c)]
 elif ord(c) >= 127:
 r = r + '<%d>' % ord(c)
 r = r + c
 return r

The reverse conversion is slightly more complex, but in essence its just a reverse function of the above.

reverse_dic5 = { 
'<NUL>': 0, '<SOH>': 1, '<STX>': 2, '<ETX>': 3, '<EOT>': 4, '<ENQ>': 5, '<ACK>': 6,
'<BEL>': 7, '<DLE>': 16, '<DC1>': 17, '<DC2>': 18, '<DC3>': 19,
'<DC4>': 20, '<NAK>': 21, '<SYN>': 22, '<ETB>': 23, '<CAN>': 24, 
'<SUB>': 26, '<ESC>': 27

reverse_dic4 = {
'<BS>' : 8, '<HT>' : 9, '<LF>' : 10, '<VT>' : 11, '<FF>' : 12, '<CR>' : 13,
'<SO>' : 14, '<SI>' : 15, '<EM>' : 25, '<FS>' : 28, '<GS>' : 29, '<RS>' : 30, '<US>' : 31

reverse_dicN = {
 '<130>':130, '<140>':140, '<150>':150, '<160>':160, '<170>':170, '<180>':180, '<190>':190, '<200>':200, '<210>':210, '<220>':220, '<230>':230, '<240>':240, '<250>':250, 
 '<131>':131, '<141>':141, '<151>':151, '<161>':161, '<171>':171, '<181>':181, '<191>':191, '<201>':201, '<211>':211, '<221>':221, '<231>':231, '<241>':241, '<251>':251, 
 '<132>':132, '<142>':142, '<152>':152, '<162>':162, '<172>':172, '<182>':182, '<192>':192, '<202>':202, '<212>':212, '<222>':222, '<232>':232, '<242>':242, '<252>':252, 
 '<133>':133, '<143>':143, '<153>':153, '<163>':163, '<173>':173, '<183>':183, '<193>':193, '<203>':203, '<213>':213, '<223>':223, '<233>':233, '<243>':243, '<253>':253, 
 '<134>':134, '<144>':144, '<154>':154, '<164>':164, '<174>':174, '<184>':184, '<194>':194, '<204>':204, '<214>':214, '<224>':224, '<234>':234, '<244>':244, '<254>':254, 
 '<135>':135, '<145>':145, '<155>':155, '<165>':165, '<175>':175, '<185>':185, '<195>':195, '<205>':205, '<215>':215, '<225>':225, '<235>':235, '<245>':245, '<255>':255, 
 '<136>':136, '<146>':146, '<156>':156, '<166>':166, '<176>':176, '<186>':186, '<196>':196, '<206>':206, '<216>':216, '<226>':226, '<236>':236, '<246>':246, 
'<127>' : 127, '<137>':137, '<147>':147, '<157>':157, '<167>':167, '<177>':177, '<187>':187, '<197>':197, '<207>':207, '<217>':217, '<227>':227, '<237>':237, '<247>':247, 
'<128>' : 128, '<138>':138, '<148>':148, '<158>':158, '<168>':168, '<178>':178, '<188>':188, '<198>':198, '<208>':208, '<218>':218, '<228>':228, '<238>':238, '<248>':248, 
'<129>' : 129, '<139>':139, '<149>':149, '<159>':159, '<169>':169, '<179>':179, '<189>':189, '<199>':199, '<209>':209, '<219>':219, '<229>':229, '<239>':239, '<249>':249 

def Ascii2Str(s):
 r = ''
 i = 0
 l = len(s)
 while i < l:
 if s[i] == '<':
 x = reverse_dic5[s[i:i+5]]
 r = r + chr(x)
 i = i + 5
 except KeyError:
 x = reverse_dic4[s[i:i+4]]
 r = r + chr(x)
 i = i + 4
 except KeyError:
 x = reverse_dicN[s[i:i+5]]
 r = r + chr(x)
 i = i + 5
 except KeyError:
 r = r + s[i]
 i = i + 1 
 r = r + s[i]
 i = i + 1
 return r

We can now pass a simulated transaction to the function and test the output and see if the class is correctly implemented acceding to the Triton Standard Protocol.


message = '05 02 39 56 44 44 39 30 30 32 32 30 30 30 30 30 32 1C 31 31 1C 30 34 30 36 1C 34 32 31 36 34 36 30 30 30 30 30 30 30 30 30 38 3D 31 34 31 32 31 30 31 31 31 38 38 35 32 34 39 31 32 33 34 35 1C 30 30 30 30 32 30 30 30 1C 30 30 30 30 30 30 30 30 1C 38 36 32 37 42 38 36 33 34 37 41 30 33 37 37 35 1C 1C 1C 1C 5E 35 45 36 32 20 44 35 37 37 1C 03 6E '.replace(" ", "").decode("hex")


<ENQ><STX>9U00009D       <FS>11<FS>0406<FS>4216460000000008=14121011188524912345<FS>00002000<FS>00000000<FS>8627B86347A03775<FS><FS><FS><FS>^5E62 D577<ETX>

Reversing this back to an encoded string we just need to pass it to the ASCIIToString Function of course.

Tracing Data

Now that we have all the field from the request from the ATM, the next step is to create a server component that can run and pass this transactional information to our processing party.

Creating a Server is not part of this post, but all source code is available in the gitbub repository. But the basic structure should be that you should create a incoming and sending socket, and poll for connections using a daemon thread.  Passing ALL information to the processing party, but saving the requests to the database.

Port Listening for ATM connections

 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

 self.sock.bind(('', int(listening_port)))


Port forwarding to Processor with SSL

fwd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

 ssl_sock = ssl.wrap_socket(fwd)
 ssl_sock.connect((session.destination_ip, int(session.destination_port)))
 self.log.info('SSL Handshake Completed')

 self.log.debug('%s: Connected to switch %s:%s' % (
 session.SessionUUID, session.destination_ip, session.destination_port,))
 except socket.timeout:
 session.error = "Timeout connecting to switch"
 self.log.critical('%s: EE Cant Connect to %s:%s : %s' % (
 session.SessionUUID, session.destination_ip, session.destination_port, session.error,))
 except socket.sslerror:
 session.error = "SSL Error connecting to TNS"
 self.log.critical('%s: EE Cant Connect to %s:%s : %s' % (
 session.SessionUUID, session.destination_ip, session.destination_port, session.error,))
 session.error = "Unknown Error connecting to switch"
 self.log.exception('%s: EE Unknown Error Connecting to %s:%s : %s' % (
 session.SessionUUID, session.destination_ip, session.destination_port, session.error,))
 RequestThread(session, newsock, ssl_sock).start()
 ResponseThread(session, ssl_sock, newsock).start()

When a specific Transaction type is received from the ATM you can dissect the readable ascii and save the request to database, when the corresponding session has a response then you can of course save the response information with the request.

In my code example I provide the implementation of request  and response saving as based on the Triton Specification. If you are using NDC+ or ISO 8583 then this will dramatically change.


The security aspect of this project should be implemented in the raw socket components (SSL Sockets), if there is a need for SSL certificate validation and ACL’s then it should not be difficult to add this to the required class.

PCI DSS require us not to store raw Personal Account numbers in the database so we should in fact use a hash function.

A simple method for doing this is the following:

return pan[:6] + ("*" * (len(pan)-10)) + pan[-4:]

Final Product

The final product is a python implementation of a Transactional Middleware. Allot of changes will be required to make the project work for your environment, but here are basic instructions to make it work for you.

  1. Get a clean Linux installation
  2. Install the LAMP Stack
  3. run the SQL file in the project
  4. copy the project directory to the server
  5. app_get all the project dependencies. (pymysql ect.)
  6. change the config file to point to your database and your switching provider (Middleware.ini)
  7. create a screen session (command: screen -S TransactionServer )
  8. start the Server in the screen session (sudo python pyMiddlewareServer foreground)

When starting it up you should see the following:

2014-05-24 17:53:38,177 MyDaemon INFO run() Start
2014-05-24 17:53:38,180 Pinhole INFO Redirecting: localhost:9100 -> xxx.xxx.xxx.xx:xxxxx
2014-05-24 17:53:38,263 QueueLogger INFO Connected to Database <_mysql.connection open to '' at 100837020>
2014-05-24 17:53:45,704 Pinhole INFO 8850226b-e318-11e3-8a9a-28cfe91efd87: NN New Session from ('', 49660)
2014-05-24 17:53:45,817 Pinhole INFO SSL Handshake Completed
2014-05-24 17:53:45,832 Request INFO 8850226b-e318-11e3-8a9a-28cfe91efd87: > AtmID=[9VDD90022000002], Type=[Authorization from Cheque Account], Operation=[CW]
2014-05-24 17:53:45,832 Request INFO 8850226b-e318-11e3-8a9a-28cfe91efd87: > PAN=[216460000000008]
2014-05-24 17:53:46,072 Request INFO 8850226b-e318-11e3-8a9a-28cfe91efd87: > AtmID=[9VDD90022000002], Type=[Authorization from Cheque Account], Operation=[CW]
2014-05-24 17:53:46,072 Request INFO 8850226b-e318-11e3-8a9a-28cfe91efd87: > PAN=[216460000000008]

Source code: https://github.com/Arthurvdmerwe/ATM-Transaction-Trace.git