From 7a24f4c237f44c229debd37c3acb2ec71ca71b00 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 7 Oct 2017 18:22:09 +0200 Subject: [PATCH 1/5] refactor CcidTransceiver --- .../securitytoken/usb/CcidTransceiver.java | 248 ++++++++++++------ .../usb/T1ShortApduProtocol.java | 14 +- .../usb/tpdu/T1TpduProtocol.java | 42 ++- 3 files changed, 189 insertions(+), 115 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransceiver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransceiver.java index bf6711474..f5a726f1d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransceiver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransceiver.java @@ -21,66 +21,68 @@ import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.os.SystemClock; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; +import com.google.auto.value.AutoValue; import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.encoders.Hex; +import org.sufficientlysecure.keychain.Constants; import java.nio.ByteBuffer; import java.nio.ByteOrder; public class CcidTransceiver { + private static final int CCID_HEADER_LENGTH = 10; + + 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_XFR_BLOCK = 0x6f; + + private static final int COMMAND_STATUS_SUCCESS = 0; + private static final int COMMAND_STATUS_TIME_EXTENSION_RQUESTED = 2; + + private static final int SLOT_NUMBER = 0x00; + + private static final int ICC_STATUS_SUCCESS = 0; + private static final int TIMEOUT = 20 * 1000; // 20s - private byte mCounter; - private UsbDeviceConnection mConnection; - private UsbEndpoint mBulkIn; - private UsbEndpoint mBulkOut; + private final UsbDeviceConnection usbConnection; + private final UsbEndpoint usbBulkIn; + private final UsbEndpoint usbBulkOut; - public CcidTransceiver(final UsbDeviceConnection connection, final UsbEndpoint bulkIn, - final UsbEndpoint bulkOut) { + private byte currentSequenceNumber; - mConnection = connection; - mBulkIn = bulkIn; - mBulkOut = bulkOut; - } - - public byte[] receiveRaw() throws UsbTransportException { - byte[] bytes; - do { - bytes = receive(); - } while (isDataBlockNotReady(bytes)); - - checkDataBlockResponse(bytes); - - return Arrays.copyOfRange(bytes, 10, bytes.length); + CcidTransceiver(UsbDeviceConnection connection, UsbEndpoint bulkIn, UsbEndpoint bulkOut) { + usbConnection = connection; + usbBulkIn = bulkIn; + usbBulkOut = bulkOut; } /** * Power of ICC * Spec: 6.1.1 PC_to_RDR_IccPowerOn - * - * @throws UsbTransportException */ @NonNull - public byte[] iccPowerOn() throws UsbTransportException { + public CcidDataBlock iccPowerOn() throws UsbTransportException { + byte sequenceNumber = currentSequenceNumber++; final byte[] iccPowerCommand = { - 0x62, + MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_ON, 0x00, 0x00, 0x00, 0x00, - 0x00, - mCounter++, - 0x00, - 0x00, 0x00 + SLOT_NUMBER, + sequenceNumber, + 0x00, // voltage select = auto + 0x00, 0x00 // reserved for future use }; - sendRaw(iccPowerCommand); + sendRaw(iccPowerCommand, 0, iccPowerCommand.length); long startTime = System.currentTimeMillis(); - byte[] atr = null; while (true) { try { - atr = receiveRaw(); - break; + return receiveDataBlock(sequenceNumber); } catch (Exception e) { + Log.e(Constants.TAG, "Error waiting for device power on", e); // Try more startTime if (System.currentTimeMillis() - startTime > TIMEOUT) { break; @@ -89,81 +91,155 @@ public class CcidTransceiver { SystemClock.sleep(100); } - if (atr == null) { - throw new UsbTransportException("Couldn't power up Security Token"); - } - - return atr; + throw new UsbTransportException("Couldn't power up Security Token"); } /** * Transmits XfrBlock * 6.1.4 PC_to_RDR_XfrBlock + * * @param payload payload to transmit - * @throws UsbTransportException */ - public void sendXfrBlock(byte[] payload) throws UsbTransportException { + public CcidDataBlock sendXfrBlock(byte[] payload) throws UsbTransportException { int l = payload.length; - byte[] data = Arrays.concatenate(new byte[]{ - 0x6f, - (byte) l, (byte) (l >> 8), (byte) (l >> 16), (byte) (l >> 24), - 0x00, - mCounter++, - 0x00, - 0x00, 0x00}, - payload); + byte sequenceNumber = currentSequenceNumber++; + byte[] headerData = { + MESSAGE_TYPE_PC_TO_RDR_XFR_BLOCK, + (byte) l, (byte) (l >> 8), (byte) (l >> 16), (byte) (l >> 24), + SLOT_NUMBER, + sequenceNumber, + 0x00, // block waiting time + 0x00, 0x00 // level parameters + }; + byte[] data = Arrays.concatenate(headerData, payload); - int send = 0; - while (send < data.length) { - final int len = Math.min(mBulkIn.getMaxPacketSize(), data.length - send); - sendRaw(Arrays.copyOfRange(data, send, send + len)); - send += len; + int sentBytes = 0; + while (sentBytes < data.length) { + int bytesToSend = Math.min(usbBulkIn.getMaxPacketSize(), data.length - sentBytes); + sendRaw(data, sentBytes, bytesToSend); + sentBytes += bytesToSend; } + + return receiveDataBlock(sequenceNumber); } - public byte[] receive() throws UsbTransportException { - byte[] buffer = new byte[mBulkIn.getMaxPacketSize()]; - byte[] result = null; - int readBytes = 0, totalBytes = 0; - + private CcidDataBlock receiveDataBlock(byte expectedSequenceNumber) throws UsbTransportException { + CcidDataBlock response; do { - int res = mConnection.bulkTransfer(mBulkIn, buffer, buffer.length, TIMEOUT); - if (res < 0) { - throw new UsbTransportException("USB error - failed to receive response " + res); - } - if (result == null) { - if (res < 10) { - throw new UsbTransportException("USB-CCID error - failed to receive CCID header"); - } - totalBytes = ByteBuffer.wrap(buffer, 1, 4).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().get() + 10; - result = new byte[totalBytes]; - } - System.arraycopy(buffer, 0, result, readBytes, res); - readBytes += res; - } while (readBytes < totalBytes); + response = receiveDataBlockImmediate(expectedSequenceNumber); + } while (response.isStatusTimeoutExtensionRequest()); + if (!response.isStatusSuccess()) { + throw new UsbTransportException("USB-CCID error: " + response); + } + + return response; + } + + private CcidDataBlock receiveDataBlockImmediate(byte expectedSequenceNumber) throws UsbTransportException { + byte[] buffer = new byte[usbBulkIn.getMaxPacketSize()]; + + int readBytes = usbConnection.bulkTransfer(usbBulkIn, buffer, buffer.length, TIMEOUT); + if (readBytes < CCID_HEADER_LENGTH) { + throw new UsbTransportException("USB-CCID error - failed to receive CCID header"); + } + if (buffer[0] != (byte) MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK) { + throw new UsbTransportException("USB-CCID error - bad CCID header type " + buffer[0]); + } + + CcidDataBlock result = CcidDataBlock.parseHeaderFromBytes(buffer); + + if (expectedSequenceNumber != result.getSeq()) { + throw new UsbTransportException("USB-CCID error - expected sequence number " + + expectedSequenceNumber + ", got " + result); + } + + byte[] dataBuffer = new byte[result.getDataLength()]; + int bufferedBytes = readBytes - CCID_HEADER_LENGTH; + System.arraycopy(buffer, CCID_HEADER_LENGTH, dataBuffer, 0, bufferedBytes); + + while (bufferedBytes < dataBuffer.length) { + readBytes = usbConnection.bulkTransfer(usbBulkIn, buffer, buffer.length, TIMEOUT); + if (readBytes < 0) { + throw new UsbTransportException("USB error - failed reading response data! Header: " + result); + } + System.arraycopy(buffer, 0, dataBuffer, bufferedBytes, readBytes); + bufferedBytes += readBytes; + } + + result = result.withData(dataBuffer); return result; } - private void sendRaw(final byte[] data) throws UsbTransportException { - final int tr1 = mConnection.bulkTransfer(mBulkOut, data, data.length, TIMEOUT); - if (tr1 != data.length) { - throw new UsbTransportException("USB error - failed to transmit data " + tr1); + private void sendRaw(byte[] data, int offset, int length) throws UsbTransportException { + int tr1; + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) { + tr1 = usbConnection.bulkTransfer(usbBulkOut, data, offset, length, TIMEOUT); + } else { + byte[] dataToSend = Arrays.copyOfRange(data, offset, offset+length); + tr1 = usbConnection.bulkTransfer(usbBulkOut, dataToSend, dataToSend.length, TIMEOUT); + } + + if (tr1 != length) { + throw new UsbTransportException("USB error - failed to transmit data (" + tr1 + "/" + length + ")"); } } - private static byte getStatus(byte[] bytes) { - return (byte) ((bytes[7] >> 6) & 0x03); - } + /** Corresponds to 6.2.1 RDR_to_PC_DataBlock. */ + @AutoValue + public abstract static class CcidDataBlock { + public abstract int getDataLength(); + public abstract byte getSlot(); + public abstract byte getSeq(); + public abstract byte getStatus(); + public abstract byte getError(); + public abstract byte getChainParameter(); + @Nullable + public abstract byte[] getData(); - private void checkDataBlockResponse(byte[] bytes) throws UsbTransportException { - final byte status = getStatus(bytes); - if (status != 0) { - throw new UsbTransportException("USB-CCID error - status " + status + " error code: " + Hex.toHexString(bytes, 8, 1)); + static CcidDataBlock parseHeaderFromBytes(byte[] headerBytes) { + ByteBuffer buf = ByteBuffer.wrap(headerBytes); + buf.order(ByteOrder.LITTLE_ENDIAN); + + byte type = buf.get(); + if (type != (byte) MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK) { + throw new IllegalArgumentException("Header has incorrect type value!"); + } + int dwLength = buf.getInt(); + byte bSlot = buf.get(); + byte bSeq = buf.get(); + byte bStatus = buf.get(); + byte bError = buf.get(); + byte bChainParameter = buf.get(); + + return new AutoValue_CcidTransceiver_CcidDataBlock( + dwLength, bSlot, bSeq, bStatus, bError, bChainParameter, null); } - } - private static boolean isDataBlockNotReady(byte[] bytes) { - return getStatus(bytes) == 2; + CcidDataBlock withData(byte[] data) { + if (getData() != null) { + throw new IllegalStateException("Cannot add data to this class twice!"); + } + + return new AutoValue_CcidTransceiver_CcidDataBlock( + getDataLength(), getSlot(), getSeq(), getStatus(), getError(), getChainParameter(), data); + } + + byte getIccStatus() { + return (byte) (getStatus() & 0x03); + } + + byte getCommandStatus() { + return (byte) ((getStatus() >> 6) & 0x03); + } + + boolean isStatusTimeoutExtensionRequest() { + return getCommandStatus() == COMMAND_STATUS_TIME_EXTENSION_RQUESTED; + } + + boolean isStatusSuccess() { + return getIccStatus() == ICC_STATUS_SUCCESS && getCommandStatus() == COMMAND_STATUS_SUCCESS; + } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/T1ShortApduProtocol.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/T1ShortApduProtocol.java index 883f11cef..539762d03 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/T1ShortApduProtocol.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/T1ShortApduProtocol.java @@ -19,23 +19,23 @@ package org.sufficientlysecure.keychain.securitytoken.usb; import android.support.annotation.NonNull; -import org.bouncycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransceiver.CcidDataBlock; import org.sufficientlysecure.keychain.util.Log; -public class T1ShortApduProtocol implements CcidTransportProtocol { +class T1ShortApduProtocol implements CcidTransportProtocol { private CcidTransceiver mTransceiver; - public T1ShortApduProtocol(CcidTransceiver transceiver) throws UsbTransportException { + T1ShortApduProtocol(CcidTransceiver transceiver) throws UsbTransportException { mTransceiver = transceiver; - byte[] atr = mTransceiver.iccPowerOn(); - Log.d(Constants.TAG, "Usb transport connected T1/Short APDU, ATR=" + Hex.toHexString(atr)); + CcidDataBlock atr = mTransceiver.iccPowerOn(); + Log.d(Constants.TAG, "Usb transport connected T1/Short APDU, ATR=" + atr); } @Override public byte[] transceive(@NonNull final byte[] apdu) throws UsbTransportException { - mTransceiver.sendXfrBlock(apdu); - return mTransceiver.receiveRaw(); + CcidDataBlock response = mTransceiver.sendXfrBlock(apdu); + return response.getData(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java index 2d0b4c383..4d74a312f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java @@ -20,9 +20,9 @@ package org.sufficientlysecure.keychain.securitytoken.usb.tpdu; import android.support.annotation.NonNull; import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransceiver; +import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransceiver.CcidDataBlock; import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransportProtocol; import org.sufficientlysecure.keychain.util.Log; @@ -31,30 +31,28 @@ public class T1TpduProtocol implements CcidTransportProtocol { private final static int MAX_FRAME_LEN = 254; private byte mCounter = 0; - private CcidTransceiver mTransceiver; - private BlockChecksumType mChecksumType; + private final CcidTransceiver ccidTransceiver; + private final BlockChecksumType checksumType; - public T1TpduProtocol(final CcidTransceiver transceiver) throws UsbTransportException { - mTransceiver = transceiver; + public T1TpduProtocol(CcidTransceiver transceiver) throws UsbTransportException { + ccidTransceiver = transceiver; // Connect - byte[] atr = mTransceiver.iccPowerOn(); - Log.d(Constants.TAG, "Usb transport connected T1/TPDU, ATR=" + Hex.toHexString(atr)); + CcidDataBlock response = ccidTransceiver.iccPowerOn(); + Log.d(Constants.TAG, "Usb transport connected T1/TPDU, ATR=" + response); + // TODO: set checksum from atr - mChecksumType = BlockChecksumType.LRC; + checksumType = BlockChecksumType.LRC; // PPS all auto pps(); } - protected void pps() throws UsbTransportException { + private void pps() throws UsbTransportException { byte[] pps = new byte[]{(byte) 0xFF, 1, (byte) (0xFF ^ 1)}; - mTransceiver.sendXfrBlock(pps); - - byte[] ppsResponse = mTransceiver.receiveRaw(); - - Log.d(Constants.TAG, "PPS response " + Hex.toHexString(ppsResponse)); + CcidDataBlock response = ccidTransceiver.sendXfrBlock(pps); + Log.d(Constants.TAG, "PPS response " + response); } public byte[] transceive(@NonNull byte[] apdu) throws UsbTransportException { @@ -72,10 +70,10 @@ public class T1TpduProtocol implements CcidTransportProtocol { // Send next frame Block block = newIBlock(mCounter++, hasMore, Arrays.copyOfRange(apdu, start, start + len)); - mTransceiver.sendXfrBlock(block.getRawData()); + CcidDataBlock response = ccidTransceiver.sendXfrBlock(block.getRawData()); // Receive I or R block - responseBlock = getBlockFromResponse(mTransceiver.receiveRaw()); + responseBlock = getBlockFromResponse(response); start += len; @@ -104,9 +102,9 @@ public class T1TpduProtocol implements CcidTransportProtocol { while (((IBlock) responseBlock).getChaining()) { Block ackBlock = newRBlock((byte) (((IBlock) responseBlock).getSequence() + 1)); - mTransceiver.sendXfrBlock(ackBlock.getRawData()); + CcidDataBlock response = ccidTransceiver.sendXfrBlock(ackBlock.getRawData()); - responseBlock = getBlockFromResponse(mTransceiver.receiveRaw()); + responseBlock = getBlockFromResponse(response); if (responseBlock instanceof IBlock) { responseApdu = Arrays.concatenate(responseApdu, responseBlock.getApdu()); @@ -120,8 +118,8 @@ public class T1TpduProtocol implements CcidTransportProtocol { } // Factory methods - public Block getBlockFromResponse(byte[] data) throws UsbTransportException { - final Block baseBlock = new Block(mChecksumType, data); + public Block getBlockFromResponse(CcidDataBlock dataBlock) throws UsbTransportException { + final Block baseBlock = new Block(checksumType, dataBlock.getData()); if ((baseBlock.getPcb() & IBlock.MASK_RBLOCK) == IBlock.MASK_VALUE_RBLOCK) { return new IBlock(baseBlock); @@ -135,10 +133,10 @@ public class T1TpduProtocol implements CcidTransportProtocol { } public IBlock newIBlock(byte sequence, boolean chaining, byte[] apdu) throws UsbTransportException { - return new IBlock(mChecksumType, (byte) 0, sequence, chaining, apdu); + return new IBlock(checksumType, (byte) 0, sequence, chaining, apdu); } public RBlock newRBlock(byte sequence) throws UsbTransportException { - return new RBlock(mChecksumType, (byte) 0, sequence); + return new RBlock(checksumType, (byte) 0, sequence); } } From 31da4c450dd13dcbc3d6cfc648c99cf215d40239 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 8 Oct 2017 01:05:43 +0200 Subject: [PATCH 2/5] Ignore leading data before IccPowerOn operation in CcidTransceiver --- .../securitytoken/usb/CcidTransceiver.java | 93 ++++++++++++------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransceiver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransceiver.java index f5a726f1d..08c7be922 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransceiver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransceiver.java @@ -17,20 +17,23 @@ package org.sufficientlysecure.keychain.securitytoken.usb; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.os.SystemClock; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.WorkerThread; import android.util.Log; import com.google.auto.value.AutoValue; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - public class CcidTransceiver { private static final int CCID_HEADER_LENGTH = 10; @@ -45,18 +48,24 @@ public class CcidTransceiver { private static final int ICC_STATUS_SUCCESS = 0; - private static final int TIMEOUT = 20 * 1000; // 20s + private static final int DEVICE_COMMUNICATE_TIMEOUT_MILLIS = 5000; + private static final int DEVICE_SKIP_TIMEOUT_MILLIS = 100; + private final UsbDeviceConnection usbConnection; private final UsbEndpoint usbBulkIn; private final UsbEndpoint usbBulkOut; + private final byte[] inputBuffer; private byte currentSequenceNumber; + CcidTransceiver(UsbDeviceConnection connection, UsbEndpoint bulkIn, UsbEndpoint bulkOut) { usbConnection = connection; usbBulkIn = bulkIn; usbBulkOut = bulkOut; + + inputBuffer = new byte[usbBulkIn.getMaxPacketSize()]; } /** @@ -64,7 +73,12 @@ public class CcidTransceiver { * Spec: 6.1.1 PC_to_RDR_IccPowerOn */ @NonNull - public CcidDataBlock iccPowerOn() throws UsbTransportException { + @WorkerThread + public synchronized CcidDataBlock iccPowerOn() throws UsbTransportException { + long startTime = SystemClock.elapsedRealtime(); + + skipAvailableInput(); + byte sequenceNumber = currentSequenceNumber++; final byte[] iccPowerCommand = { MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_ON, @@ -76,22 +90,12 @@ public class CcidTransceiver { }; sendRaw(iccPowerCommand, 0, iccPowerCommand.length); + CcidDataBlock response = receiveDataBlock(sequenceNumber); - long startTime = System.currentTimeMillis(); - while (true) { - try { - return receiveDataBlock(sequenceNumber); - } catch (Exception e) { - Log.e(Constants.TAG, "Error waiting for device power on", e); - // Try more startTime - if (System.currentTimeMillis() - startTime > TIMEOUT) { - break; - } - } - SystemClock.sleep(100); - } + long elapsedTime = SystemClock.elapsedRealtime() - startTime; + Log.d(Constants.TAG, "USB IccPowerOn call took " + elapsedTime + "ms"); - throw new UsbTransportException("Couldn't power up Security Token"); + return response; } /** @@ -100,7 +104,10 @@ public class CcidTransceiver { * * @param payload payload to transmit */ - public CcidDataBlock sendXfrBlock(byte[] payload) throws UsbTransportException { + @WorkerThread + public synchronized CcidDataBlock sendXfrBlock(byte[] payload) throws UsbTransportException { + long startTime = SystemClock.elapsedRealtime(); + int l = payload.length; byte sequenceNumber = currentSequenceNumber++; byte[] headerData = { @@ -120,7 +127,24 @@ public class CcidTransceiver { sentBytes += bytesToSend; } - return receiveDataBlock(sequenceNumber); + CcidDataBlock ccidDataBlock = receiveDataBlock(sequenceNumber); + + long elapsedTime = SystemClock.elapsedRealtime() - startTime; + Log.d(Constants.TAG, "USB XferBlock call took " + elapsedTime + "ms"); + + return ccidDataBlock; + } + + private void skipAvailableInput() { + int ignoredBytes; + do { + ignoredBytes = usbConnection.bulkTransfer( + usbBulkIn, inputBuffer, inputBuffer.length, DEVICE_SKIP_TIMEOUT_MILLIS); + if (ignoredBytes > 0) { + Log.e(Constants.TAG, + "Skipped " + ignoredBytes + " bytes: " + Hex.toHexString(inputBuffer, 0, ignoredBytes)); + } + } while (ignoredBytes > 0); } private CcidDataBlock receiveDataBlock(byte expectedSequenceNumber) throws UsbTransportException { @@ -137,17 +161,21 @@ public class CcidTransceiver { } private CcidDataBlock receiveDataBlockImmediate(byte expectedSequenceNumber) throws UsbTransportException { - byte[] buffer = new byte[usbBulkIn.getMaxPacketSize()]; - - int readBytes = usbConnection.bulkTransfer(usbBulkIn, buffer, buffer.length, TIMEOUT); + int readBytes = usbConnection.bulkTransfer(usbBulkIn, inputBuffer, inputBuffer.length, DEVICE_COMMUNICATE_TIMEOUT_MILLIS); if (readBytes < CCID_HEADER_LENGTH) { throw new UsbTransportException("USB-CCID error - failed to receive CCID header"); } - if (buffer[0] != (byte) MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK) { - throw new UsbTransportException("USB-CCID error - bad CCID header type " + buffer[0]); + if (inputBuffer[0] != (byte) MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK) { + if (expectedSequenceNumber != inputBuffer[6]) { + throw new UsbTransportException("USB-CCID error - bad CCID header, type " + inputBuffer[0] + " (expected " + + MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK + "), sequence number " + inputBuffer[6] + " (expected " + + expectedSequenceNumber + ")"); + } + + throw new UsbTransportException("USB-CCID error - bad CCID header type " + inputBuffer[0]); } - CcidDataBlock result = CcidDataBlock.parseHeaderFromBytes(buffer); + CcidDataBlock result = CcidDataBlock.parseHeaderFromBytes(inputBuffer); if (expectedSequenceNumber != result.getSeq()) { throw new UsbTransportException("USB-CCID error - expected sequence number " + @@ -156,14 +184,14 @@ public class CcidTransceiver { byte[] dataBuffer = new byte[result.getDataLength()]; int bufferedBytes = readBytes - CCID_HEADER_LENGTH; - System.arraycopy(buffer, CCID_HEADER_LENGTH, dataBuffer, 0, bufferedBytes); + System.arraycopy(inputBuffer, CCID_HEADER_LENGTH, dataBuffer, 0, bufferedBytes); while (bufferedBytes < dataBuffer.length) { - readBytes = usbConnection.bulkTransfer(usbBulkIn, buffer, buffer.length, TIMEOUT); + readBytes = usbConnection.bulkTransfer(usbBulkIn, inputBuffer, inputBuffer.length, DEVICE_COMMUNICATE_TIMEOUT_MILLIS); if (readBytes < 0) { throw new UsbTransportException("USB error - failed reading response data! Header: " + result); } - System.arraycopy(buffer, 0, dataBuffer, bufferedBytes, readBytes); + System.arraycopy(inputBuffer, 0, dataBuffer, bufferedBytes, readBytes); bufferedBytes += readBytes; } @@ -173,12 +201,11 @@ public class CcidTransceiver { private void sendRaw(byte[] data, int offset, int length) throws UsbTransportException { int tr1; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) { - tr1 = usbConnection.bulkTransfer(usbBulkOut, data, offset, length, TIMEOUT); + tr1 = usbConnection.bulkTransfer(usbBulkOut, data, offset, length, DEVICE_COMMUNICATE_TIMEOUT_MILLIS); } else { byte[] dataToSend = Arrays.copyOfRange(data, offset, offset+length); - tr1 = usbConnection.bulkTransfer(usbBulkOut, dataToSend, dataToSend.length, TIMEOUT); + tr1 = usbConnection.bulkTransfer(usbBulkOut, dataToSend, dataToSend.length, DEVICE_COMMUNICATE_TIMEOUT_MILLIS); } if (tr1 != length) { From 6cc058e25f26f6ef1e9c72b2630963ff6c83ca85 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 8 Oct 2017 02:05:29 +0200 Subject: [PATCH 3/5] some more cleanup in usb classes --- .../securitytoken/usb/CcidTransceiver.java | 4 +- .../usb/CcidTransportProtocol.java | 1 + .../usb/T1ShortApduProtocol.java | 12 +- .../securitytoken/usb/UsbTransport.java | 299 +++++++++--------- .../usb/tpdu/T1TpduProtocol.java | 31 +- .../ui/base/BaseSecurityTokenActivity.java | 17 +- 6 files changed, 185 insertions(+), 179 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransceiver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransceiver.java index 08c7be922..471cefbbf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransceiver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransceiver.java @@ -93,7 +93,9 @@ public class CcidTransceiver { CcidDataBlock response = receiveDataBlock(sequenceNumber); long elapsedTime = SystemClock.elapsedRealtime() - startTime; - Log.d(Constants.TAG, "USB IccPowerOn call took " + elapsedTime + "ms"); + + Log.d(Constants.TAG, "Usb transport connected T1/TPDU, took " + elapsedTime + "ms, ATR=" + + Hex.toHexString(response.getData())); return response; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransportProtocol.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransportProtocol.java index 5cfc3586d..ebe5daf9c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransportProtocol.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidTransportProtocol.java @@ -20,5 +20,6 @@ package org.sufficientlysecure.keychain.securitytoken.usb; import android.support.annotation.NonNull; public interface CcidTransportProtocol { + void connect(@NonNull CcidTransceiver transceiver) throws UsbTransportException; byte[] transceive(@NonNull byte[] apdu) throws UsbTransportException; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/T1ShortApduProtocol.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/T1ShortApduProtocol.java index 539762d03..caee33c43 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/T1ShortApduProtocol.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/T1ShortApduProtocol.java @@ -24,18 +24,16 @@ import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransceiver.CcidDat import org.sufficientlysecure.keychain.util.Log; class T1ShortApduProtocol implements CcidTransportProtocol { - private CcidTransceiver mTransceiver; + private CcidTransceiver ccidTransceiver; - T1ShortApduProtocol(CcidTransceiver transceiver) throws UsbTransportException { - mTransceiver = transceiver; - - CcidDataBlock atr = mTransceiver.iccPowerOn(); - Log.d(Constants.TAG, "Usb transport connected T1/Short APDU, ATR=" + atr); + public void connect(@NonNull CcidTransceiver transceiver) throws UsbTransportException { + ccidTransceiver = transceiver; + ccidTransceiver.iccPowerOn(); } @Override public byte[] transceive(@NonNull final byte[] apdu) throws UsbTransportException { - CcidDataBlock response = mTransceiver.sendXfrBlock(apdu); + CcidDataBlock response = ccidTransceiver.sendXfrBlock(apdu); return response.getData(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java index ed42b9987..23ee412f6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java @@ -54,20 +54,154 @@ public class UsbTransport implements Transport { private static final int MASK_EXTENDED_APDU = 0x40000; - private final UsbManager mUsbManager; - private final UsbDevice mUsbDevice; - private UsbInterface mUsbInterface; - private UsbEndpoint mBulkIn; - private UsbEndpoint mBulkOut; - private UsbDeviceConnection mConnection; - private CcidTransceiver mTransceiver; - private CcidTransportProtocol mProtocol; + private final UsbDevice usbDevice; + private final UsbManager usbManager; + + private UsbDeviceConnection usbConnection; + private UsbInterface usbInterface; + private CcidTransportProtocol ccidTransportProtocol; public UsbTransport(UsbDevice usbDevice, UsbManager usbManager) { - mUsbDevice = usbDevice; - mUsbManager = usbManager; + this.usbDevice = usbDevice; + this.usbManager = usbManager; } + @Override + public void release() { + if (usbConnection != null) { + usbConnection.releaseInterface(usbInterface); + usbConnection.close(); + usbConnection = null; + } + + Log.d(Constants.TAG, "Usb transport disconnected"); + } + + /** + * Check if device is was connected to and still is connected + * @return true if device is connected + */ + @Override + public boolean isConnected() { + return usbConnection != null && usbManager.getDeviceList().containsValue(usbDevice) && + usbConnection.getSerial() != null; + } + + /** + * Check if Transport supports persistent connections e.g connections which can + * handle multiple operations in one session + * @return true if transport supports persistent connections + */ + @Override + public boolean isPersistentConnectionAllowed() { + return true; + } + + /** + * Connect to OTG device + */ + @Override + public void connect() throws IOException { + usbInterface = getSmartCardInterface(usbDevice); + if (usbInterface == null) { + // Shouldn't happen as we whitelist only class 11 devices + throw new UsbTransportException("USB error - device doesn't have class 11 interface"); + } + + final Pair ioEndpoints = getIoEndpoints(usbInterface); + UsbEndpoint usbBulkIn = ioEndpoints.first; + UsbEndpoint usbBulkOut = ioEndpoints.second; + + if (usbBulkIn == null || usbBulkOut == null) { + throw new UsbTransportException("USB error - invalid class 11 interface"); + } + + usbConnection = usbManager.openDevice(usbDevice); + if (usbConnection == null) { + throw new UsbTransportException("USB error - failed to connect to device"); + } + + if (!usbConnection.claimInterface(usbInterface, true)) { + throw new UsbTransportException("USB error - failed to claim interface"); + } + + byte[] rawDescriptors = usbConnection.getRawDescriptors(); + ccidTransportProtocol = getCcidTransportProtocolForRawDescriptors(rawDescriptors); + + CcidTransceiver transceiver = new CcidTransceiver(usbConnection, usbBulkIn, usbBulkOut); + ccidTransportProtocol.connect(transceiver); + } + + private CcidTransportProtocol getCcidTransportProtocolForRawDescriptors(byte[] desc) throws UsbTransportException { + int dwProtocols = 0, dwFeatures = 0; + boolean hasCcidDescriptor = false; + + ByteBuffer byteBuffer = ByteBuffer.wrap(desc).order(ByteOrder.LITTLE_ENDIAN); + + while (byteBuffer.hasRemaining()) { + byteBuffer.mark(); + byte len = byteBuffer.get(), type = byteBuffer.get(); + + if (type == 0x21 && len == 0x36) { + byteBuffer.reset(); + + byteBuffer.position(byteBuffer.position() + PROTOCOLS_OFFSET); + dwProtocols = byteBuffer.getInt(); + + byteBuffer.reset(); + + byteBuffer.position(byteBuffer.position() + FEATURES_OFFSET); + dwFeatures = byteBuffer.getInt(); + hasCcidDescriptor = true; + break; + } else { + byteBuffer.position(byteBuffer.position() + len - 2); + } + } + + if (!hasCcidDescriptor) { + throw new UsbTransportException("CCID descriptor not found"); + } + + if ((dwProtocols & MASK_T1_PROTO) == 0) { + throw new UsbTransportException("T=0 protocol is not supported"); + } + + if ((dwFeatures & MASK_TPDU) != 0) { + return new T1TpduProtocol(); + } else if (((dwFeatures & MASK_SHORT_APDU) != 0) || ((dwFeatures & MASK_EXTENDED_APDU) != 0)) { + return new T1ShortApduProtocol(); + } else { + throw new UsbTransportException("Character level exchange is not supported"); + } + } + + /** + * Transmit and receive data + * @param data data to transmit + * @return received data + */ + @Override + public ResponseAPDU transceive(CommandAPDU data) throws UsbTransportException { + return new ResponseAPDU(ccidTransportProtocol.transceive(data.getBytes())); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final UsbTransport that = (UsbTransport) o; + + return usbDevice != null ? usbDevice.equals(that.usbDevice) : that.usbDevice == null; + } + + @Override + public int hashCode() { + return usbDevice != null ? usbDevice.hashCode() : 0; + } + + /** * Get first class 11 (Chip/Smartcard) interface of the device * @@ -108,149 +242,4 @@ public class UsbTransport implements Transport { } return new Pair<>(bulkIn, bulkOut); } - - /** - * Release interface and disconnect - */ - @Override - public void release() { - if (mConnection != null) { - mConnection.releaseInterface(mUsbInterface); - mConnection.close(); - mConnection = null; - } - - Log.d(Constants.TAG, "Usb transport disconnected"); - } - - /** - * Check if device is was connected to and still is connected - * @return true if device is connected - */ - @Override - public boolean isConnected() { - return mConnection != null && mUsbManager.getDeviceList().containsValue(mUsbDevice) && - mConnection.getSerial() != null; - } - - /** - * Check if Transport supports persistent connections e.g connections which can - * handle multiple operations in one session - * @return true if transport supports persistent connections - */ - @Override - public boolean isPersistentConnectionAllowed() { - return true; - } - - /** - * Connect to OTG device - * @throws IOException - */ - @Override - public void connect() throws IOException { - mUsbInterface = getSmartCardInterface(mUsbDevice); - if (mUsbInterface == null) { - // Shouldn't happen as we whitelist only class 11 devices - throw new UsbTransportException("USB error - device doesn't have class 11 interface"); - } - - final Pair ioEndpoints = getIoEndpoints(mUsbInterface); - mBulkIn = ioEndpoints.first; - mBulkOut = ioEndpoints.second; - - if (mBulkIn == null || mBulkOut == null) { - throw new UsbTransportException("USB error - invalid class 11 interface"); - } - - mConnection = mUsbManager.openDevice(mUsbDevice); - if (mConnection == null) { - throw new UsbTransportException("USB error - failed to connect to device"); - } - - if (!mConnection.claimInterface(mUsbInterface, true)) { - throw new UsbTransportException("USB error - failed to claim interface"); - } - - mTransceiver = new CcidTransceiver(mConnection, mBulkIn, mBulkOut); - - - - configureProtocol(); - } - - private void configureProtocol() throws UsbTransportException { - byte[] desc = mConnection.getRawDescriptors(); - int dwProtocols = 0, dwFeatures = 0; - boolean hasCcidDescriptor = false; - - ByteBuffer byteBuffer = ByteBuffer.wrap(desc).order(ByteOrder.LITTLE_ENDIAN); - - while (byteBuffer.hasRemaining()) { - byteBuffer.mark(); - byte len = byteBuffer.get(), type = byteBuffer.get(); - - if (type == 0x21 && len == 0x36) { - byteBuffer.reset(); - - byteBuffer.position(byteBuffer.position() + PROTOCOLS_OFFSET); - dwProtocols = byteBuffer.getInt(); - - byteBuffer.reset(); - - byteBuffer.position(byteBuffer.position() + FEATURES_OFFSET); - dwFeatures = byteBuffer.getInt(); - hasCcidDescriptor = true; - break; - } else { - byteBuffer.position(byteBuffer.position() + len - 2); - } - } - - if (!hasCcidDescriptor) { - throw new UsbTransportException("CCID descriptor not found"); - } - - if ((dwProtocols & MASK_T1_PROTO) == 0) { - throw new UsbTransportException("T=0 protocol is not supported"); - } - - if ((dwFeatures & MASK_TPDU) != 0) { - mProtocol = new T1TpduProtocol(mTransceiver); - } else if (((dwFeatures & MASK_SHORT_APDU) != 0) || ((dwFeatures & MASK_EXTENDED_APDU) != 0)) { - mProtocol = new T1ShortApduProtocol(mTransceiver); - } else { - throw new UsbTransportException("Character level exchange is not supported"); - } - } - - /** - * Transmit and receive data - * @param data data to transmit - * @return received data - * @throws UsbTransportException - */ - @Override - public ResponseAPDU transceive(CommandAPDU data) throws UsbTransportException { - return new ResponseAPDU(mProtocol.transceive(data.getBytes())); - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - final UsbTransport that = (UsbTransport) o; - - return mUsbDevice != null ? mUsbDevice.equals(that.mUsbDevice) : that.mUsbDevice == null; - } - - @Override - public int hashCode() { - return mUsbDevice != null ? mUsbDevice.hashCode() : 0; - } - - public UsbDevice getUsbDevice() { - return mUsbDevice; - } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java index 4d74a312f..67a52be68 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.securitytoken.usb.tpdu; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import org.bouncycastle.util.Arrays; import org.sufficientlysecure.keychain.Constants; @@ -30,22 +31,28 @@ import org.sufficientlysecure.keychain.util.Log; public class T1TpduProtocol implements CcidTransportProtocol { private final static int MAX_FRAME_LEN = 254; - private byte mCounter = 0; - private final CcidTransceiver ccidTransceiver; - private final BlockChecksumType checksumType; - public T1TpduProtocol(CcidTransceiver transceiver) throws UsbTransportException { - ccidTransceiver = transceiver; + private CcidTransceiver ccidTransceiver; + private BlockChecksumType checksumType; + + private byte mCounter = 0; + + + public void connect(@NonNull CcidTransceiver ccidTransceiver) throws UsbTransportException { + if (this.ccidTransceiver != null) { + throw new IllegalStateException("Protocol already connected!"); + } + this.ccidTransceiver = ccidTransceiver; // Connect - CcidDataBlock response = ccidTransceiver.iccPowerOn(); - Log.d(Constants.TAG, "Usb transport connected T1/TPDU, ATR=" + response); + CcidDataBlock response = this.ccidTransceiver.iccPowerOn(); // TODO: set checksum from atr checksumType = BlockChecksumType.LRC; // PPS all auto pps(); + } private void pps() throws UsbTransportException { @@ -56,6 +63,10 @@ public class T1TpduProtocol implements CcidTransportProtocol { } public byte[] transceive(@NonNull byte[] apdu) throws UsbTransportException { + if (this.ccidTransceiver == null) { + throw new IllegalStateException("Protocol not connected!"); + } + int start = 0; if (apdu.length == 0) { @@ -118,7 +129,7 @@ public class T1TpduProtocol implements CcidTransportProtocol { } // Factory methods - public Block getBlockFromResponse(CcidDataBlock dataBlock) throws UsbTransportException { + private Block getBlockFromResponse(CcidDataBlock dataBlock) throws UsbTransportException { final Block baseBlock = new Block(checksumType, dataBlock.getData()); if ((baseBlock.getPcb() & IBlock.MASK_RBLOCK) == IBlock.MASK_VALUE_RBLOCK) { @@ -132,11 +143,11 @@ public class T1TpduProtocol implements CcidTransportProtocol { throw new UsbTransportException("TPDU Unknown block type"); } - public IBlock newIBlock(byte sequence, boolean chaining, byte[] apdu) throws UsbTransportException { + private IBlock newIBlock(byte sequence, boolean chaining, byte[] apdu) throws UsbTransportException { return new IBlock(checksumType, (byte) 0, sequence, chaining, apdu); } - public RBlock newRBlock(byte sequence) throws UsbTransportException { + private RBlock newRBlock(byte sequence) throws UsbTransportException { return new RBlock(checksumType, (byte) 0, sequence); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java index db7153e3d..7278324ce 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java @@ -112,21 +112,26 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity onSecurityTokenError(error); } - public void tagDiscovered(final Tag tag) { + public void tagDiscovered(Tag tag) { // Actual NFC operations are executed in doInBackground to not block the UI thread - if (!mTagHandlingEnabled) + if (!mTagHandlingEnabled) { return; + } - securityTokenDiscovered(new NfcTransport(tag)); + NfcTransport nfcTransport = new NfcTransport(tag); + securityTokenDiscovered(nfcTransport); } - public void usbDeviceDiscovered(final UsbDevice usbDevice) { + public void usbDeviceDiscovered(UsbDevice usbDevice) { // Actual USB operations are executed in doInBackground to not block the UI thread - if (!mTagHandlingEnabled) + if (!mTagHandlingEnabled) { return; + } UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); - securityTokenDiscovered(new UsbTransport(usbDevice, usbManager)); + + UsbTransport usbTransport = new UsbTransport(usbDevice, usbManager); + securityTokenDiscovered(usbTransport); } public void securityTokenDiscovered(final Transport transport) { From a51252910bf41b916161fbef2a7e2a6b036774b7 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 8 Oct 2017 04:35:11 +0200 Subject: [PATCH 4/5] some T=1 refactoring --- .../securitytoken/usb/tpdu/Block.java | 81 ++++++++----- .../securitytoken/usb/tpdu/IBlock.java | 27 +++-- .../securitytoken/usb/tpdu/RBlock.java | 21 ++-- .../securitytoken/usb/tpdu/SBlock.java | 18 ++- .../usb/tpdu/T1TpduBlockFactory.java | 36 ++++++ .../usb/tpdu/T1TpduProtocol.java | 111 +++++++----------- 6 files changed, 174 insertions(+), 120 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduBlockFactory.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/Block.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/Block.java index 1fe29b2a2..ef98050ab 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/Block.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/Block.java @@ -22,78 +22,99 @@ import org.bouncycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; public class Block { - protected static final int MAX_PAYLOAD_LEN = 254; - protected static final int OFFSET_NAD = 0; - protected static final int OFFSET_PCB = 1; - protected static final int OFFSET_LEN = 2; - protected static final int OFFSET_DATA = 3; + private static final int MAX_PAYLOAD_LEN = 254; + private static final int OFFSET_NAD = 0; + static final int OFFSET_PCB = 1; + private static final int OFFSET_LEN = 2; + private static final int OFFSET_DATA = 3; - protected byte[] mData; - protected BlockChecksumType mChecksumType; + private final byte[] blockData; + private final BlockChecksumType checksumType; - public Block(BlockChecksumType checksumType, byte[] data) throws UsbTransportException { - this.mChecksumType = checksumType; - this.mData = data; + Block(BlockChecksumType checksumType, byte[] data) throws UsbTransportException { + this.checksumType = checksumType; + this.blockData = data; - int checksumOffset = this.mData.length - mChecksumType.getLength(); - byte[] checksum = mChecksumType.computeChecksum(data, 0, checksumOffset); + int checksumOffset = blockData.length - checksumType.getLength(); + byte[] checksum = checksumType.computeChecksum(data, 0, checksumOffset); if (!Arrays.areEqual(checksum, getEdc())) { throw new UsbTransportException("TPDU CRC doesn't match"); } } - protected Block(BlockChecksumType checksumType, byte nad, byte pcb, byte[] apdu) + /* + protected Block(BlockChecksumType checksumType, byte nad, byte pcb, byte[] apdu, int offset, int length) throws UsbTransportException { - this.mChecksumType = checksumType; + apdu = Arrays.copyOfRange(apdu, offset, offset + length); + + this.checksumType = checksumType; if (apdu.length > MAX_PAYLOAD_LEN) { throw new UsbTransportException("APDU is too long; should be split"); } - this.mData = Arrays.concatenate( + blockData = Arrays.concatenate( new byte[]{nad, pcb, (byte) apdu.length}, apdu, - new byte[mChecksumType.getLength()]); + new byte[checksumType.getLength()]); - int checksumOffset = this.mData.length - mChecksumType.getLength(); - byte[] checksum = mChecksumType.computeChecksum(this.mData, 0, checksumOffset); + int checksumOffset = blockData.length - checksumType.getLength(); + byte[] checksum = checksumType.computeChecksum(blockData, 0, checksumOffset); - System.arraycopy(checksum, 0, this.mData, checksumOffset, mChecksumType.getLength()); + System.arraycopy(checksum, 0, blockData, checksumOffset, checksumType.getLength()); } + */ - protected Block(Block baseBlock) { - this.mChecksumType = baseBlock.getChecksumType(); - this.mData = baseBlock.getRawData(); +// /* + Block(BlockChecksumType checksumType, byte nad, byte pcb, byte[] apdu, int offset, int length) + throws UsbTransportException { + this.checksumType = checksumType; + if (length > MAX_PAYLOAD_LEN) { + throw new IllegalArgumentException("Payload too long! " + length + " > " + MAX_PAYLOAD_LEN); + } + + int lengthWithoutChecksum = length + 3; + int checksumLength = this.checksumType.getLength(); + + blockData = new byte[lengthWithoutChecksum + checksumLength]; + blockData[0] = nad; + blockData[1] = pcb; + blockData[2] = (byte) length; + System.arraycopy(apdu, offset, blockData, 3, length); + + byte[] checksum = this.checksumType.computeChecksum(blockData, 0, lengthWithoutChecksum); + System.arraycopy(checksum, 0, blockData, lengthWithoutChecksum, checksumLength); } public byte getNad() { - return mData[OFFSET_NAD]; + return blockData[OFFSET_NAD]; } public byte getPcb() { - return mData[OFFSET_PCB]; + return blockData[OFFSET_PCB]; } public byte getLen() { - return mData[OFFSET_LEN]; + return blockData[OFFSET_LEN]; } public byte[] getEdc() { - return Arrays.copyOfRange(mData, mData.length - mChecksumType.getLength(), mData.length); + return Arrays.copyOfRange(blockData, blockData.length - checksumType.getLength(), blockData.length); } public BlockChecksumType getChecksumType() { - return mChecksumType; + return checksumType; } public byte[] getApdu() { - return Arrays.copyOfRange(mData, OFFSET_DATA, mData.length - mChecksumType.getLength()); + return Arrays.copyOfRange(blockData, OFFSET_DATA, blockData.length - checksumType.getLength()); } public byte[] getRawData() { - return mData; + return blockData; } @Override public String toString() { - return Hex.toHexString(mData); + return Hex.toHexString(blockData); } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/IBlock.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/IBlock.java index 3df4d9cdf..681ed54d2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/IBlock.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/IBlock.java @@ -17,31 +17,38 @@ package org.sufficientlysecure.keychain.securitytoken.usb.tpdu; + import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; -public class IBlock extends Block { - public static final byte MASK_RBLOCK = (byte) 0b10000000; - public static final byte MASK_VALUE_RBLOCK = (byte) 0b00000000; + +class IBlock extends Block { + static final byte MASK_IBLOCK = (byte) 0b10000000; + static final byte MASK_VALUE_IBLOCK = (byte) 0b00000000; private static final byte BIT_SEQUENCE = 6; private static final byte BIT_CHAINING = 5; - public IBlock(final Block baseBlock) { - super(baseBlock); + IBlock(BlockChecksumType checksumType, byte[] data) throws UsbTransportException { + super(checksumType, data); + + if ((getPcb() & MASK_IBLOCK) != MASK_VALUE_IBLOCK) { + throw new IllegalArgumentException("Data contained incorrect block type!"); + } } - public IBlock(BlockChecksumType checksumType, byte nad, byte sequence, boolean chaining, - byte[] apdu) throws UsbTransportException { + IBlock(BlockChecksumType checksumType, byte nad, byte sequence, boolean chaining, byte[] apdu, int offset, + int length) + throws UsbTransportException { super(checksumType, nad, (byte) (((sequence & 1) << BIT_SEQUENCE) | (chaining ? 1 << BIT_CHAINING : 0)), - apdu); + apdu, offset, length); } - public byte getSequence() { + byte getSequence() { return (byte) ((getPcb() >> BIT_SEQUENCE) & 1); } - public boolean getChaining() { + boolean getChaining() { return ((getPcb() >> BIT_CHAINING) & 1) != 0; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/RBlock.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/RBlock.java index 153758fb2..9095c2b8c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/RBlock.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/RBlock.java @@ -21,29 +21,34 @@ import android.support.annotation.NonNull; import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; -public class RBlock extends Block { - public static final byte MASK_RBLOCK = (byte) 0b11000000; - public static final byte MASK_VALUE_RBLOCK = (byte) 0b10000000; +class RBlock extends Block { + static final byte MASK_RBLOCK = (byte) 0b11000000; + static final byte MASK_VALUE_RBLOCK = (byte) 0b10000000; private static final byte BIT_SEQUENCE = 4; - public RBlock(Block baseBlock) throws UsbTransportException { - super(baseBlock); + RBlock(BlockChecksumType checksumType, byte[] data) throws UsbTransportException { + super(checksumType, data); + + if ((getPcb() & MASK_RBLOCK) != MASK_VALUE_RBLOCK) { + throw new IllegalArgumentException("Data contained incorrect block type!"); + } + if (getApdu().length != 0) { throw new UsbTransportException("Data in R-block"); } } - public RBlock(BlockChecksumType checksumType, byte nad, byte sequence) + RBlock(BlockChecksumType checksumType, byte nad, byte sequence) throws UsbTransportException { - super(checksumType, nad, (byte) (MASK_VALUE_RBLOCK | ((sequence & 1) << BIT_SEQUENCE)), new byte[0]); + super(checksumType, nad, (byte) (MASK_VALUE_RBLOCK | ((sequence & 1) << BIT_SEQUENCE)), new byte[0], 0, 0); } public RError getError() throws UsbTransportException { return RError.from(getPcb()); } - public enum RError { + enum RError { NO_ERROR(0), EDC_ERROR(1), OTHER_ERROR(2); private byte mLowBits; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/SBlock.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/SBlock.java index 72302fc4b..36eb8bd6a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/SBlock.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/SBlock.java @@ -17,11 +17,19 @@ package org.sufficientlysecure.keychain.securitytoken.usb.tpdu; -public class SBlock extends Block { - public static final byte MASK_SBLOCK = (byte) 0b11000000; - public static final byte MASK_VALUE_SBLOCK = (byte) 0b11000000; - public SBlock(Block baseBlock) { - super(baseBlock); +import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; + + +class SBlock extends Block { + static final byte MASK_SBLOCK = (byte) 0b11000000; + static final byte MASK_VALUE_SBLOCK = (byte) 0b11000000; + + SBlock(BlockChecksumType checksumType, byte[] data) throws UsbTransportException { + super(checksumType, data); + + if ((getPcb() & MASK_SBLOCK) != MASK_VALUE_SBLOCK) { + throw new IllegalArgumentException("Data contained incorrect block type!"); + } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduBlockFactory.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduBlockFactory.java new file mode 100644 index 000000000..12ff33b87 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduBlockFactory.java @@ -0,0 +1,36 @@ +package org.sufficientlysecure.keychain.securitytoken.usb.tpdu; + + +import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; + + +class T1TpduBlockFactory { + private BlockChecksumType checksumType; + + T1TpduBlockFactory(BlockChecksumType checksumType) { + this.checksumType = checksumType; + } + + Block fromBytes(byte[] data) throws UsbTransportException { + byte pcbByte = data[Block.OFFSET_PCB]; + + if ((pcbByte & IBlock.MASK_IBLOCK) == IBlock.MASK_VALUE_IBLOCK) { + return new IBlock(checksumType, data); + } else if ((pcbByte & SBlock.MASK_SBLOCK) == SBlock.MASK_VALUE_SBLOCK) { + return new SBlock(checksumType, data); + } else if ((pcbByte & RBlock.MASK_RBLOCK) == RBlock.MASK_VALUE_RBLOCK) { + return new RBlock(checksumType, data); + } + + throw new UsbTransportException("TPDU Unknown block type"); + } + + IBlock newIBlock(byte sequence, boolean chaining, byte[] apdu, int offset, int length) + throws UsbTransportException { + return new IBlock(checksumType, (byte) 0, sequence, chaining, apdu, offset, length); + } + + RBlock createAckRBlock(byte receivedSeqNum) throws UsbTransportException { + return new RBlock(checksumType, (byte) 0, (byte) (receivedSeqNum + 1)); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java index 67a52be68..d5f2b736f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java @@ -17,15 +17,15 @@ package org.sufficientlysecure.keychain.securitytoken.usb.tpdu; + import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import org.bouncycastle.util.Arrays; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransceiver; import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransceiver.CcidDataBlock; -import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransportProtocol; +import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; import org.sufficientlysecure.keychain.util.Log; public class T1TpduProtocol implements CcidTransportProtocol { @@ -33,9 +33,9 @@ public class T1TpduProtocol implements CcidTransportProtocol { private CcidTransceiver ccidTransceiver; - private BlockChecksumType checksumType; + private T1TpduBlockFactory blockFactory; - private byte mCounter = 0; + private byte sequenceCounter = 0; public void connect(@NonNull CcidTransceiver ccidTransceiver) throws UsbTransportException { @@ -44,22 +44,22 @@ public class T1TpduProtocol implements CcidTransportProtocol { } this.ccidTransceiver = ccidTransceiver; - // Connect - CcidDataBlock response = this.ccidTransceiver.iccPowerOn(); + this.ccidTransceiver.iccPowerOn(); // TODO: set checksum from atr - checksumType = BlockChecksumType.LRC; - - // PPS all auto - pps(); + blockFactory = new T1TpduBlockFactory(BlockChecksumType.LRC); + performPpsExchange(); } - private void pps() throws UsbTransportException { - byte[] pps = new byte[]{(byte) 0xFF, 1, (byte) (0xFF ^ 1)}; + private void performPpsExchange() throws UsbTransportException { + byte[] pps = { (byte) 0xFF, 1, (byte) (0xFF ^ 1) }; CcidDataBlock response = ccidTransceiver.sendXfrBlock(pps); - Log.d(Constants.TAG, "PPS response " + response); + + if (!Arrays.areEqual(pps, response.getData())) { + throw new UsbTransportException("Protocol and parameters (PPS) negotiation failed!"); + } } public byte[] transceive(@NonNull byte[] apdu) throws UsbTransportException { @@ -67,87 +67,64 @@ public class T1TpduProtocol implements CcidTransportProtocol { throw new IllegalStateException("Protocol not connected!"); } - int start = 0; - if (apdu.length == 0) { throw new UsbTransportException("Cant transcive zero-length apdu(tpdu)"); } - Block responseBlock = null; - while (apdu.length - start > 0) { - boolean hasMore = start + MAX_FRAME_LEN < apdu.length; - int len = Math.min(MAX_FRAME_LEN, apdu.length - start); + IBlock responseBlock = sendChainedData(apdu); + return receiveChainedResponse(responseBlock); + } - // Send next frame - Block block = newIBlock(mCounter++, hasMore, Arrays.copyOfRange(apdu, start, start + len)); + private IBlock sendChainedData(@NonNull byte[] apdu) throws UsbTransportException { + int sentLength = 0; + while (sentLength < apdu.length) { + boolean hasMore = sentLength + MAX_FRAME_LEN < apdu.length; + int len = Math.min(MAX_FRAME_LEN, apdu.length - sentLength); - CcidDataBlock response = ccidTransceiver.sendXfrBlock(block.getRawData()); + Block sendBlock = blockFactory.newIBlock(sequenceCounter++, hasMore, apdu, sentLength, len); + CcidDataBlock response = ccidTransceiver.sendXfrBlock(sendBlock.getRawData()); + Block responseBlock = blockFactory.fromBytes(response.getData()); - // Receive I or R block - responseBlock = getBlockFromResponse(response); - - start += len; + sentLength += len; if (responseBlock instanceof SBlock) { - Log.d(Constants.TAG, "S-Block received " + responseBlock.toString()); + Log.d(Constants.TAG, "S-Block received " + responseBlock); // just ignore } else if (responseBlock instanceof RBlock) { - Log.d(Constants.TAG, "R-Block received " + responseBlock.toString()); + Log.d(Constants.TAG, "R-Block received " + responseBlock); if (((RBlock) responseBlock).getError() != RBlock.RError.NO_ERROR) { - throw new UsbTransportException("R-Block reports error " - + ((RBlock) responseBlock).getError()); + throw new UsbTransportException("R-Block reports error " + ((RBlock) responseBlock).getError()); } } else { // I block - if (start != apdu.length) { + if (sentLength != apdu.length) { throw new UsbTransportException("T1 frame response underflow"); } - break; + return (IBlock) responseBlock; } } - // Receive - if (responseBlock == null || !(responseBlock instanceof IBlock)) - throw new UsbTransportException("Invalid tpdu sequence state"); + throw new UsbTransportException("Invalid tpdu sequence state"); + } - byte[] responseApdu = responseBlock.getApdu(); + private byte[] receiveChainedResponse(IBlock responseIBlock) throws UsbTransportException { + byte[] responseApdu = responseIBlock.getApdu(); - while (((IBlock) responseBlock).getChaining()) { - Block ackBlock = newRBlock((byte) (((IBlock) responseBlock).getSequence() + 1)); + while (responseIBlock.getChaining()) { + byte receivedSeqNum = responseIBlock.getSequence(); + + Block ackBlock = blockFactory.createAckRBlock(receivedSeqNum); CcidDataBlock response = ccidTransceiver.sendXfrBlock(ackBlock.getRawData()); + Block responseBlock = blockFactory.fromBytes(response.getData()); - responseBlock = getBlockFromResponse(response); - - if (responseBlock instanceof IBlock) { - responseApdu = Arrays.concatenate(responseApdu, responseBlock.getApdu()); - } else { - Log.d(Constants.TAG, "Response block received " + responseBlock.toString()); + if (!(responseBlock instanceof IBlock)) { + Log.e(Constants.TAG, "Invalid response block received " + responseBlock); throw new UsbTransportException("Response: invalid state - invalid block received"); } + + responseIBlock = (IBlock) responseBlock; + responseApdu = Arrays.concatenate(responseApdu, responseBlock.getApdu()); } return responseApdu; } - - // Factory methods - private Block getBlockFromResponse(CcidDataBlock dataBlock) throws UsbTransportException { - final Block baseBlock = new Block(checksumType, dataBlock.getData()); - - if ((baseBlock.getPcb() & IBlock.MASK_RBLOCK) == IBlock.MASK_VALUE_RBLOCK) { - return new IBlock(baseBlock); - } else if ((baseBlock.getPcb() & SBlock.MASK_SBLOCK) == SBlock.MASK_VALUE_SBLOCK) { - return new SBlock(baseBlock); - } else if ((baseBlock.getPcb() & RBlock.MASK_RBLOCK) == RBlock.MASK_VALUE_RBLOCK) { - return new RBlock(baseBlock); - } - - throw new UsbTransportException("TPDU Unknown block type"); - } - - private IBlock newIBlock(byte sequence, boolean chaining, byte[] apdu) throws UsbTransportException { - return new IBlock(checksumType, (byte) 0, sequence, chaining, apdu); - } - - private RBlock newRBlock(byte sequence) throws UsbTransportException { - return new RBlock(checksumType, (byte) 0, sequence); - } } From 0cb000a0bedd9af953e9a38c59e124f4ce6ee5d9 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 9 Oct 2017 14:24:42 +0200 Subject: [PATCH 5/5] Change some package structure --- .../securitytoken/usb/UsbTransport.java | 1 + .../securitytoken/usb/tpdu/Block.java | 10 ++-- ...mType.java => BlockChecksumAlgorithm.java} | 4 +- .../securitytoken/usb/tpdu/FrameType.java | 56 ------------------- .../securitytoken/usb/tpdu/IBlock.java | 4 +- .../securitytoken/usb/tpdu/RBlock.java | 4 +- .../securitytoken/usb/tpdu/SBlock.java | 2 +- .../usb/{ => tpdu}/T1ShortApduProtocol.java | 10 ++-- .../usb/tpdu/T1TpduBlockFactory.java | 4 +- .../usb/tpdu/T1TpduProtocol.java | 2 +- 10 files changed, 22 insertions(+), 75 deletions(-) rename OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/{BlockChecksumType.java => BlockChecksumAlgorithm.java} (95%) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/FrameType.java rename OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/{ => tpdu}/T1ShortApduProtocol.java (77%) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java index 23ee412f6..cd6412560 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java @@ -31,6 +31,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.securitytoken.Transport; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; +import org.sufficientlysecure.keychain.securitytoken.usb.tpdu.T1ShortApduProtocol; import org.sufficientlysecure.keychain.securitytoken.usb.tpdu.T1TpduProtocol; import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/Block.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/Block.java index ef98050ab..97c35d1e9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/Block.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/Block.java @@ -21,7 +21,7 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; -public class Block { +class Block { private static final int MAX_PAYLOAD_LEN = 254; private static final int OFFSET_NAD = 0; static final int OFFSET_PCB = 1; @@ -29,9 +29,9 @@ public class Block { private static final int OFFSET_DATA = 3; private final byte[] blockData; - private final BlockChecksumType checksumType; + private final BlockChecksumAlgorithm checksumType; - Block(BlockChecksumType checksumType, byte[] data) throws UsbTransportException { + Block(BlockChecksumAlgorithm checksumType, byte[] data) throws UsbTransportException { this.checksumType = checksumType; this.blockData = data; @@ -64,7 +64,7 @@ public class Block { */ // /* - Block(BlockChecksumType checksumType, byte nad, byte pcb, byte[] apdu, int offset, int length) + Block(BlockChecksumAlgorithm checksumType, byte nad, byte pcb, byte[] apdu, int offset, int length) throws UsbTransportException { this.checksumType = checksumType; if (length > MAX_PAYLOAD_LEN) { @@ -100,7 +100,7 @@ public class Block { return Arrays.copyOfRange(blockData, blockData.length - checksumType.getLength(), blockData.length); } - public BlockChecksumType getChecksumType() { + public BlockChecksumAlgorithm getChecksumType() { return checksumType; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/BlockChecksumType.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/BlockChecksumAlgorithm.java similarity index 95% rename from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/BlockChecksumType.java rename to OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/BlockChecksumAlgorithm.java index 4af6364e3..70b8197fe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/BlockChecksumType.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/BlockChecksumAlgorithm.java @@ -19,12 +19,12 @@ package org.sufficientlysecure.keychain.securitytoken.usb.tpdu; import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; -public enum BlockChecksumType { +enum BlockChecksumAlgorithm { LRC(1), CRC(2); private int mLength; - BlockChecksumType(int length) { + BlockChecksumAlgorithm(int length) { mLength = length; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/FrameType.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/FrameType.java deleted file mode 100644 index 03a8b109a..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/FrameType.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2016 Nikita Mikhailov - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.securitytoken.usb.tpdu; - -import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; - -public enum FrameType { - I_BLOCK(0b00000000, 0b10000000, 6, true), // Information - R_BLOCK(0b10000000, 0b11000000, 4, false), // Receipt ack - S_BLOCK(0b11000000, 0b11000000, -1, false); // System - - private byte mValue; - private byte mMask; - private int mSequenceBit; - private boolean mChainingSupported; - - FrameType(int value, int mask, int sequenceBit, boolean chaining) { - // Accept ints just to avoid cast in creation - this.mValue = (byte) value; - this.mMask = (byte) mask; - this.mSequenceBit = sequenceBit; - this.mChainingSupported = chaining; - } - - public static FrameType fromPCB(byte pcb) throws UsbTransportException { - for (final FrameType frameType : values()) { - if ((frameType.mMask & pcb) == frameType.mValue) { - return frameType; - } - } - throw new UsbTransportException("Invalid PCB byte"); - } - - public int getSequenceBit() { - return mSequenceBit; - } - - public boolean isChainingSupported() { - return mChainingSupported; - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/IBlock.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/IBlock.java index 681ed54d2..38af55067 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/IBlock.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/IBlock.java @@ -28,7 +28,7 @@ class IBlock extends Block { private static final byte BIT_SEQUENCE = 6; private static final byte BIT_CHAINING = 5; - IBlock(BlockChecksumType checksumType, byte[] data) throws UsbTransportException { + IBlock(BlockChecksumAlgorithm checksumType, byte[] data) throws UsbTransportException { super(checksumType, data); if ((getPcb() & MASK_IBLOCK) != MASK_VALUE_IBLOCK) { @@ -36,7 +36,7 @@ class IBlock extends Block { } } - IBlock(BlockChecksumType checksumType, byte nad, byte sequence, boolean chaining, byte[] apdu, int offset, + IBlock(BlockChecksumAlgorithm checksumType, byte nad, byte sequence, boolean chaining, byte[] apdu, int offset, int length) throws UsbTransportException { super(checksumType, nad, diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/RBlock.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/RBlock.java index 9095c2b8c..97340168a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/RBlock.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/RBlock.java @@ -27,7 +27,7 @@ class RBlock extends Block { private static final byte BIT_SEQUENCE = 4; - RBlock(BlockChecksumType checksumType, byte[] data) throws UsbTransportException { + RBlock(BlockChecksumAlgorithm checksumType, byte[] data) throws UsbTransportException { super(checksumType, data); if ((getPcb() & MASK_RBLOCK) != MASK_VALUE_RBLOCK) { @@ -39,7 +39,7 @@ class RBlock extends Block { } } - RBlock(BlockChecksumType checksumType, byte nad, byte sequence) + RBlock(BlockChecksumAlgorithm checksumType, byte nad, byte sequence) throws UsbTransportException { super(checksumType, nad, (byte) (MASK_VALUE_RBLOCK | ((sequence & 1) << BIT_SEQUENCE)), new byte[0], 0, 0); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/SBlock.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/SBlock.java index 36eb8bd6a..6ffb56e17 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/SBlock.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/SBlock.java @@ -25,7 +25,7 @@ class SBlock extends Block { static final byte MASK_SBLOCK = (byte) 0b11000000; static final byte MASK_VALUE_SBLOCK = (byte) 0b11000000; - SBlock(BlockChecksumType checksumType, byte[] data) throws UsbTransportException { + SBlock(BlockChecksumAlgorithm checksumType, byte[] data) throws UsbTransportException { super(checksumType, data); if ((getPcb() & MASK_SBLOCK) != MASK_VALUE_SBLOCK) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/T1ShortApduProtocol.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1ShortApduProtocol.java similarity index 77% rename from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/T1ShortApduProtocol.java rename to OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1ShortApduProtocol.java index caee33c43..b86715d19 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/T1ShortApduProtocol.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1ShortApduProtocol.java @@ -15,15 +15,17 @@ * along with this program. If not, see . */ -package org.sufficientlysecure.keychain.securitytoken.usb; +package org.sufficientlysecure.keychain.securitytoken.usb.tpdu; + import android.support.annotation.NonNull; -import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransceiver; import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransceiver.CcidDataBlock; -import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransportProtocol; +import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; -class T1ShortApduProtocol implements CcidTransportProtocol { +public class T1ShortApduProtocol implements CcidTransportProtocol { private CcidTransceiver ccidTransceiver; public void connect(@NonNull CcidTransceiver transceiver) throws UsbTransportException { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduBlockFactory.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduBlockFactory.java index 12ff33b87..89a6bb721 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduBlockFactory.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduBlockFactory.java @@ -5,9 +5,9 @@ import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; class T1TpduBlockFactory { - private BlockChecksumType checksumType; + private BlockChecksumAlgorithm checksumType; - T1TpduBlockFactory(BlockChecksumType checksumType) { + T1TpduBlockFactory(BlockChecksumAlgorithm checksumType) { this.checksumType = checksumType; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java index d5f2b736f..a2de18245 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T1TpduProtocol.java @@ -47,7 +47,7 @@ public class T1TpduProtocol implements CcidTransportProtocol { this.ccidTransceiver.iccPowerOn(); // TODO: set checksum from atr - blockFactory = new T1TpduBlockFactory(BlockChecksumType.LRC); + blockFactory = new T1TpduBlockFactory(BlockChecksumAlgorithm.LRC); performPpsExchange(); }