Skip to content

Commit

Permalink
Work-around for Native iOS Support
Browse files Browse the repository at this point in the history
iOS/Safari now offers native support for FIDO2 authenticators.
However, while all existing U2F authenticators are also FIDO2 CTAP1
authenticators, this applet doesn't seem to be compatible with this
new iOS feature. This commit remedies this by adding a lightweight
work-around that coerces recent versions of iOS to use CTAP1 to both
register and authenticate with this applet via NFC.

The work-around is to implement support for the FIDO2 `NFCCTAP_MSG`
command and have it always return the error
`CTAP1_ERR_INVALID_COMMAND`. This is apparently enough to coerce iOS
to use CTAP1 with the authenticator instead of CTAP2.

This work-around should remain viable since the FIDO2 CTAP spec states
that the platform should attempt to use CTAP1 when the authenticator
returns a command error or improperly formated CBOR in response to the
get info command. Apparently iOS isn't considering APDU errors to be
"command errors", but CTAP errors apparently are interpreted as such.
  • Loading branch information
darconeous committed Mar 20, 2020
1 parent 7a7dcc7 commit 8b58c4c
Showing 1 changed file with 48 additions and 29 deletions.
77 changes: 48 additions & 29 deletions src/main/java/com/ledger/u2f/U2FApplet.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public class U2FApplet extends Applet implements ExtendedLength {
private static final byte FIDO_INS_SIGN = (byte)0x02;
private static final byte FIDO_INS_VERSION = (byte)0x03;
private static final byte ISO_INS_GET_DATA = (byte)0xC0;
private static final byte FIDO2_INS_NFCCTAP_MSG = (byte)0x10;

private static final byte PROPRIETARY_CLA = (byte)0xF0;
private static final byte FIDO_ADM_SET_ATTESTATION_CERT = (byte)0x01;
Expand Down Expand Up @@ -102,6 +103,8 @@ public class U2FApplet extends Applet implements ExtendedLength {

private static final byte INSTALL_FLAG_DISABLE_USER_PRESENCE = (byte)0x01;

private static final byte CTAP1_ERR_INVALID_COMMAND = (byte)0x01;

// Parameters
// 1 byte : flags
// 2 bytes big endian short : length of attestation certificate
Expand Down Expand Up @@ -387,42 +390,58 @@ public void process(APDU apdu) throws ISOException {
}
return;
}
if (buffer[ISO7816.OFFSET_CLA] == PROPRIETARY_CLA) {
if (attestationCertificateSet) {
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
}
switch(buffer[ISO7816.OFFSET_INS]) {
case FIDO_ADM_SET_ATTESTATION_CERT:

if (!attestationCertificateSet) {
if ((buffer[ISO7816.OFFSET_CLA] & (byte)0x80) == (byte)0x80
&& buffer[ISO7816.OFFSET_INS] == FIDO_ADM_SET_ATTESTATION_CERT
) {
handleSetAttestationCert(apdu);
break;
default:
} else {
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
return;
}
else if (buffer[ISO7816.OFFSET_CLA] == FIDO_CLA) {
if (!attestationCertificateSet) {
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
}
switch(buffer[ISO7816.OFFSET_INS]) {
case FIDO_INS_ENROLL:
handleEnroll(apdu);
break;
case FIDO_INS_SIGN:
handleSign(apdu);
break;
case FIDO_INS_VERSION:
handleVersion(apdu);
break;
case ISO_INS_GET_DATA:
handleGetData(apdu);
break;
default:

if ((buffer[ISO7816.OFFSET_CLA] & (byte)0x80) == (byte)0x80) {
if (buffer[ISO7816.OFFSET_INS] == FIDO2_INS_NFCCTAP_MSG) {
handleCtap2(apdu);
} else {
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
} else {
switch (buffer[ISO7816.OFFSET_INS]) {
case FIDO_INS_ENROLL:
handleEnroll(apdu);
break;
case FIDO_INS_SIGN:
handleSign(apdu);
break;
case FIDO_INS_VERSION:
handleVersion(apdu);
break;
case ISO_INS_GET_DATA:
handleGetData(apdu);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
else {
ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
}
}

private void
handleCtap2(APDU apdu)
{
// We don't actually support CTAP2, so we always
// return CTAP1_ERR_INVALID_COMMAND.
final byte[] buffer = apdu.getBuffer();
apdu.setIncomingAndReceive();
short dataOffset = apdu.getOffsetCdata();

short len = 0;

buffer[len++] = CTAP1_ERR_INVALID_COMMAND;

apdu.setOutgoingAndSend((short)0, len);
}

public static void install (byte bArray[], short bOffset, byte bLength) throws ISOException {
Expand Down

0 comments on commit 8b58c4c

Please sign in to comment.