Skip to content

Commit

Permalink
Keep YubiKey claimed throughout all I/O operations during a command
Browse files Browse the repository at this point in the history
This fixes compatibility with the old YubiKey Standard. Took me ages to figure out.
  • Loading branch information
pp3345 committed Jun 23, 2018
1 parent 6b61e36 commit 9d3d355
Showing 1 changed file with 32 additions and 31 deletions.
63 changes: 32 additions & 31 deletions app/src/main/java/net/pp3345/ykdroid/yubikey/UsbYubiKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,19 +135,32 @@ public Type getType() {
* @return The 32-bit serial number of the connected YubiKey.
*/
public int getSerialNumber() throws YubiKeyException {
this.write(Slot.DEVICE_SERIAL, new byte[REPORT_TYPE_FEATURE_DATA_SIZE * 2]);
this.tryClaim();

final byte[] response = this.readResponse(4, true);
final byte[] response;
try {
this.write(Slot.DEVICE_SERIAL, new byte[REPORT_TYPE_FEATURE_DATA_SIZE * 2]);

response = this.readResponse(4, true);
} finally {
this.release();
}

return (response[0] << 24) + (response[1] << 16) + (response[2] << 8) + (response[3] & 0xff);
}

@Override
public byte[] challengeResponse(final Slot slot, final byte[] challenge) throws YubiKeyException {
slot.ensureChallengeResponseSlot();
this.write(slot, challenge);

return this.readResponse(CHALLENGE_RESPONSE_LENGTH, true);
this.tryClaim();
try {
this.write(slot, challenge);

return this.readResponse(CHALLENGE_RESPONSE_LENGTH, true);
} finally {
this.release();
}
}

private char CRC16(final byte[] buffer, final int bytes) {
Expand Down Expand Up @@ -178,6 +191,7 @@ private void reset() throws YubiKeyException {
final byte[] dummy = new byte[REPORT_TYPE_FEATURE_DATA_SIZE];
dummy[REPORT_TYPE_FEATURE_DATA_SIZE - 1] = DUMMY_REPORT;

// this requires that the YubiKey was already claimed
this.write(Slot.DUMMY, dummy);
}

Expand All @@ -186,6 +200,10 @@ private void tryClaim() throws YubiKeyException {
throw new YubiKeyException("Failed to claim interface");
}

private void release() {
this.connection.releaseInterface(this.device.getInterface(0)); // We probably don't really need to care about errors here
}

private byte[] waitForStatus(final boolean mayBlock, final short mask, final StatusMode mode) throws YubiKeyException {
int waitInterval = 1;
boolean waitingForUserInteraction = false;
Expand All @@ -199,15 +217,10 @@ private byte[] waitForStatus(final boolean mayBlock, final short mask, final Sta

waitInterval *= 2;

this.tryClaim();
try {
final int bytes = this.connection.controlTransfer(UsbConstants.USB_TYPE_CLASS | UsbConstants.USB_DIR_IN | 0x1, HID_GET_REPORT, REPORT_TYPE_FEATURE, 0, data, REPORT_TYPE_FEATURE_DATA_SIZE, YUBIKEY_OPERATION_TIMEOUT_MS);
final int bytes = this.connection.controlTransfer(UsbConstants.USB_TYPE_CLASS | UsbConstants.USB_DIR_IN | 0x1, HID_GET_REPORT, REPORT_TYPE_FEATURE, 0, data, REPORT_TYPE_FEATURE_DATA_SIZE, YUBIKEY_OPERATION_TIMEOUT_MS);

if (bytes != REPORT_TYPE_FEATURE_DATA_SIZE)
throw new YubiKeyException("controlTransfer failed: " + bytes);
} finally {
this.connection.releaseInterface(this.device.getInterface(0));
}
if (bytes != REPORT_TYPE_FEATURE_DATA_SIZE)
throw new YubiKeyException("controlTransfer failed: " + bytes);

switch (mode) {
case SET:
Expand Down Expand Up @@ -250,24 +263,17 @@ private byte[] readResponse(final int expectedBytes, final boolean mayBlock) thr
while (bytesRead + REPORT_TYPE_FEATURE_DATA_SIZE <= response.length || expectedBytes == 0) {
final byte[] data = new byte[REPORT_TYPE_FEATURE_DATA_SIZE];

this.tryClaim();
try {
final int bytes = this.connection.controlTransfer(UsbConstants.USB_TYPE_CLASS | UsbConstants.USB_DIR_IN | 0x1, HID_GET_REPORT, REPORT_TYPE_FEATURE, 0, data, REPORT_TYPE_FEATURE_DATA_SIZE, YUBIKEY_OPERATION_TIMEOUT_MS);
final int bytes = this.connection.controlTransfer(UsbConstants.USB_TYPE_CLASS | UsbConstants.USB_DIR_IN | 0x1, HID_GET_REPORT, REPORT_TYPE_FEATURE, 0, data, REPORT_TYPE_FEATURE_DATA_SIZE, YUBIKEY_OPERATION_TIMEOUT_MS);

if (bytes != REPORT_TYPE_FEATURE_DATA_SIZE)
throw new YubiKeyException("controlTransfer failed: " + bytes);
} finally {
this.connection.releaseInterface(this.device.getInterface(0));
}
if (bytes != REPORT_TYPE_FEATURE_DATA_SIZE)
throw new YubiKeyException("controlTransfer failed: " + bytes);

if ((data[REPORT_TYPE_FEATURE_DATA_SIZE - 1] & STATUS_FLAG_RESPONSE_PENDING) == STATUS_FLAG_RESPONSE_PENDING) {
if ((data[REPORT_TYPE_FEATURE_DATA_SIZE - 1] & 0b11111) == 0) {
if (expectedBytes > 0) {
this.verifyCRC16(response, expectedBytes + 2);
}

this.reset();

if (response.length > expectedBytes) {
final byte[] result = new byte[expectedBytes];
System.arraycopy(response, 0, result, 0, expectedBytes);
Expand Down Expand Up @@ -327,16 +333,11 @@ private void write(final Slot slot, final byte[] data) throws YubiKeyException {

this.waitForStatus(false, STATUS_FLAG_WRITE, StatusMode.CLEAR);

this.tryClaim();
try {
//noinspection PointlessBitwiseExpression
final int bytes = this.connection.controlTransfer(UsbConstants.USB_TYPE_CLASS | UsbConstants.USB_DIR_OUT | 0x1, HID_SET_REPORT, REPORT_TYPE_FEATURE, 0, sequenceData, REPORT_TYPE_FEATURE_DATA_SIZE, YUBIKEY_OPERATION_TIMEOUT_MS);
//noinspection PointlessBitwiseExpression
final int bytes = this.connection.controlTransfer(UsbConstants.USB_TYPE_CLASS | UsbConstants.USB_DIR_OUT | 0x1, HID_SET_REPORT, REPORT_TYPE_FEATURE, 0, sequenceData, REPORT_TYPE_FEATURE_DATA_SIZE, YUBIKEY_OPERATION_TIMEOUT_MS);

if (bytes != REPORT_TYPE_FEATURE_DATA_SIZE)
throw new YubiKeyException("controlTransfer failed: " + bytes);
} finally {
this.connection.releaseInterface(this.device.getInterface(0));
}
if (bytes != REPORT_TYPE_FEATURE_DATA_SIZE)
throw new YubiKeyException("controlTransfer failed: " + bytes);
}
}
}

0 comments on commit 9d3d355

Please sign in to comment.