From 24b6f13f8c221771df6f087530574d222a71d6a1 Mon Sep 17 00:00:00 2001 From: Robert Quattlebaum Date: Wed, 18 Mar 2020 18:34:39 -0700 Subject: [PATCH] Added support for "dont-enforce-user-presence-and-sign" (0x08) This change adds support for the control byte value 0x08, which is the value for "dont-enforce-user-presence-and-sign". In this mode, user presence is not enforced but a signature is still created. --- .../java/com/ledger/u2f/NullPresence.java | 8 ++++- .../java/com/ledger/u2f/OneShotPresence.java | 16 ++++++++-- src/main/java/com/ledger/u2f/Presence.java | 32 ++++++++++++++++++- src/main/java/com/ledger/u2f/U2FApplet.java | 26 ++++++++------- 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/ledger/u2f/NullPresence.java b/src/main/java/com/ledger/u2f/NullPresence.java index ae3ef0b..64c8289 100644 --- a/src/main/java/com/ledger/u2f/NullPresence.java +++ b/src/main/java/com/ledger/u2f/NullPresence.java @@ -6,6 +6,12 @@ */ public class NullPresence implements Presence { @Override - public void verify_user_presence() { + public byte enforce_user_presence() { + return FLAG_USER_PRESENT; + } + + @Override + public byte check_user_presence() { + return 0; } } diff --git a/src/main/java/com/ledger/u2f/OneShotPresence.java b/src/main/java/com/ledger/u2f/OneShotPresence.java index 9c96f0b..5da05b5 100644 --- a/src/main/java/com/ledger/u2f/OneShotPresence.java +++ b/src/main/java/com/ledger/u2f/OneShotPresence.java @@ -29,10 +29,22 @@ public class OneShotPresence implements Presence { } @Override - public void verify_user_presence() { - if (did_verify_flag[0] != 0) { + public byte enforce_user_presence() { + byte presence = check_user_presence(); + + if ((presence & FLAG_USER_PRESENT) != FLAG_USER_PRESENT) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } + + return presence; + } + + @Override + public byte check_user_presence() { + if (did_verify_flag[0] != 0) { + return 0; + } did_verify_flag[0] = 1; + return FLAG_USER_PRESENT; } } diff --git a/src/main/java/com/ledger/u2f/Presence.java b/src/main/java/com/ledger/u2f/Presence.java index 01b26da..b4ac85b 100644 --- a/src/main/java/com/ledger/u2f/Presence.java +++ b/src/main/java/com/ledger/u2f/Presence.java @@ -1,6 +1,36 @@ package com.ledger.u2f; public interface Presence { + /** A user is present. + * + * This means that someone interacted with the + * authenticator while performing this operation. + */ + byte FLAG_USER_PRESENT = (byte)0x01; - void verify_user_presence(); + /** The user was verified. From webauthn. + * + * This means that the user has verified themselves + * to the authenticator using, for example, their + * fingerprint or a PIN. + */ + byte FLAG_USER_VERIFIED = (byte)0x04; + + /** + * Waits for user presence, throwing an exception if that + * is impossible or if there is a timeout. + * + * @return the value of the user presence byte + * in the FIDO U2F signature response. + */ + byte enforce_user_presence(); + + /** + * Checks user presence. Should not throw an exception. + * Returns immediately. + * + * @return the value of the user presence byte + * in the FIDO U2F signature response. + */ + byte check_user_presence(); } diff --git a/src/main/java/com/ledger/u2f/U2FApplet.java b/src/main/java/com/ledger/u2f/U2FApplet.java index e70448b..98e411a 100644 --- a/src/main/java/com/ledger/u2f/U2FApplet.java +++ b/src/main/java/com/ledger/u2f/U2FApplet.java @@ -71,8 +71,9 @@ public class U2FApplet extends Applet implements ExtendedLength { private static final byte TRANSPORT_NOT_EXTENDED_CERT = (byte)3; private static final byte TRANSPORT_NOT_EXTENDED_SIGNATURE = (byte)4; - private static final byte P1_SIGN_OPERATION = (byte)0x03; + private static final byte P1_ENFORCE_PRESENCE_AND_SIGN = (byte)0x03; private static final byte P1_SIGN_CHECK_ONLY = (byte)0x07; + private static final byte P1_IGNORE_PRESENCE_AND_SIGN = (byte)0x08; private static final byte ENROLL_LEGACY_VERSION = (byte)0x05; private static final byte RFU_ENROLL_SIGNED_VERSION[] = { (byte)0x00 }; @@ -83,8 +84,6 @@ public class U2FApplet extends Applet implements ExtendedLength { private static final short APDU_CHALLENGE_OFFSET = (short)0; private static final short APDU_APPLICATION_PARAMETER_OFFSET = (short)32; - private static final byte FLAG_USER_PRESENCE_VERIFIED = (byte)0x01; - private static final short FIDO_SW_TEST_OF_PRESENCE_REQUIRED = ISO7816.SW_CONDITIONS_NOT_SATISFIED; private static final short FIDO_SW_INVALID_KEY_HANDLE = ISO7816.SW_WRONG_DATA; @@ -163,7 +162,7 @@ private void handleEnroll(APDU apdu) throws ISOException { } // Deny if user presence cannot be validated - presence.verify_user_presence(); + presence.enforce_user_presence(); // Generate the key pair if (localPrivateTransient) { @@ -208,6 +207,7 @@ private void handleSign(APDU apdu) throws ISOException { short dataOffset = apdu.getOffsetCdata(); byte p1 = buffer[ISO7816.OFFSET_P1]; boolean sign = false; + boolean enforce_presence = false; short keyHandleLength; boolean extendedLength = (dataOffset != ISO7816.OFFSET_CDATA); short outOffset = SCRATCH_PAD; @@ -215,7 +215,11 @@ private void handleSign(APDU apdu) throws ISOException { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } switch(p1) { - case P1_SIGN_OPERATION: + case P1_ENFORCE_PRESENCE_AND_SIGN: + sign = true; + enforce_presence = true; + break; + case P1_IGNORE_PRESENCE_AND_SIGN: sign = true; break; case P1_SIGN_CHECK_ONLY: @@ -237,15 +241,15 @@ private void handleSign(APDU apdu) throws ISOException { ISOException.throwIt(FIDO_SW_TEST_OF_PRESENCE_REQUIRED); } - // If signing, only proceed if user presence can be validated - presence.verify_user_presence(); + if (enforce_presence) { + scratch[outOffset++] = presence.enforce_user_presence(); + } else { + scratch[outOffset++] = presence.check_user_presence(); + } - // Increase the counter + // Increase the signature counter counter.inc(); - // Prepare reply - scratch[outOffset++] = FLAG_USER_PRESENCE_VERIFIED; - outOffset = counter.writeValue(scratch, outOffset); localSignature.init(localPrivateKey, Signature.MODE_SIGN);