All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yubico.u2f.U2fPrimitives Maven / Gradle / Ivy

There is a newer version: 0.19.12
Show newest version
/*
 * Copyright 2014 Yubico.
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file or at
 * https://developers.google.com/open-source/licenses/bsd
 */

package com.yubico.u2f;

import com.google.common.base.Optional;
import com.yubico.u2f.crypto.BouncyCastleCrypto;
import com.yubico.u2f.crypto.ChallengeGenerator;
import com.yubico.u2f.crypto.Crypto;
import com.yubico.u2f.crypto.RandomChallengeGenerator;
import com.yubico.u2f.data.DeviceRegistration;
import com.yubico.u2f.data.messages.*;
import com.yubico.u2f.data.messages.key.RawAuthenticateResponse;
import com.yubico.u2f.data.messages.key.RawRegisterResponse;
import com.yubico.u2f.data.messages.key.util.U2fB64Encoding;
import com.yubico.u2f.exceptions.DeviceCompromisedException;
import com.yubico.u2f.exceptions.U2fBadInputException;

import java.util.Set;

import static com.google.common.base.Preconditions.checkArgument;

public class U2fPrimitives {

    public static final String AUTHENTICATE_TYP = "navigator.id.getAssertion";
    public static final String REGISTER_TYPE = "navigator.id.finishEnrollment";
    public static final String U2F_VERSION = "U2F_V2";

    private final Crypto crypto;
    private final ChallengeGenerator challengeGenerator;

    public U2fPrimitives(Crypto crypto, ChallengeGenerator challengeGenerator) {
        this.crypto = crypto;
        this.challengeGenerator = challengeGenerator;
    }

    public U2fPrimitives() {
        this(new BouncyCastleCrypto(), new RandomChallengeGenerator());
    }

    /**
     * @see U2fPrimitives#startRegistration(String, byte[])
     */
    public RegisterRequest startRegistration(String appId) {
        return startRegistration(appId, challengeGenerator.generateChallenge());
    }

    /**
     * Initiates the registration of a device.
     *
     * @param appId     the U2F AppID. Set this to the Web Origin of the login page, unless you need to
     *                  support logging in from multiple Web Origins.
     * @param challenge the challenge to use
     * @return a RegisterRequest, which should be sent to the client and temporary saved by the
     * server.
     */
    public RegisterRequest startRegistration(String appId, byte[] challenge) {
        return new RegisterRequest(U2fB64Encoding.encode(challenge), appId);
    }

    /**
     * @see U2fPrimitives#finishRegistration(com.yubico.u2f.data.messages.RegisterRequest, com.yubico.u2f.data.messages.RegisterResponse, java.util.Set)
     */
    public DeviceRegistration finishRegistration(RegisterRequest registerRequest, RegisterResponse response) throws U2fBadInputException {
        return finishRegistration(registerRequest, response, null);
    }

    /**
     * Finishes a previously started registration.
     *
     * @param registerRequest
     * @param response        the response from the device/client.
     * @return a DeviceRegistration object, holding information about the registered device. Servers should
     * persist this.
     */
    public DeviceRegistration finishRegistration(RegisterRequest registerRequest,
                                                 RegisterResponse response,
                                                 Set facets) throws U2fBadInputException {
        ClientData clientData = response.getClientData();
        clientData.checkContent(REGISTER_TYPE, registerRequest.getChallenge(), Optional.fromNullable(facets));

        RawRegisterResponse rawRegisterResponse = RawRegisterResponse.fromBase64(response.getRegistrationData(), crypto);
        rawRegisterResponse.checkSignature(registerRequest.getAppId(), clientData.asJson());
        return rawRegisterResponse.createDevice();
    }

    /**
     * Initiates the authentication process.
     *
     * @see U2fPrimitives#startAuthentication(String, com.yubico.u2f.data.DeviceRegistration, byte[])
     */
    public AuthenticateRequest startAuthentication(String appId, DeviceRegistration deviceRegistration) {
        return startAuthentication(appId, deviceRegistration, challengeGenerator.generateChallenge());
    }

    /**
     * Initiates the authentication process.
     *
     * @param appId              the U2F AppID. Set this to the Web Origin of the login page, unless you need to
     *                           support logging in from multiple Web Origins.
     * @param deviceRegistration the DeviceRegistration for which to initiate authentication.
     * @param challenge          the challenge to use
     * @return an AuthenticateRequest which should be sent to the client and temporary saved by
     * the server.
     */
    public AuthenticateRequest startAuthentication(String appId, DeviceRegistration deviceRegistration, byte[] challenge) {
        checkArgument(!deviceRegistration.isCompromised(), "Device has been marked as compromised, cannot authenticate");

        return new AuthenticateRequest(
                U2fB64Encoding.encode(challenge),
                appId,
                deviceRegistration.getKeyHandle()
        );
    }

    /**
     * @see U2fPrimitives#finishAuthentication(com.yubico.u2f.data.messages.AuthenticateRequest, com.yubico.u2f.data.messages.AuthenticateResponse, com.yubico.u2f.data.DeviceRegistration, java.util.Set)
     */
    public void finishAuthentication(AuthenticateRequest authenticateRequest,
                                     AuthenticateResponse response,
                                     DeviceRegistration deviceRegistration) throws U2fBadInputException, DeviceCompromisedException {
        finishAuthentication(authenticateRequest, response, deviceRegistration, null);
    }

    /**
     * Finishes a previously started authentication.
     *
     * @param authenticateRequest
     * @param response            the response from the device/client.
     */
    public void finishAuthentication(AuthenticateRequest authenticateRequest,
                                     AuthenticateResponse response,
                                     DeviceRegistration deviceRegistration,
                                     Set facets) throws U2fBadInputException, DeviceCompromisedException {
        checkArgument(!deviceRegistration.isCompromised(), "Device has been marked as compromised, cannot authenticate");
        checkArgument(authenticateRequest.getKeyHandle().equals(deviceRegistration.getKeyHandle()), "Wrong DeviceRegistration for the given AuthenticateRequest");
        if (!deviceRegistration.getKeyHandle().equals(response.getKeyHandle())) {
            throw new U2fBadInputException("KeyHandle of AuthenticateResponse does not match");
        }

        ClientData clientData = response.getClientData();
        clientData.checkContent(AUTHENTICATE_TYP, authenticateRequest.getChallenge(), Optional.fromNullable(facets));

        RawAuthenticateResponse rawAuthenticateResponse = RawAuthenticateResponse.fromBase64(
                response.getSignatureData(), crypto
        );
        rawAuthenticateResponse.checkSignature(
                authenticateRequest.getAppId(),
                clientData.asJson(),
                U2fB64Encoding.decode(deviceRegistration.getPublicKey())
        );
        rawAuthenticateResponse.checkUserPresence();
        deviceRegistration.checkAndUpdateCounter(rawAuthenticateResponse.getCounter());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy