private TLV Pack91(CryptoMetaData cryptoMetaData, byte[] arpc, TLV _8A) { TLV _91; if (arpc == null) { arpc = new byte[8]; } switch (cryptoMetaData.SKDMethod) { case SKDMethod.VSDC: _91 = TLV.Create(EMVTagsEnum.ISSUER_AUTHENTICATION_DATA_91_KRN.Tag, Formatting.ConcatArrays(arpc, _8A.Value)); break; case SKDMethod.MCHIP: _91 = TLV.Create(EMVTagsEnum.ISSUER_AUTHENTICATION_DATA_91_KRN.Tag, Formatting.ConcatArrays(arpc, _8A.Value)); break; case SKDMethod.EMV_CSKD: _91 = TLV.Create(EMVTagsEnum.ISSUER_AUTHENTICATION_DATA_91_KRN.Tag, Formatting.ConcatArrays(arpc, PackCSU())); break; default: throw new SimulatedPaymentProviderException("Pack91: SKDMethod not supported:" + cryptoMetaData.SKDMethod); } return(_91); }
internal bool ValidateHash() { byte[] concatenated = Formatting.ConcatArrays( new byte[] { CertificateFormat }, IssuerIdentifier, ExpiryDate, CertificateSerialNumber, new byte[] { HashAlgorithmIndicator }, new byte[] { PublicKeyAlgorithmIndicator }, new byte[] { IssuerPublicKeyLength }, new byte[] { IssuerPublicKeyExponentLength }, IssuerPublicKeyorLeftmostDigitsofIssuerPublicKey, IssuerPublicKeyRemainder, Exponent ); byte[] hash = SHA1.Create().ComputeHash(concatenated); if (Formatting.ByteArrayToHexString(HashResult) != Formatting.ByteArrayToHexString(hash)) { return(false); } return(true); }
public virtual byte[] Serialize() { if (InstallParamC9 != null) { InstallParametersfield = InstallParamC9.Serialize(); } else { InstallParametersfield = new byte[2] { 0xC9, 0x00 } }; return(Formatting.ConcatArrays( new byte[] { BitConverter.GetBytes(ExecutableLoadFileAID.Length / 2)[0] }, Formatting.HexStringToByteArray(ExecutableLoadFileAID), new byte[] { BitConverter.GetBytes(ExecutableModuleAID.Length / 2)[0] }, Formatting.HexStringToByteArray(ExecutableModuleAID), new byte[] { BitConverter.GetBytes(ApplicationAID.Length / 2)[0] }, Formatting.HexStringToByteArray(ApplicationAID), new byte[] { BitConverter.GetBytes(Privileges.Length)[0] }, Privileges, ConvertLengthLength(InstallParametersfield.Length), InstallParametersfield, ConvertLengthLength(InstallToken.Length), InstallToken )); }
public static IKey FormDESKey(short keyLength, byte[] clearKeyBytes) { IKey key = null; switch (keyLength) { case SMAdapter.LENGTH_DES: key = new SecretKeySpec(clearKeyBytes, ALG_DES); break; case SMAdapter.LENGTH_DES3_2KEY: // make it 3 components to work with JCE clearKeyBytes = Formatting.ConcatArrays(clearKeyBytes, 0, GetBytesLength(SMAdapter.LENGTH_DES3_2KEY), clearKeyBytes, 0, GetBytesLength(SMAdapter.LENGTH_DES)); key = new SecretKeySpec(clearKeyBytes, ALG_TRIPLE_DES); break; case SMAdapter.LENGTH_DES3_3KEY: key = new SecretKeySpec(clearKeyBytes, ALG_TRIPLE_DES); break; } if (key == null) { throw new Exception("Unsupported DES key length: " + keyLength + " bits"); } return(key); }
public virtual byte[] Serialize() { byte[] data; if (Data != null) { data = Data.Serialize(); } else { data = DataBytes; } byte[] result = Formatting.ConcatArrays( DGI, new byte[] { BitConverter.GetBytes(data.Length)[0] }, data ); if (MAC != null) { result = Formatting.ConcatArrays(result, MAC); } return(result); }
private async Task <ApduResponse> Transceive(ApduCommand apduCommand) { ApduResponse apduRes = Activator.CreateInstance(apduCommand.ApduResponseType) as ApduResponse; byte[] dataIn = apduCommand.Serialize(); bool debugOut = true; if (debugOut) { StringBuilder sb = new StringBuilder(); sb.AppendLine("********************************************************************************************************"); sb.AppendLine(apduCommand.ToString()); sb.Append("Raw Data Sent:" + Formatting.ByteArrayToHexString(dataIn)); Logger.Log(sb.ToString()); } byte[] dataOut = await CardInterfaceManger.TransmitAsync(dataIn); if (debugOut) { StringBuilder sb = new StringBuilder(); sb.AppendLine("Raw Data Received:" + Formatting.ByteArrayToHexString(dataOut)); sb.AppendLine("********************************************************************************************************"); Logger.Log(sb.ToString()); } apduRes.Deserialize(dataOut); if (apduRes.SW1 == 0x61) { GetResponseRequest getResponseRequest = new GetResponseRequest(apduRes.SW2); ApduResponse getResponseResponse = await Transceive(getResponseRequest); if (getResponseResponse.Succeeded || (getResponseResponse.SW1 == 0x62 && getResponseResponse.SW2 == 0x83)) { apduRes.ResponseData = Formatting.ConcatArrays(apduRes.ResponseData, getResponseResponse.ResponseData, new byte[] { 0x90, 0x00 }); apduRes.Deserialize(apduRes.ResponseData); } else { throw new Exception("GetResponse failed"); } } if (apduRes.SW1 == 0x6C) { //repeat command with correct Le apduCommand.Le = apduRes.SW2; apduRes = await Transceive(apduCommand); } return(apduRes); }
public static byte[] CalculateVISLegacyPinBlockCVN_10_18(String newPin, IKey deaKey) { byte[] block1 = Formatting.HexStringToByteArray(new String(FormatPINBlock(newPin, 0x0))); byte[] block2 = new byte[8]; Array.Copy(deaKey.GetEncoded(), 4, block2, 4, 4); byte[] pinBlock = Formatting.Xor(block1, block2); byte length = (byte)pinBlock.Length; pinBlock = Formatting.ConcatArrays(new byte[] { length }, pinBlock); pinBlock = EMVDESSecurity.PaddingISO9797Method2(pinBlock); return(pinBlock); }
private static void Transmit(TCPClientStream stream, byte[] txBytes) { short length = (short)(txBytes.Length); byte[] lengthBytes = new byte[] { (byte)(length / 256), (byte)(length % 256) }; byte[] txBytesTCP = Formatting.ConcatArrays(lengthBytes, txBytes); Logger.Log("Sending: [" + Formatting.ConvertToInt16(lengthBytes) + "]" + "[" + Formatting.ByteArrayToHexString(txBytes) + "]"); stream.Write(txBytesTCP); }
public virtual byte[] Serialize() { return(Formatting.ConcatArrays( new byte[] { BitConverter.GetBytes(LoadFileAID.Length / 2)[0] }, Formatting.HexStringToByteArray(LoadFileAID), new byte[] { BitConverter.GetBytes(SecurityDomainAID.Length / 2)[0] }, Formatting.HexStringToByteArray(SecurityDomainAID), ConvertLengthLength(LoadFileDataBlockHash.Length), LoadFileDataBlockHash, ConvertLengthLength(LoadParametersfield.Length), LoadParametersfield, ConvertLengthLength(LoadToken.Length), LoadToken )); }
private static void Transmit(TCPClientStream stream, byte[] txBytes) { byte chrSTX = 0x02; // Start of Text byte chrETX = 0x03; // End of Text //byte[] LRC; short length = (short)(txBytes.Length + 2); byte[] lengthBytes = new byte[] { (byte)(length / 256), (byte)(length % 256) }; byte[] txBytesTCP = Formatting.ConcatArrays(lengthBytes, new byte[] { chrSTX }, txBytes, new byte[] { chrETX }); Logger.Log("Sending: [" + Formatting.ConvertToInt16(lengthBytes) + "]" + "[" + Formatting.ByteArrayToHexString(new byte[] { chrSTX }) + "]" + "[" + Formatting.ByteArrayToASCIIString(txBytes) + "]" + "[" + Formatting.ByteArrayToHexString(new byte[] { chrETX }) + "]"); stream.Write(txBytesTCP); }
protected byte[] ConvertLengthLength(int length) { if (length <= 0x80) { return(new byte[] { BitConverter.GetBytes(length).Reverse().ToArray()[3] }); } if (length >= 0x81 && (length - 0x81) <= 0xFF) { byte val = BitConverter.GetBytes(length - 0x81).Reverse().ToArray()[3]; return(new byte[] { 0x81, val }); } if ((length - 0x81) > 0xFF) { byte[] vals = new byte[2]; Array.Copy(BitConverter.GetBytes(length - 0x82).Reverse().ToArray(), 2, vals, 0, 2); return(Formatting.ConcatArrays(new byte[] { 0x82 }, vals)); } throw new Exception("Invalid length"); }
public byte[] BuildStaticDataToBeAuthenticated() { TLV aip = database.Get(EMVTagsEnum.APPLICATION_INTERCHANGE_PROFILE_82_KRN.Tag); if (aip != null) { TLVList newList = new TLVList(); foreach (TLV tlv in listToManage) { if (tlv.Tag.TagLable != aip.Tag.TagLable) { newList.AddToList(tlv, true); } } if (database.IsNotEmpty(EMVTagsEnum.STATIC_DATA_AUTHENTICATION_TAG_LIST_9F4A_KRN.Tag)) { StringBuilder sb = new StringBuilder(); int depth = 0; sb.Append("Final StaticDataToBeAuthenticatedList (AIP and STATIC_DATA_AUTHENTICATION_TAG_LIST_9F4A_KRN in DB): \n"); sb.AppendLine(newList.ToPrintString(ref depth)); depth = 1; sb.AppendLine(aip.ToPrintString(ref depth)); Logger.Log(sb.ToString()); return(Formatting.ConcatArrays(newList.Serialize(), aip.Value)); } else { int depth = 0; Logger.Log("Final StaticDataToBeAuthenticatedList: (AIP in DB but No STATIC_DATA_AUTHENTICATION_TAG_LIST_9F4A_KRN in DB)\n" + ToPrintString(ref depth)); return(newList.Serialize()); } } else { int depth = 0; Logger.Log("Final StaticDataToBeAuthenticatedList (No AIP in DB): \n" + ToPrintString(ref depth)); return(Serialize()); } }
public static byte[] BuildPinVerifyData(KernelDatabaseBase database, CAPublicKeyCertificate caPublicKey, byte[] pinBlock, byte[] challenge) { IssuerPublicKeyCertificate ipk = IssuerPublicKeyCertificate.BuildAndValidatePublicKey(database, caPublicKey.Modulus, caPublicKey.Exponent); if (ipk == null) { return(null); } int keyLength = 0; PublicKeyCertificate iccKey = IccPinKeyCertificate.BuildAndValidatePublicKey(database, ipk.Modulus, ipk.Exponent); if (iccKey == null) { iccKey = IccPublicKeyCertificate.BuildAndValidatePublicKey(database, database.StaticDataToBeAuthenticated, ipk.Modulus, ipk.Exponent); if (iccKey == null) { return(null); } keyLength = ((IccPublicKeyCertificate)iccKey).ICCPublicKeyLength; } else { keyLength = ((IccPinKeyCertificate)iccKey).ICCPinKeyLength; } int paddingLength = keyLength - 17; byte[] padding = new byte[paddingLength]; byte[] pinData = Formatting.ConcatArrays(new byte[] { 0x7F }, pinBlock, challenge, padding); //apply recovery function byte[] encryptedPin = PublicKeyCertificate.DecryptRSA(pinData, iccKey.Modulus, iccKey.Exponent); return(encryptedPin); }
public void OpenSecureChannel(GPPlaintextKeys keys, List <APDUMode> securityLevel, byte[] hostChallenge = null, byte[] initUpdateResponse = null, byte[] externalAuthReponse = null) { if (securityLevel.Contains(APDUMode.ENC) && !securityLevel.Contains(APDUMode.MAC)) { securityLevel.Add(APDUMode.MAC); } if (hostChallenge == null) { // Generate host challenge hostChallenge = new byte[8]; SecureRandom sr = new SecureRandom(); sr.NextBytes(hostChallenge); } GPInitializeUpdateReqest initUpdate = new GPInitializeUpdateReqest(keys.GetKeysetVersion(), keys.GetKeysetID(), hostChallenge); //System.Diagnostics.Debug.WriteLine(initUpdate.ToPrintString()); GPInitializeUpdateResponse response; if (initUpdateResponse != null) { response = new GPInitializeUpdateResponse(); response.Deserialize(initUpdateResponse); System.Diagnostics.Debug.WriteLine(response.ToPrintString()); } else { response = (GPInitializeUpdateResponse)SendCommand(initUpdate); } // Detect and report locked cards in a more sensible way. if ((response.SW == (ushort)ISO7816ReturnCodes.SW_SECURITY_STATUS_NOT_SATISFIED) || (response.SW == (ushort)ISO7816ReturnCodes.SW_AUTHENTICATION_METHOD_BLOCKED)) { throw new Exception("INITIALIZE UPDATE failed, Card possibly locked."); } if (response.SW != (ushort)ISO7816ReturnCodes.SW_NO_ERROR) { throw new Exception("INITIALIZE UPDATE failed"); } // Verify response length (SCP01/SCP02 + SCP03 + SCP03 w/ pseudorandom) if (response.ResponseData.Length != 28 && response.ResponseData.Length != 29 && response.ResponseData.Length != 32) { throw new Exception("Invalid INITIALIZE UPDATE response length: " + response.ResponseData.Length); } System.Diagnostics.Debug.WriteLine("Host challenge: " + Formatting.ByteArrayToHexString(hostChallenge)); System.Diagnostics.Debug.WriteLine("Card challenge: " + Formatting.ByteArrayToHexString(response.CardChallenge)); // Verify response // If using explicit key version, it must match. if ((keys.GetKeysetVersion() > 0) && (response.KeyVersionNumber != keys.GetKeysetVersion())) { throw new Exception("Key version mismatch: " + keys.GetKeysetVersion() + " != " + response.KeyVersionNumber); } System.Diagnostics.Debug.WriteLine("Card reports SCP0" + response.SCPId + " with version " + response.KeyVersionNumber + " keys"); if (response.SCPId == 3) { System.Diagnostics.Debug.WriteLine("SCP03 i=" + response.SCPI); } SCPVersions scpVersion; // Derive session keys GPKeySet sessionKeys; if (response.SCPId == 1) { if (securityLevel.Contains(APDUMode.RMAC)) { throw new Exception("SCP01 does not support RMAC"); } scpVersion = SCPVersions.SCP_01_05; sessionKeys = keys.GetSessionKeys(response.SCPId, response.KeyDiversificationData, hostChallenge, response.CardChallenge); } else if (response.SCPId == 2) { scpVersion = SCPVersions.SCP_02_15; sessionKeys = keys.GetSessionKeys(response.SCPId, response.KeyDiversificationData, response.CardChallengeSeq); } else if (response.SCPId == 3) { scpVersion = SCPVersions.SCP_03; sessionKeys = keys.GetSessionKeys(response.SCPId, response.KeyDiversificationData, hostChallenge, response.CardChallenge); } else { throw new Exception("Unsupported scpVersion: " + response.SCPId); } // Verify card cryptogram byte[] my_card_cryptogram = null; byte[] cntx; if (response.SCPId == 2) { cntx = Formatting.ConcatArrays(hostChallenge, response.CardChallengeSeq, response.CardChallenge); } else { cntx = Arrays.Concatenate(hostChallenge, response.CardChallenge); } if (response.SCPId == 1 || response.SCPId == 2) { my_card_cryptogram = SCP0102Wrapper.Mac_3des_nulliv(sessionKeys.GetKey(KeySessionType.ENC), cntx); } else { my_card_cryptogram = SCP03Wrapper.Scp03_kdf(sessionKeys.GetKey(KeySessionType.MAC), (byte)0x00, cntx, 64); } // This is the main check for possible successful authentication. if (!Arrays.AreEqual(response.CardCryptogram, my_card_cryptogram)) { string message = "Card cryptogram invalid." + "\nCard: " + Formatting.ByteArrayToHexString(response.CardCryptogram) + "\nCalculated: " + Formatting.ByteArrayToHexString(my_card_cryptogram) + "\nRetrying the same parameters may disable the card"; System.Diagnostics.Debug.WriteLine(message); throw new Exception(message); } else { System.Diagnostics.Debug.WriteLine("Verified card cryptogram: " + Formatting.ByteArrayToHexString(my_card_cryptogram)); } // Calculate host cryptogram and initialize SCP wrapper byte[] host_cryptogram = null; if (response.SCPId == 1 || response.SCPId == 2) { if (response.SCPId == 2) { host_cryptogram = SCP0102Wrapper.Mac_3des_nulliv(sessionKeys.GetKey(KeySessionType.ENC), Formatting.ConcatArrays(response.CardChallengeSeq, response.CardChallenge, hostChallenge)); } else { host_cryptogram = SCP0102Wrapper.Mac_3des_nulliv(sessionKeys.GetKey(KeySessionType.ENC), Arrays.Concatenate(response.CardChallenge, hostChallenge)); } wrapper = new SCP0102Wrapper(sessionKeys, scpVersion, new List <APDUMode>() { APDUMode.MAC }, null, null, blockSize); } else { host_cryptogram = SCP03Wrapper.Scp03_kdf(sessionKeys.GetKey(KeySessionType.MAC), (byte)0x01, cntx, 64); wrapper = new SCP03Wrapper(sessionKeys, scpVersion, new List <APDUMode>() { APDUMode.MAC }, null, null, blockSize); } System.Diagnostics.Debug.WriteLine("Calculated host cryptogram: " + Formatting.ByteArrayToHexString(host_cryptogram)); GPExternalAuthenticateReqest externalAuthenticate = new GPExternalAuthenticateReqest(GetSetValue(securityLevel), host_cryptogram); GPExternalAuthenticateResponse response2; if (externalAuthReponse != null) { response2 = new GPExternalAuthenticateResponse(); response2.Deserialize(externalAuthReponse); System.Diagnostics.Debug.WriteLine(response2.ToPrintString()); } else { response2 = (GPExternalAuthenticateResponse)SendCommand(externalAuthenticate); } if (response2.SW != (ushort)ISO7816ReturnCodes.SW_NO_ERROR) { throw new Exception("External authenticate failed"); } wrapper.SetSecurityLevel(securityLevel); }
internal static IssuerPublicKeyCertificate BuildAndValidatePublicKey(KernelDatabaseBase database, byte[] caPublicKeyModulus, byte[] caPublicKeyExponent) { //section 6.3 EMV 4.3 Book 2 TLV issuerPublicKeyCertificate = database.Get(EMVTagsEnum.ISSUER_PUBLIC_KEY_CERTIFICATE_90_KRN); TLV issuerPublicKeyExponent = database.Get(EMVTagsEnum.ISSUER_PUBLIC_KEY_EXPONENT_9F32_KRN); TLV issuerPublicKeyRemainder = database.Get(EMVTagsEnum.ISSUER_PUBLIC_KEY_REMAINDER_92_KRN); if (issuerPublicKeyCertificate.Value.Length != caPublicKeyModulus.Length) { return(null); } byte[] decrypt = DecryptRSA(issuerPublicKeyCertificate.Value, caPublicKeyModulus, caPublicKeyExponent); IssuerPublicKeyCertificate issuerCertData = new IssuerPublicKeyCertificate(decrypt, caPublicKeyModulus.Length, issuerPublicKeyRemainder == null ? new byte[0] : issuerPublicKeyRemainder.Value, issuerPublicKeyExponent.Value); if (issuerCertData.RecoveredDataTrailer != 0xBC) { return(null); } if (issuerCertData.RecoveredDataHeader != 0x6A) { return(null); } if (issuerCertData.CertificateFormat != 0x02) { return(null); } if (!issuerCertData.ValidateHash()) { return(null); } string pan = Formatting.ByteArrayToHexString(database.Get(EMVTagsEnum.APPLICATION_PRIMARY_ACCOUNT_NUMBER_PAN_5A_KRN).Value); string issuerIdentifier = Formatting.ByteArrayToHexString(issuerCertData.IssuerIdentifier).Replace("FF", ""); if (!pan.StartsWith(issuerIdentifier)) { return(null); } DateTime expiry = DateTime.ParseExact(Formatting.BcdToString(issuerCertData.ExpiryDate), "MMyy", System.Globalization.CultureInfo.InvariantCulture); //TODO: if you have a test tool trying to use an expired cert then comment this test out or update your test tool //if (expiry <= DateTime.Now) //{ // Logger.Log("Error: Trying to use an expired issuer public key"); // return null; //} //step 10 optional if (issuerCertData.PublicKeyAlgorithmIndicator != 0x01) { return(null); } if (issuerPublicKeyRemainder != null) { issuerCertData.Modulus = Formatting.ConcatArrays(issuerCertData.UnpaddedIssuerPublicKeyorLeftmostDigitsofIssuerPublicKey, issuerPublicKeyRemainder.Value); } else { issuerCertData.Modulus = issuerCertData.UnpaddedIssuerPublicKeyorLeftmostDigitsofIssuerPublicKey; } return(issuerCertData); }
public static byte[] DecryptRSA(byte[] bytesToDecrypt, byte[] publicKeyModulus, byte[] publicKeyExponent) { //Prepend 0x00 to unsigned data to avoid that the most significant bit is interpreted as the "signed" bit byte[] modAppend; byte[] expAppend; byte[] dataAppend; if (publicKeyModulus[0] >= 0x80) { modAppend = Formatting.ConcatArrays(new byte[] { 0x00 }, publicKeyModulus); } else { modAppend = publicKeyModulus; } if (publicKeyExponent[0] >= 0x80) { expAppend = Formatting.ConcatArrays(new byte[] { 0x00 }, publicKeyExponent); } else { expAppend = publicKeyExponent.Reverse().ToArray(); } if (bytesToDecrypt[0] >= 0x80) { dataAppend = Formatting.ConcatArrays(new byte[] { 0x00 }, bytesToDecrypt); } else { dataAppend = bytesToDecrypt; } //Bouncy Castle BigInteger expects array to be in big endian order, as per EMV data, if this were a .NET BigInteger is would expect it in little endian BigInteger biMod = new BigInteger(1, modAppend); BigInteger biExp = new BigInteger(1, expAppend); BigInteger biDta = new BigInteger(1, dataAppend); byte[] result = biDta.ModPow(biExp, biMod).ToByteArray(); //these seems to be a bug in the bouncy castle RsaCoreEngine ConvertInput method, we add 0x00 to avoid misinterpretation of our numbers as -, //since they are unsigned, and BigInteger expects a signed number, this adding of the byte makes the length of the input longer and //the following lines of code => int maxLength = (bitSizeOfModulas + 7) / 8; if (inLen > maxLength) <= compares the length of the Modulus after //BigInteger conversion to the length of the input array before BigInteger confusion, resulting in different lengths //RsaKeyParameters keyParameter = new RsaKeyParameters(false, biMod, biExp); //Pkcs1Encoding encryptEngine = new Pkcs1Encoding(new RsaEngine()); //encryptEngine.Init(false, keyParameter); //byte[] decryptedBytes = encryptEngine.ProcessBlock(dataAppend, 0, dataAppend.Length); if (result.Length == (bytesToDecrypt.Length + 1) && result[0] == (byte)0x00) { //Remove 0x00 from beginning of array byte[] tmp = new byte[bytesToDecrypt.Length]; Array.Copy(result, 1, tmp, 0, bytesToDecrypt.Length); result = tmp; } return(result); }
internal static IccPublicKeyCertificate BuildAndValidatePublicKey(KernelDatabaseBase database, StaticDataToBeAuthenticatedList staticDataToBeAuthenticated, byte[] issuerPublicKeyModulus, byte[] issuerPublicKeyExponent) { //section 6.4 EMV 4.3 Book 2 TLV iccPublicKeyCertificate = database.Get(EMVTagsEnum.INTEGRATED_CIRCUIT_CARD_ICC_PUBLIC_KEY_CERTIFICATE_9F46_KRN); TLV iccPublicKeyExponent = database.Get(EMVTagsEnum.INTEGRATED_CIRCUIT_CARD_ICC_PUBLIC_KEY_EXPONENT_9F47_KRN); TLV iccPublicKeyRemainder = database.Get(EMVTagsEnum.INTEGRATED_CIRCUIT_CARD_ICC_PUBLIC_KEY_REMAINDER_9F48_KRN); if (iccPublicKeyCertificate.Value.Length != issuerPublicKeyModulus.Length) { return(null); } byte[] decrypt = DecryptRSA(iccPublicKeyCertificate.Value, issuerPublicKeyModulus, issuerPublicKeyExponent); IccPublicKeyCertificate iccCertData = new IccPublicKeyCertificate(decrypt, issuerPublicKeyModulus.Length, iccPublicKeyRemainder == null ? new byte[0] : iccPublicKeyRemainder.Value, iccPublicKeyExponent.Value, database.StaticDataToBeAuthenticated.BuildStaticDataToBeAuthenticated()); if (iccCertData.RecoveredDataTrailer != 0xBC) { return(null); } if (iccCertData.RecoveredDataHeader != 0x6A) { return(null); } if (iccCertData.CertificateFormat != 0x04) { return(null); } if (!iccCertData.ValidateHash()) { return(null); } string pan = Formatting.ByteArrayToHexString(database.Get(EMVTagsEnum.APPLICATION_PRIMARY_ACCOUNT_NUMBER_PAN_5A_KRN).Value); string panToCompare = Formatting.ByteArrayToHexString(iccCertData.ApplicationPAN).Replace("FF", ""); if (!pan.StartsWith(panToCompare)) { return(null); } DateTime expiry = DateTime.ParseExact(Formatting.BcdToString(iccCertData.ExpiryDate), "MMyy", System.Globalization.CultureInfo.InvariantCulture); //TODO: if you have a test tool trying to use an expired cert then comment this test out or update your test tool //if (expiry <= DateTime.Now) //{ // Logger.Log("Error: Trying to use an expired issuer public key"); // return null; //} if (iccCertData.PublicKeyAlgorithmIndicator != 0x01) { return(null); } if (iccPublicKeyRemainder != null) { iccCertData.Modulus = Formatting.ConcatArrays(iccCertData.UnpaddedICCPublicKeyorLeftmostDigitsofIssuerPublicKey, iccPublicKeyRemainder.Value); } else { iccCertData.Modulus = iccCertData.UnpaddedICCPublicKeyorLeftmostDigitsofIssuerPublicKey; } return(iccCertData); }
private byte[] DoGPO(byte[] adpu) { TLVList db = new TLVList(); EMVGetProcessingOptionsRequest request = new EMVGetProcessingOptionsRequest(); request.Deserialize(adpu); TLV _83 = TLV.Create(EMVTagsEnum.COMMAND_TEMPLATE_83_KRN.Tag); _83.Deserialize(request.CommandData, 0); int pos = 0; byte[] amount = Formatting.copyOfRange(_83.Value, pos, pos + MAX_AMOUNT_AUTH__LENGTH); pos = pos + MAX_AMOUNT_AUTH__LENGTH; byte[] upn = Formatting.copyOfRange(_83.Value, pos, pos + MAX_UNPRED_NUM__LENGTH); pos = pos + MAX_UNPRED_NUM__LENGTH; byte[] ttq = Formatting.copyOfRange(_83.Value, pos, pos + MAX_TTQ_LENGTH); //pos = pos + MAX_TTQ_LENGTH; db.AddToList(TLV.Create(EMVTagsEnum.TERMINAL_TRANSACTION_QUALIFIERS_TTQ_9F66_KRN.Tag, ttq)); /* * supported by card and reader (TTQ byte 1 bit 6 set to 1b) * return 6985 if not */ TERMINAL_TRANSACTION_QUALIFIERS_9F66_KRN ttqST = new TERMINAL_TRANSACTION_QUALIFIERS_9F66_KRN(db.Get(EMVTagsEnum.TERMINAL_TRANSACTION_QUALIFIERS_TTQ_9F66_KRN.Tag)); if (!ttqST.Value.EMVModeSupported) { return(BitConverter.GetBytes((int)ISO7816ReturnCodes.SW_CONDITIONS_OF_USE_NOT_SATISFIED)); } /* * Card action analysis */ /* * Card Risk Management Processing */ /* * Initialize data */ //Req H.2 (Initialization of Card Transaction Qualifiers) //The card shall reset CTQ byte 1 bits 8-7 to 00b (indicating Online PIN Not Required and Signature Not Required). CARD_TRANSACTION_QUALIFIERS_CTQ_9F6C_KRN3 ctq = new CARD_TRANSACTION_QUALIFIERS_CTQ_9F6C_KRN3(); ctq.Value.OnlinePINRequired = false; ctq.Value.SignatureRequired = false; ctq.Value.GoOnlineIfApplicationExpired = true; //hardcoded perso ctq.Value.GoOnlineIfOfflineDataAuthenticationFailsAndReaderIsOnlineCapable = true; //hardcoded perso //Req H.3 (Initialization of Issuer Application Data) //The card shall set the CVR to '03 80 00 00' (indicating Second GENERATE AC not requested) byte[] cvr = new byte[4]; cvr[Byte1] = 0x03; cvr[Byte2] = (byte)0x80; cvr[Byte3] = 0x00; cvr[Byte4] = 0x00; //Req H.4 (Initialization of Cryptogram Information Data) //The card shall reset the Cryptogram Information Data (CID) to '00'. byte[] cid_9F27 = new byte[1]; cid_9F27[Byte1] = 0x00; /* * Application Block Check */ //Req H.5 (Application Blocked Check) //If the application is blocked, then the card shall discontinue processing the command and shall respond with SW1 SW2 = '6985' /* * PIN tries exceeded check */ /* * Refunds and Credits Check */ /* * Reader Indicators Check */ /* * Cardholder Verification Method Check */ //Req H.6 (CVM Required Check) //If CVM Required by reader (TTQ byte 2 bit 7 is 1b), then a CVM is required for the //transaction, and the card shall determine the common CVM to be performed byte[] capDefault = PersoAndCardStateStorage.CARD_ADDITIONAL_PROCESSES_9F68_KRN.Value; if (ttqST.Value.CVMRequired) { //Req H.7 (Determine Common CVM) //If a CVM is required for the transaction, then the card shall attempt to select a //common CVM supported by both itself and the reader, as defined in this requirement. //If there is more than one CVM supported by both the card and the reader, the //selected CVM is chosen based on the following defined CVM hierarchy: 1) Online //PIN, 2) Signature. if (isCapPersonalized) { //If CVM Required by reader (TTQ byte 2 bit 7 is 1b), then a CVM is required for the //transaction, and the card shall determine the common CVM to be performed. if (ttqST.Value.CVMRequired) { //Online PIN supported by reader (TTQ byte 1 bit 3 is 1b) and either //Online PIN supported by card for domestic transactions (CAP byte 3 bit 8 is 1b) //or Online PIN supported by card for international transactions (CAP byte 3 bit 7 is 1b) if (ttqST.Value.OnlinePINSupported && Formatting.GetBitPosition(capDefault[Byte3], Bit8 + 1) || Formatting.GetBitPosition(capDefault[Byte3], Bit7 + 1)) { //Then the card shall indicate Online PIN Required (set CTQ byte 1 bit 8 to 1b) ctq.Value.OnlinePINRequired = true; //Else, if both of the following are true } else if (ttqST.Value.OfflineDataAuthenticationForOnlineAuthorizationsSupported && Formatting.GetBitPosition(capDefault[Byte3], Bit5 + 1)) { ctq.Value.GoOnlineIfOfflineDataAuthenticationFailsAndReaderIsOnlineCapable = true; } else { return(BitConverter.GetBytes((int)ISO7816ReturnCodes.SW_DATA_INVALID)); } } } else { //if Signature is not supported by the reader (TTQ byte 1 bit 2 is 0b), then the card //shall discontinue processing and respond to the GPO command with SW1 SW2 = '6984' if (!ttqST.Value.SignatureSupported) { return(BitConverter.GetBytes((int)ISO7816ReturnCodes.SW_DATA_INVALID)); } } } /* * Domestic Velocity Checking */ /* * International Velocity Checking */ /* * Contactless Transaction Counter Velocity Checking */ /* * Transaction Disposition */ //Req H.8 (Online) //Indicate Authorization Request Cryptogram returned (set CVR byte 2 bits 6-5 //to 10b and set CID bits 8-7 to 10b). Formatting.SetBitPosition(ref cvr[Byte2], true, Bit6 + 1); Formatting.SetBitPosition(ref cvr[Byte2], false, Bit5 + 1); Formatting.SetBitPosition(ref cid_9F27[Byte1], true, Bit8 + 1); Formatting.SetBitPosition(ref cid_9F27[Byte1], false, Bit7 + 1); //Increment the Application Transaction Counter (ATC) by one. The ATC shall be //incremented prior to the performance of any cryptographic operations. //If incrementing the ATC results in the ATC reaching its maximum value, then the //application shall be permanently blocked, Req 6.7 (Application Permanently //Blocked), and shall respond to the GPO command with error SW1 SW2 = '6985' //Increment the Application Transaction Counter (ATC) by one. The ATC shall be //incremented prior to the performance of any cryptographic operations TLV atc_9F36 = PersoAndCardStateStorage.APPLICATION_TRANSACTION_COUNTER_ATC_9F36_KRN; if (atc_9F36.Value[0] == 0xFF) { return(BitConverter.GetBytes((int)ISO7816ReturnCodes.SW_CONDITIONS_OF_USE_NOT_SATISFIED)); } else { if (atc_9F36.Value[1] == 0xFF) { atc_9F36.Value[0] = (byte)(atc_9F36.Value[0] + 1); atc_9F36.Value[1] = 0x00; } else { atc_9F36.Value[1] = (byte)(atc_9F36.Value[1] + 1); } } //Construct the Issuer Application Data. If an Issuer Discretionary Data Option (IDD //Option) is supported (see Appendix E), it shall be constructed and the MAC //generated (if applicable). //Only Option 0 supported, IDD already included during perso //If the card is capable of performing fDDA and all of the following are true: //the card supports fDDA for Online Authorizations (AIP byte 1 bit 6 is 1b for the //"Online (with ODA)" GPO response) //and ODA for Online Authorizations supported by card (CAP byte 2 bit 6 is 0b) //and ODA for Online Authorizations supported by reader (TTQ byte 1 bit 1 is 1b) //Then the card shall construct the Card Authentication Related Data and generate //the Signed Dynamic Application Data (tag '9F4B'). The Signed Dynamic //Application Data shall be generated as defined in Appendix A. byte[] iapDefault = PersoAndCardStateStorage.APPLICATION_INTERCHANGE_PROFILE_82_KRN.Value; if (Formatting.GetBitPosition(iapDefault[Byte1], Bit6 + 1) && !Formatting.GetBitPosition(capDefault[Byte2], Bit6 + 1) && ttqST.Value.OfflineDataAuthenticationForOnlineAuthorizationsSupported) { //fdda not supported return(BitConverter.GetBytes((int)ISO7816ReturnCodes.SW_CONDITIONS_OF_USE_NOT_SATISFIED)); } //Generate the Application Cryptogram //The path shall implement support for Cryptogram Version Number 10 and //Cryptogram Version Number 17, and may implement support for Cryptogram Version //Number 18 at implementer discretion. The Cryptogram Version to be used for //cryptogram generation shall be issuer configurable, and indicated by the issuer in the //Cryptogram Version Number of the Issuer Application Data returned for //transactions. //We use only 17 at this stage //if(tm_CVN[Byte1] != 0x11) // ISOException.throwIt((short) 0x6984); //9F10 IAD Byte 5 (IAD byte 5 is CVR byte 2) from card TLV iad_9F10 = PersoAndCardStateStorage.ISSUER_APPLICATION_DATA_9F10_KRN; iad_9F10.Value[Byte5] = cvr[Byte2]; //9F02 Amount, Authorized from terminal via pdol //9F37 Unpredictable Number from terminal via pdol //9F36 ATC from card //9F10 IAD Byte 5 (IAD byte 5 is CVR byte 2) from card byte[] crytogram_9F26 = generateCryptogram17(PersoAndCardStateStorage.ICC_MK, Formatting.ConcatArrays(amount, upn, atc_9F36.Value, new byte[] { iad_9F10.Value[Byte5] })); //If ODA for Online Authorizations supported by reader (TTQ byte 1 bit 1 is 1b), then //construct and send the GPO response in [EMV] Format 2 with the data shown in //Table 6-2 using Condition column "Online (with ODA)". EMVGetProcessingOptionsResponse response; //pdol sent back during select is 9F38 09 -> 9F02 06 9F37 04 9F66 04 ctq.Serialize(); db.AddToList(TLV.Create(EMVTagsEnum.AMOUNT_AUTHORISED_NUMERIC_9F02_KRN.Tag, amount)); db.AddToList(TLV.Create(EMVTagsEnum.UNPREDICTABLE_NUMBER_9F37_KRN.Tag, upn)); db.AddToList(TLV.Create(EMVTagsEnum.TERMINAL_TRANSACTION_QUALIFIERS_TTQ_9F66_KRN.Tag, ttq)); db.AddToList(TLV.Create(EMVTagsEnum.CARD_TRANSACTION_QUALIFIERS_CTQ_9F6C_KRN3.Tag, ctq.Value.Value)); db.AddToList(TLV.Create(EMVTagsEnum.CRYPTOGRAM_INFORMATION_DATA_9F27_KRN.Tag, cid_9F27)); db.AddToList(TLV.Create(EMVTagsEnum.APPLICATION_CRYPTOGRAM_9F26_KRN.Tag, crytogram_9F26)); TLV sdad = TLV.Create(EMVTagsEnum.SIGNED_DYNAMIC_APPLICATION_DATA_9F4B_KRN.Tag); sdad.Val.PackValue(15); db.AddToList(sdad); db.AddToList(atc_9F36); db.AddToList(iad_9F10); if (ttqST.Value.OfflineDataAuthenticationForOnlineAuthorizationsSupported) { //only diff is to add empty 9F4B SDAD response = genGPOResponse(true, db); } //Else (TTQ byte 1 bit 1 is 0b), construct and send the GPO response in [EMV] //Format 2 with the data shown in Table 6-2 using Condition column "Online and //Decline (without ODA)". else { response = genGPOResponse(false, db); } //Note: The Available Offline Spending Amount (tag '9F5D') is not supported return(response.Serialize()); }
private ApproverResponseBase DoEMVAuth(ApproverRequestBase requestIn) { EMVApproverRequest request = ((EMVApproverRequest)requestIn); CryptoMetaData cryptoMetaData = EMVDESSecurity.BuildCryptoMeta(request.EMV_Data); //Do additional checking here, e.g. customer balances etc //if decline set isApproved to false bool isApproved = true; //do we want to send back a pin change script, string newPin = "";// = "4315"; //decide whether to send 71 or 72 script template, 71 scripts applied before 2nd gen ac , 72 scripts applied after 2nd gen ac bool doPinChangeBefore = false; TLV _8A; string responseMessage; if (isApproved) { _8A = TLV.Create(EMVTagsEnum.AUTHORISATION_RESPONSE_CODE_8A_KRN.Tag, arcApproved); responseMessage = "Approved"; } else { _8A = TLV.Create(EMVTagsEnum.AUTHORISATION_RESPONSE_CODE_8A_KRN.Tag, arcDeclined); responseMessage = "Declined"; } TLV _91; byte[] arpc; //returns null if arqc cannot be verified if (cryptoMetaData.CryptoVersion == CrptoVersionEnum._18) { arpc = EMVDESSecurity.VerifyCryptogramGenARPC(request.EMV_Data, cryptoMetaData, PackCSU()); } else { arpc = EMVDESSecurity.VerifyCryptogramGenARPC(request.EMV_Data, cryptoMetaData, _8A.Value); } if (arpc != null) { _91 = Pack91(cryptoMetaData, arpc, _8A);// TLV.Create(EMVTagsEnum.ISSUER_AUTHENTICATION_DATA_91_KRN.Tag, Formatting.ConcatArrays(arpc, _8A.Value)); Logger.Log("Tx approved: " + isApproved + " ARQC passed, ARPC is " + Formatting.ByteArrayToHexString(arpc)); } else { isApproved = false; responseMessage = "Tx Declined: ARQC Failure"; _8A = TLV.Create(EMVTagsEnum.AUTHORISATION_RESPONSE_CODE_8A_KRN.Tag, new byte[] { 0x20, 0x20 }); _91 = Pack91(cryptoMetaData, arpc, _8A); //TLV.Create(EMVTagsEnum.ISSUER_AUTHENTICATION_DATA_91_KRN.Tag, new byte[8]); Logger.Log("ARQC failed"); } byte[] _86 = new byte[0]; //don't allow pin change if arqc could not be validated if (!string.IsNullOrWhiteSpace(newPin) && arpc != null) { try { TLV _9F26 = request.EMV_Data.Children.Get(EMVTagsEnum.APPLICATION_CRYPTOGRAM_9F26_KRN.Tag); if (_9F26 == null) { throw new Exception("No Cryptogram found"); } //TODO: for mchip we must increment the arqc by one for each subsequent command created _86 = EMVDESSecurity.CalculatePinChangeScript(request.EMV_Data, cryptoMetaData, newPin, _9F26.Value); } catch { _86 = new byte[0]; } } TLV _71TLV; TLV _72TLV; if (doPinChangeBefore) { _71TLV = TLV.Create(EMVTagsEnum.ISSUER_SCRIPT_TEMPLATE_1_71_KRN.Tag); _71TLV.Deserialize(Formatting.ConcatArrays(new byte[] { 0x71, (byte)_86.Length }, _86), 0); _72TLV = TLV.Create(EMVTagsEnum.ISSUER_SCRIPT_TEMPLATE_2_72_KRN.Tag); _72TLV.Deserialize(Formatting.ConcatArrays(new byte[] { 0x72, 0x00 }, new byte[0]), 0); } else { _72TLV = TLV.Create(EMVTagsEnum.ISSUER_SCRIPT_TEMPLATE_2_72_KRN.Tag); _72TLV.Deserialize(Formatting.ConcatArrays(new byte[] { 0x72, (byte)_86.Length }, _86), 0); _71TLV = TLV.Create(EMVTagsEnum.ISSUER_SCRIPT_TEMPLATE_1_71_KRN.Tag); _71TLV.Deserialize(Formatting.ConcatArrays(new byte[] { 0x71, 0x00 }, new byte[0]), 0); } return(new EMVApproverResponse() { IsApproved = isApproved, ResponseMessage = responseMessage, AuthCode_8A = _8A, IssuerAuthData_91 = _91, IssuerScriptTemplate_72 = _72TLV, IssuerScriptTemplate_71 = _71TLV, }); }
internal static IccPinKeyCertificate BuildAndValidatePublicKey(KernelDatabaseBase database, byte[] issuerPublicKeyModulus, byte[] issuerPublicKeyExponent) { TLV iccPinKeyCertificate = database.Get(EMVTagsEnum.INTEGRATED_CIRCUIT_CARD_ICC_PIN_ENCIPHERMENT_PUBLIC_KEY_CERTIFICATE_9F2D_KRN); if (iccPinKeyCertificate == null) { return(null); } TLV iccPinKeyExponent = database.Get(EMVTagsEnum.INTEGRATED_CIRCUIT_CARD_ICC_PIN_ENCIPHERMENT_PUBLIC_KEY_EXPONENT_9F2E_KRN); if (iccPinKeyExponent == null) { return(null); } TLV iccPinKeyRemainder = database.Get(EMVTagsEnum.INTEGRATED_CIRCUIT_CARD_ICC_PIN_ENCIPHERMENT_PUBLIC_KEY_REMAINDER_9F2F_KRN); if (iccPinKeyRemainder == null) { return(null); } if (iccPinKeyCertificate.Value.Length != issuerPublicKeyModulus.Length) { return(null); } byte[] decrypt = DecryptRSA(iccPinKeyCertificate.Value, issuerPublicKeyModulus, issuerPublicKeyExponent); IccPinKeyCertificate iccCertData = new IccPinKeyCertificate(decrypt, issuerPublicKeyModulus.Length, iccPinKeyRemainder == null ? new byte[0] : iccPinKeyRemainder.Value, iccPinKeyExponent.Value); if (iccCertData.RecoveredDataTrailer != 0xBC) { return(null); } if (iccCertData.RecoveredDataHeader != 0x6A) { return(null); } if (iccCertData.CertificateFormat != 0x04) { return(null); } if (!iccCertData.ValidateHash()) { return(null); } string pan = Formatting.ByteArrayToHexString(database.Get(EMVTagsEnum.APPLICATION_PRIMARY_ACCOUNT_NUMBER_PAN_5A_KRN).Value); string panToCompare = Formatting.ByteArrayToHexString(iccCertData.ApplicationPAN).Replace("FF", ""); if (!pan.StartsWith(panToCompare)) { return(null); } DateTime expiry = DateTime.ParseExact(Formatting.BcdToString(iccCertData.ExpiryDate), "MMyy", System.Globalization.CultureInfo.InvariantCulture); if (expiry <= DateTime.Now) { return(null); } if (iccCertData.PublicKeyAlgorithmIndicator != 0x01) { return(null); } if (iccPinKeyRemainder != null) { iccCertData.Modulus = Formatting.ConcatArrays(iccCertData.UnpaddedICCPinKeyorLeftmostDigitsofIssuerPublicKey, iccPinKeyRemainder.Value); } else { iccCertData.Modulus = iccCertData.UnpaddedICCPinKeyorLeftmostDigitsofIssuerPublicKey; } return(iccCertData); }