public async Task <APDUResponse> ExchangeAPDUAsync(APDUCommand apdu) { var finalAPDU = new APDUCommand(apdu.ToByteArray()); if (SessionKeys != null) { byte[] apduMac = null; if (SessionKeys.SecurityLevel.HasFlag(DaplugSecurityLevel.COMMAND_MAC)) { finalAPDU.InstructionClass |= 0x04; var apduBytes = finalAPDU.ToByteArray(); apduBytes[4] += 0x08; apduMac = DaplugCrypto.CalculateApduMac(SessionKeys.CMacKey, apduBytes, SessionKeys.CMac); Array.Copy(apduMac, 0, SessionKeys.CMac, 0, 8); } if ((apdu.InstructionClass == 0x80 && apdu.InstructionCode == 0x82) == false) // Do not encrypt EXTERNAL_AUTHENTICATE APDU { if (SessionKeys.SecurityLevel.HasFlag(DaplugSecurityLevel.COMMAND_ENC)) { finalAPDU.CommandData = DaplugCrypto.EncryptAPDUData(SessionKeys, apdu); } } if (apduMac != null) { finalAPDU.CommandData = finalAPDU.CommandData.Concat(apduMac).ToArray(); } } Debug.WriteLine("=> " + BitConverter.ToString(finalAPDU.ToByteArray())); var response = await dongle.ExchangeAPDU(finalAPDU); if (SessionKeys != null && response.HasData) { byte[] responseMac = null; if (SessionKeys.SecurityLevel.HasFlag(DaplugSecurityLevel.RESPONSE_MAC)) { // extract MAC from the response (the last 8 bytes) responseMac = new byte[8]; Array.Copy(response.ResponseData, response.ResponseData.Length - 8, responseMac, 0, 8); //resize the response data to exclude the last 8 bytes (MAC) var tmpData = response.ResponseData; Array.Resize(ref tmpData, response.ResponseData.Length - 8); response.ResponseData = tmpData; } if (SessionKeys.SecurityLevel.HasFlag(DaplugSecurityLevel.RESPONSE_DEC)) { response.ResponseData = DaplugCrypto.DecryptAPDUResponse(SessionKeys, response.ResponseData); } if (SessionKeys.SecurityLevel.HasFlag(DaplugSecurityLevel.RESPONSE_MAC)) { //construct MAC input var apduCommandBytes = apdu.ToByteArray(); //command bytes + data length + data + sw1 & sw2 //var macInput = new byte[apduCommandBytes.Length + 1 + response.ResponseData.Length + 2]; byte[] macInput = apduCommandBytes.Concat(new byte[] { (byte)response.ResponseData.Length }) .Concat(response.ResponseData).Concat(new byte[] { response.SW1, response.SW2 }).ToArray(); byte[] calculatedResponseMac = DaplugCrypto.CalculateApduMac(SessionKeys.RMacKey, macInput, SessionKeys.RMac, true); if (calculatedResponseMac.SequenceEqual(responseMac) == false) { throw new DaplugAPIException("Secure Channel error: Invalid RMAC."); } Array.Copy(calculatedResponseMac, SessionKeys.RMac, 8); } } Debug.WriteLine("<= " + BitConverter.ToString(response.ToByteArray())); return(response); }
public async Task OpenSecureChannelAsync(DaplugKeySet keyset, DaplugSecurityLevel securityLevel, byte[] diversifier = null, byte[] hostChallenge = null) { if (keyset.EncKey == null || keyset.MacKey == null || keyset.DeKey == null) { throw new DaplugAPIException("Invalid keyset."); } if (hostChallenge == null) { Random rnd = new Random(); hostChallenge = new byte[8]; rnd.NextBytes(hostChallenge); } var authCommandHeader = new byte[] { 0x80, 0x50, keyset.Version, 0x00, 0x00 }; var authCommand = new APDUCommand(authCommandHeader, hostChallenge); var response = await ExchangeAPDUAsync(authCommand); if (response.IsSuccessfulResponse == false) { throw new DaplugAPIException("INITIALIZE UPDATE failed.", response.SW1, response.SW2); } byte[] counter = new byte[2]; byte[] cardChallenge = new byte[8]; byte[] cardCryptogram = new byte[8]; Array.Copy(response.ResponseData, 12, counter, 0, 2); Array.Copy(response.ResponseData, 12, cardChallenge, 0, 8); Array.Copy(response.ResponseData, 20, cardCryptogram, 0, 8); var tempSessionKeys = DaplugCrypto.ComputeSessionKeys(keyset, counter); var computedCardCryptogram = DaplugCrypto.CalculateCryptogram(tempSessionKeys, hostChallenge, cardChallenge); if (computedCardCryptogram.SequenceEqual(cardCryptogram) == false) { throw new DaplugAPIException("Invalid card cryptogram."); } var hostCryptogram = DaplugCrypto.CalculateCryptogram(tempSessionKeys, cardChallenge, hostChallenge); if (securityLevel.HasFlag(DaplugSecurityLevel.COMMAND_MAC) == false) { securityLevel |= DaplugSecurityLevel.COMMAND_MAC; } tempSessionKeys.SecurityLevel = securityLevel; SessionKeys = tempSessionKeys; var extAuthCommandHeader = new byte[] { 0x80, 0x82, (byte)SessionKeys.SecurityLevel, 0x00, 0x00 }; var extAuthCommand = new APDUCommand(extAuthCommandHeader, hostCryptogram); var extAuthResponse = await ExchangeAPDUAsync(extAuthCommand); if (extAuthResponse.IsSuccessfulResponse == false) { SessionKeys = null; throw new DaplugAPIException("EXTERNAL AUTHENTICATE failed.", response.SW1, response.SW2); } Array.Copy(SessionKeys.CMac, SessionKeys.RMac, 8); }