Advertisement
Kikku80

PaymentHSMManager.cs

Nov 13th, 2019
339
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 61.09 KB | None | 0 0
  1. using System;
  2. using EMS.Utilities;
  3. using EMS.Encryption.PaymentHSM.ConfigSection;
  4. using System.Collections.Generic;
  5. using System.Text;
  6. using System.Threading;
  7. using EMS.HSM.Core.Pin;
  8. using EMS.HSM.Core.Terminals;
  9. using EMS.HSM.Core.ApplicationCryptogram;
  10. using EMS.HSM.Core.Exceptions;
  11. using log4net;
  12. using EMS.HSM.Core.Cipher;
  13. using System.Threading.Tasks;
  14.  
  15. namespace EMS.Encryption.PaymentHSM
  16. {
  17.     public class PaymentHSMManager : IPinService, ITerminalKeyService, IApplicationCryptogramService, ICipher
  18.     {
  19.         protected readonly ILog _log = LogManager.GetLogger(typeof(PaymentHSMManager));
  20.         protected static volatile int _sequenceNumber = 0;
  21.         FirstInFirstOutList<HsmTcpConnection> hsmConnections = new FirstInFirstOutList<HsmTcpConnection>();
  22.        
  23.        
  24.         /// <summary>
  25.         /// Initialize Payment HSM Manager. This Init methods takes a list of connection endpoints to setup.
  26.         /// The order of the list determines how they are added, i.e. usage.
  27.         /// </summary>
  28.         /// <param name="connections">List of connections</param>
  29.         public void Initialize(List<HSMConnectionConfiguration> connections)
  30.         {
  31.             //We are going to add connections to a stack list.
  32.             foreach (var connection in connections)
  33.             {
  34.                 hsmConnections.Push(new HsmTcpConnection(connection.Name,connection.Hostname,connection.Port,connection.ConnectTimeout * 1000)); //Timeout is in seconds, we need to convert to milliseconds
  35.             }            
  36.         }
  37.  
  38.         /// <summary>
  39.         /// Return a unique sequence number
  40.         /// </summary>
  41.         /// <returns></returns>
  42.         protected static ushort GetNextSequenceNumber()
  43.         {
  44.             int result = Interlocked.Increment(ref _sequenceNumber);
  45.             if (result +1 > ushort.MaxValue)
  46.                 Interlocked.Exchange(ref _sequenceNumber, 1);
  47.  
  48.             return (ushort)result;
  49.         }
  50.  
  51.         protected HsmTcpConnection GetConnection()
  52.         {
  53.             var connection = hsmConnections.GetItem();
  54.             if (connection==null)
  55.             {
  56.                 throw new HsmConnectionException("No HSM connection available!");
  57.             }
  58.             return connection;
  59.         }
  60.  
  61.         /// <summary>
  62.         /// Translate a PIN Block encrypted under one working key, returned encrypted on another
  63.         /// </summary>
  64.         /// <param name="pinBlock"> encrypted PIN Block</param>
  65.         /// <param name="PAN">PAN for PIN Block in question</param>
  66.         /// <param name="workingKey">Working key ecrypted on database key as stored in database</param>
  67.         /// <param name="keyIndex">key Index to determine working key to use</param>
  68.         /// <returns></returns>
  69.         public async Task<byte[]> TranslatePinBlockAsync(byte[] pinBlock, string PAN, byte[] workingKey, byte keyIndex)
  70.         {
  71.             HsmTcpConnection connection = null;
  72.             try
  73.             {
  74.                 byte[] req = new byte[47];
  75.                 req[0] = 0x01; // SOH Character
  76.                 req[1] = 0x01; // Version Number
  77.                 var seqNum = GetNextSequenceNumber();
  78.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  79.                 req[3] = (byte)seqNum; // Sequence Number
  80.                
  81.                 req[4] = 0x00; // length of message
  82.                 req[5] = 41; // length of message
  83.  
  84.                 req[6] = 0xEE;
  85.                 req[7] = 0x06;
  86.                 req[8] = 0x02; // Function name
  87.  
  88.                 req[9] = 0x00; // Function modifier
  89.  
  90.                 Buffer.BlockCopy(pinBlock, 0, req, 10, 8); //pinblock
  91.  
  92.                 //Host Key
  93.                 req[18] = 0x11;  // PPKi spec(length format index)
  94.                 req[19] = 0x11;
  95.                 Buffer.BlockCopy(workingKey, 0, req, 20, 16);
  96.  
  97.                 req[36] = 0x01;  // Input pin block format. In this case its ISO Format 0
  98.  
  99.                 byte[] ANBBCD = Convertor.StringToBCD(PAN.Substring(PAN.Length - 13, 12));
  100.                 Buffer.BlockCopy(ANBBCD, 0, req, 37, 6);
  101.  
  102.                 req[43] = 0x01;  // output pin block format.
  103.  
  104.                 req[44] = 0x02;  // PPKo spec(length format index)
  105.                 req[45] = 0x01;
  106.                 req[46] = keyIndex;
  107.                 connection = GetConnection();
  108.  
  109.                 var response = await connection.SendRequestAsync(req);
  110.  
  111.                 if (response == null || response.Length < 4)
  112.                     throw new HsmPinTranslationException("PIN-TRAN-2 function returned invalid response");
  113.  
  114.                 //check response code
  115.                 if (response[3] != 0x00) //response is not success
  116.                     throw new HsmPinTranslationException(string.Format("PIN-TRAN-2 response is {0:X2}", response[3]));
  117.  
  118.                 byte[] result = new byte[8];
  119.                 Buffer.BlockCopy(response, 4, result, 0, 8);
  120.                
  121.                 _log.Debug("Translate PIN Block succeeded");
  122.                 return result;
  123.             }
  124.             finally
  125.             {
  126.                 //return connection to stack
  127.                 if (hsmConnections != null)
  128.                     hsmConnections.Push(connection);
  129.             }
  130.         }
  131.  
  132.         /// <summary>
  133.         /// Translate a PIN Block encrypted under a Pin Protect key, to another Pin Protect Key both stored in HSM
  134.         /// </summary>
  135.         /// <param name="pinBlock"> encrypted PIN Block</param>
  136.         /// <param name="PAN">PAN for PIN Block in question</param>
  137.         /// <param name="ppkInputIndex">PPK HSM Index under which the parameter PinBlock is encrypted on</param>
  138.         /// <param name="ppkOutputIndex">PPK Hsm Index the PinBlock should be encrypted on and returned as result</param>
  139.         /// <returns></returns>
  140.         public async Task<byte[]> TranslatePinBlockAsync(byte[] pinBlock, string PAN, byte ppkInputIndex, byte ppkOutputIndex)
  141.         {
  142.             HsmTcpConnection connection = null;
  143.             try
  144.             {
  145.                 _log.Debug($"Translate Pin block from index {ppkInputIndex} to {ppkOutputIndex}");
  146.                 byte[] req = new byte[32];
  147.                 req[0] = 0x01; // SOH Character
  148.                 req[1] = 0x01; // Version Number
  149.                 var seqNum = GetNextSequenceNumber();
  150.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  151.                 req[3] = (byte)seqNum; // Sequence Number
  152.  
  153.                 req[4] = 0x00; // length of message
  154.                 req[5] = 26; // length of message
  155.  
  156.                 req[6] = 0xEE;
  157.                 req[7] = 0x06;
  158.                 req[8] = 0x02; // Function name
  159.  
  160.                 req[9] = 0x00; // Function modifier
  161.  
  162.                 Buffer.BlockCopy(pinBlock, 0, req, 10, 8); //pinblock
  163.  
  164.                 //Host Key
  165.                 req[18] = 0x02;  // PPKi spec(length format index)
  166.                 req[19] = 0x01;
  167.                 req[20] = ppkInputIndex;
  168.  
  169.                 req[21] = 0x01;  // Input pin block format. In this case its ISO Format 0
  170.  
  171.                 byte[] ANBBCD = Convertor.StringToBCD(PAN.Substring(PAN.Length - 13, 12));
  172.                 Buffer.BlockCopy(ANBBCD, 0, req, 22, 6);
  173.  
  174.                 req[28] = 0x01;  // output pin block format.
  175.  
  176.                 req[29] = 0x02;  // PPKo spec(length format index)
  177.                 req[30] = 0x01;
  178.                 req[31] = ppkOutputIndex;
  179.                 connection = GetConnection();
  180.  
  181.                 var response = await connection.SendRequestAsync(req);
  182.  
  183.                 if (response == null || response.Length < 4)
  184.                     throw new HsmPinTranslationException("PIN-TRAN-2 HSM stored PPK function returned invalid response");
  185.  
  186.                 //check response code
  187.                 if (response[3] != 0x00) //response is not success
  188.                     throw new HsmPinTranslationException(string.Format("PIN-TRAN-2 HSM stored PPK response is {0:X2}", response[3]));
  189.  
  190.                 byte[] result = new byte[8];
  191.                 Buffer.BlockCopy(response, 4, result, 0, 8);
  192.  
  193.                 _log.Debug("Translate PIN Block succeeded");
  194.                 return result;
  195.             }
  196.             finally
  197.             {
  198.                 //return connection to stack
  199.                 if (hsmConnections != null)
  200.                     hsmConnections.Push(connection);
  201.             }
  202.         }
  203.  
  204.         #region PIN Generation / Verification
  205.  
  206.         /// <summary>
  207.         /// This function generates a random PIN, formats and encrypts it for host storage.
  208.         /// </summary>
  209.         /// <param name="keyIndex">PPK (Store PIN Protect Key) hsm key index</param>
  210.         /// <param name="pan">Card Pan</param>
  211.         /// <param name="pinLength">PIN Length</param>
  212.         /// <returns></returns>
  213.         public async Task<byte[]> GeneratePinAsync(byte keyIndex, string pan, byte pinLength = 4)
  214.         {
  215.             _log.Debug($"Generating new pin for {PAN.MaskPAN(pan)}");
  216.  
  217.             if (pinLength <4 || pinLength>12)
  218.             {
  219.                 throw new HsmPinGenerateException(string.Format("PIN length of {0} is not valid", pinLength));
  220.             }
  221.  
  222.             HsmTcpConnection connection = null;
  223.             try
  224.             {
  225.                 byte[] req = new byte[21];
  226.                 req[0] = 0x01; // SOH Character
  227.                 req[1] = 0x01; // Version Number
  228.                 var seqNum = GetNextSequenceNumber();
  229.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  230.                 req[3] = (byte)seqNum; // Sequence Number
  231.  
  232.                 req[4] = 0x00; // length of message
  233.                 req[5] = 15; // length of message
  234.  
  235.                 req[6] = 0xEE;
  236.                 req[7] = 0x0E;
  237.                 req[8] = 0x04; // Function name
  238.  
  239.                 req[9] = 0x00; // Function modifier
  240.  
  241.                 req[10] = pinLength; // PIN Length – in the range 04 - 12
  242.                 req[11] = 0x01; // PFo ANSII
  243.  
  244.  
  245.                 Convertor.StringToBCD(pan, pan.Length - 13, 12, req, 12);//ANB
  246.  
  247.                 req[18] = 0x02;  //PPk var len
  248.                 req[19] = 0x01;
  249.                 req[20] = keyIndex;
  250.                 connection = GetConnection();
  251.  
  252.                 var response = await connection.SendRequestAsync(req);
  253.  
  254.                 if (response == null || response.Length < 4)
  255.                     throw new HsmPinGenerateException("PIN-GENERATE function returned invalid response");
  256.  
  257.                 //check response code
  258.                 if (response[3] != 0x00) //response is not success
  259.                     throw new HsmPinGenerateException(string.Format("PIN-GENERATE response is {0:X2}", response[3]));
  260.  
  261.                 byte[] result = new byte[8];
  262.                 Buffer.BlockCopy(response, 4, result, 0, 8);
  263.  
  264.                 _log.Debug("PIN generate succeeded");
  265.                 return result;
  266.             }
  267.             finally
  268.             {
  269.                 //return connection to stack
  270.                 if (hsmConnections != null)
  271.                     hsmConnections.Push(connection);
  272.             }
  273.         }
  274.  
  275.         /// <summary>
  276.         /// This function generates a PIN Offset which is used for issuing PIN verification
  277.         /// </summary>
  278.         /// <param name="pinProtectKeyIndex">PPK (Store PIN Protect Key) hsm host key index by which pin stored in database is encrypted with</param>
  279.         /// <param name="pinVerificationKeyIndex">PVK </param>
  280.         /// <param name="pan">Card Pan</param>
  281.         /// <returns></returns>
  282.         public async Task<byte[]> GeneratePinOffsetAsync(byte pinProtectKeyIndex, byte pinVerificationKeyIndex, string pan, byte[] pinBlock)
  283.         {
  284.             _log.Debug("Generating Pin offset");
  285.             if (pinBlock ==null || pinBlock.Length!=8)
  286.             {
  287.                 throw new HsmPinGenerateException("PIN Offset pin block lenght is not valid");
  288.             }
  289.  
  290.             HsmTcpConnection connection = null;
  291.             try
  292.             {
  293.                 byte[] req = new byte[39];
  294.                 req[0] = 0x01; // SOH Character
  295.                 req[1] = 0x01; // Version Number
  296.                 var seqNum = GetNextSequenceNumber();
  297.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  298.                 req[3] = (byte)seqNum; // Sequence Number
  299.  
  300.                 req[4] = 0x00; // length of message
  301.                 req[5] = 33; // length of message
  302.  
  303.                 req[6] = 0xEE;
  304.                 req[7] = 0x06;
  305.                 req[8] = 0x04; // Function name
  306.  
  307.                 req[9] = 0x00; // Function modifier
  308.  
  309.                 Buffer.BlockCopy(pinBlock, 0, req, 10, 8); //PIN Block
  310.  
  311.                 req[18] = 0x02;  //PPk var len
  312.                 req[19] = 0x01;
  313.                 req[20] = pinProtectKeyIndex;
  314.  
  315.                 req[21] = 0x01; //PIN Block Format 01
  316.  
  317.                 Convertor.StringToBCD(pan, pan.Length - 13, 12, req, 22); //ANB
  318.                
  319.                 req[28] = 0x02;  //PVK var len
  320.                 req[29] = 0x01;
  321.                 req[30] = pinVerificationKeyIndex;
  322.  
  323.                 //Validation Data
  324.                 Convertor.StringToBCD(pan, 0, 16, req, 31);
  325.                
  326.                 connection = GetConnection();
  327.  
  328.                 var response = await connection.SendRequestAsync(req);
  329.  
  330.                 if (response == null || response.Length < 4)
  331.                     throw new HsmPinGenerateException("PIN-OFF function returned invalid response");
  332.  
  333.                 //check response code
  334.                 if (response[3] != 0x00) //response is not success
  335.                     throw new HsmPinGenerateException(string.Format("PIN-OFF response is {0:X2}", response[3]));
  336.  
  337.                 byte[] result = new byte[6];
  338.                 Buffer.BlockCopy(response, 4, result, 0, 6);
  339.  
  340.                 byte pinLen = response[10];
  341.  
  342.                 _log.DebugFormat("PIN OFF succeeded, pin lenght {0}", pinLen);
  343.                 return result;
  344.             }
  345.             finally
  346.             {
  347.                 //return connection to stack
  348.                 if (hsmConnections != null)
  349.                     hsmConnections.Push(connection);
  350.             }
  351.         }
  352.  
  353.         public async Task<bool> VerifyPinAsync(byte[] pinBlock, byte pinProtectKeyIndex, byte pinVerificationKeyIndex, string pan, byte[] pinOffset )
  354.         {
  355.             if (pinBlock == null || pinBlock.Length != 8)
  356.             {
  357.                 throw new HsmPinVerificationException("PIN-VER-IBM-MULTI (EE0603) pin block lenght is not valid");
  358.             }
  359.  
  360.             HsmTcpConnection connection = null;
  361.             try
  362.             {
  363.                 byte[] req = new byte[46];
  364.                 req[0] = 0x01; // SOH Character
  365.                 req[1] = 0x01; // Version Number
  366.                 var seqNum = GetNextSequenceNumber();
  367.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  368.                 req[3] = (byte)seqNum; // Sequence Number
  369.  
  370.                 req[4] = 0x00; // length of message
  371.                 req[5] = 40; // length of message
  372.  
  373.                 req[6] = 0xEE;
  374.                 req[7] = 0x06;
  375.                 req[8] = 0x03; // Function name
  376.  
  377.                 req[9] = 0x00; // Function modifier
  378.  
  379.                 Buffer.BlockCopy(pinBlock, 0, req, 10, 8); //PIN Block
  380.  
  381.                 req[18] = 0x02;  //PPk var len
  382.                 req[19] = 0x01;
  383.                 req[20] = pinProtectKeyIndex;
  384.  
  385.                 req[21] = 0x01; //PIN Block Format 01
  386.  
  387.                 Convertor.StringToBCD(pan, pan.Length - 13, 12, req, 22); //ANB
  388.  
  389.                 req[28] = 0x02;  //PVK var len
  390.                 req[29] = 0x01;
  391.                 req[30] = pinVerificationKeyIndex;
  392.  
  393.                 //Validation Data
  394.                 Convertor.StringToBCD(pan, 0, 16, req, 31);
  395.  
  396.                 Buffer.BlockCopy(pinOffset, 0, req, 39, 6); //Pin Offset
  397.  
  398.                 req[45] = 4; //PIN Check Length
  399.  
  400.                 connection = GetConnection();
  401.  
  402.                 var response = await connection.SendRequestAsync(req);
  403.  
  404.                 if (response == null || response.Length < 4)
  405.                     throw new HsmPinVerificationException("PIN-VER-IBM-MULTI (EE0603) function returned invalid response");
  406.  
  407.                 //check response code
  408.                 if (response[3] == 0x00)
  409.                 {
  410.                     _log.Debug("PIN verification succeeded");
  411.                     return true;
  412.                 }
  413.                 else if (response[3] == 0x08)
  414.                 {
  415.                     _log.Debug("PIN verification failed");
  416.                     return false;
  417.                 }
  418.                 else  //response is not success
  419.                 {
  420.                     throw new HsmPinVerificationException(string.Format("PIN-VER-IBM-MULTI (EE0603)  response is {0:X2}", response[3]));
  421.                 }            
  422.                          
  423.             }
  424.             finally
  425.             {
  426.                 //return connection to stack
  427.                 if (hsmConnections != null)
  428.                     hsmConnections.Push(connection);
  429.             }
  430.         }
  431.                
  432.        
  433.         #endregion
  434.  
  435.         /// <summary>
  436.         /// Verify MAC for a POS terminal
  437.         /// </summary>
  438.         /// <param name="MACHostKey">MAC key to use for verification encrypted on database key</param>
  439.         /// <param name="MAC">MAC computed by terminal</param>
  440.         /// <param name="dataBuffer">Data to compute MAC on</param>
  441.         /// <param name="dataOffset">Data offset where to start computing MAC on</param>
  442.         /// <param name="dataLength">Data length</param>
  443.         /// <returns>true if verification succeeds otherwise false</returns>
  444.         public async Task<bool> VerifyMacAsync(byte[] MACHostKey, byte[] MAC, byte[] dataBuffer, int dataOffset, int dataLength)
  445.         {
  446.             HsmTcpConnection connection = null;
  447.             try
  448.             {
  449.                 if (dataLength > 32767)
  450.                     throw new HsmMacVerificationException("Data length of more than 32,767 not supported");
  451.  
  452.                 if (MACHostKey == null || MACHostKey.Length != 16)
  453.                     throw new HsmMacVerificationException("MAC Host key is not valid");
  454.  
  455.                 int msgLength = 6 + 3 + 1 + 1 + 8 + 2 + 16 + 1 + 8 + 2 + dataLength;
  456.                 //header + function name + function modifier + MAC Algo + ICD + Var Len & format + MPK + Var Len + MAC + varlen + data
  457.  
  458.                 byte[] req = new byte[msgLength];
  459.                 req[0] = 0x01; // SOH Character
  460.                 req[1] = 0x01; // Version Number
  461.                 var seqNum = GetNextSequenceNumber();
  462.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  463.                 req[3] = (byte)seqNum; // Sequence Number
  464.                
  465.                 req[4] = (byte)((msgLength - 6) >> 8); // length of message (header is not included that is why -6)
  466.                 req[5] = (byte)(msgLength - 6); // length of message
  467.  
  468.                 req[6] = 0xEE; // Function name
  469.                 req[7] = 0x07;
  470.                 req[8] = 0x02;
  471.  
  472.                 req[9] = 0x00; // Function modifier
  473.  
  474.                 /*
  475.                  * Algorithm Qualifier. Specifies details of the MACing algorithm.
  476.                  * Left nibble (Padding):
  477.                  * = 0 : pad with zeroes.
  478.                  * = 1 : pad with a single one bit and subsequent zeroes
  479.                  * Right nibble (Algorithm):
  480.                  * For single length MPK must be zero.
  481.                  * For double length MPK:
  482.                  * = 0 : ISO 9807 method
  483.                  * = 1 : triple-DES CBC method
  484.                  * */
  485.                 req[10] = 0x00; // MACing algorithm
  486.  
  487.                 req[11] = 0x00; // Input Chaining Data (ICD) - not used
  488.                 req[12] = 0x00;
  489.                 req[13] = 0x00;
  490.                 req[14] = 0x00;
  491.                 req[15] = 0x00;
  492.                 req[16] = 0x00;
  493.                 req[17] = 0x00;
  494.                 req[18] = 0x00;
  495.  
  496.                 req[19] = 0x11; // Var Len
  497.                 req[20] = 0x11; // Key specifier for MPK (we will use host encrypted key)
  498.                 Buffer.BlockCopy(MACHostKey, 0, req, 21, 16);
  499.  
  500.                 req[37] = 0x08; // Var Len
  501.                 Buffer.BlockCopy(MAC, 0, req, 38, 8); //MAC
  502.  
  503.                 req[46] = (byte)(dataLength >> 8); // Data to be MACed including VAR length of 2 bytes
  504.                 req[46] |= 0x80; // Indicates that we're using a 2 byte length
  505.                 req[47] = (byte)dataLength;
  506.  
  507.                 Buffer.BlockCopy(dataBuffer, dataOffset, req, 48, dataLength);
  508.  
  509.                 connection = GetConnection();
  510.                 var response = await connection.SendRequestAsync(req);
  511.  
  512.                 if (response == null || response.Length < 4)
  513.                     throw new HsmMacVerificationException("MAC-VER-FINAL (EE0702) function returned invalid response");
  514.  
  515.                 //check response code
  516.                 if (response[3] != 0x00 && response[3] != 0x08) //function failed
  517.                     throw new HsmMacVerificationException(string.Format("MAC-VER-FINAL (EE0702) response is {0:X2}", response[3]));
  518.                 else if (response[3] == 0x08)
  519.                 {
  520.                     _log.DebugFormat("Verify MAC failed for host key {0}", MACHostKey);
  521.                     return false;
  522.                 }
  523.                 else //0x00
  524.                 {
  525.                     _log.Debug("Verify MAC succeeded");
  526.                     return true;
  527.                 }
  528.             }
  529.             finally
  530.             {
  531.                 //return connection to stack
  532.                 if (hsmConnections != null)
  533.                     hsmConnections.Push(connection);
  534.             }
  535.         }
  536.  
  537.         /// <summary>
  538.         /// Generate Terminal Master Key
  539.         /// </summary>
  540.         /// <param name="TKLDZMKKeyIndex">TKLD ZMK Key Index</param>
  541.         /// <param name="MKonZMK">Result master key encrypted on ZMK</param>
  542.         /// <param name="MKonKM">Result master key encrypted on DMK (KM)(host key)</param>
  543.         /// <param name="MK_KCV">Master key KCV</param>
  544.         public void GenerateTerminalMasterKey(byte TKLDZMKKeyIndex, out byte[] MKonZMK, out byte[] MKonKM, out byte[] MK_KCV)
  545.         {
  546.             HsmTcpConnection connection = null;
  547.             try
  548.             {
  549.                 byte[] req = new byte[15];
  550.                 req[0] = 0x01; // SOH Character
  551.                 req[1] = 0x01; // Version Number
  552.  
  553.                 var seqNum = GetNextSequenceNumber();
  554.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  555.                 req[3] = (byte)seqNum; // Sequence Number
  556.                
  557.                 req[4] = 0x00; // length of message
  558.                 req[5] = 9; // length of message
  559.  
  560.                 req[6] = 0xEE;  // Function name
  561.                 req[7] = 0x04;
  562.                 req[8] = 0x00;
  563.  
  564.                 req[9] = 0x00;  // Function modifier
  565.  
  566.                 connection = GetConnection();
  567.  
  568.                 req[10] = 0x02; // Key Specifier for KTM
  569.                 req[11] = 0x01; // index of KTM
  570.                 req[12] = TKLDZMKKeyIndex;
  571.  
  572.                 req[13] = 0x08; //Double-length terminal master key (KTM)
  573.                 req[14] = 0x00;
  574.  
  575.  
  576.                 var response = connection.SendRequestAsync(req).Result;
  577.  
  578.                 if (response == null || response.Length < 4)
  579.                     throw new HsmGenerateKeyException("IT-KEY-GEN (EE0400) function returned invalid response");
  580.  
  581.                 //check response code
  582.                 if (response[3] != 0x00) //function failed
  583.                 {
  584.                     throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) response is {0:X2}", response[3]));
  585.                 }
  586.  
  587.                 if (response[4] != 0x01)
  588.                 {
  589.                     throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) 'n' response is {0:X2}", response[4]));
  590.                 }
  591.  
  592.                 byte MKKeyOnZmkLen = response[5];
  593.                 MKonZMK = new byte[MKKeyOnZmkLen];
  594.                 Buffer.BlockCopy(response, 6, MKonZMK, 0, MKKeyOnZmkLen);
  595.  
  596.                 int index = 6 + MKKeyOnZmkLen;
  597.                 byte hostKeyVarLen = response[index++];
  598.                 if (hostKeyVarLen != 0x11)
  599.                 {
  600.                     throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) host key var length mismatch, length is {0:X2}", hostKeyVarLen));
  601.                 }
  602.                 if (response[index] != 0x11)
  603.                 {
  604.                     throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) KS-Spec format response is {0:X2}", response[index]));
  605.                 }
  606.                 index++;
  607.  
  608.                 MKonKM = new byte[16];
  609.                 Buffer.BlockCopy(response, index, MKonKM, 0, 16);
  610.                 index += 16;
  611.  
  612.                 MK_KCV = new byte[3];
  613.                 Buffer.BlockCopy(response, index, MK_KCV, 0, 3);
  614.             }
  615.             finally
  616.             {
  617.                 //return connection to stack
  618.                 if (hsmConnections != null)
  619.                     hsmConnections.Push(connection);
  620.             }
  621.         }
  622.  
  623.         /// <summary>
  624.         /// Re-Encrypt a key which is encrypted on a ZMK to be encrypted on host key
  625.         /// </summary>
  626.         /// <param name="KI_KeyIndex">ZMK key index in HSM (KI)</param>
  627.         /// <param name="encryptedKey">Encrypted key on ZMK</param>
  628.         /// <returns>Encrypted key on KM</returns>
  629.         public async Task<byte[]> ReEncryptKeyOnHostKeyAsync(byte KI_KeyIndex, byte[] encryptedKey)
  630.         {
  631.             HsmTcpConnection connection = null;
  632.             try
  633.             {
  634.                 byte[] req = new byte[19+encryptedKey.Length];
  635.                 req[0] = 0x01; // SOH Character
  636.                 req[1] = 0x01; // Version Number
  637.  
  638.                 var seqNum = GetNextSequenceNumber();
  639.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  640.                 req[3] = (byte)seqNum; // Sequence Number
  641.  
  642.                 req[4] = 0x00; // length of message
  643.                 req[5] = (byte) (req.Length-6); // length of message
  644.  
  645.                 req[6] = 0xEE;  // Function name
  646.                 req[7] = 0x02;
  647.                 req[8] = 0x00;
  648.  
  649.                 req[9] = 0x00;  // Function modifier
  650.  
  651.                 connection = GetConnection();
  652.  
  653.                 req[10] = 0x05; // Len -  Key Specifier for KI (ZMK)
  654.                 req[11] = 0x08; // Format for KI
  655.                 req[12] = 0x10; // KI
  656.                 req[13] = 0x02; // Var KSpec
  657.                 req[14] = 0x01;
  658.                 req[15] = KI_KeyIndex; //Index
  659.  
  660.                 req[16] = 0x01; //Key Type (01 = PPK)
  661.                 req[17] = 0x01; //Ecnryption Mode ECB
  662.  
  663.                 req[18] = (byte)encryptedKey.Length;  // Encrypted key
  664.                 Buffer.BlockCopy(encryptedKey, 0, req, 19, encryptedKey.Length);
  665.                                
  666.                 var response = await connection.SendRequestAsync(req);
  667.  
  668.                 if (response == null || response.Length < 4)
  669.                     throw new HsmGenerateKeyException("KEY-IMPORT (EE0200) function returned invalid response");
  670.  
  671.                 //check response code
  672.                 if (response[3] != 0x00) //function failed
  673.                 {
  674.                     throw new HsmGenerateKeyException(string.Format("KEY-IMPORT (EE0200) response is {0:X2}", response[3]));
  675.                 }
  676.                 else
  677.                 {
  678.                     byte[] reEncryptedKey = new byte[16];
  679.                     Buffer.BlockCopy(response, 6, reEncryptedKey, 0, 16);
  680.                     return reEncryptedKey;
  681.                 }
  682.             }
  683.             finally
  684.             {
  685.                 //return connection to stack
  686.                 if (hsmConnections != null)
  687.                     hsmConnections.Push(connection);
  688.             }
  689.         }
  690.  
  691.         /// <summary>
  692.         /// Generate Terminal Working key and MAC Key
  693.         /// </summary>
  694.         /// <param name="terminalMK">Terminal Master key encrypted on KM</param>
  695.         /// <param name="WKonKTM">Working Key on KTM (TMS Key)</param>
  696.         /// <param name="WKonKM">Working key on KM (DB Key)</param>
  697.         /// <param name="WK_KCV">Working key KCV</param>
  698.         /// <param name="MAConKTM">MAC key on KTM (TMS Key)</param>
  699.         /// <param name="MAConKM">MAC key on KM (DB key)</param>
  700.         /// <param name="MAC_KCV">MAC KCV</param>
  701.         public void GenerateTerminalWorkingKeyAndMac(byte[] terminalMK, out byte[] WKonKTM, out byte[] WKonKM, out byte[] WK_KCV,
  702.                                                                             out byte[] MAConKTM, out byte[] MAConKM, out byte[] MAC_KCV)
  703.         {
  704.             HsmTcpConnection connection = null;
  705.  
  706.             try
  707.             {
  708.                 if (terminalMK == null || terminalMK.Length != 16)
  709.                 {
  710.                     throw new HsmGenerateKeyException("IT-KEY-GEN (EE0400) terminal master key not valid!");
  711.                 }
  712.  
  713.                 byte[] req = new byte[30];
  714.                 req[0] = 0x01; // SOH Character
  715.                 req[1] = 0x01; // Version Number
  716.  
  717.                 var seqNum = GetNextSequenceNumber();
  718.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  719.                 req[3] = (byte)seqNum; // Sequence Number
  720.                
  721.                 req[4] = 0x00; // length of message
  722.                 req[5] = 24; // length of message
  723.  
  724.                 req[6] = 0xEE;  // Function name
  725.                 req[7] = 0x04;
  726.                 req[8] = 0x00;
  727.  
  728.                 req[9] = 0x00;  // Function modifier
  729.  
  730.                 req[10] = 0x11;  // Specify MK (KTM)
  731.                 req[11] = 0x11;
  732.  
  733.                 Buffer.BlockCopy(terminalMK, 0, req, 12, 16);
  734.  
  735.                 req[28] = 0x06;
  736.                 req[29] = 0x00; // Double length MPK and PPK
  737.  
  738.                 connection = GetConnection();
  739.  
  740.                 var response = connection.SendRequestAsync(req).Result;
  741.  
  742.                 if (response == null || response.Length < 4)
  743.                     throw new HsmGenerateKeyException("IT-KEY-GEN (EE0400) WK function returned invalid response");
  744.  
  745.                 //check response code
  746.                 if (response[3] != 0x00) //function failed
  747.                 {
  748.                     throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) WK response is {0:X2}", response[3]));
  749.                 }
  750.  
  751.                 if (response[4] != 0x02)
  752.                 {
  753.                     throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) WK 'n' response is {0:X2}", response[4]));
  754.                 }
  755.  
  756.                 //TMS WK Key
  757.                 byte WKonKTMLen = response[5];
  758.                 WKonKTM = new byte[WKonKTMLen];
  759.                 Buffer.BlockCopy(response, 6, WKonKTM, 0, WKonKTMLen);
  760.  
  761.                 //WK Key host key (Db)
  762.                 int index = 6 + WKonKTMLen;
  763.                 byte WKhostKeyVarLen = response[index++];
  764.                 if (WKhostKeyVarLen != 0x11)
  765.                 {
  766.                     throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) WK host key var length mismatch, length is {0:X2}", WKhostKeyVarLen));
  767.                 }
  768.                 if (response[index] != 0x11)
  769.                 {
  770.                     throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) WK KS-Spec format response is {0:X2}", response[index]));
  771.                 }
  772.                 index++;
  773.  
  774.                 WKonKM = new byte[16];
  775.                 Buffer.BlockCopy(response, index, WKonKM, 0, 16);
  776.                 index += 16;
  777.  
  778.                 WK_KCV = new byte[3];
  779.                 Buffer.BlockCopy(response, index, WK_KCV, 0, 3);
  780.                 index += 3;
  781.  
  782.  
  783.                 //TMS MAC Key
  784.                 byte MAConKTMLen = response[index++];
  785.                 MAConKTM = new byte[MAConKTMLen];
  786.                 Buffer.BlockCopy(response, index, MAConKTM, 0, MAConKTMLen);
  787.  
  788.                 //MAC Key host key (Db)
  789.                 index += MAConKTMLen;
  790.                 byte MAChostKeyVarLen = response[index++];
  791.                 if (WKhostKeyVarLen != 0x11)
  792.                 {
  793.                     throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) WK MAC host key var length mismatch, length is {0:X2}", WKhostKeyVarLen));
  794.                 }
  795.                 if (response[index] != 0x11)
  796.                 {
  797.                     throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) WK MAC KS-Spec format response is {0:X2}", response[index]));
  798.                 }
  799.                 index++;
  800.  
  801.                 MAConKM = new byte[16];
  802.                 Buffer.BlockCopy(response, index, MAConKM, 0, 16);
  803.                 index += 16;
  804.  
  805.                 MAC_KCV = new byte[3];
  806.                 Buffer.BlockCopy(response, index, MAC_KCV, 0, 3);
  807.  
  808.             }
  809.             finally
  810.             {
  811.                 //return connection to stack
  812.                 if (hsmConnections != null)
  813.                     hsmConnections.Push(connection);
  814.             }
  815.         }
  816.  
  817.         /// <summary>
  818.         /// This method is obsolete
  819.         /// </summary>
  820.         /// <param name="PANBlock"></param>
  821.         /// <param name="randomNumber"></param>
  822.         /// <param name="applicationCryptogram"></param>
  823.         /// <param name="applicationCryptogramData"></param>
  824.         /// <returns></returns>
  825.         public async Task<bool> VerifyApplicationCryptogramAsync(byte[] PANBlock, byte[] randomNumber, byte[] applicationCryptogram, byte[] applicationCryptogramData)
  826.         {
  827.             HsmTcpConnection connection = null;
  828.             try
  829.             {
  830.                 if (PANBlock == null || PANBlock.Length != 8)
  831.                     throw new HsmApplicationCryptogramVerifyException("Application PAN Block is not valid");
  832.  
  833.                 if (randomNumber == null || randomNumber.Length != 8)
  834.                     throw new HsmApplicationCryptogramVerifyException("Random number is not valid");
  835.  
  836.                 if (applicationCryptogram == null || applicationCryptogram.Length != 8)
  837.                     throw new HsmApplicationCryptogramVerifyException("Application Cryptogram is not valid");
  838.  
  839.                 if (applicationCryptogramData == null || (applicationCryptogram.Length % 8 != 0)) // applicatin cyrptogram data must be a multiple of 8
  840.                     throw new HsmApplicationCryptogramVerifyException("Application Cryptogram Data is not valid");
  841.  
  842.                 int msgLength = 6 + 3 + 1 + 3 + 8 + 8 + 8 + 1 + applicationCryptogramData.Length;
  843.                 //header + function name + function modifier + Key Specifier + PAN  + Random Number + AC + VAR AC Data Len + AC Data Len
  844.  
  845.                 byte[] req = new byte[msgLength];
  846.                 req[0] = 0x01; // SOH Character
  847.                 req[1] = 0x01; // Version Number
  848.  
  849.                 var seqNum = GetNextSequenceNumber();
  850.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  851.                 req[3] = (byte)seqNum; // Sequence Number
  852.                
  853.                 req[4] = (byte)((msgLength - 6) >> 8); // length of message (header is not included that is why -6)
  854.                 req[5] = (byte)(msgLength - 6); // length of message
  855.  
  856.                 req[6] = 0xEE; // Function name
  857.                 req[7] = 0x20;
  858.                 req[8] = 0x01;
  859.  
  860.                 req[9] = 0x00; // Function modifier
  861.  
  862.                
  863.  
  864.                 req[10] = 0x02; // Var Len
  865.                 req[11] = 0x00;
  866.                 req[12] = 0x01; // Key specifier for IMKAC
  867.                
  868.                 //PAN BLOCK
  869.                 Buffer.BlockCopy(PANBlock, 0, req, 13, 8);
  870.  
  871.                 //Ranmdom Number
  872.                 Buffer.BlockCopy(randomNumber, 0, req, 21, 8);
  873.  
  874.                 //Application Cryptogram
  875.                 Buffer.BlockCopy(applicationCryptogram, 0, req, 29, 8);
  876.  
  877.                 //Application Cryptogram Data
  878.                 req[37] = (byte)applicationCryptogramData.Length;
  879.                 Buffer.BlockCopy(applicationCryptogramData, 0, req, 38, applicationCryptogramData.Length);
  880.  
  881.                 //we need to maintain pos due to application cyrptogram data being dynamic (not fixed size)
  882.                 int pos = 38 + applicationCryptogramData.Length;
  883.  
  884.                 //req[pos++] = 0x00; //Bitmap, not used with FM 00
  885.                 //req[pos] = 0x00; // Transaction data. only present when FM = 04
  886.  
  887.                 connection = GetConnection();
  888.                 var response = await connection.SendRequestAsync(req);
  889.  
  890.                 if (response == null || response.Length < 4)
  891.                     throw new HsmApplicationCryptogramVerifyException("EMV-AC-VERIFY (EE2001) function returned invalid response");
  892.  
  893.                 Console.WriteLine(BitConverter.ToString(req));
  894.                 Console.WriteLine(BitConverter.ToString(response));
  895.  
  896.                 //check response code
  897.                 if (response[3] != 0x00 && response[3] != 0x08) //function failed
  898.                     throw new HsmApplicationCryptogramVerifyException(string.Format("EMV-AC-VERIFY (EE2001) response is {0:X2}", response[3]));
  899.                 else if (response[3] == 0x08)
  900.                 {
  901.                    
  902.                     return false;
  903.                 }
  904.                 else //0x00
  905.                 {
  906.                     _log.Debug("Verify Application Cryptogram succeeded");
  907.                     return true;
  908.                 }
  909.             }
  910.             finally
  911.             {
  912.                 //return connection to stack
  913.                 if (hsmConnections != null)
  914.                     hsmConnections.Push(connection);
  915.             }
  916.         }
  917.  
  918.         /// <summary>
  919.         /// This function can be used to
  920.         /// •verify an Application Cryptogram(AC),
  921.         /// •generates an ARPC
  922.         /// •both verify an Application Cryptogram(AC) and generate an ARPC
  923.         /// The AC can be an ARQC, a TC or an AAC.
  924.         /// </summary>
  925.         /// <param name="keyIndex">IMK Key Index in HSM key store</param>
  926.         /// <param name="MKData">Data used to generate session key from IMK, PAN + Sequence Number</param>
  927.         /// <param name="ACKeyData">Data used with MKAC to derive the session key SKAC. Typically this data is a series of tags obatined from DE55. (ATC & unpredictable number)</param>
  928.         /// <param name="applicationCryptogramData">Data on which the AC is calculated</param>
  929.         /// <param name="applicationCryptogram">Application cryptogram to verify against</param>
  930.         /// <param name="ArpcData">Data to generate ARPC. Typically the data value is 0x0100 </param>
  931.         /// <returns>Result is a Tuple of boolean and byte array. The boolean indicates if verification is succesful. The byte array contains the ARPC response provided that verification and generation of response is successful</returns>
  932.         public async Task<(bool ApplicationCrytogramVerificationResult, byte[] ArpcResponse)> VerifyApplicationCryptogramGenARPCAsync(byte keyIndex, byte[] MKData, byte[] ACKeyData, byte[] applicationCryptogramData, byte[] applicationCryptogram
  933.             ,byte[] ArpcData)
  934.         {
  935.             HsmTcpConnection connection = null;
  936.             try
  937.             {
  938.                 if (MKData == null )
  939.                     throw new HsmApplicationCryptogramVerifyException("MK Data is not valid");
  940.  
  941.                 if (ACKeyData == null)
  942.                     throw new HsmApplicationCryptogramVerifyException("AC Key Data is not valid");
  943.  
  944.                 if (applicationCryptogram == null || applicationCryptogram.Length != 8)
  945.                     throw new HsmApplicationCryptogramVerifyException("Application Cryptogram is not valid");
  946.  
  947.                 if (applicationCryptogramData == null) // applicatin cyrptogram data must be a multiple of 8
  948.                     throw new HsmApplicationCryptogramVerifyException("Application Cryptogram Data is not valid");
  949.  
  950.                 int msgLength = 6 + 3 + 1 + 1 + 3 + 1 + 1 + MKData.Length + 1 + 1 + ACKeyData.Length + 1 + 1 + applicationCryptogramData.Length + applicationCryptogram.Length + 4 + ArpcData.Length;
  951.                 //header + function name + function modifier + Action + Key Specifier + PAN  + Random Number + AC + VAR AC Data Len + AC Data Len + ARPC Stuff
  952.  
  953.                 byte[] req = new byte[msgLength];
  954.                 req[0] = 0x01; // SOH Character
  955.                 req[1] = 0x01; // Version Number
  956.  
  957.                 var seqNum = GetNextSequenceNumber();
  958.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  959.                 req[3] = (byte)seqNum; // Sequence Number
  960.  
  961.                 req[4] = (byte)((msgLength - 6) >> 8); // length of message (header is not included that is why -6)
  962.                 req[5] = (byte)(msgLength - 6); // length of message
  963.  
  964.  
  965.                 req[6] = 0xEE; // Function name
  966.                 req[7] = 0x20;
  967.                 req[8] = 0x18;
  968.  
  969.                 req[9] = 0x00; // Function modifier
  970.                 req[10] = 0x03; //Action - Verify AC & Generate ARPC only
  971.                                
  972.                 req[11] = 0x02; // Var Len
  973.                 req[12] = 0x00;
  974.                 req[13] = keyIndex; // Key specifier for IMKAC
  975.  
  976.                 req[14] = 0x00; // MK-Method Common
  977.  
  978.                 req[15] = (byte)MKData.Length; //var len
  979.                 Buffer.BlockCopy(MKData, 0, req, 16, MKData.Length); //MK Data
  980.                 int pos = 16 + MKData.Length;
  981.  
  982.                 req[pos++] = 0x01; //AC Key Method - SKD function using ATC and UN  M / Chip 2.1[31], SECCOS[34]
  983.                 req[pos++] = (byte)ACKeyData.Length;  //VAR AC Key Data
  984.  
  985.                 Buffer.BlockCopy(ACKeyData, 0, req, pos, ACKeyData.Length); //AC Key Data
  986.                 pos += ACKeyData.Length;
  987.  
  988.                 req[pos++] = 0x03;//AC Methods
  989.  
  990.                 req[pos++] = (byte) applicationCryptogramData.Length; //VAR AC Data
  991.                 Buffer.BlockCopy(applicationCryptogramData, 0, req, pos, applicationCryptogramData.Length); //AC Data
  992.                 pos += applicationCryptogramData.Length;
  993.  
  994.                 Buffer.BlockCopy(applicationCryptogram, 0, req, pos, 8); //AC
  995.                 pos += 8;
  996.  
  997.                 req[pos++] = 0x01; // ARPC Key Method
  998.                 req[pos++] = 0x00; // Length of ARPC Key Data
  999.  
  1000.                 req[pos++] = 0x01; //ARPC Method
  1001.  
  1002.                 req[pos++] = (byte) ArpcData.Length; //Lenght of ARPC Data
  1003.                 Buffer.BlockCopy(ArpcData, 0, req, pos, ArpcData.Length);
  1004.  
  1005.                 connection = GetConnection();
  1006.                 var response = await connection.SendRequestAsync(req);
  1007.  
  1008.                 if (response == null || response.Length < 4)
  1009.                     throw new HsmApplicationCryptogramVerifyException("EMV-VERIFY-AC-GEN-ARPC (EE2018) function returned invalid response");
  1010.  
  1011.                
  1012.                 //check response code
  1013.                 if (response[3] != 0x00 && response[3] != 0x08) //function failed
  1014.                     throw new HsmApplicationCryptogramVerifyException(string.Format("EMV-VERIFY-AC-GEN-ARPC (EE2018) response is {0:X2}", response[3]));
  1015.                 else if (response[3] == 0x08)
  1016.                 {
  1017.                     return (false, null);
  1018.                 }
  1019.                 else //0x00
  1020.                 {
  1021.                     _log.Debug("Verify Application Cryptogram succeeded");
  1022.                     byte[] ARPC = new byte[response[4]];
  1023.                     Buffer.BlockCopy(response, 5, ARPC, 0, response[4]);
  1024.                     return (true,ARPC);
  1025.                 }
  1026.             }
  1027.             finally
  1028.             {
  1029.                 //return connection to stack
  1030.                 if (hsmConnections != null)
  1031.                     hsmConnections.Push(connection);
  1032.             }
  1033.         }
  1034.  
  1035.         #region PIN Unblock
  1036.        
  1037.         public async Task<(bool ApplicationCrytogramVerificationResult, byte[] ArpcResponse)> CardUnblockAsync(string PAN, string sequenceNumber, byte smiKeyIndex, byte smcKeyIndex, byte acKeyIndex,
  1038.             byte[] applicationCryptogram, byte[] ATC)
  1039.         {
  1040.             HsmTcpConnection connection = null;
  1041.             try
  1042.             {
  1043.                 if (string.IsNullOrEmpty(PAN) || string.IsNullOrEmpty(sequenceNumber))
  1044.                     throw new HsmApplicationCryptogramVerifyException("PAN Data is not valid");
  1045.  
  1046.                 if (applicationCryptogram == null)
  1047.                     throw new HsmApplicationCryptogramVerifyException("Application Cryptogram is not valid");
  1048.  
  1049.                 if (applicationCryptogram == null || applicationCryptogram.Length != 8)
  1050.                     throw new HsmApplicationCryptogramVerifyException("Application Cryptogram is not valid");
  1051.  
  1052.                 if (ATC == null)
  1053.                     throw new HsmApplicationCryptogramVerifyException("ATC is not valid");
  1054.  
  1055.                 int msgLength = 6 + 3 + 1 + 1 + 1 + 3 + 3 + 1 + 1 + 8 + 1 + applicationCryptogram.Length + 8 + 8 + 3 + 1 + 6 + 2 + 7 + 8 + ATC.Length + applicationCryptogram.Length;
  1056.           //    int msgLength = 6 + 3 + 1 + 1 + 1 + 3 + 3 + 1 + 1 + (((PAN.Length + sequenceNumber.Length) /2) + ((PAN.Length+sequenceNumber.Length) %2)) + 1 + applicationCryptogram.Length + 8 + 8 + 3 + 1 + 6 + 2 + 7 + ATC.Length + applicationCryptogram.Length;
  1057.    
  1058.                 //header + function name + function modifier + function flag + scheme + SMI + SMC + IMKAC + (Len +PAN + SequenceNos) + (Len + AC) + (OLD & New PIN) + PPK
  1059.                 //+ PinBlockFormat + ANB + ScriptDataPosition + Script Data
  1060.  
  1061.                 byte[] req = new byte[msgLength];
  1062.                 req[0] = 0x01; // SOH Character
  1063.                 req[1] = 0x01; // Version Number
  1064.  
  1065.                 var seqNum = GetNextSequenceNumber();
  1066.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  1067.                 req[3] = (byte)seqNum; // Sequence Number
  1068.  
  1069.                 req[4] = (byte)((msgLength - 6) >> 8); // length of message (header is not included that is why -6)
  1070.                 req[5] = (byte)(msgLength - 6); // length of message
  1071.  
  1072.  
  1073.                 req[6] = 0xEE; // Function name
  1074.                 req[7] = 0x20;
  1075.                 req[8] = 0x16;
  1076.  
  1077.                 req[9] = 0x00; // Function modifier
  1078.                 req[10] = 0x02; //Function flag - 00= Pin Unblock only, 02= Pin Change
  1079.                 req[11] = 0x01; //Scheme - 01=MasterCard
  1080.  
  1081.                 req[12] = 0x02; // Var Len
  1082.                 req[13] = 0x00;
  1083.                 req[14] = smiKeyIndex; // Key specifier for IMK SMI
  1084.  
  1085.                 req[15] = 0x02; // Var Len
  1086.                 req[16] = 0x00;
  1087.                 req[17] = smcKeyIndex; // Key specifier for IMK SMC
  1088.  
  1089.                 req[18] = 0x00; // IMK AC
  1090.  
  1091.                 //Formatted PAN and PAN Sequence Number
  1092.                
  1093. //                //Hardcoded PAN
  1094. //                req[19] = 0x08;
  1095. //                req[20] = 0x04;
  1096. //                req[21] = 0x74;
  1097. //                req[22] = 0x00;
  1098. //                req[23] = 0x00;
  1099. //                req[24] = 0x00;
  1100. //                req[25] = 0x14;
  1101. //                req[26] = 0x16;
  1102. //
  1103. //                //Hardcoded Sequence Number
  1104. //                req[27] = 0x00;
  1105.  
  1106.                 req[19] = (byte) Convertor.StringToBCD(PAN + sequenceNumber,2, PAN.Length,req,20);
  1107. //
  1108.               //  var pos = 28;
  1109.              
  1110.                 var pos = 20 + req[19];
  1111.  
  1112.                 req[pos++] = (byte) applicationCryptogram.Length;
  1113.                 Buffer.BlockCopy(applicationCryptogram, 0, req, pos, applicationCryptogram.Length);
  1114.                 pos += applicationCryptogram.Length;
  1115.                
  1116.                 req[pos++] = 0x32;
  1117.                 req[pos++] = 0x13;
  1118.                 req[pos++] = 0x17;
  1119.                 req[pos++] = 0x5A;
  1120.                 req[pos++] = 0xBC;
  1121.                 req[pos++] = 0x32;
  1122.                 req[pos++] = 0xB9;
  1123.                 req[pos++] = 0x49;
  1124.  
  1125.                 req[pos++] = 0x32;
  1126.                 req[pos++] = 0x13;
  1127.                 req[pos++] = 0x17;
  1128.                 req[pos++] = 0x5A;
  1129.                 req[pos++] = 0xBC;
  1130.                 req[pos++] = 0x32;
  1131.                 req[pos++] = 0xB9;
  1132.                 req[pos++] = 0x49;
  1133.            
  1134.  
  1135. //                pos += 8; //Encrypted PIN Block (Existing PIN)
  1136. //                pos += 8; //Encrypted PIN Block (New PIN)
  1137.  
  1138.                 req[pos++] = 0x02;
  1139.                 req[pos++] = 0x00;
  1140.                 req[pos++] = 0x07; //PPK. we should not be using this for this function - index 7 is staging , 2 is live
  1141.  
  1142.                 req[pos++] = 0x10; //PIN Block Format
  1143.  
  1144.                 //Account Number Block
  1145.                 pos += Convertor.StringToBCD(PAN, PAN.Length - 13, 12, req, pos);//ANB
  1146.  
  1147.                 req[pos++] = 0x00; //Script Data Position
  1148.                 req[pos++] = 0x0F;
  1149.  
  1150.                 req[pos++] = 0x18; //Script data len
  1151.                 req[pos++] = 0x84; //Class
  1152.                 req[pos++] = 0x24; //Ins
  1153.                 req[pos++] = 0x00; //P1
  1154.                 req[pos++] = 0x00;// P2
  1155.                 req[pos++] = 0x08; //Lc
  1156.  
  1157.                 Buffer.BlockCopy(ATC, 0, req, pos, ATC.Length);
  1158.                 pos += ATC.Length;
  1159.  
  1160.                 Buffer.BlockCopy(applicationCryptogram, 0, req, pos, applicationCryptogram.Length);
  1161.                 pos += applicationCryptogram.Length;
  1162.                
  1163.                 req[pos++] = 0x00; //8 bytes used for Pin change only
  1164.                 req[pos++] = 0x00;
  1165.                 req[pos++] = 0x00;
  1166.                 req[pos++] = 0x00;
  1167.                 req[pos++] = 0x00;
  1168.                 req[pos++] = 0x00;
  1169.                 req[pos++] = 0x00;
  1170.                 req[pos++] = 0x00;
  1171.                
  1172.                 req[pos++] = 0x80;
  1173.                 Console.WriteLine($"Request Buffer: {ByteArrayToString(req)}");    
  1174.                
  1175.                 connection = GetConnection();
  1176.                 var response = await connection.SendRequestAsync(req);
  1177.                
  1178.                 Console.WriteLine($"Response Buffer: {ByteArrayToString(response)}");    
  1179.  
  1180.  
  1181.                 if (response == null || response.Length < 4)
  1182.                     throw new HsmApplicationCryptogramVerifyException("EMV-PIN-CHANGE-UNBLOCK (EE2016) function returned invalid response");
  1183.  
  1184.  
  1185.                 //check response code
  1186.                 if (response[3] != 0x00 && response[3] != 0x08) //function failed
  1187.                     throw new HsmApplicationCryptogramVerifyException(string.Format("EMV-PIN-CHANGE-UNBLOCK (EE2016) response is {0:X2}", response[3]));
  1188.                 else if (response[3] == 0x08)
  1189.                 {
  1190.                     return (false, null);
  1191.                 }
  1192.                 else //0x00
  1193.                 {
  1194.                     _log.Debug("EMV-PIN-CHANGE-UNBLOCK (EE2016) succeeded");
  1195.                     byte[] ARPC = new byte[response[4]];
  1196.                     Buffer.BlockCopy(response, 5, ARPC, 0, response[4]);
  1197.                     return (true, ARPC);
  1198.                 }
  1199.             }
  1200.             finally
  1201.             {
  1202.                 //return connection to stack
  1203.                 if (hsmConnections != null)
  1204.                     hsmConnections.Push(connection);
  1205.             }
  1206.         }
  1207.        
  1208.         #endregion
  1209.        
  1210.         public static string ByteArrayToString(byte[] ba)
  1211.         {
  1212.             StringBuilder hex = new StringBuilder(ba.Length * 2);
  1213.             foreach (byte b in ba)
  1214.                 hex.AppendFormat("{0:x2}", b);
  1215.             return hex.ToString();
  1216.         }
  1217.  
  1218.         public async Task<byte[]> EncryptAsync(byte keyIndex, byte[] data)
  1219.         {
  1220.             HsmTcpConnection connection = null;
  1221.             try
  1222.             {
  1223.                 _log.Debug("Encrypting ...");
  1224.                 int dataLength = data.Length; //Data lenght must be mulitple of 8 bytes for 3DES
  1225.                 if (dataLength % 8 !=0)
  1226.                 {
  1227.                     dataLength += 8 - (dataLength % 8);
  1228.                 }
  1229.  
  1230.                 var variableByteLen = CalculateVariableLength(dataLength);
  1231.  
  1232.  
  1233.                 int msgLength = 6 + 3 + 1 + 3 + 1 + 9 + variableByteLen.Length + dataLength;
  1234.                 //header + function name + function modifier + DPK-Spec + CM  + ICV + VAR Data Len + Data  
  1235.                 byte[] req = new byte[msgLength];
  1236.                 req[0] = 0x01; // SOH Character
  1237.                 req[1] = 0x01; // Version Number
  1238.  
  1239.                 var seqNum = GetNextSequenceNumber();
  1240.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  1241.                 req[3] = (byte)seqNum; // Sequence Number
  1242.  
  1243.                
  1244.                
  1245.                 req[4] = (byte)((msgLength - 6) >> 8); // length of message (header is not included that is why -6)
  1246.                 req[5] = (byte)(msgLength - 6); // length of message
  1247.                
  1248.                 req[6] = 0xEE;  // Function name
  1249.                 req[7] = 0x08;
  1250.                 req[8] = 0x04;
  1251.  
  1252.                 req[9] = 0x00;  // Function modifier
  1253.  
  1254.                 connection = GetConnection();
  1255.  
  1256.                 req[10] = 0x02; // Key Specifier for DPK, Var len
  1257.                 req[11] = 0x01; // DPK format
  1258.                 req[12] = keyIndex; //index
  1259.  
  1260.                 req[13] = 0x00; //Cipher mode - ECB
  1261.                
  1262.  
  1263.                 req[14] = 0x08; //ICV for ECB
  1264.                 req[15] = 0x00;
  1265.                 req[16] = 0x00;
  1266.                 req[17] = 0x00;
  1267.                 req[18] = 0x00;
  1268.                 req[19] = 0x00;
  1269.                 req[20] = 0x00;
  1270.                 req[21] = 0x00;
  1271.                 req[22] = 0x00;
  1272.  
  1273.                 Buffer.BlockCopy(variableByteLen, 0, req, 23, variableByteLen.Length); //Data var len
  1274.                 Buffer.BlockCopy(data, 0, req, 23+variableByteLen.Length, data.Length);
  1275.                
  1276.                 var response = await connection.SendRequestAsync(req);
  1277.  
  1278.                 if (response == null || response.Length < 4)
  1279.                     throw new HsmCipherException("ENCIPHER-3 (EE0804) function returned invalid response");
  1280.  
  1281.                 //check response code
  1282.                 if (response[3] != 0x00) //function failed
  1283.                 {
  1284.                     throw new HsmCipherException(string.Format("ENCIPHER-3 (EE0804) response is {0:X2}", response[3]));
  1285.                 }
  1286.                 else
  1287.                 { //Success
  1288.                     //Read/skip Output Chaining Value
  1289.                     byte OCVlen = response[4];
  1290.                     int pos = 5 + OCVlen;
  1291.                     int bytesConsumed;
  1292.                     int cipherDataLen = ReadVariableLength(response, pos, out bytesConsumed);
  1293.                     byte[] result = new byte[cipherDataLen];
  1294.                     Buffer.BlockCopy(response, pos + bytesConsumed, result, 0, cipherDataLen);
  1295.                     return result;
  1296.                 }                                                          
  1297.             }
  1298.             finally
  1299.             {
  1300.                 //return connection to stack
  1301.                 if (hsmConnections != null)
  1302.                     hsmConnections.Push(connection);
  1303.             }
  1304.         }
  1305.  
  1306.         public async Task<byte[]> DecryptAsync(byte keyIndex, byte[] encryptedData)
  1307.         {
  1308.             HsmTcpConnection connection = null;
  1309.             try
  1310.             {
  1311.                 _log.Debug("Decrypting ...");
  1312.                 var variableByteLen = CalculateVariableLength(encryptedData.Length);
  1313.                 int msgLength = 6 + 3 + 1 + 3 + 1 + 9 + variableByteLen.Length + encryptedData.Length;
  1314.                 //header + function name + function modifier + DPK-Spec + CM  + ICV + VAR Data Len + Data  
  1315.                 byte[] req = new byte[msgLength];
  1316.                 req[0] = 0x01; // SOH Character
  1317.                 req[1] = 0x01; // Version Number
  1318.  
  1319.                 var seqNum = GetNextSequenceNumber();
  1320.                 req[2] = (byte)(seqNum >> 8); // Sequence Number
  1321.                 req[3] = (byte)seqNum; // Sequence Number
  1322.  
  1323.                
  1324.                 req[4] = (byte)((msgLength - 6) >> 8); // length of message (header is not included that is why -6)
  1325.                 req[5] = (byte)(msgLength - 6); // length of message
  1326.  
  1327.                 req[6] = 0xEE;  // Function name
  1328.                 req[7] = 0x08;
  1329.                 req[8] = 0x05;
  1330.  
  1331.                 req[9] = 0x00;  // Function modifier
  1332.  
  1333.                 connection = GetConnection();
  1334.  
  1335.                 req[10] = 0x02; // Key Specifier for DPK, Var len
  1336.                 req[11] = 0x01; // DPK format
  1337.                 req[12] = keyIndex; //index
  1338.  
  1339.                 req[13] = 0x00; //Cipher mode - ECB
  1340.  
  1341.                 req[14] = 0x08; //ICV for ECB
  1342.                 req[15] = 0x00;
  1343.                 req[16] = 0x00;
  1344.                 req[17] = 0x00;
  1345.                 req[18] = 0x00;
  1346.                 req[19] = 0x00;
  1347.                 req[20] = 0x00;
  1348.                 req[21] = 0x00;
  1349.                 req[22] = 0x00;
  1350.  
  1351.                 Buffer.BlockCopy(variableByteLen, 0, req, 23, variableByteLen.Length);
  1352.                 Buffer.BlockCopy(encryptedData, 0, req, 23+ variableByteLen.Length, encryptedData.Length);
  1353.  
  1354.                 var response = await connection.SendRequestAsync(req);
  1355.  
  1356.                 if (response == null || response.Length < 4)
  1357.                     throw new HsmCipherException("DECIPHER-3 (EE0805) function returned invalid response");
  1358.  
  1359.                 //check response code
  1360.                 if (response[3] != 0x00) //function failed
  1361.                 {
  1362.                     throw new HsmCipherException(string.Format("DECIPHER-3 (EE0805) response is {0:X2}", response[3]));
  1363.                 }
  1364.                 else
  1365.                 { // success
  1366.                     //Read/skip Output Chaining Value
  1367.                     byte OCVlen = response[4];
  1368.                     int pos = 5 + OCVlen;
  1369.                     int bytesConsumed;
  1370.                     int decipheredDataLen = ReadVariableLength(response, pos, out bytesConsumed);
  1371.                     byte[] result = new byte[decipheredDataLen];
  1372.                     Buffer.BlockCopy(response, pos+ bytesConsumed, result, 0, decipheredDataLen);
  1373.                     return result;
  1374.                 }
  1375.  
  1376.             }
  1377.             finally
  1378.             {
  1379.                 //return connection to stack
  1380.                 if (hsmConnections != null)
  1381.                     hsmConnections.Push(connection);
  1382.             }
  1383.         }
  1384.  
  1385.         /// <summary>
  1386.         /// Calculate variable length.
  1387.         /// The variable-length field construct provides a standard mechanism for incorporating a field of varying length into HSM Request or Response messages. It comprises the variable-length data and a prefix which specifies the length of the data, and which is also of variable-length. This section describes the method for specifying the actual length of a variable-length data field in a function request or response. The actual length of the length prefix is specified by the most significant bits of the most significant byte within the prefix. The remaining bits within the most significant byte form part (or all, in the single-byte case) of the value of the length prefix.
  1388.         /// </summary>
  1389.         /// <param name="length"></param>
  1390.         /// <returns></returns>
  1391.         protected byte[] CalculateVariableLength(int length)
  1392.         {
  1393.             byte[] result;
  1394.             if (length > 0x1FFFFF)
  1395.             {
  1396.                 result = new byte[4];
  1397.             }
  1398.             else if (length > 0x3FFF)
  1399.             {
  1400.                 result = new byte[3];
  1401.             }
  1402.             else if (length > 0x7F)
  1403.             {
  1404.                 result = new byte[2];
  1405.             }
  1406.             else
  1407.             {
  1408.                 result = new byte[1];
  1409.             }
  1410.            
  1411.             for (int i = 0; i < result.Length; i++)
  1412.             {
  1413.                 result[i] =(byte) (length >> (8 * (result.Length -1 - i)));
  1414.             }
  1415.  
  1416.             switch (result.Length)
  1417.             {
  1418.                 case 4:
  1419.                     result[0] |= 0xE0;
  1420.                     break;
  1421.                 case 3:
  1422.                     result[0] |= 0xC0;
  1423.                     break;
  1424.                 case 2:
  1425.                     result[0] |= 0x80;
  1426.                     break;
  1427.             }
  1428.  
  1429.             return result;
  1430.         }
  1431.  
  1432.         /// <summary>
  1433.         /// Read variable lenght. The number of bytes used is indicated by the MSB bits.
  1434.         ///
  1435.         /// </summary>
  1436.         /// <param name="buffer">Buffer to read from</param>
  1437.         /// <param name="readLenIndex">Index to start reading in buffer</param>
  1438.         /// <param name="bytesConsumed">out paramater indicating the number of bytes used to read variable length</param>
  1439.         /// <returns></returns>
  1440.         protected int ReadVariableLength(byte[] buffer, int readLenIndex, out int bytesConsumed)
  1441.         {
  1442.             int nosBytes;
  1443.             if (((byte)buffer[readLenIndex] & 0x80) ==0xE0)
  1444.             {
  1445.                 nosBytes = 4;
  1446.                 buffer[readLenIndex] ^= 0xE0;
  1447.             }
  1448.             else if (((byte)buffer[readLenIndex] & 0x80) == 0xC0)
  1449.             {
  1450.                 nosBytes = 3;
  1451.                 buffer[readLenIndex] ^= 0xC0;
  1452.             }
  1453.             else if (((byte)buffer[readLenIndex] & 0x80) == 0x80)
  1454.             {
  1455.                 nosBytes = 2;
  1456.                 buffer[readLenIndex] ^= 0x80;
  1457.             }
  1458.             else
  1459.             {
  1460.                 nosBytes = 1;
  1461.             }
  1462.             int result = 0;
  1463.             for (int i = 0; i < nosBytes; i++)
  1464.             {
  1465.                 result += buffer[readLenIndex + i] << (8 * (nosBytes -1 - i));
  1466.             }
  1467.  
  1468.             bytesConsumed = nosBytes;
  1469.             return result;
  1470.         }
  1471.     }
  1472. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement