Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement authenticatorBioEnrollment #118

Merged
merged 15 commits into from
Mar 5, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class CtapException extends CommandException {
public static final byte ERR_MISSING_PARAMETER = 0x14;
public static final byte ERR_LIMIT_EXCEEDED = 0x15;
public static final byte ERR_UNSUPPORTED_EXTENSION = 0x16;
public static final byte ERR_FP_DATABASE_FULL = 0x17;
public static final byte ERR_CREDENTIAL_EXCLUDED = 0x19;
public static final byte ERR_PROCESSING = 0x21;
public static final byte ERR_INVALID_CREDENTIAL = 0x22;
Expand Down
78 changes: 78 additions & 0 deletions fido/src/main/java/com/yubico/yubikit/fido/ctap/BioEnrollment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (C) 2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.yubico.yubikit.fido.ctap;

import com.yubico.yubikit.core.application.CommandException;

import java.io.IOException;
import java.util.Map;
import java.util.Objects;

/**
* Implements Bio enrollment commands.
*
* @see <a href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#authenticatorBioEnrollment">authenticatorBioEnrollment</a>
*/
public class BioEnrollment {
protected static final int RESULT_MODALITY = 0x01;
protected static final int MODALITY_FINGERPRINT = 0x01;

protected final Ctap2Session ctap;
protected final int modality;

public BioEnrollment(Ctap2Session ctap, int modality) throws IOException, CommandException {
if (!isSupported(ctap.getCachedInfo())) {
throw new IllegalStateException("Bio enrollment not supported");
}

this.ctap = ctap;
this.modality = getModality(ctap);

if (this.modality != modality) {
throw new IllegalStateException("Device does not support modality " + modality);
}
}

public static boolean isSupported(Ctap2Session.InfoData info) {
final Map<String, ?> options = info.getOptions();
if (options.containsKey("bioEnroll")) {
return true;
} else return info.getVersions().contains("FIDO_2_1_PRE") &&
options.containsKey("userVerificationMgmtPreview");
}

/**
* Get the type of modality the authenticator supports.
*
* @param ctap CTAP2 session
* @return The type of modality authenticator supports. For fingerprint, its value is 1.
* @throws IOException A communication error in the transport layer.
* @throws CommandException A communication in the protocol layer.
* @see <a href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#getUserVerificationModality">Get bio modality</a>
*/
public static int getModality(Ctap2Session ctap) throws IOException, CommandException {
final Map<Integer, ?> result = ctap.bioEnrollment(
null,
null,
null,
null,
null,
Boolean.TRUE,
null);
return Objects.requireNonNull((Integer) result.get(RESULT_MODALITY));
}
}
50 changes: 50 additions & 0 deletions fido/src/main/java/com/yubico/yubikit/fido/ctap/Ctap2Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ public class Ctap2Session extends ApplicationSession<Ctap2Session> {
private final InfoData info;
@Nullable
private final Byte credentialManagerCommand;
@Nullable
private final Byte bioEnrollmentCommand;

private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Ctap2Session.class);

Expand Down Expand Up @@ -136,6 +138,15 @@ private Ctap2Session(Version version, Backend<?> backend)
} else {
this.credentialManagerCommand = null;
}

if (options.containsKey("bioEnroll")) {
this.bioEnrollmentCommand = CMD_BIO_ENROLLMENT;
} else if (info.getVersions().contains("FIDO_2_1_PRE") &&
options.containsKey("userVerificationMgmtPreview")) {
this.bioEnrollmentCommand = CMD_BIO_ENROLLMENT_PRE;
} else {
this.bioEnrollmentCommand = null;
}
}

private static Backend<SmartCardProtocol> getSmartCardBackend(SmartCardConnection connection)
Expand Down Expand Up @@ -394,6 +405,45 @@ public void reset(@Nullable CommandState state) throws IOException, CommandExcep
sendCbor(CMD_RESET, null, state);
}

/**
* This command is used by the platform to provision/enumerate/delete bio enrollments in the
* authenticator.
*
* @param modality the user verification modality being requested
* @param subCommand the user verification sub command currently being requested
* @param subCommandParams a map of subCommands parameters
* @param pinUvAuthProtocol PIN/UV protocol version chosen by the platform
* @param pinUvAuthParam first 16 bytes of HMAC-SHA-256 of contents using pinUvAuthToken
* @param getModality get the user verification type modality
* @param state an optional state object to cancel a request and handle
* keepalive signals
* @throws IOException A communication error in the transport layer.
* @throws CommandException A communication in the protocol layer.
* @see <a href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#authenticatorBioEnrollment">authenticatorBioEnrollment</a>
*/
Map<Integer, ?> bioEnrollment(
@Nullable Integer modality,
@Nullable Integer subCommand,
@Nullable Map<?, ?> subCommandParams,
@Nullable Integer pinUvAuthProtocol,
@Nullable byte[] pinUvAuthParam,
@Nullable Boolean getModality,
@Nullable CommandState state
) throws IOException, CommandException {
if (bioEnrollmentCommand == null) {
throw new IllegalStateException("Bio enrollment not supported");
}
return sendCbor(
bioEnrollmentCommand, args(
modality,
subCommand,
subCommandParams,
pinUvAuthProtocol,
pinUvAuthParam,
getModality
), state);
}

/**
* This command is used by the platform to manage discoverable credentials on the
* authenticator.
Expand Down
Loading
Loading