Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using EMS.Utilities;
- using EMS.Encryption.PaymentHSM.ConfigSection;
- using System.Collections.Generic;
- using System.Text;
- using System.Threading;
- using EMS.HSM.Core.Pin;
- using EMS.HSM.Core.Terminals;
- using EMS.HSM.Core.ApplicationCryptogram;
- using EMS.HSM.Core.Exceptions;
- using log4net;
- using EMS.HSM.Core.Cipher;
- using System.Threading.Tasks;
- namespace EMS.Encryption.PaymentHSM
- {
- public class PaymentHSMManager : IPinService, ITerminalKeyService, IApplicationCryptogramService, ICipher
- {
- protected readonly ILog _log = LogManager.GetLogger(typeof(PaymentHSMManager));
- protected static volatile int _sequenceNumber = 0;
- FirstInFirstOutList<HsmTcpConnection> hsmConnections = new FirstInFirstOutList<HsmTcpConnection>();
- /// <summary>
- /// Initialize Payment HSM Manager. This Init methods takes a list of connection endpoints to setup.
- /// The order of the list determines how they are added, i.e. usage.
- /// </summary>
- /// <param name="connections">List of connections</param>
- public void Initialize(List<HSMConnectionConfiguration> connections)
- {
- //We are going to add connections to a stack list.
- foreach (var connection in connections)
- {
- hsmConnections.Push(new HsmTcpConnection(connection.Name,connection.Hostname,connection.Port,connection.ConnectTimeout * 1000)); //Timeout is in seconds, we need to convert to milliseconds
- }
- }
- /// <summary>
- /// Return a unique sequence number
- /// </summary>
- /// <returns></returns>
- protected static ushort GetNextSequenceNumber()
- {
- int result = Interlocked.Increment(ref _sequenceNumber);
- if (result +1 > ushort.MaxValue)
- Interlocked.Exchange(ref _sequenceNumber, 1);
- return (ushort)result;
- }
- protected HsmTcpConnection GetConnection()
- {
- var connection = hsmConnections.GetItem();
- if (connection==null)
- {
- throw new HsmConnectionException("No HSM connection available!");
- }
- return connection;
- }
- /// <summary>
- /// Translate a PIN Block encrypted under one working key, returned encrypted on another
- /// </summary>
- /// <param name="pinBlock"> encrypted PIN Block</param>
- /// <param name="PAN">PAN for PIN Block in question</param>
- /// <param name="workingKey">Working key ecrypted on database key as stored in database</param>
- /// <param name="keyIndex">key Index to determine working key to use</param>
- /// <returns></returns>
- public async Task<byte[]> TranslatePinBlockAsync(byte[] pinBlock, string PAN, byte[] workingKey, byte keyIndex)
- {
- HsmTcpConnection connection = null;
- try
- {
- byte[] req = new byte[47];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = 0x00; // length of message
- req[5] = 41; // length of message
- req[6] = 0xEE;
- req[7] = 0x06;
- req[8] = 0x02; // Function name
- req[9] = 0x00; // Function modifier
- Buffer.BlockCopy(pinBlock, 0, req, 10, 8); //pinblock
- //Host Key
- req[18] = 0x11; // PPKi spec(length format index)
- req[19] = 0x11;
- Buffer.BlockCopy(workingKey, 0, req, 20, 16);
- req[36] = 0x01; // Input pin block format. In this case its ISO Format 0
- byte[] ANBBCD = Convertor.StringToBCD(PAN.Substring(PAN.Length - 13, 12));
- Buffer.BlockCopy(ANBBCD, 0, req, 37, 6);
- req[43] = 0x01; // output pin block format.
- req[44] = 0x02; // PPKo spec(length format index)
- req[45] = 0x01;
- req[46] = keyIndex;
- connection = GetConnection();
- var response = await connection.SendRequestAsync(req);
- if (response == null || response.Length < 4)
- throw new HsmPinTranslationException("PIN-TRAN-2 function returned invalid response");
- //check response code
- if (response[3] != 0x00) //response is not success
- throw new HsmPinTranslationException(string.Format("PIN-TRAN-2 response is {0:X2}", response[3]));
- byte[] result = new byte[8];
- Buffer.BlockCopy(response, 4, result, 0, 8);
- _log.Debug("Translate PIN Block succeeded");
- return result;
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- /// <summary>
- /// Translate a PIN Block encrypted under a Pin Protect key, to another Pin Protect Key both stored in HSM
- /// </summary>
- /// <param name="pinBlock"> encrypted PIN Block</param>
- /// <param name="PAN">PAN for PIN Block in question</param>
- /// <param name="ppkInputIndex">PPK HSM Index under which the parameter PinBlock is encrypted on</param>
- /// <param name="ppkOutputIndex">PPK Hsm Index the PinBlock should be encrypted on and returned as result</param>
- /// <returns></returns>
- public async Task<byte[]> TranslatePinBlockAsync(byte[] pinBlock, string PAN, byte ppkInputIndex, byte ppkOutputIndex)
- {
- HsmTcpConnection connection = null;
- try
- {
- _log.Debug($"Translate Pin block from index {ppkInputIndex} to {ppkOutputIndex}");
- byte[] req = new byte[32];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = 0x00; // length of message
- req[5] = 26; // length of message
- req[6] = 0xEE;
- req[7] = 0x06;
- req[8] = 0x02; // Function name
- req[9] = 0x00; // Function modifier
- Buffer.BlockCopy(pinBlock, 0, req, 10, 8); //pinblock
- //Host Key
- req[18] = 0x02; // PPKi spec(length format index)
- req[19] = 0x01;
- req[20] = ppkInputIndex;
- req[21] = 0x01; // Input pin block format. In this case its ISO Format 0
- byte[] ANBBCD = Convertor.StringToBCD(PAN.Substring(PAN.Length - 13, 12));
- Buffer.BlockCopy(ANBBCD, 0, req, 22, 6);
- req[28] = 0x01; // output pin block format.
- req[29] = 0x02; // PPKo spec(length format index)
- req[30] = 0x01;
- req[31] = ppkOutputIndex;
- connection = GetConnection();
- var response = await connection.SendRequestAsync(req);
- if (response == null || response.Length < 4)
- throw new HsmPinTranslationException("PIN-TRAN-2 HSM stored PPK function returned invalid response");
- //check response code
- if (response[3] != 0x00) //response is not success
- throw new HsmPinTranslationException(string.Format("PIN-TRAN-2 HSM stored PPK response is {0:X2}", response[3]));
- byte[] result = new byte[8];
- Buffer.BlockCopy(response, 4, result, 0, 8);
- _log.Debug("Translate PIN Block succeeded");
- return result;
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- #region PIN Generation / Verification
- /// <summary>
- /// This function generates a random PIN, formats and encrypts it for host storage.
- /// </summary>
- /// <param name="keyIndex">PPK (Store PIN Protect Key) hsm key index</param>
- /// <param name="pan">Card Pan</param>
- /// <param name="pinLength">PIN Length</param>
- /// <returns></returns>
- public async Task<byte[]> GeneratePinAsync(byte keyIndex, string pan, byte pinLength = 4)
- {
- _log.Debug($"Generating new pin for {PAN.MaskPAN(pan)}");
- if (pinLength <4 || pinLength>12)
- {
- throw new HsmPinGenerateException(string.Format("PIN length of {0} is not valid", pinLength));
- }
- HsmTcpConnection connection = null;
- try
- {
- byte[] req = new byte[21];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = 0x00; // length of message
- req[5] = 15; // length of message
- req[6] = 0xEE;
- req[7] = 0x0E;
- req[8] = 0x04; // Function name
- req[9] = 0x00; // Function modifier
- req[10] = pinLength; // PIN Length – in the range 04 - 12
- req[11] = 0x01; // PFo ANSII
- Convertor.StringToBCD(pan, pan.Length - 13, 12, req, 12);//ANB
- req[18] = 0x02; //PPk var len
- req[19] = 0x01;
- req[20] = keyIndex;
- connection = GetConnection();
- var response = await connection.SendRequestAsync(req);
- if (response == null || response.Length < 4)
- throw new HsmPinGenerateException("PIN-GENERATE function returned invalid response");
- //check response code
- if (response[3] != 0x00) //response is not success
- throw new HsmPinGenerateException(string.Format("PIN-GENERATE response is {0:X2}", response[3]));
- byte[] result = new byte[8];
- Buffer.BlockCopy(response, 4, result, 0, 8);
- _log.Debug("PIN generate succeeded");
- return result;
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- /// <summary>
- /// This function generates a PIN Offset which is used for issuing PIN verification
- /// </summary>
- /// <param name="pinProtectKeyIndex">PPK (Store PIN Protect Key) hsm host key index by which pin stored in database is encrypted with</param>
- /// <param name="pinVerificationKeyIndex">PVK </param>
- /// <param name="pan">Card Pan</param>
- /// <returns></returns>
- public async Task<byte[]> GeneratePinOffsetAsync(byte pinProtectKeyIndex, byte pinVerificationKeyIndex, string pan, byte[] pinBlock)
- {
- _log.Debug("Generating Pin offset");
- if (pinBlock ==null || pinBlock.Length!=8)
- {
- throw new HsmPinGenerateException("PIN Offset pin block lenght is not valid");
- }
- HsmTcpConnection connection = null;
- try
- {
- byte[] req = new byte[39];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = 0x00; // length of message
- req[5] = 33; // length of message
- req[6] = 0xEE;
- req[7] = 0x06;
- req[8] = 0x04; // Function name
- req[9] = 0x00; // Function modifier
- Buffer.BlockCopy(pinBlock, 0, req, 10, 8); //PIN Block
- req[18] = 0x02; //PPk var len
- req[19] = 0x01;
- req[20] = pinProtectKeyIndex;
- req[21] = 0x01; //PIN Block Format 01
- Convertor.StringToBCD(pan, pan.Length - 13, 12, req, 22); //ANB
- req[28] = 0x02; //PVK var len
- req[29] = 0x01;
- req[30] = pinVerificationKeyIndex;
- //Validation Data
- Convertor.StringToBCD(pan, 0, 16, req, 31);
- connection = GetConnection();
- var response = await connection.SendRequestAsync(req);
- if (response == null || response.Length < 4)
- throw new HsmPinGenerateException("PIN-OFF function returned invalid response");
- //check response code
- if (response[3] != 0x00) //response is not success
- throw new HsmPinGenerateException(string.Format("PIN-OFF response is {0:X2}", response[3]));
- byte[] result = new byte[6];
- Buffer.BlockCopy(response, 4, result, 0, 6);
- byte pinLen = response[10];
- _log.DebugFormat("PIN OFF succeeded, pin lenght {0}", pinLen);
- return result;
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- public async Task<bool> VerifyPinAsync(byte[] pinBlock, byte pinProtectKeyIndex, byte pinVerificationKeyIndex, string pan, byte[] pinOffset )
- {
- if (pinBlock == null || pinBlock.Length != 8)
- {
- throw new HsmPinVerificationException("PIN-VER-IBM-MULTI (EE0603) pin block lenght is not valid");
- }
- HsmTcpConnection connection = null;
- try
- {
- byte[] req = new byte[46];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = 0x00; // length of message
- req[5] = 40; // length of message
- req[6] = 0xEE;
- req[7] = 0x06;
- req[8] = 0x03; // Function name
- req[9] = 0x00; // Function modifier
- Buffer.BlockCopy(pinBlock, 0, req, 10, 8); //PIN Block
- req[18] = 0x02; //PPk var len
- req[19] = 0x01;
- req[20] = pinProtectKeyIndex;
- req[21] = 0x01; //PIN Block Format 01
- Convertor.StringToBCD(pan, pan.Length - 13, 12, req, 22); //ANB
- req[28] = 0x02; //PVK var len
- req[29] = 0x01;
- req[30] = pinVerificationKeyIndex;
- //Validation Data
- Convertor.StringToBCD(pan, 0, 16, req, 31);
- Buffer.BlockCopy(pinOffset, 0, req, 39, 6); //Pin Offset
- req[45] = 4; //PIN Check Length
- connection = GetConnection();
- var response = await connection.SendRequestAsync(req);
- if (response == null || response.Length < 4)
- throw new HsmPinVerificationException("PIN-VER-IBM-MULTI (EE0603) function returned invalid response");
- //check response code
- if (response[3] == 0x00)
- {
- _log.Debug("PIN verification succeeded");
- return true;
- }
- else if (response[3] == 0x08)
- {
- _log.Debug("PIN verification failed");
- return false;
- }
- else //response is not success
- {
- throw new HsmPinVerificationException(string.Format("PIN-VER-IBM-MULTI (EE0603) response is {0:X2}", response[3]));
- }
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- #endregion
- /// <summary>
- /// Verify MAC for a POS terminal
- /// </summary>
- /// <param name="MACHostKey">MAC key to use for verification encrypted on database key</param>
- /// <param name="MAC">MAC computed by terminal</param>
- /// <param name="dataBuffer">Data to compute MAC on</param>
- /// <param name="dataOffset">Data offset where to start computing MAC on</param>
- /// <param name="dataLength">Data length</param>
- /// <returns>true if verification succeeds otherwise false</returns>
- public async Task<bool> VerifyMacAsync(byte[] MACHostKey, byte[] MAC, byte[] dataBuffer, int dataOffset, int dataLength)
- {
- HsmTcpConnection connection = null;
- try
- {
- if (dataLength > 32767)
- throw new HsmMacVerificationException("Data length of more than 32,767 not supported");
- if (MACHostKey == null || MACHostKey.Length != 16)
- throw new HsmMacVerificationException("MAC Host key is not valid");
- int msgLength = 6 + 3 + 1 + 1 + 8 + 2 + 16 + 1 + 8 + 2 + dataLength;
- //header + function name + function modifier + MAC Algo + ICD + Var Len & format + MPK + Var Len + MAC + varlen + data
- byte[] req = new byte[msgLength];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = (byte)((msgLength - 6) >> 8); // length of message (header is not included that is why -6)
- req[5] = (byte)(msgLength - 6); // length of message
- req[6] = 0xEE; // Function name
- req[7] = 0x07;
- req[8] = 0x02;
- req[9] = 0x00; // Function modifier
- /*
- * Algorithm Qualifier. Specifies details of the MACing algorithm.
- * Left nibble (Padding):
- * = 0 : pad with zeroes.
- * = 1 : pad with a single one bit and subsequent zeroes
- * Right nibble (Algorithm):
- * For single length MPK must be zero.
- * For double length MPK:
- * = 0 : ISO 9807 method
- * = 1 : triple-DES CBC method
- * */
- req[10] = 0x00; // MACing algorithm
- req[11] = 0x00; // Input Chaining Data (ICD) - not used
- req[12] = 0x00;
- req[13] = 0x00;
- req[14] = 0x00;
- req[15] = 0x00;
- req[16] = 0x00;
- req[17] = 0x00;
- req[18] = 0x00;
- req[19] = 0x11; // Var Len
- req[20] = 0x11; // Key specifier for MPK (we will use host encrypted key)
- Buffer.BlockCopy(MACHostKey, 0, req, 21, 16);
- req[37] = 0x08; // Var Len
- Buffer.BlockCopy(MAC, 0, req, 38, 8); //MAC
- req[46] = (byte)(dataLength >> 8); // Data to be MACed including VAR length of 2 bytes
- req[46] |= 0x80; // Indicates that we're using a 2 byte length
- req[47] = (byte)dataLength;
- Buffer.BlockCopy(dataBuffer, dataOffset, req, 48, dataLength);
- connection = GetConnection();
- var response = await connection.SendRequestAsync(req);
- if (response == null || response.Length < 4)
- throw new HsmMacVerificationException("MAC-VER-FINAL (EE0702) function returned invalid response");
- //check response code
- if (response[3] != 0x00 && response[3] != 0x08) //function failed
- throw new HsmMacVerificationException(string.Format("MAC-VER-FINAL (EE0702) response is {0:X2}", response[3]));
- else if (response[3] == 0x08)
- {
- _log.DebugFormat("Verify MAC failed for host key {0}", MACHostKey);
- return false;
- }
- else //0x00
- {
- _log.Debug("Verify MAC succeeded");
- return true;
- }
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- /// <summary>
- /// Generate Terminal Master Key
- /// </summary>
- /// <param name="TKLDZMKKeyIndex">TKLD ZMK Key Index</param>
- /// <param name="MKonZMK">Result master key encrypted on ZMK</param>
- /// <param name="MKonKM">Result master key encrypted on DMK (KM)(host key)</param>
- /// <param name="MK_KCV">Master key KCV</param>
- public void GenerateTerminalMasterKey(byte TKLDZMKKeyIndex, out byte[] MKonZMK, out byte[] MKonKM, out byte[] MK_KCV)
- {
- HsmTcpConnection connection = null;
- try
- {
- byte[] req = new byte[15];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = 0x00; // length of message
- req[5] = 9; // length of message
- req[6] = 0xEE; // Function name
- req[7] = 0x04;
- req[8] = 0x00;
- req[9] = 0x00; // Function modifier
- connection = GetConnection();
- req[10] = 0x02; // Key Specifier for KTM
- req[11] = 0x01; // index of KTM
- req[12] = TKLDZMKKeyIndex;
- req[13] = 0x08; //Double-length terminal master key (KTM)
- req[14] = 0x00;
- var response = connection.SendRequestAsync(req).Result;
- if (response == null || response.Length < 4)
- throw new HsmGenerateKeyException("IT-KEY-GEN (EE0400) function returned invalid response");
- //check response code
- if (response[3] != 0x00) //function failed
- {
- throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) response is {0:X2}", response[3]));
- }
- if (response[4] != 0x01)
- {
- throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) 'n' response is {0:X2}", response[4]));
- }
- byte MKKeyOnZmkLen = response[5];
- MKonZMK = new byte[MKKeyOnZmkLen];
- Buffer.BlockCopy(response, 6, MKonZMK, 0, MKKeyOnZmkLen);
- int index = 6 + MKKeyOnZmkLen;
- byte hostKeyVarLen = response[index++];
- if (hostKeyVarLen != 0x11)
- {
- throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) host key var length mismatch, length is {0:X2}", hostKeyVarLen));
- }
- if (response[index] != 0x11)
- {
- throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) KS-Spec format response is {0:X2}", response[index]));
- }
- index++;
- MKonKM = new byte[16];
- Buffer.BlockCopy(response, index, MKonKM, 0, 16);
- index += 16;
- MK_KCV = new byte[3];
- Buffer.BlockCopy(response, index, MK_KCV, 0, 3);
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- /// <summary>
- /// Re-Encrypt a key which is encrypted on a ZMK to be encrypted on host key
- /// </summary>
- /// <param name="KI_KeyIndex">ZMK key index in HSM (KI)</param>
- /// <param name="encryptedKey">Encrypted key on ZMK</param>
- /// <returns>Encrypted key on KM</returns>
- public async Task<byte[]> ReEncryptKeyOnHostKeyAsync(byte KI_KeyIndex, byte[] encryptedKey)
- {
- HsmTcpConnection connection = null;
- try
- {
- byte[] req = new byte[19+encryptedKey.Length];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = 0x00; // length of message
- req[5] = (byte) (req.Length-6); // length of message
- req[6] = 0xEE; // Function name
- req[7] = 0x02;
- req[8] = 0x00;
- req[9] = 0x00; // Function modifier
- connection = GetConnection();
- req[10] = 0x05; // Len - Key Specifier for KI (ZMK)
- req[11] = 0x08; // Format for KI
- req[12] = 0x10; // KI
- req[13] = 0x02; // Var KSpec
- req[14] = 0x01;
- req[15] = KI_KeyIndex; //Index
- req[16] = 0x01; //Key Type (01 = PPK)
- req[17] = 0x01; //Ecnryption Mode ECB
- req[18] = (byte)encryptedKey.Length; // Encrypted key
- Buffer.BlockCopy(encryptedKey, 0, req, 19, encryptedKey.Length);
- var response = await connection.SendRequestAsync(req);
- if (response == null || response.Length < 4)
- throw new HsmGenerateKeyException("KEY-IMPORT (EE0200) function returned invalid response");
- //check response code
- if (response[3] != 0x00) //function failed
- {
- throw new HsmGenerateKeyException(string.Format("KEY-IMPORT (EE0200) response is {0:X2}", response[3]));
- }
- else
- {
- byte[] reEncryptedKey = new byte[16];
- Buffer.BlockCopy(response, 6, reEncryptedKey, 0, 16);
- return reEncryptedKey;
- }
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- /// <summary>
- /// Generate Terminal Working key and MAC Key
- /// </summary>
- /// <param name="terminalMK">Terminal Master key encrypted on KM</param>
- /// <param name="WKonKTM">Working Key on KTM (TMS Key)</param>
- /// <param name="WKonKM">Working key on KM (DB Key)</param>
- /// <param name="WK_KCV">Working key KCV</param>
- /// <param name="MAConKTM">MAC key on KTM (TMS Key)</param>
- /// <param name="MAConKM">MAC key on KM (DB key)</param>
- /// <param name="MAC_KCV">MAC KCV</param>
- public void GenerateTerminalWorkingKeyAndMac(byte[] terminalMK, out byte[] WKonKTM, out byte[] WKonKM, out byte[] WK_KCV,
- out byte[] MAConKTM, out byte[] MAConKM, out byte[] MAC_KCV)
- {
- HsmTcpConnection connection = null;
- try
- {
- if (terminalMK == null || terminalMK.Length != 16)
- {
- throw new HsmGenerateKeyException("IT-KEY-GEN (EE0400) terminal master key not valid!");
- }
- byte[] req = new byte[30];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = 0x00; // length of message
- req[5] = 24; // length of message
- req[6] = 0xEE; // Function name
- req[7] = 0x04;
- req[8] = 0x00;
- req[9] = 0x00; // Function modifier
- req[10] = 0x11; // Specify MK (KTM)
- req[11] = 0x11;
- Buffer.BlockCopy(terminalMK, 0, req, 12, 16);
- req[28] = 0x06;
- req[29] = 0x00; // Double length MPK and PPK
- connection = GetConnection();
- var response = connection.SendRequestAsync(req).Result;
- if (response == null || response.Length < 4)
- throw new HsmGenerateKeyException("IT-KEY-GEN (EE0400) WK function returned invalid response");
- //check response code
- if (response[3] != 0x00) //function failed
- {
- throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) WK response is {0:X2}", response[3]));
- }
- if (response[4] != 0x02)
- {
- throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) WK 'n' response is {0:X2}", response[4]));
- }
- //TMS WK Key
- byte WKonKTMLen = response[5];
- WKonKTM = new byte[WKonKTMLen];
- Buffer.BlockCopy(response, 6, WKonKTM, 0, WKonKTMLen);
- //WK Key host key (Db)
- int index = 6 + WKonKTMLen;
- byte WKhostKeyVarLen = response[index++];
- if (WKhostKeyVarLen != 0x11)
- {
- throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) WK host key var length mismatch, length is {0:X2}", WKhostKeyVarLen));
- }
- if (response[index] != 0x11)
- {
- throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) WK KS-Spec format response is {0:X2}", response[index]));
- }
- index++;
- WKonKM = new byte[16];
- Buffer.BlockCopy(response, index, WKonKM, 0, 16);
- index += 16;
- WK_KCV = new byte[3];
- Buffer.BlockCopy(response, index, WK_KCV, 0, 3);
- index += 3;
- //TMS MAC Key
- byte MAConKTMLen = response[index++];
- MAConKTM = new byte[MAConKTMLen];
- Buffer.BlockCopy(response, index, MAConKTM, 0, MAConKTMLen);
- //MAC Key host key (Db)
- index += MAConKTMLen;
- byte MAChostKeyVarLen = response[index++];
- if (WKhostKeyVarLen != 0x11)
- {
- throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) WK MAC host key var length mismatch, length is {0:X2}", WKhostKeyVarLen));
- }
- if (response[index] != 0x11)
- {
- throw new HsmGenerateKeyException(string.Format("IT-KEY-GEN (EE0400) WK MAC KS-Spec format response is {0:X2}", response[index]));
- }
- index++;
- MAConKM = new byte[16];
- Buffer.BlockCopy(response, index, MAConKM, 0, 16);
- index += 16;
- MAC_KCV = new byte[3];
- Buffer.BlockCopy(response, index, MAC_KCV, 0, 3);
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- /// <summary>
- /// This method is obsolete
- /// </summary>
- /// <param name="PANBlock"></param>
- /// <param name="randomNumber"></param>
- /// <param name="applicationCryptogram"></param>
- /// <param name="applicationCryptogramData"></param>
- /// <returns></returns>
- public async Task<bool> VerifyApplicationCryptogramAsync(byte[] PANBlock, byte[] randomNumber, byte[] applicationCryptogram, byte[] applicationCryptogramData)
- {
- HsmTcpConnection connection = null;
- try
- {
- if (PANBlock == null || PANBlock.Length != 8)
- throw new HsmApplicationCryptogramVerifyException("Application PAN Block is not valid");
- if (randomNumber == null || randomNumber.Length != 8)
- throw new HsmApplicationCryptogramVerifyException("Random number is not valid");
- if (applicationCryptogram == null || applicationCryptogram.Length != 8)
- throw new HsmApplicationCryptogramVerifyException("Application Cryptogram is not valid");
- if (applicationCryptogramData == null || (applicationCryptogram.Length % 8 != 0)) // applicatin cyrptogram data must be a multiple of 8
- throw new HsmApplicationCryptogramVerifyException("Application Cryptogram Data is not valid");
- int msgLength = 6 + 3 + 1 + 3 + 8 + 8 + 8 + 1 + applicationCryptogramData.Length;
- //header + function name + function modifier + Key Specifier + PAN + Random Number + AC + VAR AC Data Len + AC Data Len
- byte[] req = new byte[msgLength];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = (byte)((msgLength - 6) >> 8); // length of message (header is not included that is why -6)
- req[5] = (byte)(msgLength - 6); // length of message
- req[6] = 0xEE; // Function name
- req[7] = 0x20;
- req[8] = 0x01;
- req[9] = 0x00; // Function modifier
- req[10] = 0x02; // Var Len
- req[11] = 0x00;
- req[12] = 0x01; // Key specifier for IMKAC
- //PAN BLOCK
- Buffer.BlockCopy(PANBlock, 0, req, 13, 8);
- //Ranmdom Number
- Buffer.BlockCopy(randomNumber, 0, req, 21, 8);
- //Application Cryptogram
- Buffer.BlockCopy(applicationCryptogram, 0, req, 29, 8);
- //Application Cryptogram Data
- req[37] = (byte)applicationCryptogramData.Length;
- Buffer.BlockCopy(applicationCryptogramData, 0, req, 38, applicationCryptogramData.Length);
- //we need to maintain pos due to application cyrptogram data being dynamic (not fixed size)
- int pos = 38 + applicationCryptogramData.Length;
- //req[pos++] = 0x00; //Bitmap, not used with FM 00
- //req[pos] = 0x00; // Transaction data. only present when FM = 04
- connection = GetConnection();
- var response = await connection.SendRequestAsync(req);
- if (response == null || response.Length < 4)
- throw new HsmApplicationCryptogramVerifyException("EMV-AC-VERIFY (EE2001) function returned invalid response");
- Console.WriteLine(BitConverter.ToString(req));
- Console.WriteLine(BitConverter.ToString(response));
- //check response code
- if (response[3] != 0x00 && response[3] != 0x08) //function failed
- throw new HsmApplicationCryptogramVerifyException(string.Format("EMV-AC-VERIFY (EE2001) response is {0:X2}", response[3]));
- else if (response[3] == 0x08)
- {
- return false;
- }
- else //0x00
- {
- _log.Debug("Verify Application Cryptogram succeeded");
- return true;
- }
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- /// <summary>
- /// This function can be used to
- /// •verify an Application Cryptogram(AC),
- /// •generates an ARPC
- /// •both verify an Application Cryptogram(AC) and generate an ARPC
- /// The AC can be an ARQC, a TC or an AAC.
- /// </summary>
- /// <param name="keyIndex">IMK Key Index in HSM key store</param>
- /// <param name="MKData">Data used to generate session key from IMK, PAN + Sequence Number</param>
- /// <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>
- /// <param name="applicationCryptogramData">Data on which the AC is calculated</param>
- /// <param name="applicationCryptogram">Application cryptogram to verify against</param>
- /// <param name="ArpcData">Data to generate ARPC. Typically the data value is 0x0100 </param>
- /// <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>
- public async Task<(bool ApplicationCrytogramVerificationResult, byte[] ArpcResponse)> VerifyApplicationCryptogramGenARPCAsync(byte keyIndex, byte[] MKData, byte[] ACKeyData, byte[] applicationCryptogramData, byte[] applicationCryptogram
- ,byte[] ArpcData)
- {
- HsmTcpConnection connection = null;
- try
- {
- if (MKData == null )
- throw new HsmApplicationCryptogramVerifyException("MK Data is not valid");
- if (ACKeyData == null)
- throw new HsmApplicationCryptogramVerifyException("AC Key Data is not valid");
- if (applicationCryptogram == null || applicationCryptogram.Length != 8)
- throw new HsmApplicationCryptogramVerifyException("Application Cryptogram is not valid");
- if (applicationCryptogramData == null) // applicatin cyrptogram data must be a multiple of 8
- throw new HsmApplicationCryptogramVerifyException("Application Cryptogram Data is not valid");
- int msgLength = 6 + 3 + 1 + 1 + 3 + 1 + 1 + MKData.Length + 1 + 1 + ACKeyData.Length + 1 + 1 + applicationCryptogramData.Length + applicationCryptogram.Length + 4 + ArpcData.Length;
- //header + function name + function modifier + Action + Key Specifier + PAN + Random Number + AC + VAR AC Data Len + AC Data Len + ARPC Stuff
- byte[] req = new byte[msgLength];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = (byte)((msgLength - 6) >> 8); // length of message (header is not included that is why -6)
- req[5] = (byte)(msgLength - 6); // length of message
- req[6] = 0xEE; // Function name
- req[7] = 0x20;
- req[8] = 0x18;
- req[9] = 0x00; // Function modifier
- req[10] = 0x03; //Action - Verify AC & Generate ARPC only
- req[11] = 0x02; // Var Len
- req[12] = 0x00;
- req[13] = keyIndex; // Key specifier for IMKAC
- req[14] = 0x00; // MK-Method Common
- req[15] = (byte)MKData.Length; //var len
- Buffer.BlockCopy(MKData, 0, req, 16, MKData.Length); //MK Data
- int pos = 16 + MKData.Length;
- req[pos++] = 0x01; //AC Key Method - SKD function using ATC and UN M / Chip 2.1[31], SECCOS[34]
- req[pos++] = (byte)ACKeyData.Length; //VAR AC Key Data
- Buffer.BlockCopy(ACKeyData, 0, req, pos, ACKeyData.Length); //AC Key Data
- pos += ACKeyData.Length;
- req[pos++] = 0x03;//AC Methods
- req[pos++] = (byte) applicationCryptogramData.Length; //VAR AC Data
- Buffer.BlockCopy(applicationCryptogramData, 0, req, pos, applicationCryptogramData.Length); //AC Data
- pos += applicationCryptogramData.Length;
- Buffer.BlockCopy(applicationCryptogram, 0, req, pos, 8); //AC
- pos += 8;
- req[pos++] = 0x01; // ARPC Key Method
- req[pos++] = 0x00; // Length of ARPC Key Data
- req[pos++] = 0x01; //ARPC Method
- req[pos++] = (byte) ArpcData.Length; //Lenght of ARPC Data
- Buffer.BlockCopy(ArpcData, 0, req, pos, ArpcData.Length);
- connection = GetConnection();
- var response = await connection.SendRequestAsync(req);
- if (response == null || response.Length < 4)
- throw new HsmApplicationCryptogramVerifyException("EMV-VERIFY-AC-GEN-ARPC (EE2018) function returned invalid response");
- //check response code
- if (response[3] != 0x00 && response[3] != 0x08) //function failed
- throw new HsmApplicationCryptogramVerifyException(string.Format("EMV-VERIFY-AC-GEN-ARPC (EE2018) response is {0:X2}", response[3]));
- else if (response[3] == 0x08)
- {
- return (false, null);
- }
- else //0x00
- {
- _log.Debug("Verify Application Cryptogram succeeded");
- byte[] ARPC = new byte[response[4]];
- Buffer.BlockCopy(response, 5, ARPC, 0, response[4]);
- return (true,ARPC);
- }
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- #region PIN Unblock
- public async Task<(bool ApplicationCrytogramVerificationResult, byte[] ArpcResponse)> CardUnblockAsync(string PAN, string sequenceNumber, byte smiKeyIndex, byte smcKeyIndex, byte acKeyIndex,
- byte[] applicationCryptogram, byte[] ATC)
- {
- HsmTcpConnection connection = null;
- try
- {
- if (string.IsNullOrEmpty(PAN) || string.IsNullOrEmpty(sequenceNumber))
- throw new HsmApplicationCryptogramVerifyException("PAN Data is not valid");
- if (applicationCryptogram == null)
- throw new HsmApplicationCryptogramVerifyException("Application Cryptogram is not valid");
- if (applicationCryptogram == null || applicationCryptogram.Length != 8)
- throw new HsmApplicationCryptogramVerifyException("Application Cryptogram is not valid");
- if (ATC == null)
- throw new HsmApplicationCryptogramVerifyException("ATC is not valid");
- 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;
- // 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;
- //header + function name + function modifier + function flag + scheme + SMI + SMC + IMKAC + (Len +PAN + SequenceNos) + (Len + AC) + (OLD & New PIN) + PPK
- //+ PinBlockFormat + ANB + ScriptDataPosition + Script Data
- byte[] req = new byte[msgLength];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = (byte)((msgLength - 6) >> 8); // length of message (header is not included that is why -6)
- req[5] = (byte)(msgLength - 6); // length of message
- req[6] = 0xEE; // Function name
- req[7] = 0x20;
- req[8] = 0x16;
- req[9] = 0x00; // Function modifier
- req[10] = 0x02; //Function flag - 00= Pin Unblock only, 02= Pin Change
- req[11] = 0x01; //Scheme - 01=MasterCard
- req[12] = 0x02; // Var Len
- req[13] = 0x00;
- req[14] = smiKeyIndex; // Key specifier for IMK SMI
- req[15] = 0x02; // Var Len
- req[16] = 0x00;
- req[17] = smcKeyIndex; // Key specifier for IMK SMC
- req[18] = 0x00; // IMK AC
- //Formatted PAN and PAN Sequence Number
- // //Hardcoded PAN
- // req[19] = 0x08;
- // req[20] = 0x04;
- // req[21] = 0x74;
- // req[22] = 0x00;
- // req[23] = 0x00;
- // req[24] = 0x00;
- // req[25] = 0x14;
- // req[26] = 0x16;
- //
- // //Hardcoded Sequence Number
- // req[27] = 0x00;
- req[19] = (byte) Convertor.StringToBCD(PAN + sequenceNumber,2, PAN.Length,req,20);
- //
- // var pos = 28;
- var pos = 20 + req[19];
- req[pos++] = (byte) applicationCryptogram.Length;
- Buffer.BlockCopy(applicationCryptogram, 0, req, pos, applicationCryptogram.Length);
- pos += applicationCryptogram.Length;
- req[pos++] = 0x32;
- req[pos++] = 0x13;
- req[pos++] = 0x17;
- req[pos++] = 0x5A;
- req[pos++] = 0xBC;
- req[pos++] = 0x32;
- req[pos++] = 0xB9;
- req[pos++] = 0x49;
- req[pos++] = 0x32;
- req[pos++] = 0x13;
- req[pos++] = 0x17;
- req[pos++] = 0x5A;
- req[pos++] = 0xBC;
- req[pos++] = 0x32;
- req[pos++] = 0xB9;
- req[pos++] = 0x49;
- // pos += 8; //Encrypted PIN Block (Existing PIN)
- // pos += 8; //Encrypted PIN Block (New PIN)
- req[pos++] = 0x02;
- req[pos++] = 0x00;
- req[pos++] = 0x07; //PPK. we should not be using this for this function - index 7 is staging , 2 is live
- req[pos++] = 0x10; //PIN Block Format
- //Account Number Block
- pos += Convertor.StringToBCD(PAN, PAN.Length - 13, 12, req, pos);//ANB
- req[pos++] = 0x00; //Script Data Position
- req[pos++] = 0x0F;
- req[pos++] = 0x18; //Script data len
- req[pos++] = 0x84; //Class
- req[pos++] = 0x24; //Ins
- req[pos++] = 0x00; //P1
- req[pos++] = 0x00;// P2
- req[pos++] = 0x08; //Lc
- Buffer.BlockCopy(ATC, 0, req, pos, ATC.Length);
- pos += ATC.Length;
- Buffer.BlockCopy(applicationCryptogram, 0, req, pos, applicationCryptogram.Length);
- pos += applicationCryptogram.Length;
- req[pos++] = 0x00; //8 bytes used for Pin change only
- req[pos++] = 0x00;
- req[pos++] = 0x00;
- req[pos++] = 0x00;
- req[pos++] = 0x00;
- req[pos++] = 0x00;
- req[pos++] = 0x00;
- req[pos++] = 0x00;
- req[pos++] = 0x80;
- Console.WriteLine($"Request Buffer: {ByteArrayToString(req)}");
- connection = GetConnection();
- var response = await connection.SendRequestAsync(req);
- Console.WriteLine($"Response Buffer: {ByteArrayToString(response)}");
- if (response == null || response.Length < 4)
- throw new HsmApplicationCryptogramVerifyException("EMV-PIN-CHANGE-UNBLOCK (EE2016) function returned invalid response");
- //check response code
- if (response[3] != 0x00 && response[3] != 0x08) //function failed
- throw new HsmApplicationCryptogramVerifyException(string.Format("EMV-PIN-CHANGE-UNBLOCK (EE2016) response is {0:X2}", response[3]));
- else if (response[3] == 0x08)
- {
- return (false, null);
- }
- else //0x00
- {
- _log.Debug("EMV-PIN-CHANGE-UNBLOCK (EE2016) succeeded");
- byte[] ARPC = new byte[response[4]];
- Buffer.BlockCopy(response, 5, ARPC, 0, response[4]);
- return (true, ARPC);
- }
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- #endregion
- public static string ByteArrayToString(byte[] ba)
- {
- StringBuilder hex = new StringBuilder(ba.Length * 2);
- foreach (byte b in ba)
- hex.AppendFormat("{0:x2}", b);
- return hex.ToString();
- }
- public async Task<byte[]> EncryptAsync(byte keyIndex, byte[] data)
- {
- HsmTcpConnection connection = null;
- try
- {
- _log.Debug("Encrypting ...");
- int dataLength = data.Length; //Data lenght must be mulitple of 8 bytes for 3DES
- if (dataLength % 8 !=0)
- {
- dataLength += 8 - (dataLength % 8);
- }
- var variableByteLen = CalculateVariableLength(dataLength);
- int msgLength = 6 + 3 + 1 + 3 + 1 + 9 + variableByteLen.Length + dataLength;
- //header + function name + function modifier + DPK-Spec + CM + ICV + VAR Data Len + Data
- byte[] req = new byte[msgLength];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = (byte)((msgLength - 6) >> 8); // length of message (header is not included that is why -6)
- req[5] = (byte)(msgLength - 6); // length of message
- req[6] = 0xEE; // Function name
- req[7] = 0x08;
- req[8] = 0x04;
- req[9] = 0x00; // Function modifier
- connection = GetConnection();
- req[10] = 0x02; // Key Specifier for DPK, Var len
- req[11] = 0x01; // DPK format
- req[12] = keyIndex; //index
- req[13] = 0x00; //Cipher mode - ECB
- req[14] = 0x08; //ICV for ECB
- req[15] = 0x00;
- req[16] = 0x00;
- req[17] = 0x00;
- req[18] = 0x00;
- req[19] = 0x00;
- req[20] = 0x00;
- req[21] = 0x00;
- req[22] = 0x00;
- Buffer.BlockCopy(variableByteLen, 0, req, 23, variableByteLen.Length); //Data var len
- Buffer.BlockCopy(data, 0, req, 23+variableByteLen.Length, data.Length);
- var response = await connection.SendRequestAsync(req);
- if (response == null || response.Length < 4)
- throw new HsmCipherException("ENCIPHER-3 (EE0804) function returned invalid response");
- //check response code
- if (response[3] != 0x00) //function failed
- {
- throw new HsmCipherException(string.Format("ENCIPHER-3 (EE0804) response is {0:X2}", response[3]));
- }
- else
- { //Success
- //Read/skip Output Chaining Value
- byte OCVlen = response[4];
- int pos = 5 + OCVlen;
- int bytesConsumed;
- int cipherDataLen = ReadVariableLength(response, pos, out bytesConsumed);
- byte[] result = new byte[cipherDataLen];
- Buffer.BlockCopy(response, pos + bytesConsumed, result, 0, cipherDataLen);
- return result;
- }
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- public async Task<byte[]> DecryptAsync(byte keyIndex, byte[] encryptedData)
- {
- HsmTcpConnection connection = null;
- try
- {
- _log.Debug("Decrypting ...");
- var variableByteLen = CalculateVariableLength(encryptedData.Length);
- int msgLength = 6 + 3 + 1 + 3 + 1 + 9 + variableByteLen.Length + encryptedData.Length;
- //header + function name + function modifier + DPK-Spec + CM + ICV + VAR Data Len + Data
- byte[] req = new byte[msgLength];
- req[0] = 0x01; // SOH Character
- req[1] = 0x01; // Version Number
- var seqNum = GetNextSequenceNumber();
- req[2] = (byte)(seqNum >> 8); // Sequence Number
- req[3] = (byte)seqNum; // Sequence Number
- req[4] = (byte)((msgLength - 6) >> 8); // length of message (header is not included that is why -6)
- req[5] = (byte)(msgLength - 6); // length of message
- req[6] = 0xEE; // Function name
- req[7] = 0x08;
- req[8] = 0x05;
- req[9] = 0x00; // Function modifier
- connection = GetConnection();
- req[10] = 0x02; // Key Specifier for DPK, Var len
- req[11] = 0x01; // DPK format
- req[12] = keyIndex; //index
- req[13] = 0x00; //Cipher mode - ECB
- req[14] = 0x08; //ICV for ECB
- req[15] = 0x00;
- req[16] = 0x00;
- req[17] = 0x00;
- req[18] = 0x00;
- req[19] = 0x00;
- req[20] = 0x00;
- req[21] = 0x00;
- req[22] = 0x00;
- Buffer.BlockCopy(variableByteLen, 0, req, 23, variableByteLen.Length);
- Buffer.BlockCopy(encryptedData, 0, req, 23+ variableByteLen.Length, encryptedData.Length);
- var response = await connection.SendRequestAsync(req);
- if (response == null || response.Length < 4)
- throw new HsmCipherException("DECIPHER-3 (EE0805) function returned invalid response");
- //check response code
- if (response[3] != 0x00) //function failed
- {
- throw new HsmCipherException(string.Format("DECIPHER-3 (EE0805) response is {0:X2}", response[3]));
- }
- else
- { // success
- //Read/skip Output Chaining Value
- byte OCVlen = response[4];
- int pos = 5 + OCVlen;
- int bytesConsumed;
- int decipheredDataLen = ReadVariableLength(response, pos, out bytesConsumed);
- byte[] result = new byte[decipheredDataLen];
- Buffer.BlockCopy(response, pos+ bytesConsumed, result, 0, decipheredDataLen);
- return result;
- }
- }
- finally
- {
- //return connection to stack
- if (hsmConnections != null)
- hsmConnections.Push(connection);
- }
- }
- /// <summary>
- /// Calculate variable length.
- /// 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.
- /// </summary>
- /// <param name="length"></param>
- /// <returns></returns>
- protected byte[] CalculateVariableLength(int length)
- {
- byte[] result;
- if (length > 0x1FFFFF)
- {
- result = new byte[4];
- }
- else if (length > 0x3FFF)
- {
- result = new byte[3];
- }
- else if (length > 0x7F)
- {
- result = new byte[2];
- }
- else
- {
- result = new byte[1];
- }
- for (int i = 0; i < result.Length; i++)
- {
- result[i] =(byte) (length >> (8 * (result.Length -1 - i)));
- }
- switch (result.Length)
- {
- case 4:
- result[0] |= 0xE0;
- break;
- case 3:
- result[0] |= 0xC0;
- break;
- case 2:
- result[0] |= 0x80;
- break;
- }
- return result;
- }
- /// <summary>
- /// Read variable lenght. The number of bytes used is indicated by the MSB bits.
- ///
- /// </summary>
- /// <param name="buffer">Buffer to read from</param>
- /// <param name="readLenIndex">Index to start reading in buffer</param>
- /// <param name="bytesConsumed">out paramater indicating the number of bytes used to read variable length</param>
- /// <returns></returns>
- protected int ReadVariableLength(byte[] buffer, int readLenIndex, out int bytesConsumed)
- {
- int nosBytes;
- if (((byte)buffer[readLenIndex] & 0x80) ==0xE0)
- {
- nosBytes = 4;
- buffer[readLenIndex] ^= 0xE0;
- }
- else if (((byte)buffer[readLenIndex] & 0x80) == 0xC0)
- {
- nosBytes = 3;
- buffer[readLenIndex] ^= 0xC0;
- }
- else if (((byte)buffer[readLenIndex] & 0x80) == 0x80)
- {
- nosBytes = 2;
- buffer[readLenIndex] ^= 0x80;
- }
- else
- {
- nosBytes = 1;
- }
- int result = 0;
- for (int i = 0; i < nosBytes; i++)
- {
- result += buffer[readLenIndex + i] << (8 * (nosBytes -1 - i));
- }
- bytesConsumed = nosBytes;
- return result;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement