add tests for CcidTransceiver

This commit is contained in:
Vincent Breitmoser
2017-10-29 02:40:24 +02:00
parent 8b07428ec0
commit 0021c1f15f
5 changed files with 358 additions and 14 deletions

View File

@@ -23,6 +23,7 @@ import java.nio.ByteOrder;
import java.util.ArrayList;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.securitytoken.usb.tpdu.T1ShortApduProtocol;
@@ -54,6 +55,11 @@ abstract class CcidDescription {
public abstract int getProtocols();
public abstract int getFeatures();
@VisibleForTesting
static CcidDescription fromValues(byte maxSlotIndex, byte voltageSupport, int protocols, int features) {
return new AutoValue_CcidDescription(maxSlotIndex, voltageSupport, protocols, features);
}
@NonNull
static CcidDescription fromRawDescriptors(byte[] desc) throws UsbTransportException {
int dwProtocols = 0, dwFeatures = 0;

View File

@@ -33,6 +33,7 @@ import com.google.auto.value.AutoValue;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException.UsbCcidErrorException;
public class CcidTransceiver {
@@ -40,6 +41,7 @@ public class CcidTransceiver {
private static final int MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK = 0x80;
private static final int MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_ON = 0x62;
private static final int MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_OFF = 0x63;
private static final int MESSAGE_TYPE_PC_TO_RDR_XFR_BLOCK = 0x6f;
private static final int COMMAND_STATUS_SUCCESS = 0;
@@ -86,15 +88,20 @@ public class CcidTransceiver {
CcidDataBlock response = null;
for (CcidDescription.Voltage v : usbCcidDescription.getVoltages()) {
Log.v(Constants.TAG, "CCID: attempting to power on with voltage " + v.toString());
response = iccPowerOnVoltage(v.powerOnValue);
try {
response = iccPowerOnVoltage(v.powerOnValue);
} catch (UsbCcidErrorException e) {
if (e.getErrorResponse().getError() == 7) { // Power select error
Log.v(Constants.TAG, "CCID: failed to power on with voltage " + v.toString());
iccPowerOff();
Log.v(Constants.TAG, "CCID: powered off");
continue;
}
if (response.getStatus() == 1 && response.getError() == 7) { // Power select error
Log.v(Constants.TAG, "CCID: failed to power on with voltage " + v.toString());
iccPowerOff();
Log.v(Constants.TAG, "CCID: powered off");
} else {
break;
throw e;
}
break;
}
if (response == null) {
throw new UsbTransportException("Couldn't power up ICC2");
@@ -127,7 +134,7 @@ public class CcidTransceiver {
private void iccPowerOff() throws UsbTransportException {
byte sequenceNumber = currentSequenceNumber++;
final byte[] iccPowerCommand = {
0x63,
MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_OFF,
0x00, 0x00, 0x00, 0x00,
0x00,
sequenceNumber,
@@ -193,7 +200,7 @@ public class CcidTransceiver {
} while (response.isStatusTimeoutExtensionRequest());
if (!response.isStatusSuccess()) {
throw new UsbTransportException("USB-CCID error: " + response);
throw new UsbCcidErrorException("USB-CCID error!", response);
}
return response;
@@ -213,7 +220,6 @@ public class CcidTransceiver {
throw new UsbTransportException("USB-CCID error - bad CCID header type " + inputBuffer[0]);
}
CcidDataBlock result = CcidDataBlock.parseHeaderFromBytes(inputBuffer);
if (expectedSequenceNumber != result.getSeq()) {
@@ -235,6 +241,7 @@ public class CcidTransceiver {
}
result = result.withData(dataBuffer);
return result;
}

View File

@@ -17,6 +17,9 @@
package org.sufficientlysecure.keychain.securitytoken.usb;
import java.io.IOException;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
@@ -28,15 +31,13 @@ import android.support.annotation.Nullable;
import android.util.Pair;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.securitytoken.CommandApdu;
import org.sufficientlysecure.keychain.securitytoken.ResponseApdu;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TokenType;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TransportType;
import org.sufficientlysecure.keychain.securitytoken.Transport;
import org.sufficientlysecure.keychain.securitytoken.CommandApdu;
import org.sufficientlysecure.keychain.securitytoken.ResponseApdu;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
/**
* Based on USB CCID Specification rev. 1.1
* http://www.usb.org/developers/docs/devclass_docs/DWG_Smart-Card_CCID_Rev110.pdf
@@ -136,6 +137,7 @@ public class UsbTransport implements Transport {
}
CcidDescription ccidDescription = CcidDescription.fromRawDescriptors(usbConnection.getRawDescriptors());
Log.d(Constants.TAG, "CCID Description: " + ccidDescription);
CcidTransceiver transceiver = new CcidTransceiver(usbConnection, usbBulkIn, usbBulkOut, ccidDescription);
ccidTransportProtocol = ccidDescription.getSuitableTransportProtocol();

View File

@@ -19,6 +19,9 @@ package org.sufficientlysecure.keychain.securitytoken.usb;
import java.io.IOException;
import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransceiver.CcidDataBlock;
public class UsbTransportException extends IOException {
public UsbTransportException(String detailMessage) {
super(detailMessage);
@@ -31,4 +34,17 @@ public class UsbTransportException extends IOException {
public UsbTransportException(Throwable cause) {
super(cause);
}
static class UsbCcidErrorException extends UsbTransportException {
private CcidDataBlock errorResponse;
UsbCcidErrorException(String detailMessage, CcidDataBlock errorResponse) {
super(detailMessage + " " + errorResponse);
this.errorResponse = errorResponse;
}
CcidDataBlock getErrorResponse() {
return errorResponse;
}
}
}