private ResponseApdu RetransmitOnInsufficientBuffer(CommandApdu commandApdu, ResponseApdu responseApdu, out SCardPCI receivePci) { int receiveBufferLength; var resendCmdApdu = (CommandApdu)commandApdu.Clone(); if (responseApdu.SW2 == 0) { resendCmdApdu.Le = 0; // 256 receiveBufferLength = 256 + 2; // 2 bytes for status word } else { resendCmdApdu.Le = responseApdu.SW2; receiveBufferLength = responseApdu.SW2 + 2; // 2 bytes for status word } var receiveBuffer = new byte[receiveBufferLength]; receivePci = new SCardPCI(); try { var sendBuffer = resendCmdApdu.ToArray(); // Shall we wait until we re-send we APDU/TPDU? if (RetransmitWaitTime > 0) { Thread.Sleep(RetransmitWaitTime); } // send Command APDU again with new Le value return(SimpleTransmit( sendBuffer, sendBuffer.Length, resendCmdApdu.Case, resendCmdApdu.Protocol, receivePci, receiveBuffer, receiveBufferLength)); } catch (WinErrorInsufficientBufferException ex) { throw new InvalidApduException( $"Retransmission failed because of unsufficient buffer. Le={resendCmdApdu.Le}", ex); } catch (InvalidOperationException ex) { throw new InvalidApduException( $"Got SW1={responseApdu.SW1:X}. Retransmission failed because of an invalid APDU.", resendCmdApdu, ex); } }
public virtual Response Transmit(CommandApdu cmdApdu) { if (cmdApdu == null) throw new ArgumentNullException("cmdApdu"); // prepare send buffer (Check Command APDU and convert it to an byte array) byte[] sendbuf; try { sendbuf = cmdApdu.ToArray(); } catch (InvalidOperationException ex) { throw new InvalidApduException("Invalid APDU.", cmdApdu, ex); } catch (Exception ex) { throw ex; } // create Response object Response resp = new Response(); // prepare receive buffer (Response APDU) byte[] recvbuf = null; int recvbuflen = cmdApdu.ExpectedResponseLength; // expected size that shall be returned recvbuf = new byte[recvbuflen]; ResponseApdu respApdu = null; SCardPCI recvPci = new SCardPCI(); respApdu = _SimpleTransmit( sendbuf, sendbuf.Length, cmdApdu.Case, // ISO case used by the Command APDU cmdApdu.Protocol, // Protocol used by the Command APDU recvPci, ref recvbuf, ref recvbuflen); /* Check status word SW1SW2: * * 1. 0x6cxx -> Set response buffer size Le <- SW2 * 2. AND/OR 0x61xx -> More data can be read with GET RESPONSE */ // Case 1: SW1=0x6c, Previous Le/P3 not accepted -> Set le = SW2 if (respApdu.SW1 == (byte)SW1Code.ErrorP3Incorrect) { CommandApdu resendCmdApdu = (CommandApdu)cmdApdu.Clone(); if (respApdu.SW2 == 0) { resendCmdApdu.Le = 0; // 256 recvbuflen = 256 + 2; // 2 bytes for status word } else { resendCmdApdu.Le = respApdu.SW2; recvbuflen = respApdu.SW2 + 2; // 2 bytes for status word } recvbuf = new byte[recvbuflen]; recvPci = new SCardPCI(); try { sendbuf = resendCmdApdu.ToArray(); // Shall we wait until we re-send we APDU/TPDU? if (_retransmitWaitTime > 0) Thread.Sleep(_retransmitWaitTime); // send Command APDU again with new Le value respApdu = _SimpleTransmit( sendbuf, sendbuf.Length, resendCmdApdu.Case, resendCmdApdu.Protocol, recvPci, ref recvbuf, ref recvbuflen); } catch (InvalidOperationException ex) { throw new InvalidApduException("Got SW1=0x6c. Retransmission failed because of an invalid APDU.", resendCmdApdu, ex); } } // Case 2: SW1=0x61, More data available -> GET RESPONSE if (respApdu.SW1 == (byte)SW1Code.NormalDataResponse) { /* The transmission system shall issue a GET RESPONSE command APDU (or TPDU) * to the card by assigning the minimum of SW2 and Le to parameter Le (or P3)). * Le = min(Le,SW2) */ int _le = (cmdApdu.Le < respApdu.SW2) ? cmdApdu.Le : respApdu.SW2; do { // add the last ResponseAPDU to the Response object resp.AddResponseApdu(respApdu); resp.AddRecvPci(recvPci); CommandApdu getResponseApdu = _constructGetResponseApdu(ref _le); if (_le == 0) recvbuflen = 256 + 2; // 2 bytes for status word else recvbuflen = _le + 2; // 2 bytes for status word recvbuf = new byte[recvbuflen]; try { sendbuf = getResponseApdu.ToArray(); // Shall we wait until we re-send we APDU/TPDU? if (_retransmitWaitTime > 0) Thread.Sleep(_retransmitWaitTime); // send Command APDU again with new Le value respApdu = _SimpleTransmit( sendbuf, sendbuf.Length, getResponseApdu.Case, getResponseApdu.Protocol, recvPci, ref recvbuf, ref recvbuflen); } catch (InvalidOperationException ex) { throw new InvalidApduException("Got SW1=0x61. Retransmission failed because of an invalid GET RESPONSE APDU.", getResponseApdu, ex); } // In case there is more data available. _le = respApdu.SW2; } while ( // More data available. respApdu.SW1 == (byte)SW1Code.NormalDataResponse || // Warning condition: data may be corrupted. Iso7816-4 7.1.5 (respApdu.SW1 == (byte)SW1Code.WarningNVDataNotChanged && respApdu.SW2 == (byte)0x81) ); } resp.AddResponseApdu(respApdu); resp.AddRecvPci(recvPci); return resp; }
/// <summary>Transmits the specified command APDU.</summary> /// <param name="commandApdu">The command APDU.</param> /// <returns>A response containing one ore more <see cref="ResponseApdu" />.</returns> public virtual Response Transmit(CommandApdu commandApdu) { if (commandApdu == null) { throw new ArgumentNullException("commandApdu"); } // prepare send buffer (Check Command APDU and convert it to an byte array) byte[] sendBuffer; try { sendBuffer = commandApdu.ToArray(); } catch (InvalidOperationException exception) { throw new InvalidApduException("Invalid APDU.", commandApdu, exception); } // create Response object var response = new Response(); // prepare receive buffer (Response APDU) var receiveBufferLength = commandApdu.ExpectedResponseLength; // expected size that shall be returned var receiveBuffer = new byte[receiveBufferLength]; var receivePci = new SCardPCI(); var responseApdu = SimpleTransmit( sendBuffer, sendBuffer.Length, commandApdu.Case, // ISO case used by the Command APDU commandApdu.Protocol, // Protocol used by the Command APDU receivePci, ref receiveBuffer, ref receiveBufferLength); /* Check status word SW1SW2: * * 1. 0x6cxx -> Set response buffer size Le <- SW2 * 2. AND/OR 0x61xx -> More data can be read with GET RESPONSE */ if (responseApdu.SW1 == (byte)SW1Code.ErrorP3Incorrect) { // Case 1: SW1=0x6c, Previous Le/P3 not accepted -> Set le = SW2 var resendCmdApdu = (CommandApdu)commandApdu.Clone(); if (responseApdu.SW2 == 0) { resendCmdApdu.Le = 0; // 256 receiveBufferLength = 256 + 2; // 2 bytes for status word } else { resendCmdApdu.Le = responseApdu.SW2; receiveBufferLength = responseApdu.SW2 + 2; // 2 bytes for status word } receiveBuffer = new byte[receiveBufferLength]; receivePci = new SCardPCI(); try { sendBuffer = resendCmdApdu.ToArray(); // Shall we wait until we re-send we APDU/TPDU? if (RetransmitWaitTime > 0) { Thread.Sleep(RetransmitWaitTime); } // send Command APDU again with new Le value responseApdu = SimpleTransmit( sendBuffer, sendBuffer.Length, resendCmdApdu.Case, resendCmdApdu.Protocol, receivePci, ref receiveBuffer, ref receiveBufferLength); } catch (InvalidOperationException ex) { throw new InvalidApduException("Got SW1=0x6c. Retransmission failed because of an invalid APDU.", resendCmdApdu, ex); } } if (responseApdu.SW1 == (byte)SW1Code.NormalDataResponse) { // Case 2: SW1=0x61, More data available -> GET RESPONSE /* The transmission system shall issue a GET RESPONSE command APDU (or TPDU) * to the card by assigning the minimum of SW2 and Le to parameter Le (or P3)). * Le = min(Le,SW2) */ var le = (commandApdu.Le < responseApdu.SW2) ? commandApdu.Le : responseApdu.SW2; do { // add the last ResponseAPDU to the Response object response.Add(responseApdu); response.Add(receivePci); var getResponseApdu = ConstructGetResponseApdu(ref le); if (le == 0) { receiveBufferLength = 256 + 2; // 2 bytes for status word } else { receiveBufferLength = le + 2; // 2 bytes for status word } receiveBuffer = new byte[receiveBufferLength]; try { sendBuffer = getResponseApdu.ToArray(); // Shall we wait until we re-send we APDU/TPDU? if (RetransmitWaitTime > 0) { Thread.Sleep(RetransmitWaitTime); } // send Command APDU again with new Le value responseApdu = SimpleTransmit( sendBuffer, sendBuffer.Length, getResponseApdu.Case, getResponseApdu.Protocol, receivePci, ref receiveBuffer, ref receiveBufferLength); } catch (InvalidOperationException ex) { throw new InvalidApduException( "Got SW1=0x61. Retransmission failed because of an invalid GET RESPONSE APDU.", getResponseApdu, ex); } // In case there is more data available. le = responseApdu.SW2; } while ( // More data available. responseApdu.SW1 == (byte)SW1Code.NormalDataResponse || // Warning condition: data may be corrupted. Iso7816-4 7.1.5 (responseApdu.SW1 == (byte)SW1Code.WarningNVDataNotChanged && responseApdu.SW2 == 0x81)); } response.Add(responseApdu); response.Add(receivePci); return(response); }