diff --git a/.gitignore b/.gitignore
index c2ea76b..3b905b2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,7 +12,6 @@ ant.properties
#Gradle
.gradle
build
-gradle.properties
#Maven
target
diff --git a/README.md b/README.md
index 95c3b65..c210297 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@ This includes the following artifacts of the SDK (cf. [overview of all artifacts
| de.cotech:hwsecurity-intent-nfc | 14 | |
| de.cotech:hwsecurity-intent-usb | 14 | |
| de.cotech:hwsecurity-fido | 14 | 19 |
+| de.cotech:hwsecurity-fido2 | 14 | 19 |
| de.cotech:hwsecurity-openpgp | 14 | |
| de.cotech:hwsecurity-piv | 14 | |
| de.cotech:hwsecurity-ui | 14 | 19 |
@@ -24,7 +25,7 @@ This includes the following artifacts of the SDK (cf. [overview of all artifacts
## Notice
This open source release does not reflect the newest version of the SDK.
-Some parts are currently not released as GPLv3, such as FIDO2 support.
+Some parts are currently not released as GPLv3.
## Contributing
diff --git a/build.gradle b/build.gradle
index eea7c10..f22a60e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,9 +5,8 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.4.2'
+ classpath 'com.android.tools.build:gradle:4.0.0'
classpath 'org.jetbrains.dokka:dokka-android-gradle-plugin:0.9.17'
- classpath 'digital.wup:android-maven-publish:3.6.2'
}
}
@@ -19,6 +18,6 @@ allprojects {
}
ext {
- compileSdkVersion = 28
- hwSdkVersionName = '3.2.1'
+ compileSdkVersion = 29
+ hwSdkVersionName = '4.1.0'
}
diff --git a/gen-dokka.sh b/gen-dokka.sh
deleted file mode 100755
index e8197fe..0000000
--- a/gen-dokka.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-rm -R dokka/
-java -jar hw-security/libs/dokka-hugo-fatjar-0.9.17.jar hwsecurity/src/main/java/ -output dokka/reference/ -format hugo
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..63d7b24
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,16 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+
+
+android.useAndroidX=true
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index d4202d0..ab53446 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue Jun 18 09:22:28 CEST 2019
+#Wed Jun 24 11:02:11 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
diff --git a/gradlew b/gradlew
index af6708f..b0d6d0a 100755
--- a/gradlew
+++ b/gradlew
@@ -1,5 +1,21 @@
#!/usr/bin/env sh
+#
+# Copyright 2015 the original author or authors.
+#
+# 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.
+#
+
##############################################################################
##
## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m"'
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
diff --git a/gradlew.bat b/gradlew.bat
index 0f8d593..15e1ee3 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,3 +1,19 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem http://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m"
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
diff --git a/hwsecurity/core/build.gradle b/hwsecurity/core/build.gradle
index 4ab2e28..c8b6980 100644
--- a/hwsecurity/core/build.gradle
+++ b/hwsecurity/core/build.gradle
@@ -1,11 +1,11 @@
apply plugin: 'com.android.library'
-apply plugin: 'digital.wup.android-maven-publish'
+apply plugin: 'maven-publish'
apply plugin: 'org.jetbrains.dokka-android'
dependencies {
- implementation 'androidx.lifecycle:lifecycle-runtime:2.0.0'
+ implementation 'androidx.lifecycle:lifecycle-runtime:2.2.0'
- compileOnly 'androidx.annotation:annotation:1.0.0'
+ compileOnly 'androidx.annotation:annotation:1.1.0'
api 'com.google.auto.value:auto-value-annotations:1.6.2'
annotationProcessor 'com.google.auto.value:auto-value:1.6.2'
@@ -35,51 +35,54 @@ android {
}
}
-publishing {
- publications {
- mavenAar(MavenPublication) {
- groupId = 'de.cotech'
- artifactId = 'hwsecurity'
- version = android.defaultConfig.versionName
+// https://developer.android.com/studio/build/maven-publish-plugin
+afterEvaluate {
+ publishing {
+ publications {
+ release(MavenPublication) {
+ from components.release
- from components.android
+ groupId = 'de.cotech'
+ artifactId = 'hwsecurity'
+ version = android.defaultConfig.versionName
- pom {
- url = 'https://hwsecurity.dev'
- licenses {
- license {
- name = 'Commercial'
- url = 'https://hwsecurity.dev/sales/'
- distribution = 'repo'
+ pom {
+ url = 'https://hwsecurity.dev'
+ licenses {
+ license {
+ name = 'Commercial'
+ url = 'https://hwsecurity.dev/sales/'
+ distribution = 'repo'
+ }
+ license {
+ name = 'GNU General Public License, version 3'
+ url = 'https://www.gnu.org/licenses/gpl-3.0.txt'
+ }
}
- license {
- name = 'GNU General Public License, version 3'
- url = 'https://www.gnu.org/licenses/gpl-3.0.txt'
+ organization {
+ name = 'Confidential Technologies GmbH'
+ url = 'https://www.cotech.de'
}
}
- organization {
- name = 'Confidential Technologies GmbH'
- url = 'https://www.cotech.de'
- }
}
}
- }
- /*
- * To upload release, create file gradle.properties in ~/.gradle/ with this content:
- *
- * cotechMavenName=xxx
- * cotechMavenPassword=xxx
- */
- if (project.hasProperty('cotechMavenName') && project.hasProperty('cotechMavenPassword')) {
- println "Found cotechMavenName, cotechMavenPassword in gradle.properties!"
+ /*
+ * To upload release, create file gradle.properties in ~/.gradle/ with this content:
+ *
+ * cotechMavenName=xxx
+ * cotechMavenPassword=xxx
+ */
+ if (project.hasProperty('cotechMavenName') && project.hasProperty('cotechMavenPassword')) {
+ println "Found cotechMavenName, cotechMavenPassword in gradle.properties!"
- repositories {
- maven {
- credentials {
- username cotechMavenName
- password cotechMavenPassword
+ repositories {
+ maven {
+ credentials {
+ username cotechMavenName
+ password cotechMavenPassword
+ }
+ url = "https://maven.cotech.de"
}
- url = "https://maven.cotech.de"
}
}
}
@@ -90,8 +93,9 @@ dokka {
dokkaFatJar = files('libs/dokka-hugo-fatjar-0.9.17.jar')
// does not work correctly with Maven:
//dokkaFatJar = 'de.cotech:dokka-hugo-fatjar:0.9.17'
+ moduleName = 'hwsecurity'
outputFormat = "hugo"
- outputDirectory = "$buildDir/dokka/reference"
+ outputDirectory = "$projectDir/../../hwsecurity.dev/content/reference"
sourceDirs = files('src/main/java')
packageOptions {
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKey.java b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKey.java
index 2994b55..0b8e060 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKey.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -29,7 +29,7 @@
import androidx.annotation.AnyThread;
import androidx.annotation.WorkerThread;
-import androidx.lifecycle.LifecycleOwner;
+
import de.cotech.hw.internal.transport.SecurityKeyInfo.TransportType;
import de.cotech.hw.internal.transport.Transport;
@@ -81,7 +81,7 @@ public boolean isTransportNfc() {
*/
@AnyThread
public boolean isTransportUsb() {
- return transport.getTransportType() == TransportType.USB_CCID || transport.getTransportType() == TransportType.USB_U2FHID;
+ return transport.getTransportType() == TransportType.USB_CCID || transport.getTransportType() == TransportType.USB_CTAPHID;
}
/**
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyAuthenticator.java b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyAuthenticator.java
index cacc6ba..8007770 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyAuthenticator.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyAuthenticator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyCallback.java b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyCallback.java
index 4e93f42..ac2f909 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyCallback.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyCallback.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -65,7 +65,7 @@ default void onSecurityKeyDiscoveryFailed(@NonNull IOException exception) {
*
* This callback is only called on Security Keys for which {@link SecurityKey#isPersistentlyConnected()}
* returns true. This typically applies to USB devices, but can be applied to NFC devices as well if
- * NFC tag monitoring has been enabled via {@link SecurityKeyManagerConfig.Builder#setEnableNfcTagMonitoring}.
+ * persistent NFC connection has been enabled via {@link SecurityKeyManagerConfig.Builder#setEnablePersistentNfcConnection}.
* Those Security Keys are also listed under {@link SecurityKeyManager#getConnectedPersistentSecurityKeys()}.
*
*
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyConnectionMode.java b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyConnectionMode.java
index 638847a..9b4c38b 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyConnectionMode.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyConnectionMode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyException.java b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyException.java
index e531514..b354194 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyManager.java b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyManager.java
index 346b314..9ba33a3 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyManager.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -206,7 +206,7 @@ protected boolean isLoggable(String tag, int priority) {
callbackHandlerWorker, config.isAllowUntestedUsbDevices(), config.isEnableDebugLogging());
nfcTagManager = NfcTagManager.createInstance(
this::transportConnectAndDeliverOrPostponeOrFail,
- callbackHandlerWorker, config.isEnableDebugLogging(), config.isEnableNfcTagMonitoring());
+ callbackHandlerWorker, config.isEnableDebugLogging(), config.isEnablePersistentNfcConnection());
application.registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
installCotechProviderIfAvailable();
@@ -243,6 +243,12 @@ private void bindToActivity(Activity activity) {
if (isUsbDispatchActivity(activity)) {
return;
}
+ if (config.getExcludedActivityClasses().contains(activity.getClass())) {
+ HwTimber.d(
+ "Activity with class %s is excluded, skipping SecurityKeyManager lifecycle initialization.",
+ activity.getClass().getSimpleName());
+ return;
+ }
if (activity == boundActivity) {
return;
}
@@ -401,12 +407,20 @@ private boolean hasActiveCallbacks() {
/**
* Registers a callback for when a security key is discovered.
+ *
+ * @throws IllegalArgumentException if LifecycleOwner is an excluded class, see {@link SecurityKeyManagerConfig.Builder#addExcludedActivityClass}.
*/
public void registerCallback(SecurityKeyConnectionMode mode,
LifecycleOwner lifecycleOwner, SecurityKeyCallback callback) {
if (config == null) {
throw new IllegalStateException("SecurityKeyManager must be initialized in your Application class!");
}
+ if (config.getExcludedActivityClasses().contains(lifecycleOwner.getClass())) {
+ throw new IllegalArgumentException(
+ "Cannot registerCallback for Activity with excluded class " +
+ lifecycleOwner.getClass().getSimpleName() + ". " +
+ "This is a programming error, check your SecurityKeyManagerConfig.");
+ }
RegisteredConnectionMode registeredConnectionMode = new RegisteredConnectionMode<>(mode, callback, false);
lifecycleOwner.getLifecycle().addObserver(registeredConnectionMode);
registeredCallbacks.add(0, registeredConnectionMode);
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyManagerConfig.java b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyManagerConfig.java
index f39fcda..035cd94 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyManagerConfig.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyManagerConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -25,10 +25,14 @@
package de.cotech.hw;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import android.app.Activity;
import android.app.Application;
import androidx.annotation.Nullable;
-
import com.google.auto.value.AutoValue;
import de.cotech.hw.internal.dispatch.UsbIntentDispatchActivity;
import de.cotech.hw.util.HwTimber;
@@ -50,12 +54,14 @@ public abstract class SecurityKeyManagerConfig {
@Nullable
public abstract HwTimber.Tree getLoggingTree();
- public abstract boolean isEnableNfcTagMonitoring();
+ public abstract boolean isEnablePersistentNfcConnection();
public abstract boolean isIgnoreNfcTagAfterUse();
public abstract boolean isDisableNfcDiscoverySound();
+ public abstract List> getExcludedActivityClasses();
+
static SecurityKeyManagerConfig getDefaultConfig() {
return new Builder()
.build();
@@ -70,9 +76,10 @@ public static class Builder {
private boolean isAllowUntestedUsbDevices = false;
private boolean isEnableDebugLogging = false;
private HwTimber.Tree loggingTree = null;
- private boolean isEnableNfcTagMonitoring = false;
+ private boolean isEnablePersistentNfcConnection = false;
private boolean isIgnoreNfcTagAfterUse = false;
private boolean isDisableNfcDiscoverySound = false;
+ private ArrayList> excludedActivityClasses = new ArrayList<>();
/**
* This setting controls USB permission request behavior.
@@ -156,8 +163,8 @@ public Builder setLoggingTree(HwTimber.Tree loggingTree) {
* Enable this setting if you need to retrieve NFC Security Keys via
* {@link SecurityKeyManager#getConnectedPersistentSecurityKeys()}.
*/
- public Builder setEnableNfcTagMonitoring(boolean isEnableNfcTagMonitoring) {
- this.isEnableNfcTagMonitoring = isEnableNfcTagMonitoring;
+ public Builder setEnablePersistentNfcConnection(boolean isEnablePersistentNfcConnection) {
+ this.isEnablePersistentNfcConnection = isEnablePersistentNfcConnection;
return this;
}
@@ -184,6 +191,29 @@ public Builder setDisableNfcDiscoverySound(boolean isDisableNfcDiscoverySound) {
return this;
}
+ /**
+ * Add an Activity to the list of excluded Activities.
+ *
+ * This adds a specific Activity to an exclusion list, making it exempt from the lifecycle
+ * management by SecurityKeyManager. This effectively disables all features of the hwsecurity
+ * SDK while this Activity is in the foreground. This is useful for Activities that manage
+ * their own NFC or USB connections, for example by enabling NFC reader mode via
+ * {@link android.nfc.NfcAdapter#enableReaderMode}, or yielding processing to a
+ * {@link android.nfc.cardemulation.HostApduService}.
+ *
+ * A call to the {@link SecurityKeyManager#registerCallback} method for an Activity that has
+ * been excluded in this way will result in an {@link IllegalArgumentException}.
+ *
+ *
{@code
+ * new SecurityKeyManagerConfig.Builder()
+ * .addExcludedActivityClass(MyCustomNfcActivity.class)
+ * }
+ */
+ public Builder addExcludedActivityClass(Class extends Activity> clazz) {
+ this.excludedActivityClasses.add(clazz);
+ return this;
+ }
+
/**
* Constructs a SecurityKeyManagerConfig from the Builder.
*/
@@ -193,9 +223,10 @@ public SecurityKeyManagerConfig build() {
isAllowUntestedUsbDevices,
isEnableDebugLogging,
loggingTree,
- isEnableNfcTagMonitoring,
+ isEnablePersistentNfcConnection,
isIgnoreNfcTagAfterUse,
- isDisableNfcDiscoverySound
+ isDisableNfcDiscoverySound,
+ Collections.unmodifiableList(excludedActivityClasses)
);
}
}
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyTlsClientCertificateAuthenticator.java b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyTlsClientCertificateAuthenticator.java
index d4b1b52..4b26791 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyTlsClientCertificateAuthenticator.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/SecurityKeyTlsClientCertificateAuthenticator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/AppletFileNotFoundException.java b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/AppletFileNotFoundException.java
index ebbf00f..d6545ca 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/AppletFileNotFoundException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/AppletFileNotFoundException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/AuthenticationMethodBlockedException.java b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/AuthenticationMethodBlockedException.java
index ed15fab..bd6e812 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/AuthenticationMethodBlockedException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/AuthenticationMethodBlockedException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/ClaNotSupportedException.java b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/ClaNotSupportedException.java
index e0e6cd3..7ee4053 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/ClaNotSupportedException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/ClaNotSupportedException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/ConditionsNotSatisfiedException.java b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/ConditionsNotSatisfiedException.java
index 3acfd5a..c644b67 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/ConditionsNotSatisfiedException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/ConditionsNotSatisfiedException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/FileInTerminationStateException.java b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/FileInTerminationStateException.java
index 871cd08..0945aa4 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/FileInTerminationStateException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/FileInTerminationStateException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/InsNotSupportedException.java b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/InsNotSupportedException.java
index 79ee203..9451bfd 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/InsNotSupportedException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/InsNotSupportedException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SecurityKeyDisconnectedException.java b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SecurityKeyDisconnectedException.java
new file mode 100644
index 0000000..8fd1f33
--- /dev/null
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SecurityKeyDisconnectedException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.exceptions;
+
+
+import java.io.IOException;
+
+
+public class SecurityKeyDisconnectedException extends IOException {
+ public SecurityKeyDisconnectedException(Throwable cause) {
+ super("Security Key is no longer connected (NFC: it has been taken away from the reader, USB: it has been plugged out)", cause);
+ }
+
+ public SecurityKeyDisconnectedException() {
+ this(null);
+ }
+}
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SecurityStatusNotSatisfiedException.java b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SecurityStatusNotSatisfiedException.java
index ea7c78a..5982e0c 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SecurityStatusNotSatisfiedException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SecurityStatusNotSatisfiedException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SelectAppletException.java b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SelectAppletException.java
index fdc95c5..d438365 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SelectAppletException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SelectAppletException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/WrongDataException.java b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/WrongDataException.java
index 9cb930c..e82b704 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/WrongDataException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/WrongDataException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/WrongRequestLengthException.java b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/WrongRequestLengthException.java
index 94c700c..6660175 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/WrongRequestLengthException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/WrongRequestLengthException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/dispatch/NfcIntentDispatchActivity.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/dispatch/NfcIntentDispatchActivity.java
index 6831ba2..2b0db0b 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/dispatch/NfcIntentDispatchActivity.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/dispatch/NfcIntentDispatchActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/dispatch/UsbIntentDispatchActivity.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/dispatch/UsbIntentDispatchActivity.java
index e92c99e..c7b18ba 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/dispatch/UsbIntentDispatchActivity.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/dispatch/UsbIntentDispatchActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/CommandApdu.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/CommandApdu.java
index d054fe9..114da62 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/CommandApdu.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/CommandApdu.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -31,7 +31,9 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
+
import com.google.auto.value.AutoValue;
+
import de.cotech.hw.util.Hex;
@@ -42,6 +44,23 @@
@AutoValue
@RestrictTo(Scope.LIBRARY_GROUP)
public abstract class CommandApdu {
+ public static final int MAX_APDU_NC_SHORT = 255;
+ public static final int MAX_APDU_NC_EXTENDED = 65535;
+ public static final int MAX_APDU_NE_SHORT = 256;
+ public static final int MAX_APDU_NE_EXTENDED = 65536;
+
+ /**
+ * Default Ne (expected response data length) is 0.
+ * This means that the response should return NO DATA
+ * (responses will be the result code only, such as "0x9000").
+ *
+ * Javacards will probably still return data as they don't comply with ISO/IEC 7816-4 in this case.
+ *
+ * Result: Command's Le value is absent
+ */
+ public static final int DEFAULT_APDU_NE_ZERO = 0;
+
+
public abstract int getCLA();
public abstract int getINS();
public abstract int getP1();
@@ -64,7 +83,7 @@ public static CommandApdu create(CommandApduDescriber describer, byte[] apdu, in
}
public static CommandApdu create(int cla, int ins, int p1, int p2) {
- return create(cla, ins, p1, p2, null, 0, 0, 0, null);
+ return create(cla, ins, p1, p2, null, 0, 0, DEFAULT_APDU_NE_ZERO, null);
}
public static CommandApdu create(int cla, int ins, int p1, int p2, int ne) {
@@ -72,11 +91,11 @@ public static CommandApdu create(int cla, int ins, int p1, int p2, int ne) {
}
public static CommandApdu create(int cla, int ins, int p1, int p2, byte[] data) {
- return create(cla, ins, p1, p2, data, 0, data.length, 0, null);
+ return create(cla, ins, p1, p2, data, 0, data.length, DEFAULT_APDU_NE_ZERO, null);
}
public static CommandApdu create(int cla, int ins, int p1, int p2, byte[] data, int dataOffset, int dataLength) {
- return create(cla, ins, p1, p2, data, dataOffset, dataLength, 0, null);
+ return create(cla, ins, p1, p2, data, dataOffset, dataLength, DEFAULT_APDU_NE_ZERO, null);
}
public static CommandApdu create(int cla, int ins, int p1, int p2, byte[] data, int ne, CommandApduDescriber describer) {
@@ -89,10 +108,10 @@ public static CommandApdu create(int cla, int ins, int p1, int p2, byte[] data,
public static CommandApdu create(
int cla, int ins, int p1, int p2, byte[] data, int dataOffset, int dataLength, int ne, CommandApduDescriber describer) {
- if (ne < 0) {
+ if (ne < DEFAULT_APDU_NE_ZERO) {
throw new IllegalArgumentException("ne must not be negative");
}
- if (ne > 65536) {
+ if (ne > MAX_APDU_NE_EXTENDED) {
throw new IllegalArgumentException("ne is too large");
}
if (data != null) {
@@ -108,6 +127,45 @@ public CommandApdu withNe(int ne) {
return create(getCLA(), getINS(), getP1(), getP2(), getData(), ne, getDescriber());
}
+ /**
+ * Set Ne (expected response data length) so that the response returns all bytes
+ * within the limit of 256.
+ *
+ * Only sets Ne if default of 0 has no changed.
+ *
+ * Result: Command's Le value is set to 0x00
+ */
+ public CommandApdu withShortApduNe() {
+ if (getNe() == DEFAULT_APDU_NE_ZERO) {
+ return withNe(MAX_APDU_NE_SHORT);
+ } else {
+ return this;
+ }
+ }
+
+ /**
+ * Set Ne (expected response data length) so that the response returns all bytes
+ * within the limit of 65536.
+ *
+ * Only sets Ne if default of 0 has no changed.
+ *
+ * Result: Command's Le value is set to 0x0000
+ */
+ public CommandApdu withExtendedApduNe() {
+ if (getNe() == DEFAULT_APDU_NE_ZERO) {
+ return withNe(MAX_APDU_NE_EXTENDED);
+ } else {
+ return this;
+ }
+ }
+
+ /**
+ * Override Ne to 65536
+ */
+ public CommandApdu forceExtendedApduNe() {
+ return withNe(MAX_APDU_NE_EXTENDED);
+ }
+
public CommandApdu withDescriber(CommandApduDescriber describer) {
return create(getCLA(), getINS(), getP1(), getP2(), getData(), getNe(), describer);
}
@@ -129,6 +187,8 @@ public static CommandApdu fromBytes(byte[] apdu, int offset, int length) throws
*
* LE, LE1, LE2 may be 0x00.
* LC must not be 0x00 and LC1|LC2 must not be 0x00|0x00
+ *
+ * see https://docs.oracle.com/javacard/3.0.5/prognotes/extended-apdu-nominal-cases.htm
*/
public static CommandApdu fromBytes(byte[] apdu) throws IOException {
if (apdu.length < 4) {
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/CommandApduDescriber.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/CommandApduDescriber.java
index c21b8c5..b7ff3b1 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/CommandApduDescriber.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/CommandApduDescriber.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/Iso7816TLV.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/Iso7816TLV.java
index a5b25c1..d209bca 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/Iso7816TLV.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/Iso7816TLV.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/ResponseApdu.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/ResponseApdu.java
index 9c8e744..1ef6019 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/ResponseApdu.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/iso7816/ResponseApdu.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/SecurityKeyInfo.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/SecurityKeyInfo.java
index bcc2bfa..e51d14e 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/SecurityKeyInfo.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/SecurityKeyInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -83,7 +83,7 @@ public static SecurityKeyInfo create(TransportType transportType, SecurityKeyTyp
}
public enum TransportType {
- NFC, USB_CCID, USB_U2FHID
+ NFC, USB_CCID, USB_CTAPHID
}
public enum SecurityKeyType {
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/Transport.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/Transport.java
index 38f95b1..60f3476 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/Transport.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/Transport.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/Version.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/Version.java
index a885f79..9348c2a 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/Version.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/Version.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/nfc/NfcConnectionDispatcher.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/nfc/NfcConnectionDispatcher.java
index fd609ba..b01283f 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/nfc/NfcConnectionDispatcher.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/nfc/NfcConnectionDispatcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/nfc/NfcTagManager.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/nfc/NfcTagManager.java
index 0231db0..ede6e4e 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/nfc/NfcTagManager.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/nfc/NfcTagManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -50,21 +50,21 @@ public class NfcTagManager {
private final OnDiscoveredNfcTagListener callback;
private final Handler callbackHandler;
private final boolean enableDebugLogging;
- private final boolean enableNfcTagMonitoring;
+ private final boolean enablePersistentNfcConnection;
private final HashMap managedNfcTags = new HashMap<>();
public static NfcTagManager createInstance(OnDiscoveredNfcTagListener callback,
- Handler handler, boolean enableDebugLogging, boolean enableTagMonitoring) {
- return new NfcTagManager(callback, handler, enableDebugLogging, enableTagMonitoring);
+ Handler handler, boolean enableDebugLogging, boolean enablePersistentNfcConnection) {
+ return new NfcTagManager(callback, handler, enableDebugLogging, enablePersistentNfcConnection);
}
private NfcTagManager(OnDiscoveredNfcTagListener callback, Handler handler, boolean enableDebugLogging,
- boolean enableNfcTagMonitoring) {
+ boolean enablePersistentNfcConnection) {
this.callback = callback;
this.callbackHandler = handler;
this.enableDebugLogging = enableDebugLogging;
- this.enableNfcTagMonitoring = enableNfcTagMonitoring;
+ this.enablePersistentNfcConnection = enablePersistentNfcConnection;
}
@UiThread
@@ -127,7 +127,7 @@ synchronized void createNewActiveNfcTransport() {
return;
}
- NfcTransport nfcTransport = NfcTransport.createNfcTransport(nfcTag, enableDebugLogging, enableNfcTagMonitoring);
+ NfcTransport nfcTransport = NfcTransport.createNfcTransport(nfcTag, enableDebugLogging, enablePersistentNfcConnection);
activeTransport = nfcTransport;
startMonitorThread(this).start();
callbackHandler.post(() -> callback.nfcTransportDiscovered(nfcTransport));
@@ -175,7 +175,7 @@ public void run() {
@WorkerThread
boolean deviceIsStillConnected() {
long lastTransceiveTime = managedNfcTag.activeTransport.getLastTransceiveTime();
- if (enableNfcTagMonitoring) {
+ if (enablePersistentNfcConnection) {
boolean connectionIsActive = lastTransceiveTime + MONITOR_PING_DELAY > System.currentTimeMillis();
return connectionIsActive || managedNfcTag.activeTransport.ping();
} else {
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/nfc/NfcTransport.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/nfc/NfcTransport.java
index fd4cfd5..ee252a5 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/nfc/NfcTransport.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/nfc/NfcTransport.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -38,7 +38,7 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.annotation.WorkerThread;
-import de.cotech.hw.exceptions.SecurityKeyLostException;
+import de.cotech.hw.exceptions.SecurityKeyDisconnectedException;
import de.cotech.hw.internal.iso7816.CommandApdu;
import de.cotech.hw.internal.iso7816.ResponseApdu;
import de.cotech.hw.internal.transport.SecurityKeyInfo;
@@ -83,7 +83,7 @@ private NfcTransport(Tag tag, boolean enableDebugLogging, boolean isPersistently
@Override
public ResponseApdu transceive(final CommandApdu commandApdu) throws IOException {
if (!isConnected()) {
- throw new SecurityKeyLostException();
+ throw new SecurityKeyDisconnectedException();
}
synchronized (connectionLock) {
try {
@@ -109,7 +109,7 @@ public ResponseApdu transceive(final CommandApdu commandApdu) throws IOException
}
return responseApdu;
} catch (TagLostException e) {
- throw new SecurityKeyLostException();
+ throw new SecurityKeyDisconnectedException();
} finally {
lastTransceiveTime = System.currentTimeMillis();
isTransceiving = false;
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UnsupportedUsbSecurityKeyException.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UnsupportedUsbSecurityKeyException.java
index 466c350..697294c 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UnsupportedUsbSecurityKeyException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UnsupportedUsbSecurityKeyException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbConnectionDispatcher.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbConnectionDispatcher.java
index 60413ab..b40cd70 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbConnectionDispatcher.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbConnectionDispatcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbDeviceManager.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbDeviceManager.java
index 31a980f..1433e1b 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbDeviceManager.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbDeviceManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -51,7 +51,7 @@
import androidx.annotation.WorkerThread;
import de.cotech.hw.internal.transport.Transport;
import de.cotech.hw.internal.transport.usb.ccid.UsbCcidTransport;
-import de.cotech.hw.internal.transport.usb.u2fhid.UsbU2fHidTransport;
+import de.cotech.hw.internal.transport.usb.ctaphid.UsbCtapHidTransport;
import de.cotech.hw.util.Hex;
import de.cotech.hw.util.HwTimber;
@@ -140,7 +140,7 @@ boolean refreshDeviceIfManaged(UsbDevice usbDevice) {
private ManagedUsbDevice createManagedUsbDevice(UsbDevice usbDevice) throws UsbTransportException {
HwTimber.d("Initializing managed USB security key");
- List usbInterfaces = UsbUtils.getCcidAndU2fHidInterfaces(usbDevice);
+ List usbInterfaces = UsbUtils.getCcidAndCtapHidInterfaces(usbDevice);
if (usbInterfaces.isEmpty()) {
throw new UsbTransportException("USB error: No usable USB class interface found. (Is CCID mode enabled on your security key?)");
}
@@ -162,7 +162,7 @@ boolean isRelevantDevice(UsbDevice usbDevice) {
boolean hasCcidInterface = false;
for (int i = 0; i < usbDevice.getInterfaceCount(); i++) {
UsbInterface usbInterface = usbDevice.getInterface(i);
- if (UsbUtils.usbInterfaceLooksLikeU2fHid(usbInterface)) {
+ if (UsbUtils.usbInterfaceLooksLikeCtapHid(usbInterface)) {
return true;
}
if (UsbUtils.usbInterfaceLooksLikeCcid(usbInterface)) {
@@ -228,7 +228,7 @@ synchronized void createNewActiveUsbTransport(UsbInterface usbInterface) {
usbTransport = UsbCcidTransport.createUsbTransport(
usbManager, usbDevice, usbConnection, usbInterface, enableDebugLogging);
} else if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
- usbTransport = UsbU2fHidTransport.createUsbTransport(
+ usbTransport = UsbCtapHidTransport.createUsbTransport(
usbManager, usbDevice, usbConnection, usbInterface, enableDebugLogging);
} else {
throw new RuntimeException("unsupported USB class");
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbSecurityKeyTypes.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbSecurityKeyTypes.java
index 8622485..0af69a2 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbSecurityKeyTypes.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbSecurityKeyTypes.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbTransportException.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbTransportException.java
index 810bdc4..ba8f807 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbTransportException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbTransportException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbUtils.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbUtils.java
index c72eebb..6928071 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbUtils.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/UsbUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -93,20 +93,20 @@ private static boolean checkHasIoInterruptEndpoints(UsbInterface usbInterface) {
return hasIn && hasOut;
}
- static List getCcidAndU2fHidInterfaces(UsbDevice device) {
+ static List getCcidAndCtapHidInterfaces(UsbDevice device) {
List result = new ArrayList<>();
for (int i = 0; i < device.getInterfaceCount(); i++) {
UsbInterface usbInterface = device.getInterface(i);
if (usbInterfaceLooksLikeCcid(usbInterface)) {
result.add(usbInterface);
- } else if (usbInterfaceLooksLikeU2fHid(usbInterface)) {
+ } else if (usbInterfaceLooksLikeCtapHid(usbInterface)) {
result.add(usbInterface);
}
}
return result;
}
- static boolean usbInterfaceLooksLikeU2fHid(UsbInterface usbInterface) {
+ static boolean usbInterfaceLooksLikeCtapHid(UsbInterface usbInterface) {
return usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID &&
UsbUtils.checkHasIoInterruptEndpoints(usbInterface);
}
@@ -131,7 +131,7 @@ public static byte[] requestHidReportDescriptor(UsbDeviceConnection usbConnectio
buf.length,
50);
if (bytesRead < 0) {
- throw new IOException("Unable to retrieve U2FHID Report data");
+ throw new IOException("Unable to retrieve CTAPHID Report data");
}
return Arrays.copyOf(buf, bytesRead);
}
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/CcidDescriptor.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/CcidDescriptor.java
index 8e0ac31..cb85415 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/CcidDescriptor.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/CcidDescriptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/CcidTransceiver.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/CcidTransceiver.java
index 71a47d8..2b87f62 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/CcidTransceiver.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/CcidTransceiver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/CcidTransportProtocol.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/CcidTransportProtocol.java
index d446751..36e4b15 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/CcidTransportProtocol.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/CcidTransportProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/UsbCcidErrorException.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/UsbCcidErrorException.java
index 4e9d881..7969239 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/UsbCcidErrorException.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/UsbCcidErrorException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/UsbCcidTransport.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/UsbCcidTransport.java
index 2e5f504..88c7763 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/UsbCcidTransport.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/UsbCcidTransport.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -38,7 +38,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
-import de.cotech.hw.exceptions.SecurityKeyLostException;
+import de.cotech.hw.exceptions.SecurityKeyDisconnectedException;
import de.cotech.hw.internal.iso7816.CommandApdu;
import de.cotech.hw.internal.iso7816.ResponseApdu;
import de.cotech.hw.internal.transport.SecurityKeyInfo.SecurityKeyType;
@@ -152,7 +152,7 @@ public void connect() throws IOException {
@Override
public ResponseApdu transceive(CommandApdu commandApdu) throws IOException {
if (released) {
- throw new SecurityKeyLostException();
+ throw new SecurityKeyDisconnectedException();
}
byte[] rawCommand = commandApdu.toBytes();
if (enableDebugLogging) {
@@ -171,7 +171,7 @@ public ResponseApdu transceive(CommandApdu commandApdu) throws IOException {
} catch (UsbTransportException e) {
if (!UsbUtils.isDeviceStillConnected(usbManager, usbDevice)) {
release();
- throw new SecurityKeyLostException(e);
+ throw new SecurityKeyDisconnectedException(e);
}
throw e;
}
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/Block.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/Block.java
index 2c8598d..fbd8aa5 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/Block.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/Block.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/BlockChecksumAlgorithm.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/BlockChecksumAlgorithm.java
index 1405823..4dd250e 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/BlockChecksumAlgorithm.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/BlockChecksumAlgorithm.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/IBlock.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/IBlock.java
index e5cd753..32eba22 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/IBlock.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/IBlock.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/RBlock.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/RBlock.java
index dc87fac..7019000 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/RBlock.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/RBlock.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/SBlock.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/SBlock.java
index bf3dbea..e358daf 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/SBlock.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/SBlock.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T0ShortApduProtocol.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T0ShortApduProtocol.java
index 6300ce6..c7030ac 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T0ShortApduProtocol.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T0ShortApduProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T1ShortApduProtocol.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T1ShortApduProtocol.java
index 0b06f52..24b24e7 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T1ShortApduProtocol.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T1ShortApduProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T1TpduBlockFactory.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T1TpduBlockFactory.java
index f182cbb..6aee348 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T1TpduBlockFactory.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T1TpduBlockFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T1TpduProtocol.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T1TpduProtocol.java
index 17963b2..d3c0308 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T1TpduProtocol.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ccid/tpdu/T1TpduProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidChangedChannelException.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidChangedChannelException.java
new file mode 100644
index 0000000..74fe883
--- /dev/null
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidChangedChannelException.java
@@ -0,0 +1,11 @@
+package de.cotech.hw.internal.transport.usb.ctaphid;
+
+
+import de.cotech.hw.internal.transport.usb.UsbTransportException;
+
+
+class CtapHidChangedChannelException extends UsbTransportException {
+ CtapHidChangedChannelException(int expectedChannelId, int actualChannelId) {
+ super("Channel changed during transaction, " + expectedChannelId + " to " + actualChannelId);
+ }
+}
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidFailedEnqueueException.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidFailedEnqueueException.java
new file mode 100644
index 0000000..0c75ae4
--- /dev/null
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidFailedEnqueueException.java
@@ -0,0 +1,11 @@
+package de.cotech.hw.internal.transport.usb.ctaphid;
+
+
+import de.cotech.hw.internal.transport.usb.UsbTransportException;
+
+
+class CtapHidFailedEnqueueException extends UsbTransportException {
+ CtapHidFailedEnqueueException(String message) {
+ super(message);
+ }
+}
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidFrameFactory.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidFrameFactory.java
similarity index 83%
rename from hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidFrameFactory.java
rename to hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidFrameFactory.java
index 6329219..492808a 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidFrameFactory.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidFrameFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -22,7 +22,7 @@
* along with this program. If not, see .
*/
-package de.cotech.hw.internal.transport.usb.u2fhid;
+package de.cotech.hw.internal.transport.usb.ctaphid;
import java.nio.BufferOverflowException;
@@ -33,33 +33,33 @@
import androidx.annotation.VisibleForTesting;
import de.cotech.hw.internal.transport.usb.UsbTransportException;
-final class U2fHidFrameFactory {
+final class CtapHidFrameFactory {
private static final byte TYPE_INIT = (byte) 0x80; // Initial frame identifier
@SuppressWarnings("unused") // public API
- static final byte U2FHID_PING = (byte) (TYPE_INIT | 0x01); // Echo data through local processor only
+ static final byte CTAPHID_PING = (byte) (TYPE_INIT | 0x01); // Echo data through local processor only
@SuppressWarnings("unused") // public API
- static final byte U2FHID_MSG = (byte) (TYPE_INIT | 0x03); // Send U2F message frame
+ static final byte CTAPHID_MSG = (byte) (TYPE_INIT | 0x03); // Send CTAPHID message frame
@SuppressWarnings("unused") // public API
- static final byte U2FHID_LOCK = (byte) (TYPE_INIT | 0x04); // Send lock channel command
+ static final byte CTAPHID_LOCK = (byte) (TYPE_INIT | 0x04); // Send lock channel command
@SuppressWarnings("unused") // public API
- static final byte U2FHID_INIT = (byte) (TYPE_INIT | 0x06); // Channel initialization
+ static final byte CTAPHID_INIT = (byte) (TYPE_INIT | 0x06); // Channel initialization
@SuppressWarnings("unused") // public API
- static final byte U2FHID_WINK = (byte) (TYPE_INIT | 0x08); // Send device identification wink
+ static final byte CTAPHID_WINK = (byte) (TYPE_INIT | 0x08); // Send device identification wink
@SuppressWarnings("unused") // public API
- static final byte U2FHID_CBOR = (byte) (TYPE_INIT | 0x10); // Send U2F message frame
+ static final byte CTAPHID_CBOR = (byte) (TYPE_INIT | 0x10); // Send CTAPHID message frame
@SuppressWarnings("unused") // public API
- static final byte U2FHID_ERROR = (byte) (TYPE_INIT | 0x3f); // Error response
+ static final byte CTAPHID_ERROR = (byte) (TYPE_INIT | 0x3f); // Error response
@SuppressWarnings({ "WeakerAccess" }) // public API
- static final byte U2FHID_KEEPALIVE = (byte) (TYPE_INIT | 0x3b); // Just a keepalive response
+ static final byte CTAPHID_KEEPALIVE = (byte) (TYPE_INIT | 0x3b); // Just a keepalive response
- static final int U2FHID_BUFFER_SIZE = 64;
- static final int U2FHID_CHANNEL_ID_BROADCAST = 0xffffffff;
+ static final int CTAPHID_BUFFER_SIZE = 64;
+ static final int CTAPHID_CHANNEL_ID_BROADCAST = 0xffffffff;
private static final int FRST_PKT_HDR_LEN = 7;
private static final int CONT_PACKET_HEADER_LENGTH = 5;
- private static final int MAX_LENGTH_INIT_PACKET = U2FHID_BUFFER_SIZE - FRST_PKT_HDR_LEN;
- private static final int MAX_LENGTH_CONT_PACKET = U2FHID_BUFFER_SIZE - CONT_PACKET_HEADER_LENGTH;
+ private static final int MAX_LENGTH_INIT_PACKET = CTAPHID_BUFFER_SIZE - FRST_PKT_HDR_LEN;
+ private static final int MAX_LENGTH_CONT_PACKET = CTAPHID_BUFFER_SIZE - CONT_PACKET_HEADER_LENGTH;
private static final int MAX_LENGTH_PAYLOAD = MAX_LENGTH_INIT_PACKET + 128 * MAX_LENGTH_CONT_PACKET;
private static final int KEEPALIVE_TYPE_PROCESSING = 1;
@@ -83,7 +83,7 @@ byte[] wrapFrame(int channelId, byte cmdId, byte[] payload) throws UsbTransportE
private byte[] wrapFrameOrThrow(int channelId, byte cmdId, byte[] payload) {
int packetsRequiredForPayload = calculatePacketCountForPayload(payload.length);
- ByteBuffer output = ByteBuffer.allocate(packetsRequiredForPayload * U2FHID_BUFFER_SIZE).order(ByteOrder.BIG_ENDIAN);
+ ByteBuffer output = ByteBuffer.allocate(packetsRequiredForPayload * CTAPHID_BUFFER_SIZE).order(ByteOrder.BIG_ENDIAN);
int offset = 0;
int sequenceIdx = 0;
@@ -101,7 +101,7 @@ private byte[] wrapFrameOrThrow(int channelId, byte cmdId, byte[] payload) {
* uint32_t cid; // Channel identifier
* uint8_t seq; // Sequence number - b7 cleared
* uint8_t data[HID_RPT_SIZE - 5]; // Data payload
- * } U2FHID_FRAME_CONT;
+ * } CTAPHID_FRAME_CONT;
*/
private int writeContPacket(int sequenceIdx, int channelId, byte[] payload, int offset,
ByteBuffer output) {
@@ -126,7 +126,7 @@ private int writeContPacket(int sequenceIdx, int channelId, byte[] payload, int
* uint8_t bcnth; // Message byte count - high part
* uint8_t bcntl; // Message byte count - low part
* uint8_t data[HID_RPT_SIZE - 7]; // Data payload
- * } U2FHID_FRAME_INIT;
+ * } CTAPHID_FRAME_INIT;
*/
private int writeInitPacket(byte cmdId, int channelId, byte[] payload, ByteBuffer output) {
if ((cmdId & TYPE_INIT) == 0) {
@@ -149,8 +149,8 @@ int findExpectedFramesFromInitPacketHeader(int expectedChannelId, ByteBuffer ini
try {
initPacket.mark();
FrameInitPacketHeader initPacketHeader = FrameInitPacketHeader.fromByteBuffer(initPacket);
- if (expectedChannelId != U2FHID_CHANNEL_ID_BROADCAST && initPacketHeader.channelId != expectedChannelId) {
- throw new U2fHidChangedChannelException(expectedChannelId, initPacketHeader.channelId);
+ if (expectedChannelId != CTAPHID_CHANNEL_ID_BROADCAST && initPacketHeader.channelId != expectedChannelId) {
+ throw new CtapHidChangedChannelException(expectedChannelId, initPacketHeader.channelId);
}
initPacket.reset();
return calculatePacketCountForPayload(initPacketHeader.payloadLength);
@@ -162,7 +162,7 @@ int findExpectedFramesFromInitPacketHeader(int expectedChannelId, ByteBuffer ini
KeepaliveType unwrapFrameAsKeepalivePacket(byte[] frameBytes) {
ByteBuffer frame = ByteBuffer.wrap(frameBytes).order(ByteOrder.BIG_ENDIAN);
FrameInitPacketHeader initPacket = FrameInitPacketHeader.fromByteBuffer(frame);
- if (initPacket.cmdId != U2FHID_KEEPALIVE) {
+ if (initPacket.cmdId != CTAPHID_KEEPALIVE) {
return null;
}
if (initPacket.payloadLength != 1) {
@@ -201,12 +201,12 @@ private byte[] unwrapFrameOrThrow(int expectedChannelId, byte expectedCmdId, byt
throw new UsbTransportException("Command mismatch = " + (initPacket.cmdId & 0xff) + " Tag = " + (expectedCmdId & 0xff));
}
- if (expectedChannelId != U2FHID_CHANNEL_ID_BROADCAST && initPacket.channelId != expectedChannelId) {
- throw new U2fHidChangedChannelException(expectedChannelId, initPacket.channelId);
+ if (expectedChannelId != CTAPHID_CHANNEL_ID_BROADCAST && initPacket.channelId != expectedChannelId) {
+ throw new CtapHidChangedChannelException(expectedChannelId, initPacket.channelId);
}
// check we don't have less data than claimed
- int expectedBufferLength = calculatePacketCountForPayload(initPacket.payloadLength) * U2FHID_BUFFER_SIZE;
+ int expectedBufferLength = calculatePacketCountForPayload(initPacket.payloadLength) * CTAPHID_BUFFER_SIZE;
if (frame.capacity() != expectedBufferLength) {
throw new UsbTransportException(
@@ -237,7 +237,7 @@ private int readContPacket(int expectedChannelId, ByteBuffer frame, byte[] paylo
throws UsbTransportException {
int channelId = frame.getInt();
if (channelId != expectedChannelId) {
- throw new U2fHidChangedChannelException(expectedChannelId, channelId);
+ throw new CtapHidChangedChannelException(expectedChannelId, channelId);
}
byte sequenceId = frame.get();
@@ -253,7 +253,7 @@ private int readContPacket(int expectedChannelId, ByteBuffer frame, byte[] paylo
@VisibleForTesting
int calculatePacketCountForPayload(int length) {
if (length > MAX_LENGTH_PAYLOAD) {
- throw new IllegalArgumentException("Payload too large, U2fHid maximum is 7906 bytes!");
+ throw new IllegalArgumentException("Payload too large, CtapHid maximum is 7906 bytes!");
}
int lengthAfterFirstPacket = length - MAX_LENGTH_INIT_PACKET;
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidInitStructFactory.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidInitStructFactory.java
similarity index 80%
rename from hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidInitStructFactory.java
rename to hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidInitStructFactory.java
index d8cb38d..9e8e07a 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidInitStructFactory.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidInitStructFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -22,7 +22,7 @@
* along with this program. If not, see .
*/
-package de.cotech.hw.internal.transport.usb.u2fhid;
+package de.cotech.hw.internal.transport.usb.ctaphid;
import java.nio.BufferOverflowException;
@@ -35,11 +35,11 @@
@SuppressWarnings("unused") // public API
-class U2fHidInitStructFactory {
+class CtapHidInitStructFactory {
private static final int INIT_NONCE_SIZE = 8;
private final Random random;
- U2fHidInitStructFactory(Random random) {
+ CtapHidInitStructFactory(Random random) {
this.random = random;
}
@@ -47,11 +47,11 @@ class U2fHidInitStructFactory {
*
* typedef struct {
* uint8_t nonce[INIT_NONCE_SIZE]; // Client application nonce
- * } U2FHID_INIT_REQ;
+ * } CTAPHID_INIT_REQ;
*
*/
byte[] createInitRequest() {
- byte[] nonce = new byte[U2fHidInitStructFactory.INIT_NONCE_SIZE];
+ byte[] nonce = new byte[CtapHidInitStructFactory.INIT_NONCE_SIZE];
random.nextBytes(nonce);
return nonce;
}
@@ -67,17 +67,17 @@ byte[] createInitRequest() {
* uint8_t versionMinor; // Minor version number
* uint8_t versionBuild; // Build version number
* uint8_t capFlags; // Capabilities flags
- * } U2FHID_INIT_RESP;
+ * } CTAPHID_INIT_RESP;
*/
- U2fHidInitResponse parseInitResponse(byte[] responseBytes, byte[] requestBytes) throws UsbTransportException {
+ CtapHidInitResponse parseInitResponse(byte[] responseBytes, byte[] requestBytes) throws UsbTransportException {
try {
- return getU2fHidInitResponseOrThrow(responseBytes, requestBytes);
+ return getCtapHidInitResponseOrThrow(responseBytes, requestBytes);
} catch (BufferOverflowException | BufferUnderflowException e) {
throw new UsbTransportException(e);
}
}
- private U2fHidInitResponse getU2fHidInitResponseOrThrow(byte[] responseBytes, byte[] requestBytes)
+ private CtapHidInitResponse getCtapHidInitResponseOrThrow(byte[] responseBytes, byte[] requestBytes)
throws UsbTransportException {
ByteBuffer response = ByteBuffer.wrap(responseBytes);
ByteBuffer request = ByteBuffer.wrap(requestBytes);
@@ -89,11 +89,11 @@ private U2fHidInitResponse getU2fHidInitResponseOrThrow(byte[] responseBytes, by
response.clear();
response.position(INIT_NONCE_SIZE);
- return U2fHidInitResponse.readFromByteBuffer(response);
+ return CtapHidInitResponse.readFromByteBuffer(response);
}
@AutoValue
- static abstract class U2fHidInitResponse {
+ static abstract class CtapHidInitResponse {
private static final byte CAPFLAG_WINK = 1;
private static final byte CAPFLAG_LOCK = 2;
@@ -104,7 +104,7 @@ static abstract class U2fHidInitResponse {
abstract byte versionBuild();
abstract byte capabilityFlags();
- static U2fHidInitResponse readFromByteBuffer(ByteBuffer buf) {
+ static CtapHidInitResponse readFromByteBuffer(ByteBuffer buf) {
int channelId = buf.getInt();
byte versionInterface = buf.get();
byte versionMajor = buf.get();
@@ -112,7 +112,7 @@ static U2fHidInitResponse readFromByteBuffer(ByteBuffer buf) {
byte versionBuild = buf.get();
byte capabilityFlags = buf.get();
- return new AutoValue_U2fHidInitStructFactory_U2fHidInitResponse(
+ return new AutoValue_CtapHidInitStructFactory_CtapHidInitResponse(
channelId, versionInterface, versionMajor, versionMinor, versionBuild, capabilityFlags
);
}
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidTransportProtocol.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidTransportProtocol.java
similarity index 75%
rename from hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidTransportProtocol.java
rename to hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidTransportProtocol.java
index 367709f..f8aa5d9 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidTransportProtocol.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidTransportProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -22,7 +22,7 @@
* along with this program. If not, see .
*/
-package de.cotech.hw.internal.transport.usb.u2fhid;
+package de.cotech.hw.internal.transport.usb.ctaphid;
import java.io.IOException;
@@ -45,17 +45,16 @@
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import de.cotech.hw.internal.transport.usb.UsbTransportException;
-import de.cotech.hw.internal.transport.usb.u2fhid.U2fHidFrameFactory.KeepaliveType;
-import de.cotech.hw.internal.transport.usb.u2fhid.U2fHidInitStructFactory.U2fHidInitResponse;
+import de.cotech.hw.internal.transport.usb.ctaphid.CtapHidFrameFactory.KeepaliveType;
import de.cotech.hw.util.HwTimber;
@RestrictTo(Scope.LIBRARY_GROUP)
-public class U2fHidTransportProtocol {
+public class CtapHidTransportProtocol {
@NonNull
- private final U2fHidInitStructFactory initStructFactory = new U2fHidInitStructFactory(new SecureRandom());
+ private final CtapHidInitStructFactory initStructFactory = new CtapHidInitStructFactory(new SecureRandom());
@NonNull
- private final U2fHidFrameFactory frameFactory = new U2fHidFrameFactory();
+ private final CtapHidFrameFactory frameFactory = new CtapHidFrameFactory();
@NonNull
private final UsbDeviceConnection usbCconnection;
@NonNull
@@ -67,10 +66,10 @@ public class U2fHidTransportProtocol {
@NonNull
private final ExecutorService executor;
- private int channelId = U2fHidFrameFactory.U2FHID_CHANNEL_ID_BROADCAST;
+ private int channelId = CtapHidFrameFactory.CTAPHID_CHANNEL_ID_BROADCAST;
- U2fHidTransportProtocol(@NonNull UsbDeviceConnection usbCconnection,
- @NonNull UsbEndpoint usbEndpointIn, @NonNull UsbEndpoint usbEndpointOut) {
+ CtapHidTransportProtocol(@NonNull UsbDeviceConnection usbCconnection,
+ @NonNull UsbEndpoint usbEndpointIn, @NonNull UsbEndpoint usbEndpointOut) {
// noinspection ConstantConditions, checking method contract
if (usbCconnection == null) {
throw new NullPointerException();
@@ -88,20 +87,20 @@ public class U2fHidTransportProtocol {
this.usbEndpointIn = usbEndpointIn;
this.usbEndpointOut = usbEndpointOut;
// Allocating a direct buffer here *will break* on some android devices!
- this.transferBuffer = ByteBuffer.allocate(U2fHidFrameFactory.U2FHID_BUFFER_SIZE);
+ this.transferBuffer = ByteBuffer.allocate(CtapHidFrameFactory.CTAPHID_BUFFER_SIZE);
this.executor = Executors.newSingleThreadExecutor();
}
@WorkerThread
public void connect() throws UsbTransportException {
- HwTimber.d("Initializing U2FHID transport…");
+ HwTimber.d("Initializing CTAPHID transport…");
this.channelId = negotiateChannelId();
}
private int negotiateChannelId() throws UsbTransportException {
byte[] initRequestBytes = initStructFactory.createInitRequest();
- byte[] requestFrame = frameFactory.wrapFrame(channelId, U2fHidFrameFactory.U2FHID_INIT, initRequestBytes);
+ byte[] requestFrame = frameFactory.wrapFrame(channelId, CtapHidFrameFactory.CTAPHID_INIT, initRequestBytes);
writeHidPacketsToUsbDevice(requestFrame);
return performUsbRequestWithTimeout((thread, usbRequest) -> {
@@ -113,15 +112,15 @@ private int negotiateChannelId() throws UsbTransportException {
while (true) {
checkInterrupt(thread);
transferBuffer.clear();
- if (!usbRequest.queue(transferBuffer, U2fHidFrameFactory.U2FHID_BUFFER_SIZE)) {
- throw new U2fHidFailedEnqueueException("Failed to receive data!");
+ if (!usbRequest.queue(transferBuffer, CtapHidFrameFactory.CTAPHID_BUFFER_SIZE)) {
+ throw new CtapHidFailedEnqueueException("Failed to receive data!");
}
usbCconnection.requestWait();
try {
- byte[] response = frameFactory.unwrapFrame(channelId, U2fHidFrameFactory.U2FHID_INIT, transferBuffer.array());
- U2fHidInitResponse initResponse = initStructFactory.parseInitResponse(response, initRequestBytes);
+ byte[] response = frameFactory.unwrapFrame(channelId, CtapHidFrameFactory.CTAPHID_INIT, transferBuffer.array());
+ CtapHidInitStructFactory.CtapHidInitResponse initResponse = initStructFactory.parseInitResponse(response, initRequestBytes);
- HwTimber.d("U2FHID_INIT response: %s", initResponse);
+ HwTimber.d("CTAPHID_INIT response: %s", initResponse);
return initResponse.channelId();
} catch (UsbTransportException e) {
HwTimber.d("Ignoring unrelated INIT response");
@@ -132,16 +131,16 @@ private int negotiateChannelId() throws UsbTransportException {
@WorkerThread
byte[] transceive(byte[] payload) throws UsbTransportException {
- byte[] requestFrame = frameFactory.wrapFrame(channelId, U2fHidFrameFactory.U2FHID_MSG, payload);
+ byte[] requestFrame = frameFactory.wrapFrame(channelId, CtapHidFrameFactory.CTAPHID_MSG, payload);
writeHidPacketsToUsbDevice(requestFrame);
byte[] responseFrame = readHidPacketsFromUsbDevice();
- return frameFactory.unwrapFrame(channelId, U2fHidFrameFactory.U2FHID_MSG, responseFrame);
+ return frameFactory.unwrapFrame(channelId, CtapHidFrameFactory.CTAPHID_MSG, responseFrame);
}
@WorkerThread
byte[] transceiveCbor(byte[] payload) throws UsbTransportException {
- byte[] requestFrame = frameFactory.wrapFrame(channelId, U2fHidFrameFactory.U2FHID_CBOR, payload);
+ byte[] requestFrame = frameFactory.wrapFrame(channelId, CtapHidFrameFactory.CTAPHID_CBOR, payload);
writeHidPacketsToUsbDevice(requestFrame);
while (true) {
@@ -151,7 +150,7 @@ byte[] transceiveCbor(byte[] payload) throws UsbTransportException {
HwTimber.d("Received keepalive packet (%s), waiting for response..", keepalivePacketType);
continue;
}
- return frameFactory.unwrapFrame(channelId, U2fHidFrameFactory.U2FHID_CBOR, responseFrame);
+ return frameFactory.unwrapFrame(channelId, CtapHidFrameFactory.CTAPHID_CBOR, responseFrame);
}
}
@@ -167,20 +166,20 @@ private byte[] readHidPacketsFromUsbDevice() throws UsbTransportException {
checkInterrupt(thread);
int expectedFrames = readUntilInitHeaderForChannel(usbRequest);
- byte[] data = new byte[expectedFrames * U2fHidFrameFactory.U2FHID_BUFFER_SIZE];
+ byte[] data = new byte[expectedFrames * CtapHidFrameFactory.CTAPHID_BUFFER_SIZE];
transferBuffer.clear();
- transferBuffer.get(data, 0, U2fHidFrameFactory.U2FHID_BUFFER_SIZE);
+ transferBuffer.get(data, 0, CtapHidFrameFactory.CTAPHID_BUFFER_SIZE);
- int offset = U2fHidFrameFactory.U2FHID_BUFFER_SIZE;
+ int offset = CtapHidFrameFactory.CTAPHID_BUFFER_SIZE;
for (int i = 1; i < expectedFrames; i++) {
checkInterrupt(thread);
- if (!usbRequest.queue(transferBuffer, U2fHidFrameFactory.U2FHID_BUFFER_SIZE)) {
- throw new U2fHidFailedEnqueueException("Failed to receive data!");
+ if (!usbRequest.queue(transferBuffer, CtapHidFrameFactory.CTAPHID_BUFFER_SIZE)) {
+ throw new CtapHidFailedEnqueueException("Failed to receive data!");
}
usbCconnection.requestWait();
transferBuffer.clear();
- transferBuffer.get(data, offset, U2fHidFrameFactory.U2FHID_BUFFER_SIZE);
- offset += U2fHidFrameFactory.U2FHID_BUFFER_SIZE;
+ transferBuffer.get(data, offset, CtapHidFrameFactory.CTAPHID_BUFFER_SIZE);
+ offset += CtapHidFrameFactory.CTAPHID_BUFFER_SIZE;
}
return data;
@@ -189,15 +188,15 @@ private byte[] readHidPacketsFromUsbDevice() throws UsbTransportException {
private int readUntilInitHeaderForChannel(UsbRequest usbRequest) throws IOException {
while (true) {
- if (!usbRequest.queue(transferBuffer, U2fHidFrameFactory.U2FHID_BUFFER_SIZE)) {
- throw new U2fHidFailedEnqueueException("Failed to receive data!");
+ if (!usbRequest.queue(transferBuffer, CtapHidFrameFactory.CTAPHID_BUFFER_SIZE)) {
+ throw new CtapHidFailedEnqueueException("Failed to receive data!");
}
usbCconnection.requestWait();
transferBuffer.clear();
try {
return frameFactory.findExpectedFramesFromInitPacketHeader(channelId, transferBuffer);
- } catch (U2fHidChangedChannelException e) {
+ } catch (CtapHidChangedChannelException e) {
HwTimber.d("Received message from wrong channel - ignoring");
}
}
@@ -205,7 +204,7 @@ private int readUntilInitHeaderForChannel(UsbRequest usbRequest) throws IOExcept
@WorkerThread
private void writeHidPacketsToUsbDevice(byte[] hidFrame) throws UsbTransportException {
- if ((hidFrame.length % U2fHidFrameFactory.U2FHID_BUFFER_SIZE) != 0) {
+ if ((hidFrame.length % CtapHidFrameFactory.CTAPHID_BUFFER_SIZE) != 0) {
throw new IllegalArgumentException("Invalid HID frame size!");
}
@@ -220,12 +219,12 @@ private void writeHidPacketsToUsbDevice(byte[] hidFrame) throws UsbTransportExce
while (offset < hidFrame.length) {
checkInterrupt(thread);
transferBuffer.clear();
- transferBuffer.put(hidFrame, offset, U2fHidFrameFactory.U2FHID_BUFFER_SIZE);
- if (!usbRequest.queue(transferBuffer, U2fHidFrameFactory.U2FHID_BUFFER_SIZE)) {
- throw new U2fHidFailedEnqueueException("Failed to send data!");
+ transferBuffer.put(hidFrame, offset, CtapHidFrameFactory.CTAPHID_BUFFER_SIZE);
+ if (!usbRequest.queue(transferBuffer, CtapHidFrameFactory.CTAPHID_BUFFER_SIZE)) {
+ throw new CtapHidFailedEnqueueException("Failed to send data!");
}
usbCconnection.requestWait(); // blocking
- offset += U2fHidFrameFactory.U2FHID_BUFFER_SIZE;
+ offset += CtapHidFrameFactory.CTAPHID_BUFFER_SIZE;
}
return null;
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/UsbU2fHidTransport.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/UsbCtapHidTransport.java
similarity index 76%
rename from hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/UsbU2fHidTransport.java
rename to hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/UsbCtapHidTransport.java
index e8ac85e..4940d71 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/UsbU2fHidTransport.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/ctaphid/UsbCtapHidTransport.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -22,7 +22,7 @@
* along with this program. If not, see .
*/
-package de.cotech.hw.internal.transport.usb.u2fhid;
+package de.cotech.hw.internal.transport.usb.ctaphid;
import java.io.IOException;
@@ -42,7 +42,7 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
-import de.cotech.hw.exceptions.SecurityKeyLostException;
+import de.cotech.hw.exceptions.SecurityKeyDisconnectedException;
import de.cotech.hw.internal.iso7816.CommandApdu;
import de.cotech.hw.internal.iso7816.ResponseApdu;
import de.cotech.hw.internal.transport.SecurityKeyInfo.SecurityKeyType;
@@ -56,11 +56,11 @@
/**
- * USB U2FHID
- * https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-hid-protocol-v1.2-ps-20170411.html
+ * USB CTAPHID
+ * https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#usb
*/
@RestrictTo(Scope.LIBRARY_GROUP)
-public class UsbU2fHidTransport implements Transport {
+public class UsbCtapHidTransport implements Transport {
private static final int FIDO2_CLA_PROPRIETARY = 0x80;
private static final int FIDO2_INS = 0x10;
private static final int FIDO2_P1 = 0x00;
@@ -73,20 +73,20 @@ public class UsbU2fHidTransport implements Transport {
private final UsbDeviceConnection usbConnection;
private final UsbInterface usbInterface;
private boolean enableDebugLogging;
- private U2fHidTransportProtocol u2fHidTransportProtocol;
+ private CtapHidTransportProtocol ctapHidTransportProtocol;
private boolean released = false;
private TransportReleasedCallback transportReleasedCallback;
- public static UsbU2fHidTransport createUsbTransport(UsbManager usbManager, UsbDevice usbDevice,
- UsbDeviceConnection usbConnection,
- UsbInterface usbInterface, boolean enableDebugLogging) {
- return new UsbU2fHidTransport(usbManager, usbDevice, usbConnection, usbInterface, enableDebugLogging);
+ public static UsbCtapHidTransport createUsbTransport(UsbManager usbManager, UsbDevice usbDevice,
+ UsbDeviceConnection usbConnection,
+ UsbInterface usbInterface, boolean enableDebugLogging) {
+ return new UsbCtapHidTransport(usbManager, usbDevice, usbConnection, usbInterface, enableDebugLogging);
}
- private UsbU2fHidTransport(UsbManager usbManager, UsbDevice usbDevice,
- UsbDeviceConnection usbConnection, UsbInterface usbInterface,
- boolean enableDebugLogging) {
+ private UsbCtapHidTransport(UsbManager usbManager, UsbDevice usbDevice,
+ UsbDeviceConnection usbConnection, UsbInterface usbInterface,
+ boolean enableDebugLogging) {
this.usbManager = usbManager;
this.usbDevice = usbDevice;
this.usbConnection = usbConnection;
@@ -101,7 +101,7 @@ private UsbU2fHidTransport(UsbManager usbManager, UsbDevice usbDevice,
*/
@Override
public boolean isConnected() {
- return u2fHidTransportProtocol != null && !released;
+ return ctapHidTransportProtocol != null && !released;
}
@Override
@@ -130,7 +130,7 @@ public boolean isPersistentConnectionAllowed() {
*/
@Override
public void connect() throws IOException {
- if (u2fHidTransportProtocol != null) {
+ if (ctapHidTransportProtocol != null) {
throw new IllegalStateException("Already connected!");
}
@@ -140,15 +140,15 @@ public void connect() throws IOException {
UsbEndpoint usbIntOut = ioEndpoints.second;
if (usbIntIn == null || usbIntOut == null) {
- throw new UsbTransportException("USB_U2FHID error: invalid class 3 interface");
+ throw new UsbTransportException("CTAPHID error: invalid class 3 interface");
}
checkHidReportPrefix();
- U2fHidTransportProtocol u2fHidTransportProtocol =
- new U2fHidTransportProtocol(usbConnection, usbIntIn, usbIntOut);
- u2fHidTransportProtocol.connect();
- this.u2fHidTransportProtocol = u2fHidTransportProtocol;
+ CtapHidTransportProtocol ctapHidTransportProtocol =
+ new CtapHidTransportProtocol(usbConnection, usbIntIn, usbIntOut);
+ ctapHidTransportProtocol.connect();
+ this.ctapHidTransportProtocol = ctapHidTransportProtocol;
}
private void checkHidReportPrefix() throws IOException {
@@ -171,7 +171,7 @@ private void checkHidReportPrefix() throws IOException {
@Override
public ResponseApdu transceive(CommandApdu commandApdu) throws IOException {
if (released) {
- throw new SecurityKeyLostException();
+ throw new SecurityKeyDisconnectedException();
}
try {
@@ -179,7 +179,7 @@ public ResponseApdu transceive(CommandApdu commandApdu) throws IOException {
} catch (UsbTransportException e) {
if (!UsbUtils.isDeviceStillConnected(usbManager, usbDevice)) {
release();
- throw new SecurityKeyLostException(e);
+ throw new SecurityKeyDisconnectedException(e);
}
throw e;
}
@@ -188,28 +188,27 @@ public ResponseApdu transceive(CommandApdu commandApdu) throws IOException {
private ResponseApdu transceiveInternal(CommandApdu commandApdu) throws IOException {
// "For the U2FHID protocol, all raw U2F messages are encoded using extended length APDU encoding."
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-hid-protocol-v1.2-ps-20170411.html
- // This will already be the case for some APDUs, see FidoU2fAppletConnection
- CommandApdu extendedCommandApdu = commandApdu.withNe(65536);
+ CommandApdu extendedCommandApdu = commandApdu.forceExtendedApduNe();
if (enableDebugLogging) {
- HwTimber.d("U2FHID out: %s", extendedCommandApdu);
+ HwTimber.d("CTAPHID out: %s", extendedCommandApdu);
}
long startRealtime = SystemClock.elapsedRealtime();
ResponseApdu responseApdu;
if (isCtap2Apdu(commandApdu)) {
HwTimber.d("Using CTAP2 CBOR");
- byte[] rawResponse = u2fHidTransportProtocol.transceiveCbor(extendedCommandApdu.getData());
+ byte[] rawResponse = ctapHidTransportProtocol.transceiveCbor(extendedCommandApdu.getData());
responseApdu = ResponseApdu.create(0x9000, rawResponse);
} else {
- byte[] rawResponse = u2fHidTransportProtocol.transceive(extendedCommandApdu.toBytes());
+ byte[] rawResponse = ctapHidTransportProtocol.transceive(extendedCommandApdu.toBytes());
responseApdu = ResponseApdu.fromBytes(rawResponse);
}
if (enableDebugLogging) {
long totalTime = SystemClock.elapsedRealtime() - startRealtime;
- HwTimber.d("U2FHID in: %s", responseApdu);
- HwTimber.d("U2FHID communication took %dms", totalTime);
+ HwTimber.d("CTAPHID in: %s", responseApdu);
+ HwTimber.d("CTAPHID communication took %dms", totalTime);
}
return responseApdu;
}
@@ -238,7 +237,7 @@ public void release() {
@Override
public TransportType getTransportType() {
- return TransportType.USB_U2FHID;
+ return TransportType.USB_CTAPHID;
}
@Override
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidChangedChannelException.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidChangedChannelException.java
deleted file mode 100644
index 93b8f52..0000000
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidChangedChannelException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package de.cotech.hw.internal.transport.usb.u2fhid;
-
-
-import de.cotech.hw.internal.transport.usb.UsbTransportException;
-
-
-class U2fHidChangedChannelException extends UsbTransportException {
- U2fHidChangedChannelException(int expectedChannelId, int actualChannelId) {
- super("Channel changed during transaction, " + expectedChannelId + " to " + actualChannelId);
- }
-}
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidFailedEnqueueException.java b/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidFailedEnqueueException.java
deleted file mode 100644
index 183d9ec..0000000
--- a/hwsecurity/core/src/main/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidFailedEnqueueException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package de.cotech.hw.internal.transport.usb.u2fhid;
-
-
-import de.cotech.hw.internal.transport.usb.UsbTransportException;
-
-
-class U2fHidFailedEnqueueException extends UsbTransportException {
- U2fHidFailedEnqueueException(String message) {
- super(message);
- }
-}
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/raw/RawSecurityKey.java b/hwsecurity/core/src/main/java/de/cotech/hw/raw/RawSecurityKey.java
index 8d37dab..44ec121 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/raw/RawSecurityKey.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/raw/RawSecurityKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/raw/RawSecurityKeyConnectionMode.java b/hwsecurity/core/src/main/java/de/cotech/hw/raw/RawSecurityKeyConnectionMode.java
index 21da956..c116fc8 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/raw/RawSecurityKeyConnectionMode.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/raw/RawSecurityKeyConnectionMode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/openpgp/src/main/java/de/cotech/hw/openpgp/secrets/AndroidPreferenceSimplePinProvider.java b/hwsecurity/core/src/main/java/de/cotech/hw/secrets/AndroidPreferenceSimplePinProvider.java
similarity index 96%
rename from hwsecurity/openpgp/src/main/java/de/cotech/hw/openpgp/secrets/AndroidPreferenceSimplePinProvider.java
rename to hwsecurity/core/src/main/java/de/cotech/hw/secrets/AndroidPreferenceSimplePinProvider.java
index 18b0f59..4aebf2a 100644
--- a/hwsecurity/openpgp/src/main/java/de/cotech/hw/openpgp/secrets/AndroidPreferenceSimplePinProvider.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/secrets/AndroidPreferenceSimplePinProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -22,15 +22,13 @@
* along with this program. If not, see .
*/
-package de.cotech.hw.openpgp.secrets;
+package de.cotech.hw.secrets;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
-import de.cotech.hw.secrets.ByteSecret;
-import de.cotech.hw.secrets.PinProvider;
import de.cotech.hw.util.Hex;
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/secrets/ByteSecret.java b/hwsecurity/core/src/main/java/de/cotech/hw/secrets/ByteSecret.java
index be71edb..9b44626 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/secrets/ByteSecret.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/secrets/ByteSecret.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/openpgp/src/main/java/de/cotech/hw/openpgp/secrets/ByteSecretGenerator.java b/hwsecurity/core/src/main/java/de/cotech/hw/secrets/ByteSecretGenerator.java
similarity index 66%
rename from hwsecurity/openpgp/src/main/java/de/cotech/hw/openpgp/secrets/ByteSecretGenerator.java
rename to hwsecurity/core/src/main/java/de/cotech/hw/secrets/ByteSecretGenerator.java
index 08e46f0..ad926dc 100644
--- a/hwsecurity/openpgp/src/main/java/de/cotech/hw/openpgp/secrets/ByteSecretGenerator.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/secrets/ByteSecretGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -22,17 +22,12 @@
* along with this program. If not, see .
*/
-package de.cotech.hw.openpgp.secrets;
+package de.cotech.hw.secrets;
import androidx.annotation.NonNull;
-import de.cotech.hw.secrets.ByteSecret;
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
-import org.bouncycastle.crypto.params.HKDFParameters;
import java.security.SecureRandom;
-import java.util.Arrays;
/**
@@ -90,39 +85,4 @@ public ByteSecret createRandomNumeric(int numChars) {
return ByteSecret.fromCharArrayAsUtf8TakeOwnership(secret);
}
- /**
- * Derives a ByteSecret using SHA256, compatible to RFC 5869
- */
- @Deprecated
- public ByteSecret deriveWithSaltAndConsume(ByteSecret secret, String salt, int length) {
- byte[] secretBytes = null;
- try {
- secretBytes = secret.getByteCopyAndClear();
-
- SHA256Digest digest = new SHA256Digest();
- HKDFBytesGenerator kDF1BytesGenerator = new HKDFBytesGenerator(digest);
-
- kDF1BytesGenerator.init(new HKDFParameters(secretBytes, salt.getBytes(), null));
-
- byte[] derivedSecret = new byte[length];
- kDF1BytesGenerator.generateBytes(derivedSecret, 0, length);
-
- return ByteSecret.fromByteArrayAndClear(derivedSecret);
- } finally {
- zeroArrayQuietly(secretBytes);
- }
- }
-
- private void zeroArrayQuietly(byte[] secretBytes) {
- if (secretBytes != null) {
- Arrays.fill(secretBytes, (byte) 0);
- }
- }
-
- private void zeroArrayQuietly(char[] secretChars) {
- if (secretChars != null) {
- Arrays.fill(secretChars, '\0');
- }
- }
-
}
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/secrets/PinProvider.java b/hwsecurity/core/src/main/java/de/cotech/hw/secrets/PinProvider.java
index 02aff7c..d95eb20 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/secrets/PinProvider.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/secrets/PinProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/secrets/StaticPinProvider.java b/hwsecurity/core/src/main/java/de/cotech/hw/secrets/StaticPinProvider.java
index c20db0a..eeb2081 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/secrets/StaticPinProvider.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/secrets/StaticPinProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/util/HashUtil.java b/hwsecurity/core/src/main/java/de/cotech/hw/util/HashUtil.java
index ae31a83..8c81ae2 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/util/HashUtil.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/util/HashUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -35,7 +35,10 @@ private HashUtil() { }
public static byte[] sha256(String data) {
byte[] dataBytes = data.getBytes(Charset.forName("UTF-8"));
+ return sha256(dataBytes);
+ }
+ public static byte[] sha256(byte[] dataBytes) {
try {
return MessageDigest.getInstance("SHA-256").digest(dataBytes);
} catch (final NoSuchAlgorithmException e) {
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/util/NfcStatusObserver.java b/hwsecurity/core/src/main/java/de/cotech/hw/util/NfcStatusObserver.java
index c2eb034..08b677e 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/util/NfcStatusObserver.java
+++ b/hwsecurity/core/src/main/java/de/cotech/hw/util/NfcStatusObserver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/ccid/CcidTransceiverTest.java b/hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/ccid/CcidTransceiverTest.java
index ce66205..ce9e21a 100644
--- a/hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/ccid/CcidTransceiverTest.java
+++ b/hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/ccid/CcidTransceiverTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidFrameFactoryTest.java b/hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidFrameFactoryTest.java
similarity index 61%
rename from hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidFrameFactoryTest.java
rename to hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidFrameFactoryTest.java
index 86ce4a5..5fb0193 100644
--- a/hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidFrameFactoryTest.java
+++ b/hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidFrameFactoryTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -22,7 +22,7 @@
* along with this program. If not, see .
*/
-package de.cotech.hw.internal.transport.usb.u2fhid;
+package de.cotech.hw.internal.transport.usb.ctaphid;
import de.cotech.hw.internal.transport.usb.UsbTransportException;
@@ -35,22 +35,22 @@
@SuppressWarnings("WeakerAccess")
-public class U2fHidFrameFactoryTest {
+public class CtapHidFrameFactoryTest {
// message max length = 64 - 7 + 128 * (64 - 5) = 7609 bytes
- static final int U2FHID_MAX_SIZE = 7609;
+ static final int CTAPHID_MAX_SIZE = 7609;
static final int CHANNEL_ID = 12345678;
static final byte[] MESSAGE_SHORT = Hex.decodeHexOrFail("1a2b3d4f5a6b7c");
static final byte[] MESSAGE_LONG = repeat(MESSAGE_SHORT, 128);
- U2fHidFrameFactory factory = new U2fHidFrameFactory();
+ CtapHidFrameFactory factory = new CtapHidFrameFactory();
@Test
public void wrapUnwrap_short() throws Exception {
- byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, MESSAGE_SHORT);
- byte[] unwrappedCommand = factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, wrappedCommand);
+ byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, MESSAGE_SHORT);
+ byte[] unwrappedCommand = factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, wrappedCommand);
- assertEquals(U2fHidFrameFactory.U2FHID_PING, wrappedCommand[4]);
+ assertEquals(CtapHidFrameFactory.CTAPHID_PING, wrappedCommand[4]);
assertArrayEquals(MESSAGE_SHORT, unwrappedCommand);
}
@@ -63,10 +63,10 @@ public void wrapUnwrap_variableShort() throws Exception {
public void wrapUnwrap_variableLength(int len) throws Exception {
byte[] payload = new byte[len];
- byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, payload);
- byte[] unwrappedCommand = factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, wrappedCommand);
+ byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, payload);
+ byte[] unwrappedCommand = factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, wrappedCommand);
- assertEquals(U2fHidFrameFactory.U2FHID_PING, wrappedCommand[4]);
+ assertEquals(CtapHidFrameFactory.CTAPHID_PING, wrappedCommand[4]);
assertArrayEquals(payload, unwrappedCommand);
}
@@ -85,82 +85,82 @@ public void packetNumber() {
@Test
public void wrapUnwrap_long() throws Exception {
- byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, MESSAGE_LONG);
- byte[] unwrappedCommand = factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, wrappedCommand);
+ byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, MESSAGE_LONG);
+ byte[] unwrappedCommand = factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, wrappedCommand);
- assertEquals(U2fHidFrameFactory.U2FHID_PING, wrappedCommand[4]);
+ assertEquals(CtapHidFrameFactory.CTAPHID_PING, wrappedCommand[4]);
assertArrayEquals(MESSAGE_LONG, unwrappedCommand);
}
@Test
public void wrapUnwrap_max() throws Exception {
- byte[] MESSAGE_MAX = new byte[U2FHID_MAX_SIZE];
+ byte[] MESSAGE_MAX = new byte[CTAPHID_MAX_SIZE];
- byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, MESSAGE_MAX);
- byte[] unwrappedCommand = factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, wrappedCommand);
+ byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, MESSAGE_MAX);
+ byte[] unwrappedCommand = factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, wrappedCommand);
- assertEquals(U2fHidFrameFactory.U2FHID_PING, wrappedCommand[4]);
+ assertEquals(CtapHidFrameFactory.CTAPHID_PING, wrappedCommand[4]);
assertArrayEquals(MESSAGE_MAX, unwrappedCommand);
}
@Test(expected = UsbTransportException.class)
public void wrapUnwrap_badExpectedChannel() throws UsbTransportException {
- byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, MESSAGE_SHORT);
- factory.unwrapFrame(CHANNEL_ID + 1, U2fHidFrameFactory.U2FHID_PING, wrappedCommand);
+ byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, MESSAGE_SHORT);
+ factory.unwrapFrame(CHANNEL_ID + 1, CtapHidFrameFactory.CTAPHID_PING, wrappedCommand);
}
@Test(expected = UsbTransportException.class)
public void wrapUnwrap_badExpectedCommand() throws UsbTransportException {
- byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, MESSAGE_SHORT);
- factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_MSG, wrappedCommand);
+ byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, MESSAGE_SHORT);
+ factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_MSG, wrappedCommand);
}
@Test(expected = UsbTransportException.class)
public void wrapUnwrap_truncated() throws UsbTransportException {
- byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, MESSAGE_SHORT);
+ byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, MESSAGE_SHORT);
byte[] truncatedCommand = Arrays.copyOf(wrappedCommand, wrappedCommand.length - 1);
- factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, wrappedCommand);
- factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, truncatedCommand);
+ factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, wrappedCommand);
+ factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, truncatedCommand);
}
@Test(expected = UsbTransportException.class)
public void wrapUnwrap_missingPacket() throws UsbTransportException {
- byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, MESSAGE_LONG);
- byte[] truncatedCommand = Arrays.copyOf(wrappedCommand, wrappedCommand.length - U2fHidFrameFactory.U2FHID_BUFFER_SIZE);
+ byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, MESSAGE_LONG);
+ byte[] truncatedCommand = Arrays.copyOf(wrappedCommand, wrappedCommand.length - CtapHidFrameFactory.CTAPHID_BUFFER_SIZE);
- factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, wrappedCommand);
- factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, truncatedCommand);
+ factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, wrappedCommand);
+ factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, truncatedCommand);
}
@Test(expected = UsbTransportException.class)
public void wrapUnwrap_trailing() throws UsbTransportException {
- byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, MESSAGE_SHORT);
+ byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, MESSAGE_SHORT);
byte[] trailingDataCommand = Arrays.append(wrappedCommand, (byte) 0x50);
- factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, wrappedCommand);
- factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, trailingDataCommand);
+ factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, wrappedCommand);
+ factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, trailingDataCommand);
}
@Test(expected = UsbTransportException.class)
public void wrapUnwrap_incorrectLength() throws UsbTransportException {
- byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, MESSAGE_SHORT);
+ byte[] wrappedCommand = factory.wrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, MESSAGE_SHORT);
byte[] incorrectLengthCommand = Arrays.clone(wrappedCommand);
incorrectLengthCommand[5] += 1;
- factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, wrappedCommand);
- factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, incorrectLengthCommand);
+ factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, wrappedCommand);
+ factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, incorrectLengthCommand);
}
@Test(expected = UsbTransportException.class)
public void unwrap_empty() throws UsbTransportException {
- factory.unwrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, new byte[0]);
+ factory.unwrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, new byte[0]);
}
@Test(expected = IllegalArgumentException.class)
public void wrap_maxPlusOne() throws Exception {
- byte[] oversizedMessage = new byte[U2FHID_MAX_SIZE + 1];
- factory.wrapFrame(CHANNEL_ID, U2fHidFrameFactory.U2FHID_PING, oversizedMessage);
+ byte[] oversizedMessage = new byte[CTAPHID_MAX_SIZE + 1];
+ factory.wrapFrame(CHANNEL_ID, CtapHidFrameFactory.CTAPHID_PING, oversizedMessage);
}
@Test(expected = IllegalArgumentException.class)
diff --git a/hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidTransportProtocolTest.java b/hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidTransportProtocolTest.java
similarity index 81%
rename from hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidTransportProtocolTest.java
rename to hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidTransportProtocolTest.java
index 4983d7c..157c6e5 100644
--- a/hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/u2fhid/U2fHidTransportProtocolTest.java
+++ b/hwsecurity/core/src/test/java/de/cotech/hw/internal/transport/usb/ctaphid/CtapHidTransportProtocolTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -22,7 +22,7 @@
* along with this program. If not, see .
*/
-package de.cotech.hw.internal.transport.usb.u2fhid;
+package de.cotech.hw.internal.transport.usb.ctaphid;
import java.io.ByteArrayOutputStream;
@@ -58,7 +58,7 @@
@TargetApi(VERSION_CODES.JELLY_BEAN_MR2)
@RunWith(RobolectricTestRunner.class)
@Config(sdk = 24)
-public class U2fHidTransportProtocolTest {
+public class CtapHidTransportProtocolTest {
static final int CHANNEL_ID = 12345678;
static final byte[] DATA_IN = Hex.decodeHexOrFail("1a2b3d4e5f");
static final byte[] DATA_OUT = Hex.decodeHexOrFail("5f4e3d2c1b");
@@ -71,8 +71,8 @@ public class U2fHidTransportProtocolTest {
LinkedList requestQueue;
- U2fHidTransportProtocol protocol;
- U2fHidFrameFactory frameFactory = new U2fHidFrameFactory();
+ CtapHidTransportProtocol protocol;
+ CtapHidFrameFactory frameFactory = new CtapHidFrameFactory();
@Before
public void setUp() {
@@ -82,7 +82,7 @@ public void setUp() {
requestQueue = new LinkedList<>();
- protocol = new U2fHidTransportProtocol(usbConnection, usbIntIn, usbIntOut) {
+ protocol = new CtapHidTransportProtocol(usbConnection, usbIntIn, usbIntOut) {
@Override
UsbRequest newUsbRequest() {
return requestQueue.poll();
@@ -92,7 +92,7 @@ UsbRequest newUsbRequest() {
@Test
public void connect() throws Exception {
- expect(U2fHidFrameFactory.U2FHID_CHANNEL_ID_BROADCAST, CHANNEL_ID, U2fHidFrameFactory.U2FHID_INIT, nonce ->
+ expect(CtapHidFrameFactory.CTAPHID_CHANNEL_ID_BROADCAST, CHANNEL_ID, CtapHidFrameFactory.CTAPHID_INIT, nonce ->
ByteBuffer
.allocate(17)
.order(ByteOrder.BIG_ENDIAN)
@@ -113,7 +113,7 @@ public void connect() throws Exception {
@Test(expected = UsbTransportException.class)
public void connect_badNonce() throws Exception {
- expect(U2fHidFrameFactory.U2FHID_CHANNEL_ID_BROADCAST, CHANNEL_ID, U2fHidFrameFactory.U2FHID_INIT, nonce -> {
+ expect(CtapHidFrameFactory.CTAPHID_CHANNEL_ID_BROADCAST, CHANNEL_ID, CtapHidFrameFactory.CTAPHID_INIT, nonce -> {
nonce[0] ^= (byte) 0x25;
return ByteBuffer
.allocate(17)
@@ -133,11 +133,11 @@ public void connect_badNonce() throws Exception {
@Test(expected = UsbTransportException.class)
public void connect_leadingGarbage() throws Exception {
- expect(U2fHidFrameFactory.U2FHID_CHANNEL_ID_BROADCAST, CHANNEL_ID, U2fHidFrameFactory.U2FHID_INIT,
+ expect(CtapHidFrameFactory.CTAPHID_CHANNEL_ID_BROADCAST, CHANNEL_ID, CtapHidFrameFactory.CTAPHID_INIT,
nonce -> Hex.decodeHexOrFail("0102030405060708"));
- expect(U2fHidFrameFactory.U2FHID_CHANNEL_ID_BROADCAST, CHANNEL_ID, U2fHidFrameFactory.U2FHID_INIT,
+ expect(CtapHidFrameFactory.CTAPHID_CHANNEL_ID_BROADCAST, CHANNEL_ID, CtapHidFrameFactory.CTAPHID_INIT,
nonce -> Hex.decodeHexOrFail("0807060504030201"));
- expect(U2fHidFrameFactory.U2FHID_CHANNEL_ID_BROADCAST, CHANNEL_ID, U2fHidFrameFactory.U2FHID_INIT, nonce ->
+ expect(CtapHidFrameFactory.CTAPHID_CHANNEL_ID_BROADCAST, CHANNEL_ID, CtapHidFrameFactory.CTAPHID_INIT, nonce ->
ByteBuffer
.allocate(17)
.order(ByteOrder.BIG_ENDIAN)
@@ -160,7 +160,7 @@ public void connect_leadingGarbage() throws Exception {
public void transceive_short() throws Exception {
connect();
- expect(CHANNEL_ID, CHANNEL_ID, U2fHidFrameFactory.U2FHID_MSG, data -> {
+ expect(CHANNEL_ID, CHANNEL_ID, CtapHidFrameFactory.CTAPHID_MSG, data -> {
assertArrayEquals(DATA_IN, data);
return DATA_OUT;
});
@@ -175,7 +175,7 @@ public void transceive_short() throws Exception {
public void transceive_long() throws Exception {
connect();
- expect(CHANNEL_ID, CHANNEL_ID, U2fHidFrameFactory.U2FHID_MSG, data -> {
+ expect(CHANNEL_ID, CHANNEL_ID, CtapHidFrameFactory.CTAPHID_MSG, data -> {
assertArrayEquals(DATA_IN_LONG, data);
return DATA_OUT_LONG;
});
@@ -190,12 +190,12 @@ private void verifyDialog() {
assertTrue(requestQueue.isEmpty());
}
- private void expect(int inputChannelId, int outputChannelId, byte cmdId, U2fCommunicationCallback callback) {
+ private void expect(int inputChannelId, int outputChannelId, byte cmdId, CtapCommunicationCallback callback) {
RequestState state = new RequestState();
UsbRequest usbRequestOut = mock(UsbRequest.class);
when(usbRequestOut.initialize(usbConnection, usbIntOut)).thenReturn(true);
- when(usbRequestOut.queue(any(ByteBuffer.class), eq(U2fHidFrameFactory.U2FHID_BUFFER_SIZE))).thenAnswer(
+ when(usbRequestOut.queue(any(ByteBuffer.class), eq(CtapHidFrameFactory.CTAPHID_BUFFER_SIZE))).thenAnswer(
(Answer) invocation -> {
state.inputAccumulator.write(invocation.getArgument(0).array());
return true;
@@ -204,7 +204,7 @@ private void expect(int inputChannelId, int outputChannelId, byte cmdId, U2fComm
UsbRequest usbRequestIn = mock(UsbRequest.class);
when(usbRequestIn.initialize(usbConnection, usbIntIn)).thenReturn(true);
- when(usbRequestIn.queue(any(ByteBuffer.class), eq(U2fHidFrameFactory.U2FHID_BUFFER_SIZE))).thenAnswer(
+ when(usbRequestIn.queue(any(ByteBuffer.class), eq(CtapHidFrameFactory.CTAPHID_BUFFER_SIZE))).thenAnswer(
(Answer) invocation -> {
if (!state.inputFinished) {
state.inputFinished = true;
@@ -215,10 +215,10 @@ private void expect(int inputChannelId, int outputChannelId, byte cmdId, U2fComm
state.outputOffset = 0;
}
ByteBuffer buf = invocation.getArgument(0);
- assertEquals(U2fHidFrameFactory.U2FHID_BUFFER_SIZE, buf.capacity());
+ assertEquals(CtapHidFrameFactory.CTAPHID_BUFFER_SIZE, buf.capacity());
buf.clear();
- buf.put(state.output, state.outputOffset, U2fHidFrameFactory.U2FHID_BUFFER_SIZE);
- state.outputOffset += U2fHidFrameFactory.U2FHID_BUFFER_SIZE;
+ buf.put(state.output, state.outputOffset, CtapHidFrameFactory.CTAPHID_BUFFER_SIZE);
+ state.outputOffset += CtapHidFrameFactory.CTAPHID_BUFFER_SIZE;
return true;
});
requestQueue.add(usbRequestIn);
@@ -231,7 +231,7 @@ static class RequestState {
int outputOffset;
}
- interface U2fCommunicationCallback {
+ interface CtapCommunicationCallback {
byte[] communicate(byte[] payload);
}
}
diff --git a/hwsecurity/core/src/test/java/de/cotech/hw/secrets/ByteSecretTest.java b/hwsecurity/core/src/test/java/de/cotech/hw/secrets/ByteSecretTest.java
index 023206d..dd50e5d 100644
--- a/hwsecurity/core/src/test/java/de/cotech/hw/secrets/ByteSecretTest.java
+++ b/hwsecurity/core/src/test/java/de/cotech/hw/secrets/ByteSecretTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/build.gradle b/hwsecurity/fido/build.gradle
index 296a694..55fda7b 100644
--- a/hwsecurity/fido/build.gradle
+++ b/hwsecurity/fido/build.gradle
@@ -1,14 +1,14 @@
apply plugin: 'com.android.library'
-apply plugin: 'digital.wup.android-maven-publish'
+apply plugin: 'maven-publish'
apply plugin: 'org.jetbrains.dokka-android'
dependencies {
api project(':hwsecurity:core')
+ api project(':hwsecurity:ui')
- implementation 'com.google.android.material:material:1.0.0'
-
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
- implementation 'androidx.appcompat:appcompat:1.0.2'
api 'com.google.auto.value:auto-value-annotations:1.6.2'
annotationProcessor 'com.google.auto.value:auto-value:1.6.2'
@@ -26,7 +26,6 @@ android {
minSdkVersion 14
versionName rootProject.ext.hwSdkVersionName
vectorDrawables.useSupportLibrary = true
- consumerProguardFiles 'hwsecurity-fido.pro'
}
compileOptions {
@@ -40,59 +39,63 @@ android {
}
}
-publishing {
- publications {
- mavenAar(MavenPublication) {
- groupId = 'de.cotech'
- artifactId = 'hwsecurity-fido'
- version = android.defaultConfig.versionName
+// https://developer.android.com/studio/build/maven-publish-plugin
+afterEvaluate {
+ publishing {
+ publications {
+ release(MavenPublication) {
+ from components.release
- from components.android
+ groupId = 'de.cotech'
+ artifactId = 'hwsecurity-fido'
+ version = android.defaultConfig.versionName
- pom {
- url = 'https://hwsecurity.dev'
- licenses {
- license {
- name = 'Commercial'
- url = 'https://hwsecurity.dev/sales/'
- distribution = 'repo'
+ pom {
+ url = 'https://hwsecurity.dev'
+ licenses {
+ license {
+ name = 'Commercial'
+ url = 'https://hwsecurity.dev/sales/'
+ distribution = 'repo'
+ }
+ license {
+ name = 'GNU General Public License, version 3'
+ url = 'https://www.gnu.org/licenses/gpl-3.0.txt'
+ }
}
- license {
- name = 'GNU General Public License, version 3'
- url = 'https://www.gnu.org/licenses/gpl-3.0.txt'
+ organization {
+ name = 'Confidential Technologies GmbH'
+ url = 'https://www.cotech.de'
}
}
- organization {
- name = 'Confidential Technologies GmbH'
- url = 'https://www.cotech.de'
- }
}
}
- }
- /*
- * To upload release, create file gradle.properties in ~/.gradle/ with this content:
- *
- * cotechMavenName=xxx
- * cotechMavenPassword=xxx
- */
- if (project.hasProperty('cotechMavenName') && project.hasProperty('cotechMavenPassword')) {
- println "Found cotechMavenName, cotechMavenPassword in gradle.properties!"
+ /*
+ * To upload release, create file gradle.properties in ~/.gradle/ with this content:
+ *
+ * cotechMavenName=xxx
+ * cotechMavenPassword=xxx
+ */
+ if (project.hasProperty('cotechMavenName') && project.hasProperty('cotechMavenPassword')) {
+ println "Found cotechMavenName, cotechMavenPassword in gradle.properties!"
- repositories {
- maven {
- credentials {
- username cotechMavenName
- password cotechMavenPassword
+ repositories {
+ maven {
+ credentials {
+ username cotechMavenName
+ password cotechMavenPassword
+ }
+ url = "https://maven.cotech.de"
}
- url = "https://maven.cotech.de"
}
}
}
}
dokka {
+ moduleName = 'hwsecurity-fido'
outputFormat = "hugo"
- outputDirectory = "$buildDir/dokka/reference"
+ outputDirectory = "$projectDir/../../hwsecurity.dev/content/reference"
sourceDirs = files('src/main/java')
packageOptions {
diff --git a/hwsecurity/fido/hwsecurity-fido.pro b/hwsecurity/fido/hwsecurity-fido.pro
deleted file mode 100644
index 3e21df1..0000000
--- a/hwsecurity/fido/hwsecurity-fido.pro
+++ /dev/null
@@ -1,5 +0,0 @@
-# keep Javascript interfaces (WebViewFidoBridge)
--keepclassmembers class * {
- @android.webkit.JavascriptInterface ;
-}
--keepattributes JavascriptInterface
diff --git a/hwsecurity/fido/src/main/assets/fidobridge.js b/hwsecurity/fido/src/main/assets/fidobridge.js
index 707a472..ce77fce 100644
--- a/hwsecurity/fido/src/main/assets/fidobridge.js
+++ b/hwsecurity/fido/src/main/assets/fidobridge.js
@@ -72,11 +72,6 @@ function () {
fidobridgejava.sign(jsonMessage);
};
- // TODO: WebAuthn test
-// navigator.credentials.create = function(options) {
-// u2fbridge.register("TODO");
-// };
-
console.log("fidobridge end execution");
}
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoAuthenticateCallback.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoAuthenticateCallback.java
index d398614..95f492e 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoAuthenticateCallback.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoAuthenticateCallback.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoAuthenticateRequest.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoAuthenticateRequest.java
index 768a92c..5eb4ec4 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoAuthenticateRequest.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoAuthenticateRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoAuthenticateResponse.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoAuthenticateResponse.java
index 7172c59..d8defa2 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoAuthenticateResponse.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoAuthenticateResponse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoFacetIdUtil.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoFacetIdUtil.java
index 652f4f9..d8b9cc8 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoFacetIdUtil.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoFacetIdUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoRegisterCallback.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoRegisterCallback.java
index 0d3d06b..fec3cf7 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoRegisterCallback.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoRegisterCallback.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoRegisterRequest.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoRegisterRequest.java
index 2b48f5d..06b3c41 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoRegisterRequest.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoRegisterRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoRegisterResponse.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoRegisterResponse.java
index fbedf84..541a2b8 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoRegisterResponse.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoRegisterResponse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoSecurityKey.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoSecurityKey.java
index b136e25..7202e75 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoSecurityKey.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoSecurityKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoSecurityKeyConnectionMode.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoSecurityKeyConnectionMode.java
index f0bd54a..97d2192 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoSecurityKeyConnectionMode.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/FidoSecurityKeyConnectionMode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -52,7 +52,7 @@ public FidoSecurityKey establishSecurityKeyConnection(SecurityKeyManagerConfig c
@Override
protected boolean isRelevantTransport(Transport transport) {
- return transport.getTransportType() == TransportType.USB_U2FHID
+ return transport.getTransportType() == TransportType.USB_CTAPHID
// || transport.getTransportType() == TransportType.USB_CCID
|| transport.getTransportType() == TransportType.NFC;
}
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/WebViewFidoBridge.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/WebViewFidoBridge.java
index d9b97c3..129086e 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/WebViewFidoBridge.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/WebViewFidoBridge.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -35,7 +35,6 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.Parcelable;
@@ -43,6 +42,7 @@
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
+import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
@@ -64,6 +64,8 @@
import de.cotech.hw.fido.ui.FidoDialogOptions;
import de.cotech.hw.util.HwTimber;
+import de.cotech.hw.ui.R;
+
/**
* If you are using a WebView for your login flow, you can use this WebViewFidoBridge
@@ -124,17 +126,22 @@ private WebViewFidoBridge(Context context, FragmentManager fragmentManager, WebV
}
private void addJavascriptInterfaceToWebView() {
- webView.addJavascriptInterface(new Object() {
- @JavascriptInterface
- public void register(String requestJson) {
- handleRegisterRequest(requestJson);
- }
-
- @JavascriptInterface
- public void sign(String requestJson) {
- handleSignRequest(requestJson);
- }
- }, FIDO_BRIDGE_INTERFACE);
+ webView.addJavascriptInterface(new JsInterface(), FIDO_BRIDGE_INTERFACE);
+ }
+
+ @Keep
+ class JsInterface {
+ @Keep
+ @JavascriptInterface
+ public void register(String requestJson) {
+ handleRegisterRequest(requestJson);
+ }
+
+ @Keep
+ @JavascriptInterface
+ public void sign(String requestJson) {
+ handleSignRequest(requestJson);
+ }
}
// region delegate
@@ -185,11 +192,11 @@ private void injectOnInterceptRequest() {
loadingNewPage = false;
HwTimber.d("Scheduling fido bridge injection!");
Handler handler = new Handler(context.getMainLooper());
- handler.postAtFrontOfQueue(this::injectJavascriptFidoBridge);
+ handler.postAtFrontOfQueue(this::injectJavascriptBridge);
}
}
- private void injectJavascriptFidoBridge() {
+ private void injectJavascriptBridge() {
try {
String jsContent = AndroidUtils.loadTextFromAssets(context, ASSETS_BRIDGE_JS, Charset.defaultCharset());
webView.evaluateJavascript("javascript:(" + jsContent + ")()", null);
@@ -232,7 +239,7 @@ private void showRegisterFragment(RequestData requestData, String appId, String
FidoDialogOptions.Builder opsBuilder = optionsBuilder != null ? optionsBuilder : FidoDialogOptions.builder();
opsBuilder.setTimeoutSeconds(timeoutSeconds);
- opsBuilder.setTitle(context.getString(R.string.hwsecurity_title_default_register_app_id, getDisplayAppId(appId)));
+ opsBuilder.setTitle(context.getString(R.string.hwsecurity_fido_title_default_register_app_id, getDisplayAppId(appId)));
FidoDialogFragment fidoDialogFragment = FidoDialogFragment.newInstance(registerRequest, opsBuilder.build());
fidoDialogFragment.setFidoRegisterCallback(fidoRegisterCallback);
@@ -295,7 +302,7 @@ private void showSignFragment(
FidoDialogOptions.Builder opsBuilder = optionsBuilder != null ? optionsBuilder : FidoDialogOptions.builder();
opsBuilder.setTimeoutSeconds(timeoutSeconds);
- opsBuilder.setTitle(context.getString(R.string.hwsecurity_title_default_authenticate_app_id, getDisplayAppId(appId)));
+ opsBuilder.setTitle(context.getString(R.string.hwsecurity_fido_title_default_authenticate_app_id, getDisplayAppId(appId)));
FidoDialogFragment fidoDialogFragment = FidoDialogFragment.newInstance(authenticateRequest, opsBuilder.build());
fidoDialogFragment.setFidoAuthenticateCallback(fidoAuthenticateCallback);
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/WebsafeBase64.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/WebsafeBase64.java
index dada624..f42b0d7 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/WebsafeBase64.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/WebsafeBase64.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/exceptions/FidoPresenceRequiredException.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/exceptions/FidoPresenceRequiredException.java
index cdcab1c..e1b92b7 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/exceptions/FidoPresenceRequiredException.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/exceptions/FidoPresenceRequiredException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/exceptions/FidoWrongKeyHandleException.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/exceptions/FidoWrongKeyHandleException.java
index b3abb48..e1539ce 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/exceptions/FidoWrongKeyHandleException.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/exceptions/FidoWrongKeyHandleException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/FidoCommandApduDescriber.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/FidoCommandApduDescriber.java
index 5ab2b3f..0b05949 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/FidoCommandApduDescriber.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/FidoCommandApduDescriber.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/FidoU2fAppletConnection.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/FidoU2fAppletConnection.java
index f7fb72c..416fd7c 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/FidoU2fAppletConnection.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/FidoU2fAppletConnection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -34,6 +34,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
+
import de.cotech.hw.SecurityKeyException;
import de.cotech.hw.exceptions.*;
import de.cotech.hw.fido.exceptions.FidoPresenceRequiredException;
@@ -89,7 +90,7 @@ public void connectIfNecessary() throws IOException {
private void connectToDevice() throws IOException {
try {
- if (transport.getTransportType() == TransportType.USB_U2FHID) {
+ if (transport.getTransportType() == TransportType.USB_CTAPHID) {
HwTimber.d("Using USB U2F HID as a transport. No need to select AID.");
byte[] versionBytes = readVersion();
checkVersionOrThrow(versionBytes);
@@ -145,20 +146,6 @@ private byte[] selectFileOrFail(byte[] fileAid) throws IOException {
// region communication
- // ISO/IEC 7816-4
- private ResponseApdu communicate(CommandApdu commandApdu) throws IOException {
- ResponseApdu lastResponse;
-
- lastResponse = sendWithChaining(commandApdu);
- if (lastResponse.getSw1() == RESPONSE_SW1_INCORRECT_LENGTH && lastResponse.getSw2() != 0) {
- commandApdu = commandApdu.withNe(lastResponse.getSw2());
- lastResponse = sendWithChaining(commandApdu);
- }
- lastResponse = readChainedResponseIfAvailable(lastResponse);
-
- return lastResponse;
- }
-
// see "FIDO U2F Raw Message Formats", Section 3.3 Status Codes
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
public ResponseApdu communicateOrThrow(CommandApdu commandApdu) throws IOException {
@@ -187,41 +174,54 @@ public ResponseApdu communicateOrThrow(CommandApdu commandApdu) throws IOExcepti
}
// ISO/IEC 7816-4
+ private ResponseApdu communicate(CommandApdu commandApdu) throws IOException {
+ ResponseApdu lastResponse;
+
+ lastResponse = sendWithChaining(commandApdu);
+ if (lastResponse.getSw1() == RESPONSE_SW1_INCORRECT_LENGTH && lastResponse.getSw2() != 0) {
+ commandApdu = commandApdu.withNe(lastResponse.getSw2());
+ lastResponse = sendWithChaining(commandApdu);
+ }
+ lastResponse = readChainedResponseIfAvailable(lastResponse);
+
+ return lastResponse;
+ }
+
@NonNull
private ResponseApdu sendWithChaining(CommandApdu commandApdu) throws IOException {
- /* We use an Lc of 65536 to enforce extended length APDUs for the register and authenticate commands.
+ /* U2F Spec:
+ * "If the request was encoded using extended length APDU encoding,
+ * the authenticator MUST respond using the extended length APDU response format."
+ *
+ * "If the request was encoded using short APDU encoding,
+ * the authenticator MUST respond using ISO 7816-4 APDU chaining."
*
- * This forces APDU case 4e in CommandApdu:
- * apdu[apdu.length - 2] = 0;
- * apdu[apdu.length - 1] = 0;
+ * https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
*
- * see also:
- * https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-hid-protocol-v1.2-ps-20170411.html
- * https://docs.oracle.com/javacard/3.0.5/prognotes/extended_apdu_format.htm
+ * In the best case, extended length is supported by device and authenticator, so we don't need to
+ * parse chained APDUs coming from the authenticator.
*
- * Note:
* We do *not* check for `transport.isExtendedLengthSupported()` here! There are phones (including
- * the Nexus 5X) that return "false" to this, but some Security Keys (like Yubikey Neo) still
+ * the Nexus 5X, Nexus 6P) that return "false" to this, but some Security Keys (like Yubikey Neo) still
* require us to send extended APDUs. So what we do is, send an extended APDU, and if that doesn't
* work, fall back to a short one.
*/
- if (commandFactory.isSuitableForExtendedApdu(commandApdu)) {
- CommandApdu extendedLengthApdu = commandApdu.withNe(65536);
- ResponseApdu response = transport.transceive(extendedLengthApdu);
- if (response.getSw() != WrongRequestLengthException.SW_WRONG_REQUEST_LENGTH) {
- return response;
- } else {
- HwTimber.d("Received WRONG_REQUEST_LENGTH error. Retrying with compatibility workaround");
- }
+ if (!transport.isExtendedLengthSupported()) {
+ HwTimber.w("Transport protocol does not support extended length. Probably an old device with NFC, such as Nexus 5X, Nexus 6P. We still try sending extended length!");
}
- if (commandFactory.isSuitableForShortApdu(commandApdu)) {
- CommandApdu shortApdu = commandFactory.createShortApdu(commandApdu);
- return transport.transceive(shortApdu);
+ ResponseApdu response = transport.transceive(commandApdu.withExtendedApduNe());
+ if (response.getSw() != WrongRequestLengthException.SW_WRONG_REQUEST_LENGTH) {
+ return response;
+ } else {
+ HwTimber.d("Received WRONG_REQUEST_LENGTH error. Retrying with short APDU Ne.");
}
- ResponseApdu lastResponse = null;
+ if (commandFactory.isSuitableForSingleShortApdu(commandApdu)) {
+ return transport.transceive(commandApdu.withShortApduNe());
+ }
+ ResponseApdu lastResponse = null;
List chainedApdus = commandFactory.createChainedApdus(commandApdu);
for (int i = 0, totalCommands = chainedApdus.size(); i < totalCommands; i++) {
CommandApdu chainedApdu = chainedApdus.get(i);
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/FidoU2fCommandApduFactory.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/FidoU2fCommandApduFactory.java
index b2a2995..648a554 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/FidoU2fCommandApduFactory.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/FidoU2fCommandApduFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -31,6 +31,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
+
import de.cotech.hw.internal.iso7816.CommandApdu;
@@ -42,12 +43,6 @@
public class FidoU2fCommandApduFactory {
private static final FidoCommandApduDescriber DESCRIBER = new FidoCommandApduDescriber();
- // ISO/IEC 7816-4
- private static final int MAX_APDU_NC = 255;
- private static final int MAX_APDU_NC_EXT = 65535;
- private static final int MAX_APDU_NE = 256;
- private static final int MAX_APDU_NE_EXT = 65536;
-
private static final int MASK_CLA_CHAINING = 1 << 4;
private static final int CLA = 0x00;
@@ -85,9 +80,10 @@ public CommandApdu createVersionCommand() {
}
// ISO/IEC 7816-4
+ // SELECT command always as short APDU
@NonNull
public CommandApdu createSelectFileCommand(byte[] fileAid) {
- return CommandApdu.create(CLA, INS_SELECT_FILE, P1_SELECT_FILE, P2_EMPTY, fileAid, MAX_APDU_NE).withDescriber(DESCRIBER);
+ return CommandApdu.create(CLA, INS_SELECT_FILE, P1_SELECT_FILE, P2_EMPTY, fileAid, CommandApdu.MAX_APDU_NE_SHORT).withDescriber(DESCRIBER);
}
// GET RESPONSE ISO/IEC 7816-4 par.7.6.1
@@ -96,13 +92,6 @@ public CommandApdu createGetResponseCommand(int lastResponseSw2) {
return CommandApdu.create(CLA, INS_GET_RESPONSE, P1_EMPTY, P2_EMPTY, lastResponseSw2).withDescriber(DESCRIBER);
}
- // ISO/IEC 7816-4
- @NonNull
- public CommandApdu createShortApdu(CommandApdu apdu) {
- int ne = Math.min(apdu.getNe(), MAX_APDU_NE);
- return CommandApdu.create(apdu.getCLA(), apdu.getINS(), apdu.getP1(), apdu.getP2(), apdu.getData(), ne).withDescriber(DESCRIBER);
- }
-
// ISO/IEC 7816-4
@NonNull
public List createChainedApdus(CommandApdu apdu) {
@@ -111,13 +100,14 @@ public List createChainedApdus(CommandApdu apdu) {
int offset = 0;
byte[] data = apdu.getData();
while (offset < data.length) {
- int curLen = Math.min(MAX_APDU_NC, data.length - offset);
+ int curLen = Math.min(CommandApdu.MAX_APDU_NC_SHORT, data.length - offset);
boolean last = offset + curLen >= data.length;
int cla = apdu.getCLA() + (last ? 0 : MASK_CLA_CHAINING);
CommandApdu cmd;
if (last) {
- int ne = Math.min(apdu.getNe(), MAX_APDU_NE);
+ // TODO: Check this!
+ int ne = Math.min(apdu.getNe(), CommandApdu.MAX_APDU_NE_SHORT);
cmd = CommandApdu.create(cla, apdu.getINS(), apdu.getP1(), apdu.getP2(), data, offset, curLen, ne, DESCRIBER);
} else {
cmd = CommandApdu.create(cla, apdu.getINS(), apdu.getP1(), apdu.getP2(), data, offset, curLen, 0, DESCRIBER);
@@ -130,11 +120,8 @@ public List createChainedApdus(CommandApdu apdu) {
return result;
}
- public boolean isSuitableForShortApdu(CommandApdu apdu) {
- return apdu.getData().length <= MAX_APDU_NC;
+ public boolean isSuitableForSingleShortApdu(CommandApdu apdu) {
+ return apdu.getNc() <= CommandApdu.MAX_APDU_NC_SHORT;
}
- public boolean isSuitableForExtendedApdu(CommandApdu commandApdu) {
- return commandApdu.getCLA() == CLA && commandApdu.getINS() != INS_SELECT_FILE;
- }
}
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoAsyncOperationManager.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoAsyncOperationManager.java
index 0f48dc5..919f569 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoAsyncOperationManager.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoAsyncOperationManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoAuthenticateOperationThread.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoAuthenticateOperationThread.java
index 7a86251..c8c22ae 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoAuthenticateOperationThread.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoAuthenticateOperationThread.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoOperationThread.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoOperationThread.java
index c83ffd3..27b4045 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoOperationThread.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoOperationThread.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -37,7 +37,7 @@
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
-import de.cotech.hw.exceptions.SecurityKeyLostException;
+import de.cotech.hw.exceptions.SecurityKeyDisconnectedException;
import de.cotech.hw.fido.exceptions.FidoPresenceRequiredException;
import de.cotech.hw.fido.internal.FidoU2fAppletConnection;
import de.cotech.hw.util.HwTimber;
@@ -85,7 +85,7 @@ public void run() {
} catch (InterruptedException e) {
HwTimber.e("Fido operation was interrupted");
break;
- } catch (SecurityKeyLostException e) {
+ } catch (SecurityKeyDisconnectedException e) {
HwTimber.e("Transport gone during fido operation");
break;
} catch (FidoPresenceRequiredException e) {
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoRegisterOperationThread.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoRegisterOperationThread.java
index 395fc23..9489ab5 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoRegisterOperationThread.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/async/FidoRegisterOperationThread.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fApiUtils.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fApiUtils.java
index 762d700..23c96ad 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fApiUtils.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fApiUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fAuthenticateRequest.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fAuthenticateRequest.java
index ef16337..926bd8a 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fAuthenticateRequest.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fAuthenticateRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fJsonParser.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fJsonParser.java
index 97daca2..8309f6b 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fJsonParser.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fJsonParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fJsonSerializer.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fJsonSerializer.java
index d41387e..eb94dc5 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fJsonSerializer.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fJsonSerializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fRegisterRequest.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fRegisterRequest.java
index 4a04fe5..c7c3328 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fRegisterRequest.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fRegisterRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fRequest.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fRequest.java
index 12eea78..5c01a58 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fRequest.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fResponse.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fResponse.java
index 00365ac..5cce78c 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fResponse.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/jsapi/U2fResponse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/operations/AuthenticateOp.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/operations/AuthenticateOp.java
index 8d3e704..e86f704 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/operations/AuthenticateOp.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/operations/AuthenticateOp.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/operations/RegisterOp.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/operations/RegisterOp.java
index 91d09d2..61d4d80 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/operations/RegisterOp.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/operations/RegisterOp.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/utils/AndroidUtils.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/utils/AndroidUtils.java
index 40227a8..dfe6f0d 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/utils/AndroidUtils.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/utils/AndroidUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/utils/AnimatedVectorDrawableHelper.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/utils/AnimatedVectorDrawableHelper.java
index b36029a..e51fd3e 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/utils/AnimatedVectorDrawableHelper.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/internal/utils/AnimatedVectorDrawableHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/ui/FidoDialogFragment.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/ui/FidoDialogFragment.java
index 62e0f7b..bb01aae 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/ui/FidoDialogFragment.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/ui/FidoDialogFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -25,68 +25,42 @@
package de.cotech.hw.fido.ui;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.provider.Settings;
-import android.util.DisplayMetrics;
-import android.util.Pair;
-import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.Window;
import android.view.WindowManager;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.widget.Button;
import android.widget.FrameLayout;
-import android.widget.ImageView;
import android.widget.TextView;
-import android.widget.Toast;
-import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
import androidx.annotation.UiThread;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Guideline;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import androidx.core.view.ViewCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
-import androidx.transition.AutoTransition;
-import androidx.transition.Scene;
-import androidx.transition.Transition;
import androidx.transition.TransitionManager;
-import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
+import com.google.android.material.button.MaterialButton;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
import de.cotech.hw.SecurityKeyCallback;
import de.cotech.hw.SecurityKeyException;
import de.cotech.hw.SecurityKeyManager;
-import de.cotech.hw.exceptions.SecurityKeyLostException;
+import de.cotech.hw.exceptions.SecurityKeyDisconnectedException;
import de.cotech.hw.fido.FidoAuthenticateCallback;
import de.cotech.hw.fido.FidoAuthenticateRequest;
import de.cotech.hw.fido.FidoAuthenticateResponse;
@@ -95,21 +69,22 @@
import de.cotech.hw.fido.FidoRegisterResponse;
import de.cotech.hw.fido.FidoSecurityKey;
import de.cotech.hw.fido.FidoSecurityKeyConnectionMode;
-import de.cotech.hw.fido.R;
+import de.cotech.hw.ui.R;
import de.cotech.hw.fido.exceptions.FidoWrongKeyHandleException;
-import de.cotech.hw.fido.internal.utils.AnimatedVectorDrawableHelper;
-import de.cotech.hw.internal.NfcSweetspotData;
-import de.cotech.hw.util.NfcStatusObserver;
+import de.cotech.hw.ui.internal.ErrorView;
+import de.cotech.hw.ui.internal.NfcFullscreenView;
+import de.cotech.hw.ui.internal.SecurityKeyFormFactor;
+import de.cotech.hw.ui.internal.SmartcardFormFactor;
import de.cotech.hw.util.HwTimber;
-public class FidoDialogFragment extends BottomSheetDialogFragment implements SecurityKeyCallback {
+public class FidoDialogFragment extends BottomSheetDialogFragment implements SecurityKeyCallback, SecurityKeyFormFactor.SelectTransportCallback {
private static final String FRAGMENT_TAG = "hwsecurity-fido-fragment";
private static final String ARG_FIDO_REGISTER_REQUEST = "ARG_FIDO_REGISTER_REQUEST";
private static final String ARG_FIDO_AUTHENTICATE_REQUEST = "ARG_FIDO_AUTHENTICATE_REQUEST";
private static final String ARG_FIDO_OPTIONS = "de.cotech.hw.fido.ARG_FIDO_OPTIONS";
- private static final long TIME_DELAYED_STATE_CHANGE = 3000;
+ private static final long TIME_DELAYED_SCREEN_CHANGE = 3000;
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
@@ -123,45 +98,34 @@ public class FidoDialogFragment extends BottomSheetDialogFragment implements Sec
private ConstraintLayout innerBottomSheet;
private Guideline guidelineForceHeight;
- private Button buttonCancel;
+ private MaterialButton buttonLeft;
+ private MaterialButton buttonRight;
private TextView textTitle;
private TextView textDescription;
- private TextView textNfc;
- private TextView textUsb;
- private ImageView imageNfc;
- private ImageView imageNfcFullscreen;
- private ImageView imageUsb;
- private TextView textViewNfcDisabled;
- private Button buttonNfcDisabled;
+ private SecurityKeyFormFactor securityKeyFormFactor;
+ private SmartcardFormFactor smartcardFormFactor;
- private TextView textError;
- private ImageView imageError;
-
- private ImageView sweetspotIndicator;
- private TextView textNfcFullscreen;
-
- private NfcStatusObserver nfcStatusObserver;
+ private ErrorView errorView;
private FidoDialogOptions options;
private FidoRegisterRequest fidoRegisterRequest;
private FidoAuthenticateRequest fidoAuthenticateRequest;
- private NfcSweetspotData nfcSweetspotData;
+ private NfcFullscreenView nfcFullscreenView;
- private enum State {
+ private enum Screen {
START,
NFC_FULLSCREEN,
- NFC_SWEETSPOT,
USB_INSERT,
USB_PRESS_BUTTON,
USB_SELECT_AND_PRESS_BUTTON,
ERROR,
}
- private State currentState;
- private State stateBeforeError;
+ private Screen currentScreen;
+ private Screen screenBeforeError;
public void setFidoRegisterCallback(OnFidoRegisterCallback fidoRegisterCallback) {
this.fidoRegisterCallback = fidoRegisterCallback;
@@ -293,15 +257,13 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
"or FidoDialogFragment.OnFidoRegisterCallback!");
}
- nfcSweetspotData = NfcSweetspotData.getInstance(getContext());
-
SecurityKeyManager.getInstance().registerCallback(new FidoSecurityKeyConnectionMode(), this, this);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
- return inflater.inflate(R.layout.hwsecurity_fido_bottomsheet, container, false);
+ return inflater.inflate(R.layout.hwsecurity_security_key_dialog, container, false);
}
@Override
@@ -325,20 +287,20 @@ private void initTimeout(long timeoutSeconds) {
handler.postDelayed(() -> {
HwTimber.d("Timeout after %s seconds.", timeoutSeconds);
- textError.setText(R.string.hwsecurity_error_timeout);
- gotoState(State.ERROR);
+ errorView.setText(R.string.hwsecurity_fido_error_timeout);
+ gotoScreen(Screen.ERROR);
bottomSheet.postDelayed(() -> {
if (!isAdded()) {
return;
}
- dismiss();
+ dismissAllowingStateLoss();
if (fidoAuthenticateRequest != null) {
fidoAuthenticateCallback.onFidoAuthenticateTimeout(fidoAuthenticateRequest);
} else if (fidoRegisterRequest != null) {
fidoRegisterCallback.onFidoRegisterTimeout(fidoRegisterRequest);
}
- }, TIME_DELAYED_STATE_CHANGE);
+ }, TIME_DELAYED_SCREEN_CHANGE);
}, timeoutSeconds * 1000);
}
@@ -371,69 +333,24 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
initTimeout(options.getTimeoutSeconds());
}
- innerBottomSheet = view.findViewById(R.id.hwSecurityFidoBottomSheet);
+ innerBottomSheet = view.findViewById(R.id.hwSecurityDialogBottomSheet);
guidelineForceHeight = view.findViewById(R.id.guidelineForceHeight);
- buttonCancel = view.findViewById(R.id.buttonCancel);
+ buttonLeft = view.findViewById(R.id.buttonLeft);
+ buttonRight = view.findViewById(R.id.buttonRight);
textTitle = view.findViewById(R.id.textTitle);
textDescription = view.findViewById(R.id.textDescription);
- textNfc = view.findViewById(R.id.textNfc);
- textNfcFullscreen = view.findViewById(R.id.textNfcFullscreen);
- textUsb = view.findViewById(R.id.textUsb);
- imageNfc = view.findViewById(R.id.imageNfc);
- imageNfcFullscreen = view.findViewById(R.id.imageNfcFullscreen);
- sweetspotIndicator = view.findViewById(R.id.imageNfcSweetspot);
- imageUsb = view.findViewById(R.id.imageUsb);
- imageError = view.findViewById(R.id.imageError);
- textError = view.findViewById(R.id.textError);
- textViewNfcDisabled = view.findViewById(R.id.textNfcDisabled);
- buttonNfcDisabled = view.findViewById(R.id.buttonNfcDisabled);
-
- nfcStatusObserver = new NfcStatusObserver(getContext(), this, this::showOrHideNfcDisabledView);
-
- buttonCancel.setOnClickListener(v -> getDialog().cancel());
- }
- @Override
- public void onResume() {
- super.onResume();
- if (currentState == State.START) {
- // re-check NFC status, maybe user is coming back from settings
- showOrHideNfcView();
- }
- if (currentState == null ||
- currentState == State.USB_INSERT ||
- currentState == State.USB_PRESS_BUTTON ||
- currentState == State.USB_SELECT_AND_PRESS_BUTTON) {
- gotoState(State.START);
- }
- }
+ smartcardFormFactor = new SmartcardFormFactor(view.findViewById(R.id.includeSmartcardFormFactor), this);
+ securityKeyFormFactor = new SecurityKeyFormFactor(view.findViewById(R.id.includeSecurityKeyFormFactor), this, this, innerBottomSheet, options.getShowSdkLogo());
- private void showOrHideNfcView() {
- boolean isNfcHardwareAvailable = SecurityKeyManager.getInstance().isNfcHardwareAvailable();
- textNfc.setVisibility(isNfcHardwareAvailable ? View.VISIBLE : View.GONE);
- imageNfc.setVisibility(isNfcHardwareAvailable ? View.VISIBLE : View.GONE);
+ errorView = new ErrorView(view.findViewById(de.cotech.hw.ui.R.id.includeError));
- if (isNfcHardwareAvailable) {
- boolean nfcEnabled = nfcStatusObserver.isNfcEnabled();
- showOrHideNfcDisabledView(nfcEnabled);
- }
- }
+ nfcFullscreenView = new NfcFullscreenView(view.findViewById(de.cotech.hw.ui.R.id.includeNfcFullscreen), innerBottomSheet);
- private void showOrHideNfcDisabledView(boolean nfcEnabled) {
- textViewNfcDisabled.setVisibility(nfcEnabled ? View.GONE : View.VISIBLE);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- buttonNfcDisabled.setOnClickListener(v -> startAndroidNfcConfigActivityWithHint());
- buttonNfcDisabled.setVisibility(nfcEnabled ? View.GONE : View.VISIBLE);
- }
- textNfc.setVisibility(nfcEnabled ? View.VISIBLE : View.INVISIBLE);
- imageNfc.setVisibility(nfcEnabled ? View.VISIBLE : View.INVISIBLE);
- }
+ buttonRight.setVisibility(View.INVISIBLE);
+ buttonLeft.setOnClickListener(v -> getDialog().cancel());
- @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
- private void startAndroidNfcConfigActivityWithHint() {
- Toast.makeText(getContext().getApplicationContext(),
- R.string.hwsecurity_nfc_settings_toast, Toast.LENGTH_SHORT).show();
- startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
+ gotoScreen(Screen.START);
}
@Override
@@ -450,416 +367,108 @@ private String getStartTitle() {
if (options.getTitle() != null) {
return options.getTitle();
} else if (fidoRegisterRequest != null) {
- return getResources().getString(R.string.hwsecurity_title_default_register);
+ return getResources().getString(R.string.hwsecurity_fido_title_default_register);
} else {
- return getResources().getString(R.string.hwsecurity_title_default_authenticate);
+ return getResources().getString(R.string.hwsecurity_fido_title_default_authenticate);
}
}
- private void gotoState(State newState) {
- switch (newState) {
+ private void gotoScreen(Screen newScreen) {
+ switch (newScreen) {
case START: {
- imageNfc.setOnClickListener(v -> gotoState(State.NFC_FULLSCREEN));
- imageUsb.setOnClickListener(v -> gotoState(State.USB_INSERT));
animateStart();
break;
}
case NFC_FULLSCREEN: {
- removeOnClickListener();
- animateSelectNfc();
- break;
- }
- case NFC_SWEETSPOT: {
- removeOnClickListener();
- showNfcSweetSpot();
+ securityKeyFormFactor.animateSelectNfc();
break;
}
case USB_INSERT: {
- removeOnClickListener();
- animateSelectUsb();
+ securityKeyFormFactor.animateSelectUsb();
break;
}
case USB_PRESS_BUTTON: {
- removeOnClickListener();
- animateUsbPressButton();
+ securityKeyFormFactor.animateUsbPressButton();
break;
}
case USB_SELECT_AND_PRESS_BUTTON: {
- removeOnClickListener();
- animateSelectUsbAndPressButton();
+ securityKeyFormFactor.animateSelectUsbAndPressButton();
break;
}
case ERROR: {
- removeOnClickListener();
animateError();
break;
}
}
- currentState = newState;
- }
-
- private void removeOnClickListener() {
- imageNfc.setOnClickListener(null);
- imageUsb.setOnClickListener(null);
+ currentScreen = newScreen;
}
- private void animateStart() {
- imageNfc.setImageResource(R.drawable.hwsecurity_nfc_start);
- imageUsb.setImageResource(R.drawable.hwsecurity_usb_start);
- buttonCancel.setText(R.string.hwsecurity_cancel);
- textTitle.setText(getStartTitle());
- textDescription.setText(R.string.hwsecurity_description_start);
-
- AutoTransition selectModeTransition = new AutoTransition();
- selectModeTransition.setDuration(150);
-
- TransitionManager.go(new Scene(innerBottomSheet), selectModeTransition);
- showOrHideNfcView();
- imageUsb.setVisibility(View.VISIBLE);
- textUsb.setVisibility(View.VISIBLE);
- textTitle.setVisibility(View.VISIBLE);
- textDescription.setVisibility(View.VISIBLE);
- textError.setVisibility(View.GONE);
- imageError.setVisibility(View.GONE);
- }
-
- private void animateSelectNfc() {
- AutoTransition selectModeTransition = new AutoTransition();
- selectModeTransition.setDuration(150);
- selectModeTransition.addListener(new Transition.TransitionListener() {
- @Override
- public void onTransitionStart(@NonNull Transition transition) {
- }
-
- @Override
- public void onTransitionEnd(@NonNull Transition transition) {
- animateNfcFullscreen();
- }
-
- @Override
- public void onTransitionCancel(@NonNull Transition transition) {
- }
-
- @Override
- public void onTransitionPause(@NonNull Transition transition) {
- }
-
- @Override
- public void onTransitionResume(@NonNull Transition transition) {
- }
- });
-
- TransitionManager.go(new Scene(innerBottomSheet), selectModeTransition);
- imageUsb.setVisibility(View.GONE);
- imageNfc.setVisibility(View.GONE);
- textNfc.setVisibility(View.GONE);
- textViewNfcDisabled.setVisibility(View.GONE);
- buttonNfcDisabled.setVisibility(View.GONE);
- textUsb.setVisibility(View.GONE);
+ @Override
+ public void screeFullscreenNfc() {
textTitle.setVisibility(View.GONE);
textDescription.setVisibility(View.GONE);
- textError.setVisibility(View.GONE);
- imageError.setVisibility(View.GONE);
- }
-
- private void animateNfcFullscreen() {
- ValueAnimator bottomSheetFullscreenAnimator = ValueAnimator
- .ofInt(bottomSheet.getHeight(), coordinator.getHeight())
- .setDuration(250);
-
- bottomSheetFullscreenAnimator.addUpdateListener(animation -> {
- bottomSheet.getLayoutParams().height = (int) animation.getAnimatedValue();
- bottomSheet.requestLayout();
- guidelineForceHeight.setGuidelineEnd((int) animation.getAnimatedValue() - 100);
- guidelineForceHeight.requestLayout();
- });
-
- int colorFrom = getResources().getColor(R.color.hwSecurityWhite);
- int colorTo = resolveColorFromAttr(R.attr.hwSecuritySurfaceColor);
- ValueAnimator colorChange = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
- colorChange.setDuration(100);
- colorChange.addUpdateListener(animator -> {
- innerBottomSheet.setBackgroundColor((int) animator.getAnimatedValue());
- });
-
- ObjectAnimator fadeInImageNfcFullscreen = ObjectAnimator
- .ofFloat(imageNfcFullscreen, View.ALPHA, 0, 1)
- .setDuration(150);
- fadeInImageNfcFullscreen.setStartDelay(50);
- fadeInImageNfcFullscreen.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- imageNfcFullscreen.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
- });
-
- bottomSheetFullscreenAnimator.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- animateNfcFinal();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
- });
-
- List items = new ArrayList<>();
- items.add(bottomSheetFullscreenAnimator);
- items.add(fadeInImageNfcFullscreen);
- items.add(colorChange);
-
- AnimatorSet set = new AnimatorSet();
- set.playTogether(items);
- set.setInterpolator(new AccelerateDecelerateInterpolator());
- set.start();
- }
-
- private void animateNfcFinal() {
- textNfcFullscreen.setText(R.string.hwsecurity_title_nfc_fullscreen);
- textNfcFullscreen.setVisibility(View.VISIBLE);
-
- Animatable2Compat.AnimationCallback animationCallback = new Animatable2Compat.AnimationCallback() {
- @Override
- public void onAnimationEnd(Drawable drawable) {
- if (!ViewCompat.isAttachedToWindow(imageNfcFullscreen)) {
- return;
- }
-
- fadeToNfcSweetSpot();
- }
- };
-
- AnimatedVectorDrawableHelper.startAnimation(imageNfcFullscreen, R.drawable.hwsecurity_nfc_handling, animationCallback);
- }
-
- private void fadeToNfcSweetSpot() {
- Pair nfcPosition = nfcSweetspotData.getSweetspotForBuildModel();
- if (nfcPosition == null) {
- HwTimber.d("No NFC sweetspot data available for this model.");
- return;
- }
-
- int colorFrom = resolveColorFromAttr(R.attr.hwSecuritySurfaceColor);
- int colorTo = getResources().getColor(R.color.hwSecurityWhite);
- ValueAnimator colorChange = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
- colorChange.setDuration(150);
- colorChange.addUpdateListener(animator -> {
- innerBottomSheet.setBackgroundColor((int) animator.getAnimatedValue());
- });
-
- ObjectAnimator fadeOutImageNfcFullscreen = ObjectAnimator
- .ofFloat(imageNfcFullscreen, "alpha", 1, 0)
- .setDuration(150);
-
- fadeOutImageNfcFullscreen.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- gotoState(State.NFC_SWEETSPOT);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
- });
-
- List items = new ArrayList<>();
- items.add(colorChange);
- items.add(fadeOutImageNfcFullscreen);
+ errorView.setVisibility(View.GONE);
+ smartcardFormFactor.setVisibility(View.GONE);
+ securityKeyFormFactor.setVisibility(View.GONE);
- AnimatorSet set = new AnimatorSet();
- set.playTogether(items);
- set.start();
+ nfcFullscreenView.setVisibility(View.VISIBLE);
+ nfcFullscreenView.animateNfcFullscreen(getDialog());
}
- private void showNfcSweetSpot() {
- Pair nfcPosition = nfcSweetspotData.getSweetspotForBuildModel();
-
- Dialog dialog = getDialog();
- if (dialog == null) {
- return;
- }
-
- DisplayMetrics metrics = new DisplayMetrics();
- dialog.getWindow().getWindowManager().getDefaultDisplay().getMetrics(metrics);
-
- float statusBarHeight = getStatusbarHeight(dialog.getWindow());
-
- final float translationX = (float) (metrics.widthPixels * nfcPosition.first);
- final float translationY = (float) (metrics.heightPixels * nfcPosition.second) + statusBarHeight;
-
- sweetspotIndicator.post(() -> {
- sweetspotIndicator.setTranslationX(translationX - sweetspotIndicator.getWidth() / 2);
- sweetspotIndicator.setTranslationY(translationY - sweetspotIndicator.getHeight() / 2);
-
- TransitionManager.beginDelayedTransition(innerBottomSheet);
- sweetspotIndicator.setVisibility(View.VISIBLE);
- textNfcFullscreen.setVisibility(View.VISIBLE);
- imageNfcFullscreen.setVisibility(View.GONE);
- imageError.setVisibility(View.GONE);
- textError.setVisibility(View.GONE);
- });
-
- AnimatedVectorDrawableHelper.startAndLoopAnimation(sweetspotIndicator, R.drawable.hwsecurity_nfc_sweet_spot_a);
- }
-
- private static int getStatusbarHeight(Window window) {
- Rect rectangle = new Rect();
- window.getDecorView().getWindowVisibleDisplayFrame(rectangle);
- int statusBarHeight = rectangle.top;
- int contentViewTop = window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
- return contentViewTop - statusBarHeight;
- }
-
- private void animateSelectUsb() {
- AutoTransition selectModeTransition = new AutoTransition();
- selectModeTransition.setDuration(150);
- selectModeTransition.addListener(new Transition.TransitionListener() {
- @Override
- public void onTransitionStart(@NonNull Transition transition) {
- }
-
- @Override
- public void onTransitionEnd(@NonNull Transition transition) {
- AnimatedVectorDrawableHelper.startAndLoopAnimation(imageUsb, R.drawable.hwsecurity_usb_handling_a);
- }
-
- @Override
- public void onTransitionCancel(@NonNull Transition transition) {
- }
-
- @Override
- public void onTransitionPause(@NonNull Transition transition) {
- }
-
- @Override
- public void onTransitionResume(@NonNull Transition transition) {
- }
- });
-
- TransitionManager.go(new Scene(innerBottomSheet), selectModeTransition);
- textTitle.setText(R.string.hwsecurity_title_usb_selected);
- imageNfc.setVisibility(View.GONE);
- textViewNfcDisabled.setVisibility(View.GONE);
- buttonNfcDisabled.setVisibility(View.GONE);
- textDescription.setVisibility(View.GONE);
- textNfc.setVisibility(View.GONE);
- textUsb.setVisibility(View.GONE);
- textError.setVisibility(View.GONE);
- imageError.setVisibility(View.GONE);
+ @Override
+ public void onSecurityKeyFormFactorClickUsb() {
+ currentScreen = Screen.USB_INSERT;
}
- private void animateSelectUsbAndPressButton() {
- AutoTransition selectModeTransition = new AutoTransition();
- selectModeTransition.setDuration(150);
- selectModeTransition.addListener(new Transition.TransitionListener() {
- @Override
- public void onTransitionStart(@NonNull Transition transition) {
- }
-
- @Override
- public void onTransitionEnd(@NonNull Transition transition) {
- AnimatedVectorDrawableHelper.startAndLoopAnimation(imageUsb, R.drawable.hwsecurity_usb_handling_b);
- }
-
- @Override
- public void onTransitionCancel(@NonNull Transition transition) {
- }
-
- @Override
- public void onTransitionPause(@NonNull Transition transition) {
- }
-
- @Override
- public void onTransitionResume(@NonNull Transition transition) {
- }
- });
+ private void animateStart() {
+ buttonLeft.setText(R.string.hwsecurity_ui_button_cancel);
+ textTitle.setText(getStartTitle());
+ textDescription.setText(R.string.hwsecurity_ui_description_start);
- TransitionManager.go(new Scene(innerBottomSheet), selectModeTransition);
- textTitle.setText(R.string.hwsecurity_title_usb_button);
- imageNfc.setVisibility(View.GONE);
- textViewNfcDisabled.setVisibility(View.GONE);
- buttonNfcDisabled.setVisibility(View.GONE);
- textDescription.setVisibility(View.GONE);
- textNfc.setVisibility(View.GONE);
- textUsb.setVisibility(View.GONE);
- textError.setVisibility(View.GONE);
- imageError.setVisibility(View.GONE);
- }
+ textTitle.setVisibility(View.VISIBLE);
+ textDescription.setVisibility(View.VISIBLE);
+ errorView.setVisibility(View.GONE);
+ securityKeyFormFactor.setVisibility(View.VISIBLE);
- private void animateUsbPressButton() {
- TransitionManager.beginDelayedTransition(innerBottomSheet);
- textTitle.setText(R.string.hwsecurity_title_usb_button);
- AnimatedVectorDrawableHelper.startAndLoopAnimation(imageUsb, R.drawable.hwsecurity_usb_handling_b);
+ securityKeyFormFactor.resetAnimation();
}
private void animateError() {
TransitionManager.beginDelayedTransition(innerBottomSheet);
textTitle.setVisibility(View.GONE);
textDescription.setVisibility(View.GONE);
- imageNfc.setVisibility(View.GONE);
- textViewNfcDisabled.setVisibility(View.GONE);
- buttonNfcDisabled.setVisibility(View.GONE);
- imageUsb.setVisibility(View.GONE);
- textNfc.setVisibility(View.GONE);
- textUsb.setVisibility(View.GONE);
- textNfcFullscreen.setVisibility(View.GONE);
- imageNfcFullscreen.setVisibility(View.GONE);
- textError.setVisibility(View.VISIBLE);
- imageError.setVisibility(View.VISIBLE);
-
- AnimatedVectorDrawableHelper.startAnimation(imageError, R.drawable.hwsecurity_error);
+ securityKeyFormFactor.setVisibility(View.GONE);
+ smartcardFormFactor.setVisibility(View.GONE);
+ nfcFullscreenView.setVisibility(View.GONE);
+ errorView.setVisibility(View.VISIBLE);
}
@UiThread
@Override
public void onSecurityKeyDiscovered(@NonNull FidoSecurityKey securityKey) {
- switch (currentState) {
+ switch (currentScreen) {
case START: {
if (securityKey.isTransportUsb()) {
- gotoState(State.USB_SELECT_AND_PRESS_BUTTON);
+ gotoScreen(Screen.USB_SELECT_AND_PRESS_BUTTON);
}
+ sendFidoCommand(securityKey);
break;
}
case USB_INSERT: {
if (securityKey.isTransportUsb()) {
- gotoState(State.USB_PRESS_BUTTON);
+ gotoScreen(Screen.USB_PRESS_BUTTON);
}
+ sendFidoCommand(securityKey);
break;
}
default: {
- HwTimber.d("onSecurityKeyDiscovered unhandled state: %s", currentState.name());
+ HwTimber.d("onSecurityKeyDiscovered unhandled screen: %s", currentScreen.name());
}
}
+ }
+
+ private void sendFidoCommand(@NonNull FidoSecurityKey securityKey) {
if (fidoRegisterRequest != null) {
securityKey.registerAsync(fidoRegisterRequest,
new FidoRegisterCallback() {
@@ -903,12 +512,12 @@ public void onIoException(IOException e) {
public void onSecurityKeyDisconnected(@NonNull FidoSecurityKey securityKey) {
HwTimber.d("onSecurityKeyDisconnected");
- switch (currentState) {
+ switch (currentScreen) {
case USB_PRESS_BUTTON:
case USB_SELECT_AND_PRESS_BUTTON:
- gotoState(State.START);
+ gotoScreen(Screen.START);
default:
- HwTimber.d("onSecurityKeyDisconnected unhandled state: %s", currentState.name());
+ HwTimber.d("onSecurityKeyDisconnected unhandled screen: %s", currentScreen.name());
}
}
@@ -920,43 +529,36 @@ public void onSecurityKeyDiscoveryFailed(@NonNull IOException exception) {
private void handleError(IOException exception) {
HwTimber.d(exception);
- if (currentState == State.ERROR) {
- // keep stateBeforeError
- } else if (currentState == State.NFC_FULLSCREEN || currentState == State.NFC_SWEETSPOT) {
- stateBeforeError = State.NFC_SWEETSPOT;
+ if (currentScreen == Screen.ERROR) {
+ // keep current screenBeforeError
+ } else if (currentScreen == Screen.NFC_FULLSCREEN) {
+// screenBeforeError = Screen.NFC_SWEETSPOT;
} else {
- stateBeforeError = State.START;
+ screenBeforeError = Screen.START;
}
try {
throw exception;
} catch (FidoWrongKeyHandleException e) {
- showError(getString(R.string.hwsecurity_error_wrong_key_handle));
+ showError(getString(R.string.hwsecurity_fido_error_wrong_security_key));
} catch (SecurityKeyException e) {
- showError(getString(R.string.hwsecurity_error_internal, e.getShortErrorName()));
- } catch (SecurityKeyLostException e) {
+ showError(getString(R.string.hwsecurity_fido_error_internal, e.getShortErrorName()));
+ } catch (SecurityKeyDisconnectedException e) {
// not handled
} catch (IOException e) {
- showError(getString(R.string.hwsecurity_error_internal, e.getMessage()));
+ showError(getString(R.string.hwsecurity_fido_error_internal, e.getMessage()));
}
}
private void showError(String text) {
- textError.setText(text);
- gotoState(State.ERROR);
+ errorView.setText(text);
+ gotoScreen(Screen.ERROR);
bottomSheet.postDelayed(() -> {
if (!isAdded()) {
return;
}
- gotoState(stateBeforeError);
- }, TIME_DELAYED_STATE_CHANGE);
- }
-
- private int resolveColorFromAttr(@AttrRes int resId) {
- TypedValue outValue = new TypedValue();
- // must be the themed context to work correctly on Android < 5
- innerBottomSheet.getContext().getTheme().resolveAttribute(resId, outValue, true);
- return outValue.data;
+ gotoScreen(screenBeforeError);
+ }, TIME_DELAYED_SCREEN_CHANGE);
}
}
diff --git a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/ui/FidoDialogOptions.java b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/ui/FidoDialogOptions.java
index ceb79ea..eed7ccd 100644
--- a/hwsecurity/fido/src/main/java/de/cotech/hw/fido/ui/FidoDialogOptions.java
+++ b/hwsecurity/fido/src/main/java/de/cotech/hw/fido/ui/FidoDialogOptions.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -31,7 +31,7 @@
import com.google.auto.value.AutoValue;
-import de.cotech.hw.fido.R;
+import de.cotech.hw.ui.R;
@AutoValue
public abstract class FidoDialogOptions implements Parcelable {
@@ -47,11 +47,14 @@ public abstract class FidoDialogOptions implements Parcelable {
@StyleRes
public abstract int getTheme();
+ public abstract boolean getShowSdkLogo();
+
// default values
public static Builder builder() {
return new AutoValue_FidoDialogOptions.Builder()
.setPreventScreenshots(false)
- .setTheme(R.style.HwSecurity_Fido_Dialog);
+ .setShowSdkLogo(false)
+ .setTheme(R.style.HwSecurity_Dialog);
}
@AutoValue.Builder
@@ -84,7 +87,7 @@ public abstract static class Builder {
/**
* Set your own custom theme for the dialog to change colors:
* {@code
- *
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/FidoSecurityKeyTest.java b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/FidoSecurityKeyTest.java
index 10edea9..c7bc253 100644
--- a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/FidoSecurityKeyTest.java
+++ b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/FidoSecurityKeyTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/FakeU2fFidoAppletConnection.java b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/FakeU2fFidoAppletConnection.java
index b1770d8..503f850 100644
--- a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/FakeU2fFidoAppletConnection.java
+++ b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/FakeU2fFidoAppletConnection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/FidoU2fAppletConnectionTest.java b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/FidoU2fAppletConnectionTest.java
index b14ffad..9c5f38a 100644
--- a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/FidoU2fAppletConnectionTest.java
+++ b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/FidoU2fAppletConnectionTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -41,7 +41,8 @@
@SuppressWarnings("WeakerAccess")
public class FidoU2fAppletConnectionTest {
static final CommandApdu PING_APDU = CommandApdu.create(0x00, 0xc0, 0x00, 0x00);
- static final CommandApdu PING_APDU_EXTENDED = CommandApdu.create(0x00, 0xc0, 0x00, 0x00).withNe(65536);
+ static final CommandApdu PING_APDU_SHORT = CommandApdu.create(0x00, 0xc0, 0x00, 0x00).withShortApduNe();
+ static final CommandApdu PING_APDU_EXTENDED = CommandApdu.create(0x00, 0xc0, 0x00, 0x00).withExtendedApduNe();
FidoU2fAppletConnection connection;
FakeTransport transport;
@@ -60,8 +61,9 @@ public void communicate_ping() throws Exception {
@Test(expected = WrongRequestLengthException.class)
public void communicateOrThrow_wrongLength() throws Exception {
+ // On wrong length, we try again with short APDU encoding
transport.expect(PING_APDU_EXTENDED, ResponseApduUtils.createError(0x6700));
- transport.expect(PING_APDU, ResponseApduUtils.createError(0x6700));
+ transport.expect(PING_APDU_SHORT, ResponseApduUtils.createError(0x6700));
connection.communicateOrThrow(PING_APDU);
}
diff --git a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/FidoAsyncOperationManagerTest.java b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/FidoAsyncOperationManagerTest.java
index 7d5c0d9..5fd8155 100644
--- a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/FidoAsyncOperationManagerTest.java
+++ b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/FidoAsyncOperationManagerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/FidoAsyncOperationManagerUtil.java b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/FidoAsyncOperationManagerUtil.java
index 32c3df0..3a39a96 100644
--- a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/FidoAsyncOperationManagerUtil.java
+++ b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/FidoAsyncOperationManagerUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/FidoOperationThreadTest.java b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/FidoOperationThreadTest.java
index 83ec6cb..5cc9796 100644
--- a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/FidoOperationThreadTest.java
+++ b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/FidoOperationThreadTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/TestFidoOperationThread.java b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/TestFidoOperationThread.java
index ce5d4c5..c9183a3 100644
--- a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/TestFidoOperationThread.java
+++ b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/async/TestFidoOperationThread.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/operations/AuthenticateOpTest.java b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/operations/AuthenticateOpTest.java
index 62ff970..2c929aa 100644
--- a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/operations/AuthenticateOpTest.java
+++ b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/operations/AuthenticateOpTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/operations/RegisterOpTest.java b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/operations/RegisterOpTest.java
index 5548b13..c5a5365 100644
--- a/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/operations/RegisterOpTest.java
+++ b/hwsecurity/fido/src/test/java/de/cotech/hw/fido/internal/operations/RegisterOpTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/test/java/de/cotech/hw/internal/iso7816/ResponseApduUtils.java b/hwsecurity/fido/src/test/java/de/cotech/hw/internal/iso7816/ResponseApduUtils.java
index 3e551a7..081c6b1 100644
--- a/hwsecurity/fido/src/test/java/de/cotech/hw/internal/iso7816/ResponseApduUtils.java
+++ b/hwsecurity/fido/src/test/java/de/cotech/hw/internal/iso7816/ResponseApduUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
diff --git a/hwsecurity/fido/src/test/java/de/cotech/hw/internal/transport/FakeTransport.java b/hwsecurity/fido/src/test/java/de/cotech/hw/internal/transport/FakeTransport.java
index a9ed583..7db1fe5 100644
--- a/hwsecurity/fido/src/test/java/de/cotech/hw/internal/transport/FakeTransport.java
+++ b/hwsecurity/fido/src/test/java/de/cotech/hw/internal/transport/FakeTransport.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -89,7 +89,7 @@ public boolean ping() {
@Override
public TransportType getTransportType() {
- return TransportType.USB_U2FHID;
+ return TransportType.USB_CTAPHID;
}
@Nullable
@@ -108,7 +108,7 @@ public void setExtendedLengthSupported(boolean extendedLengthSupported) {
}
public void expect(String commandBytesHex, String responseBytesHex) throws IOException {
- expectCommands.add(CommandApdu.fromBytes(Hex.decodeHexOrFail(commandBytesHex)).withNe(65536));
+ expectCommands.add(CommandApdu.fromBytes(Hex.decodeHexOrFail(commandBytesHex)).withExtendedApduNe());
expectResponses.add(ResponseApdu.fromBytes(Hex.decodeHexOrFail(responseBytesHex)));
}
diff --git a/hwsecurity/fido2/build.gradle b/hwsecurity/fido2/build.gradle
new file mode 100644
index 0000000..1ddc23d
--- /dev/null
+++ b/hwsecurity/fido2/build.gradle
@@ -0,0 +1,105 @@
+apply plugin: 'com.android.library'
+apply plugin: 'maven-publish'
+apply plugin: 'org.jetbrains.dokka-android'
+
+dependencies {
+ api project(':hwsecurity:core')
+ api project(':hwsecurity:ui')
+
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'com.google.android.material:material:1.1.0'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+
+ api 'com.google.auto.value:auto-value-annotations:1.6.2'
+ annotationProcessor 'com.google.auto.value:auto-value:1.6.2'
+ annotationProcessor 'com.ryanharter.auto.value:auto-value-parcel:0.2.6'
+
+ testImplementation 'junit:junit:4.12'
+ testImplementation 'org.robolectric:robolectric:3.8'
+ testImplementation 'org.mockito:mockito-core:2.18.0'
+}
+
+android {
+ compileSdkVersion rootProject.ext.compileSdkVersion
+
+ defaultConfig {
+ minSdkVersion 14
+ versionName rootProject.ext.hwSdkVersionName
+ vectorDrawables.useSupportLibrary = true
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ // Do not abort build if lint finds errors
+ lintOptions {
+ abortOnError false
+ }
+}
+
+// https://developer.android.com/studio/build/maven-publish-plugin
+afterEvaluate {
+ publishing {
+ publications {
+ release(MavenPublication) {
+ from components.release
+
+ groupId = 'de.cotech'
+ artifactId = 'hwsecurity-fido2'
+ version = android.defaultConfig.versionName
+
+ pom {
+ url = 'https://hwsecurity.dev'
+ licenses {
+ license {
+ name = 'Commercial'
+ url = 'https://hwsecurity.dev/sales/'
+ distribution = 'repo'
+ }
+ license {
+ name = 'GNU General Public License, version 3'
+ url = 'https://www.gnu.org/licenses/gpl-3.0.txt'
+ }
+ }
+ organization {
+ name = 'Confidential Technologies GmbH'
+ url = 'https://www.cotech.de'
+ }
+ }
+ }
+ }
+ /*
+ * To upload release, create file gradle.properties in ~/.gradle/ with this content:
+ *
+ * cotechMavenName=xxx
+ * cotechMavenPassword=xxx
+ */
+ if (project.hasProperty('cotechMavenName') && project.hasProperty('cotechMavenPassword')) {
+ println "Found cotechMavenName, cotechMavenPassword in gradle.properties!"
+
+ repositories {
+ maven {
+ credentials {
+ username cotechMavenName
+ password cotechMavenPassword
+ }
+ url = "https://maven.cotech.de"
+ }
+ }
+ }
+ }
+}
+
+dokka {
+ moduleName = 'hwsecurity-fido2'
+ outputFormat = "hugo"
+ outputDirectory = "$projectDir/../../hwsecurity.dev/content/reference"
+ sourceDirs = files('src/main/java')
+
+ packageOptions {
+ prefix = "de.cotech.hw.fido2.internal"
+ suppress = true
+ }
+}
diff --git a/hwsecurity/fido2/src/main/AndroidManifest.xml b/hwsecurity/fido2/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..091276c
--- /dev/null
+++ b/hwsecurity/fido2/src/main/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/hwsecurity/fido2/src/main/assets/webauthnbridge.js b/hwsecurity/fido2/src/main/assets/webauthnbridge.js
new file mode 100644
index 0000000..3c60ad6
--- /dev/null
+++ b/hwsecurity/fido2/src/main/assets/webauthnbridge.js
@@ -0,0 +1,124 @@
+function () {
+
+ console.log("webauthnbridge start execution");
+
+ const webauthnbridge = window.webauthnbridge = {};
+
+ webauthnbridge.reqCounter = 0;
+
+ // Is called from Java
+ webauthnbridge.responseHandler = function(message) {
+ const response = message;
+ const reqId = response['requestId'];
+ if (!reqId || !webauthnbridge.callbackMap[reqId]) {
+ console.error('Unknown or missing requestId in response.');
+ return;
+ }
+ const cb = webauthnbridge.callbackMap[reqId];
+ delete webauthnbridge.callbackMap[reqId];
+ cb(response['responseData']);
+ };
+
+ const jsonArrayFix = function(k, v) {
+ if (v instanceof ArrayBuffer) {
+ return new Uint8Array(v);
+ }
+ return v;
+ };
+
+ const decodeWebsafeBase64 = function(websafeBase64) {
+ const base64 = websafeBase64.replace(/_/g, '/').replace(/-/g, '+');
+ return Uint8Array.from(atob(base64), c=>c.charCodeAt(0))
+ };
+
+ navigator.credentials = function() {
+ }
+
+ // overrides Browser API: https://www.w3.org/TR/credential-management-1
+ navigator.credentials.get = function(opt_options) {
+ return new Promise((resolve, reject) => {
+ webauthnbridge.current_resolve = resolve;
+ webauthnbridge.current_reject = reject;
+ const jsonMessage = JSON.stringify(opt_options, jsonArrayFix);
+ webauthnbridgejava.get(jsonMessage);
+ });
+ };
+
+ navigator.credentials.store = function(credential) {
+ return new Promise((resolve, reject) => {
+ const jsonMessage = JSON.stringify(credential);
+ webauthnbridgejava.store(jsonMessage);
+ });
+ };
+
+ navigator.credentials.create = function(opt_options) {
+ return new Promise((resolve, reject) => {
+ webauthnbridge.current_resolve = resolve;
+ webauthnbridge.current_reject = reject;
+ const jsonMessage = JSON.stringify(opt_options, jsonArrayFix);
+ webauthnbridgejava.create(jsonMessage);
+ });
+ };
+
+ navigator.credentials.preventSilentAccess = function() {
+ // calls to Java
+ webauthnbridgejava.preventSilentAccess();
+ };
+
+ window.PublicKeyCredential = function() {
+ }
+
+ // Fakes Windows Hello support
+ // https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential/isUserVerifyingPlatformAuthenticatorAvailable
+ window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable = function(opt_options) {
+ return Promise.resolve(true);
+ };
+
+ // Fakes Windows Hello support
+ // https://github.com/w3c/webauthn/issues/1173
+ window.PublicKeyCredential.isExternalCTAP2SecurityKeySupported = function(opt_options) {
+ return Promise.resolve(true);
+ };
+
+ webauthnbridge.handleReject = function(error) {
+ webauthnbridge.current_reject(error);
+ }
+
+ webauthnbridge.handleResolve = function(obj) {
+ console.log(JSON.stringify(obj));
+
+ obj.rawId = decodeWebsafeBase64(obj.id);
+
+ obj.response.clientDataJSON = decodeWebsafeBase64(obj.response.clientDataJsonB64);
+ delete obj.response.clientDataJsonB64;
+
+ if (obj.response.attestationObjectB64) {
+ obj.response.attestationObject = decodeWebsafeBase64(obj.response.attestationObjectB64);
+ delete obj.response.attestationObjectB64;
+ }
+
+ if (obj.response.signatureB64) {
+ obj.response.signature = decodeWebsafeBase64(obj.response.signatureB64);
+ delete obj.response.signatureB64;
+ }
+
+ if (obj.response.authenticatorDataB64) {
+ obj.response.authenticatorData = decodeWebsafeBase64(obj.response.authenticatorDataB64);
+ delete obj.response.authenticatorDataB64;
+ }
+
+ if (obj.response.userHandleB64) {
+ obj.response.userHandle = decodeWebsafeBase64(obj.response.userHandleB64);
+ delete obj.response.userHandleB64;
+ } else {
+ obj.response.userHandle = null;
+ }
+
+ obj.getClientExtensionResults = function () { return {}; };
+
+ console.log(JSON.stringify(obj));
+ webauthnbridge.current_resolve(obj);
+ }
+
+ console.log("webauthnbridge end execution");
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Credential.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Credential.java
new file mode 100644
index 0000000..d7d1596
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Credential.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2;
+
+
+abstract class Credential {
+ public abstract String id();
+ public abstract String type();
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Ctap2Callback.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Ctap2Callback.java
new file mode 100644
index 0000000..fbc20b9
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Ctap2Callback.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2;
+
+
+import java.io.IOException;
+
+import de.cotech.hw.fido2.internal.ctap2.Ctap2Response;
+
+
+public interface Ctap2Callback {
+ void onResponse(CR response);
+ void onIoException(IOException e);
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Fido2SecurityKey.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Fido2SecurityKey.java
new file mode 100644
index 0000000..9de4e0e
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Fido2SecurityKey.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2;
+
+
+import java.io.IOException;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.WorkerThread;
+import androidx.lifecycle.LifecycleOwner;
+import de.cotech.hw.SecurityKey;
+import de.cotech.hw.SecurityKeyManagerConfig;
+import de.cotech.hw.fido2.domain.create.PublicKeyCredentialCreationOptions;
+import de.cotech.hw.fido2.domain.get.PublicKeyCredentialRequestOptions;
+import de.cotech.hw.fido2.internal.Fido2AppletConnection;
+import de.cotech.hw.fido2.internal.async.Ctap2Fido2OperationThread;
+import de.cotech.hw.fido2.internal.async.Fido2AsyncOperationManager;
+import de.cotech.hw.fido2.internal.async.WebauthnFido2OperationThread;
+import de.cotech.hw.fido2.internal.ctap2.Ctap2Command;
+import de.cotech.hw.fido2.internal.ctap2.Ctap2Response;
+import de.cotech.hw.fido2.internal.json.JsonPublicKeyCredentialSerializer;
+import de.cotech.hw.fido2.internal.json.JsonWebauthnOptionsParser;
+import de.cotech.hw.fido2.internal.operations.WebauthnSecurityKeyOperation;
+import de.cotech.hw.fido2.internal.operations.WebauthnSecurityKeyOperationFactory;
+import de.cotech.hw.fido2.internal.webauthn.WebauthnCommand;
+import de.cotech.hw.fido2.internal.webauthn.WebauthnResponse;
+import de.cotech.hw.internal.transport.Transport;
+import org.json.JSONException;
+
+
+@SuppressWarnings({ "unused", "WeakerAccess" }) // All methods are public API
+public class Fido2SecurityKey extends SecurityKey {
+ private static final int USER_PRESENCE_CHECK_DELAY_MS = 250;
+
+ private final Fido2AppletConnection fido2AppletConnection;
+ private final Fido2AsyncOperationManager fido2AsyncOperationManager;
+ private final WebauthnSecurityKeyOperationFactory operationFactory;
+ private final JsonWebauthnOptionsParser jsonOptionsParser = new JsonWebauthnOptionsParser();
+ private final JsonPublicKeyCredentialSerializer jsonPublicKeyCredentialSerializer =
+ new JsonPublicKeyCredentialSerializer();
+
+ Fido2SecurityKey(SecurityKeyManagerConfig config, Fido2AppletConnection fido2AppletConnection,
+ Transport transport, Fido2AsyncOperationManager fido2AsyncOperationManager,
+ WebauthnSecurityKeyOperationFactory operationFactory) {
+ super(config, transport);
+ this.fido2AppletConnection = fido2AppletConnection;
+ this.fido2AsyncOperationManager = fido2AsyncOperationManager;
+ this.operationFactory = operationFactory;
+ }
+
+ @WorkerThread
+ String webauthnPublicKeyCredentialGet(String jsonOptions, String origin) throws IOException {
+ PublicKeyCredentialRequestOptions options;
+ try {
+ options = jsonOptionsParser.fromOptionsJsonGetAssertion(jsonOptions);
+ } catch (JSONException e) {
+ throw new IOException("Invalid input parameters!", e);
+ }
+ PublicKeyCredentialGet request = PublicKeyCredentialGet.create(origin, options);
+ PublicKeyCredential publicKeyCredential = webauthnCommand(request);
+ return jsonPublicKeyCredentialSerializer.publicKeyCredentialToJsonString(publicKeyCredential);
+ }
+
+ void webauthnPublicKeyCredentialGetAsync(String jsonOptions, String origin,
+ WebauthnJsonCallback callback, LifecycleOwner lifecycleOwner) throws IOException {
+ Handler mainThreadHandler = new Handler(Looper.getMainLooper());
+ webauthnPublicKeyCredentialGetAsync(jsonOptions, origin, callback, mainThreadHandler, lifecycleOwner);
+ }
+
+ void webauthnPublicKeyCredentialGetAsync(String jsonOptions, String origin,
+ WebauthnJsonCallback callback, Handler handler, LifecycleOwner lifecycleOwner) throws IOException {
+ PublicKeyCredentialRequestOptions options;
+ try {
+ options = jsonOptionsParser.fromOptionsJsonGetAssertion(jsonOptions);
+ } catch (JSONException e) {
+ throw new IOException("Invalid input parameters!", e);
+ }
+ PublicKeyCredentialGet request = PublicKeyCredentialGet.create(origin, options);
+ webauthnCommandAsync(request, webauthnResponseToJsonCallback(callback), handler, lifecycleOwner);
+ }
+
+ @WorkerThread
+ public String webauthnPublicKeyCredentialCreate(String jsonOptions, String origin) throws IOException {
+ PublicKeyCredentialCreationOptions options;
+ try {
+ options = jsonOptionsParser.fromOptionsJsonMakeCredential(jsonOptions);
+ } catch (JSONException e) {
+ throw new IOException("Invalid input parameters!", e);
+ }
+ PublicKeyCredentialCreate create = PublicKeyCredentialCreate.create(origin, options);
+ PublicKeyCredential publicKeyCredential = webauthnCommand(create);
+ return jsonPublicKeyCredentialSerializer.publicKeyCredentialToJsonString(publicKeyCredential);
+ }
+
+ @AnyThread
+ void webauthnPublicKeyCredentialCreateAsync(String jsonOptions, String origin,
+ WebauthnJsonCallback callback, LifecycleOwner lifecycleOwner)
+ throws IOException {
+ Handler mainThreadHandler = new Handler(Looper.getMainLooper());
+ webauthnPublicKeyCredentialCreateAsync(jsonOptions, origin, callback, mainThreadHandler, lifecycleOwner);
+ }
+
+ @AnyThread
+ void webauthnPublicKeyCredentialCreateAsync(String jsonOptions, String origin,
+ WebauthnJsonCallback callback, Handler handler, LifecycleOwner lifecycleOwner)
+ throws IOException {
+ PublicKeyCredentialCreationOptions options;
+ try {
+ options = jsonOptionsParser.fromOptionsJsonMakeCredential(jsonOptions);
+ } catch (JSONException e) {
+ throw new IOException("Invalid input parameters!", e);
+ }
+ PublicKeyCredentialCreate create = PublicKeyCredentialCreate.create(origin, options);
+ webauthnCommandAsync(create, webauthnResponseToJsonCallback(callback), handler, lifecycleOwner);
+ }
+
+ @WorkerThread
+ public
+ WR webauthnCommand(WC command) throws IOException {
+ WebauthnSecurityKeyOperation operation = operationFactory.getOperation(
+ command, fido2AppletConnection.isCtap2Capable());
+ return operation.performWebauthnSecurityKeyOperation(fido2AppletConnection, command);
+ }
+
+ @AnyThread
+ public
+ void webauthnCommandAsync(WebauthnCommand command, WebauthnCallback callback,
+ LifecycleOwner lifecycleOwner) {
+ Handler mainThreadHandler = new Handler(Looper.getMainLooper());
+ webauthnCommandAsync(command, callback, mainThreadHandler, lifecycleOwner);
+ }
+
+ @AnyThread
+ public
+ void webauthnCommandAsync(
+ WC command, WebauthnCallback callback, Handler handler,
+ LifecycleOwner lifecycleOwner) {
+ WebauthnFido2OperationThread
+ fidoOperationThread = new WebauthnFido2OperationThread<>(
+ fido2AppletConnection, operationFactory, handler, callback, command, USER_PRESENCE_CHECK_DELAY_MS);
+ fido2AsyncOperationManager.startAsyncOperation(lifecycleOwner, fidoOperationThread);
+ }
+
+ @WorkerThread
+ public CR ctap2RawCommand(Ctap2Command ctap2Command)
+ throws IOException {
+ return fido2AppletConnection.ctap2CommunicateOrThrow(ctap2Command);
+ }
+
+ @AnyThread
+ public void ctap2RawCommandAsync(Ctap2Command command,
+ Ctap2Callback callback, LifecycleOwner lifecycleOwner) {
+ Handler mainThreadHandler = new Handler(Looper.getMainLooper());
+ ctap2RawCommandAsync(command, callback, mainThreadHandler, lifecycleOwner);
+ }
+
+ @AnyThread
+ public void ctap2RawCommandAsync(Ctap2Command command,
+ Ctap2Callback callback, Handler handler, LifecycleOwner lifecycleOwner) {
+ Ctap2Fido2OperationThread ctap2Fido2OperationThread = new Ctap2Fido2OperationThread<>(
+ fido2AppletConnection, handler, command, callback, USER_PRESENCE_CHECK_DELAY_MS);
+ fido2AsyncOperationManager.startAsyncOperation(lifecycleOwner, ctap2Fido2OperationThread);
+ }
+
+ @AnyThread
+ public void clearAsyncOperation() {
+ fido2AsyncOperationManager.clearAsyncOperation();
+ }
+
+ private WebauthnCallback webauthnResponseToJsonCallback(
+ WebauthnJsonCallback callback) {
+ return new WebauthnCallback() {
+ @Override
+ public void onResponse(PublicKeyCredential jsonResponse) {
+ jsonPublicKeyCredentialSerializer.publicKeyCredentialToJsonString(jsonResponse);
+ }
+
+ @Override
+ public void onIoException(IOException e) {
+ callback.onIoException(e);
+ }
+ };
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Fido2SecurityKeyConnectionMode.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Fido2SecurityKeyConnectionMode.java
new file mode 100644
index 0000000..6bbb788
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Fido2SecurityKeyConnectionMode.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2;
+
+
+import java.io.IOException;
+
+import de.cotech.hw.SecurityKey;
+import de.cotech.hw.SecurityKeyConnectionMode;
+import de.cotech.hw.SecurityKeyManagerConfig;
+import de.cotech.hw.fido2.internal.Fido2AppletConnection;
+import de.cotech.hw.fido2.internal.async.Fido2AsyncOperationManager;
+import de.cotech.hw.fido2.internal.operations.WebauthnSecurityKeyOperationFactory;
+import de.cotech.hw.fido2.internal.pinauth.PinAuthCryptoUtil;
+import de.cotech.hw.fido2.internal.pinauth.PinProtocolV1;
+import de.cotech.hw.internal.transport.SecurityKeyInfo.TransportType;
+import de.cotech.hw.internal.transport.Transport;
+
+
+public class Fido2SecurityKeyConnectionMode extends SecurityKeyConnectionMode {
+ private static Fido2SecurityKeyConnectionMode INSTANCE;
+ private final Fido2SecurityKeyConnectionModeConfig fido2Config;
+
+ public static Fido2SecurityKeyConnectionMode getInstance() {
+ if (INSTANCE == null) {
+ Fido2SecurityKeyConnectionModeConfig defaultConfig =
+ Fido2SecurityKeyConnectionModeConfig.getDefaultConfig();
+ INSTANCE = new Fido2SecurityKeyConnectionMode(
+ defaultConfig);
+ }
+ return INSTANCE;
+ }
+
+ public static Fido2SecurityKeyConnectionMode getInstance(Fido2SecurityKeyConnectionModeConfig config) {
+ return new Fido2SecurityKeyConnectionMode(config);
+ }
+
+ public Fido2SecurityKeyConnectionMode(Fido2SecurityKeyConnectionModeConfig fido2Config) {
+ this.fido2Config = fido2Config;
+ }
+
+ @Override
+ public Fido2SecurityKey establishSecurityKeyConnection(SecurityKeyManagerConfig config, Transport transport) throws IOException {
+ if (!isRelevantTransport(transport)) {
+ throw new IllegalArgumentException("Received incompatible transport!");
+ }
+
+ Fido2AppletConnection fido2AppletConnection = Fido2AppletConnection.getInstanceForTransport(transport);
+ fido2AppletConnection.connectIfNecessary();
+ fido2AppletConnection.setForceCtap1(fido2Config.isForceU2f());
+
+ WebauthnSecurityKeyOperationFactory operationFactory =
+ new WebauthnSecurityKeyOperationFactory(new PinProtocolV1(new PinAuthCryptoUtil()));
+ return new Fido2SecurityKey(config, fido2AppletConnection, transport, new Fido2AsyncOperationManager(),
+ operationFactory);
+ }
+
+ @Override
+ protected boolean isRelevantTransport(Transport transport) {
+ return transport.getTransportType() == TransportType.USB_CTAPHID
+ // || transport.getTransportType() == TransportType.USB_CCID
+ || transport.getTransportType() == TransportType.NFC;
+ }
+
+ @Override
+ protected boolean isRelevantSecurityKey(SecurityKey securityKey) {
+ return securityKey instanceof Fido2SecurityKey;
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Fido2SecurityKeyConnectionModeConfig.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Fido2SecurityKeyConnectionModeConfig.java
new file mode 100644
index 0000000..6543060
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/Fido2SecurityKeyConnectionModeConfig.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2;
+
+
+import android.os.Parcelable;
+
+import com.google.auto.value.AutoValue;
+
+
+@AutoValue
+public abstract class Fido2SecurityKeyConnectionModeConfig implements Parcelable {
+ public abstract boolean isForceU2f();
+
+ public static Fido2SecurityKeyConnectionModeConfig getDefaultConfig() {
+ return builder().build();
+ }
+
+ public static Fido2SecurityKeyConnectionModeConfig.Builder builder() {
+ return new AutoValue_Fido2SecurityKeyConnectionModeConfig.Builder()
+ .setForceU2f(false);
+ }
+
+ @AutoValue.Builder
+ public static abstract class Builder {
+ public abstract Builder setForceU2f(boolean forceU2f);
+
+ public abstract Fido2SecurityKeyConnectionModeConfig build();
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/PublicKeyCredential.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/PublicKeyCredential.java
new file mode 100644
index 0000000..6141fed
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/PublicKeyCredential.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2;
+
+
+import com.google.auto.value.AutoValue;
+import de.cotech.hw.fido2.domain.AuthenticatorResponse;
+import de.cotech.hw.fido2.internal.utils.WebsafeBase64;
+import de.cotech.hw.fido2.internal.webauthn.WebauthnResponse;
+
+
+@AutoValue
+public abstract class PublicKeyCredential extends Credential implements WebauthnResponse {
+ public abstract byte[] rawId();
+ public abstract AuthenticatorResponse response();
+
+ public String id() {
+ return WebsafeBase64.encodeToString(rawId());
+ }
+
+ @Override
+ public String type() {
+ return "public-key";
+ }
+
+ public static PublicKeyCredential create(byte[] rawId, AuthenticatorResponse response) {
+ return new AutoValue_PublicKeyCredential(rawId, response);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/PublicKeyCredentialCreate.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/PublicKeyCredentialCreate.java
new file mode 100644
index 0000000..2db17fc
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/PublicKeyCredentialCreate.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2;
+
+
+import androidx.annotation.Nullable;
+import com.google.auto.value.AutoValue;
+import de.cotech.hw.fido2.domain.create.PublicKeyCredentialCreationOptions;
+import de.cotech.hw.fido2.internal.webauthn.WebauthnCommand;
+
+
+@AutoValue
+public abstract class PublicKeyCredentialCreate extends WebauthnCommand {
+ public abstract String origin();
+ public abstract PublicKeyCredentialCreationOptions options();
+ @Nullable
+ public abstract String clientPin();
+ public abstract boolean lastAttemptOk();
+
+ public static PublicKeyCredentialCreate create(String origin, PublicKeyCredentialCreationOptions options) {
+ return new AutoValue_PublicKeyCredentialCreate(origin, options, null, false);
+ }
+
+ @Override
+ public PublicKeyCredentialCreate withClientPin(String clientPin, boolean lastAttemptOk) {
+ return new AutoValue_PublicKeyCredentialCreate(origin(), options(), clientPin, lastAttemptOk);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/PublicKeyCredentialGet.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/PublicKeyCredentialGet.java
new file mode 100644
index 0000000..83fdd07
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/PublicKeyCredentialGet.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2;
+
+
+import androidx.annotation.Nullable;
+import com.google.auto.value.AutoValue;
+import de.cotech.hw.fido2.domain.get.PublicKeyCredentialRequestOptions;
+import de.cotech.hw.fido2.internal.webauthn.WebauthnCommand;
+
+
+@AutoValue
+public abstract class PublicKeyCredentialGet extends WebauthnCommand {
+ public abstract String origin();
+ public abstract PublicKeyCredentialRequestOptions options();
+ @Nullable
+ public abstract String clientPin();
+ public abstract boolean lastAttemptOk();
+
+ public static PublicKeyCredentialGet create(String origin, PublicKeyCredentialRequestOptions options) {
+ return new AutoValue_PublicKeyCredentialGet(origin, options, null, false);
+ }
+
+ @Override
+ public PublicKeyCredentialGet withClientPin(String clientPin, boolean lastAttemptOk) {
+ return new AutoValue_PublicKeyCredentialGet(origin(), options(), clientPin, lastAttemptOk);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/WebViewWebauthnBridge.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/WebViewWebauthnBridge.java
new file mode 100644
index 0000000..1248fb6
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/WebViewWebauthnBridge.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2;
+
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.os.Handler;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebView;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.FragmentManager;
+import de.cotech.hw.fido2.domain.create.PublicKeyCredentialCreationOptions;
+import de.cotech.hw.fido2.domain.get.PublicKeyCredentialRequestOptions;
+import de.cotech.hw.fido2.internal.json.JsonPublicKeyCredentialSerializer;
+import de.cotech.hw.fido2.internal.json.JsonWebauthnOptionsParser;
+import de.cotech.hw.fido2.internal.utils.AndroidUtils;
+import de.cotech.hw.fido2.ui.WebauthnDialogFragment;
+import de.cotech.hw.fido2.ui.WebauthnDialogFragment.OnGetAssertionCallback;
+import de.cotech.hw.fido2.ui.WebauthnDialogFragment.OnMakeCredentialCallback;
+import de.cotech.hw.fido2.ui.WebauthnDialogOptions;
+import de.cotech.hw.util.HwTimber;
+import org.json.JSONException;
+
+import de.cotech.hw.ui.R;
+
+/**
+ * If you are using a WebView for your login flow, you can use this WebViewWebauthnBridge
+ * for extending the WebView's Javascript API with the WebAuthn APIs.
+ *
+ * Note: Currently only compatible and tested with Android SDK >= 19 due to evaluateJavascript() calls.
+ */
+@TargetApi(VERSION_CODES.KITKAT)
+public class WebViewWebauthnBridge {
+ private static final String WEBAUTHN_BRIDGE_INTERFACE = "webauthnbridgejava";
+ private static final String ASSETS_BRIDGE_JS = "webauthnbridge.js";
+
+ private final Context context;
+ private final FragmentManager fragmentManager;
+ private final WebView webView;
+ private final WebauthnDialogOptions.Builder optionsBuilder;
+
+ private String currentOrigin;
+ private boolean loadingNewPage;
+ private JsonWebauthnOptionsParser jsonWebauthnOptionsParser =
+ new JsonWebauthnOptionsParser();
+ private JsonPublicKeyCredentialSerializer jsonPublicKeyCredentialSerializer =
+ new JsonPublicKeyCredentialSerializer();
+
+ @SuppressWarnings("unused") // public API
+ public static WebViewWebauthnBridge createInstanceForWebView(AppCompatActivity activity, WebView webView) {
+ return createInstanceForWebView(activity.getApplicationContext(), activity.getSupportFragmentManager(), webView, null);
+ }
+
+ /**
+ * Same as createInstanceForWebView, but allows to set WebauthnDialogOptions.Builder.
+ *
+ * Note: Timeout and Title will be overwritten.
+ */
+ @SuppressWarnings("unused") // public API
+ public static WebViewWebauthnBridge createInstanceForWebView(AppCompatActivity activity, WebView webView, WebauthnDialogOptions.Builder optionsBuilder) {
+ return createInstanceForWebView(activity.getApplicationContext(), activity.getSupportFragmentManager(), webView, optionsBuilder);
+ }
+
+ public static WebViewWebauthnBridge createInstanceForWebView(Context context, FragmentManager fragmentManager, WebView webView) {
+ return createInstanceForWebView(context, fragmentManager, webView, null);
+ }
+
+ @SuppressWarnings("WeakerAccess") // public API
+ public static WebViewWebauthnBridge createInstanceForWebView(Context context, FragmentManager fragmentManager, WebView webView, WebauthnDialogOptions.Builder optionsBuilder) {
+ Context applicationContext = context.getApplicationContext();
+
+ WebauthnDialogOptions.Builder opsBuilder = optionsBuilder != null ? optionsBuilder : WebauthnDialogOptions.builder();
+ WebViewWebauthnBridge webViewWebauthnBridge = new WebViewWebauthnBridge(applicationContext, fragmentManager, webView, opsBuilder);
+ webViewWebauthnBridge.addJavascriptInterfaceToWebView();
+
+ return webViewWebauthnBridge;
+ }
+
+ private WebViewWebauthnBridge(Context context, FragmentManager fragmentManager, WebView webView, WebauthnDialogOptions.Builder optionsBuilder) {
+ this.context = context;
+ this.fragmentManager = fragmentManager;
+ this.webView = webView;
+ this.optionsBuilder = optionsBuilder;
+ }
+
+ private void addJavascriptInterfaceToWebView() {
+ webView.addJavascriptInterface(new JsInterface(), WEBAUTHN_BRIDGE_INTERFACE);
+ }
+
+ @Keep
+ class JsInterface {
+ @Keep
+ @JavascriptInterface
+ public void get(String options) {
+ javascriptPublicKeyCredentialGet(options);
+ }
+
+ @Keep
+ @JavascriptInterface
+ public void store(String credential) {
+ javascriptPublicKeyCredentialStore(credential);
+ }
+
+ @Keep
+ @JavascriptInterface
+ public void create(String options) {
+ javascriptPublicKeyCredentialCreate(options);
+ }
+
+ @Keep
+ @JavascriptInterface
+ public void preventSilentAccess() {
+ javascriptPublicKeyCredentialPreventSilentAccess();
+ }
+ }
+
+ // region delegate
+
+ /**
+ * Call this in your WebViewClient.shouldInterceptRequest(WebView view, WebResourceRequest request)
+ */
+ @TargetApi(VERSION_CODES.LOLLIPOP)
+ @SuppressWarnings("unused")
+ // parity with WebViewClient.shouldInterceptRequest(WebView view, WebResourceRequest request)
+ public void delegateShouldInterceptRequest(WebView view, WebResourceRequest request) {
+ HwTimber.d("shouldInterceptRequest(WebView view, WebResourceRequest request) %s", request.getUrl());
+ injectOnInterceptRequest();
+ }
+
+ /**
+ * Call this in your WebViewClient.shouldInterceptRequest(WebView view, String url)
+ */
+ @TargetApi(VERSION_CODES.KITKAT)
+ @SuppressWarnings("unused")
+ // parity with WebViewClient.shouldInterceptRequest(WebView view, String url)
+ public void delegateShouldInterceptRequest(WebView view, String url) {
+ HwTimber.d("shouldInterceptRequest(WebView view, String url): %s", url);
+ injectOnInterceptRequest();
+ }
+
+ @SuppressWarnings("unused") // parity with WebViewClient.onPageStarted
+ public void delegateOnPageStarted(WebView view, String url, Bitmap favicon) {
+ this.currentOrigin = null;
+ this.loadingNewPage = false;
+
+ if (url == null) {
+ return;
+ }
+ Uri uri = Uri.parse(url);
+
+ if (!"https".equalsIgnoreCase(uri.getScheme())) {
+ HwTimber.e("WebAuthn only supported for HTTPS websites!");
+ return;
+ }
+
+ this.currentOrigin = "https://" + uri.getAuthority();
+ this.loadingNewPage = true;
+ }
+
+ private void injectOnInterceptRequest() {
+ if (loadingNewPage) {
+ loadingNewPage = false;
+ HwTimber.d("Scheduling WebAuthn bridge injection!");
+ Handler handler = new Handler(context.getMainLooper());
+ handler.postAtFrontOfQueue(this::injectJavascriptBridge);
+ }
+ }
+
+ private void injectJavascriptBridge() {
+ try {
+ String jsContent = AndroidUtils.loadTextFromAssets(context, ASSETS_BRIDGE_JS, Charset.defaultCharset());
+ webView.evaluateJavascript("javascript:(" + jsContent + ")()", null);
+ } catch (IOException e) {
+ HwTimber.e(e);
+ throw new IllegalStateException();
+ }
+ }
+
+ // endregion
+
+ private void javascriptPublicKeyCredentialGet(String optionsJsonString) {
+ HwTimber.d("javascriptPublicKeyCredentialGet: %s", optionsJsonString);
+ try {
+ PublicKeyCredentialRequestOptions options = jsonWebauthnOptionsParser.fromOptionsJsonGetAssertion(optionsJsonString);
+ javascriptPublicKeyCredentialGet(options);
+ } catch (JSONException e) {
+ HwTimber.e(e);
+ }
+ }
+
+ private void javascriptPublicKeyCredentialGet(PublicKeyCredentialRequestOptions options) {
+ PublicKeyCredentialGet
+ credentialGetCommand = PublicKeyCredentialGet.create(currentOrigin, options);
+
+ OnGetAssertionCallback onGetCredentialCallback = new OnGetAssertionCallback() {
+ @Override
+ public void onGetAssertionResponse(@NonNull PublicKeyCredential publicKeyCredential) {
+ callJavascriptResolve(publicKeyCredential);
+ HwTimber.d("response: %s", publicKeyCredential);
+ }
+
+ @Override
+ public void onGetAssertionCancel() {
+ HwTimber.d("operation cancelled.");
+ callJavascriptCancel();
+ }
+
+ @Override
+ public void onGetAssertionTimeout() {
+ HwTimber.d("timeout: %s", options);
+ callJavascriptTimeout();
+ }
+ };
+
+ optionsBuilder.setTimeoutMs(options.timeout());
+ optionsBuilder.setTitle(context.getString(R.string.hwsecurity_fido_title_default_authenticate_app_id, getDisplayOrigin(currentOrigin)));
+
+ WebauthnDialogFragment webauthnDialogFragment = WebauthnDialogFragment.newInstance(
+ credentialGetCommand, optionsBuilder.build());
+ webauthnDialogFragment.setOnGetAssertionCallback(onGetCredentialCallback);
+ webauthnDialogFragment.show(fragmentManager);
+ }
+
+ private void javascriptPublicKeyCredentialStore(String optionsJsonString) {
+ HwTimber.e("store: Not implemented");
+ }
+
+ private void javascriptPublicKeyCredentialCreate(String optionsJsonString) {
+ HwTimber.d("javascriptPublicKeyCredentialCreate: %s", optionsJsonString);
+ try {
+ PublicKeyCredentialCreationOptions options = jsonWebauthnOptionsParser.fromOptionsJsonMakeCredential(optionsJsonString);
+ javascriptPublicKeyCredentialCreate(options);
+ } catch (JSONException e) {
+ HwTimber.e(e);
+ }
+ }
+
+ private void javascriptPublicKeyCredentialCreate(PublicKeyCredentialCreationOptions options) {
+ PublicKeyCredentialCreate
+ credentialCreateCommand = PublicKeyCredentialCreate.create(currentOrigin, options);
+
+ OnMakeCredentialCallback onMakeCredentialCallback = new OnMakeCredentialCallback() {
+ @Override
+ public void onMakeCredentialResponse(@NonNull PublicKeyCredential publicKeyCredential) {
+ callJavascriptResolve(publicKeyCredential);
+ HwTimber.d("response: %s", publicKeyCredential);
+ }
+
+ @Override
+ public void onMakeCredentialCancel() {
+ HwTimber.d("operation cancelled.");
+ callJavascriptCancel();
+ }
+
+ @Override
+ public void onMakeCredentialTimeout() {
+ HwTimber.d("timeout: %s", options);
+ callJavascriptTimeout();
+ }
+ };
+
+ optionsBuilder.setTimeoutMs(options.timeout());
+ optionsBuilder.setTitle(context.getString(R.string.hwsecurity_fido_title_default_register_app_id, getDisplayOrigin(currentOrigin)));
+
+ WebauthnDialogFragment webauthnDialogFragment = WebauthnDialogFragment.newInstance(
+ credentialCreateCommand, optionsBuilder.build());
+ webauthnDialogFragment.setOnMakeCredentialCallback(onMakeCredentialCallback);
+ webauthnDialogFragment.show(fragmentManager);
+ }
+
+ private void javascriptPublicKeyCredentialPreventSilentAccess() {
+ HwTimber.e("preventSilentAccess: Not implemented");
+ }
+
+ private void callJavascriptCancel() {
+ String javascript = "javascript:webauthnbridge.handleReject(new Error('User cancelled operation.'))";
+ webView.evaluateJavascript(javascript, null);
+ }
+
+ private void callJavascriptTimeout() {
+ String javascript = "javascript:webauthnbridge.handleReject(new Error('Operation timed out.'))";
+ webView.evaluateJavascript(javascript, null);
+ }
+
+ private void callJavascriptResolve(PublicKeyCredential publicKeyCredential) {
+ String publicKeyCredentialJson =
+ jsonPublicKeyCredentialSerializer.publicKeyCredentialToJsonString(publicKeyCredential);
+ String javascript = "javascript:webauthnbridge.handleResolve(" + publicKeyCredentialJson + ")";
+ webView.evaluateJavascript(javascript, null);
+ }
+
+ public void setForceU2f(boolean forceU2f) {
+ optionsBuilder.setForceU2f(forceU2f);
+ }
+
+ private String getDisplayOrigin(String origin) {
+ try {
+ URI appIdUri = new URI(origin);
+ return appIdUri.getHost();
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException("Invalid URI used for origin");
+ }
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/WebauthnCallback.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/WebauthnCallback.java
new file mode 100644
index 0000000..5424bbc
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/WebauthnCallback.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2;
+
+
+import java.io.IOException;
+
+import de.cotech.hw.fido2.internal.ctap2.Ctap2Response;
+import de.cotech.hw.fido2.internal.webauthn.WebauthnResponse;
+
+
+public interface WebauthnCallback {
+ void onResponse(R response);
+ void onIoException(IOException e);
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/WebauthnJsonCallback.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/WebauthnJsonCallback.java
new file mode 100644
index 0000000..a4b39e9
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/WebauthnJsonCallback.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2;
+
+
+import java.io.IOException;
+
+
+public interface WebauthnJsonCallback {
+ void onResponse(String jsonResponse);
+ void onIoException(IOException e);
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/AuthenticatorResponse.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/AuthenticatorResponse.java
new file mode 100644
index 0000000..9e22123
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/AuthenticatorResponse.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain;
+
+
+public abstract class AuthenticatorResponse {
+ public abstract byte[] clientDataJson();
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/AuthenticatorTransport.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/AuthenticatorTransport.java
new file mode 100644
index 0000000..f55839c
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/AuthenticatorTransport.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain;
+
+
+import androidx.annotation.CheckResult;
+import androidx.annotation.Nullable;
+
+
+// https://www.w3.org/TR/webauthn/#enumdef-authenticatortransport
+public enum AuthenticatorTransport {
+ USB ("usb"),
+ NFC ("nfc"),
+ BLE ("ble"),
+ INTERNAL ("internal");
+
+ public final String transport;
+
+ AuthenticatorTransport(String transport) {
+ this.transport = transport;
+ }
+
+ @Nullable
+ @CheckResult
+ public static AuthenticatorTransport fromString(String transport) {
+ switch (transport.toLowerCase()) {
+ case "usb": return USB;
+ case "nfc": return NFC;
+ case "ble": return BLE;
+ case "internal": return INTERNAL;
+ default: return null;
+ }
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/CollectedClientData.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/CollectedClientData.java
new file mode 100644
index 0000000..1cc4c9e
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/CollectedClientData.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain;
+
+
+import com.google.auto.value.AutoValue;
+
+
+@AutoValue
+public abstract class CollectedClientData {
+ public abstract String type();
+ public abstract byte[] challenge();
+ public abstract String origin();
+ public abstract String hashAlgorithm();
+
+ public static CollectedClientData create(String type, byte[] challenge, String origin, String hashAlgorithm) {
+ return new AutoValue_CollectedClientData(type, challenge, origin, hashAlgorithm);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialDescriptor.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialDescriptor.java
new file mode 100644
index 0000000..4547a2f
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialDescriptor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain;
+
+
+import android.os.Parcelable;
+
+import java.util.List;
+
+import androidx.annotation.Nullable;
+import com.google.auto.value.AutoValue;
+
+
+// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialdescriptor
+@AutoValue
+public abstract class PublicKeyCredentialDescriptor implements Parcelable {
+ public abstract PublicKeyCredentialType type();
+ public abstract byte[] id();
+ @Nullable
+ public abstract List transports();
+
+ public static PublicKeyCredentialDescriptor create(PublicKeyCredentialType type, byte[] id, List transports) {
+ return new AutoValue_PublicKeyCredentialDescriptor(type, id, transports);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialEntity.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialEntity.java
new file mode 100644
index 0000000..035574e
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialEntity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain;
+
+
+import androidx.annotation.Nullable;
+
+
+public abstract class PublicKeyCredentialEntity {
+ @Nullable
+ public abstract String name();
+
+ @Nullable
+ public abstract String icon();
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialParameters.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialParameters.java
new file mode 100644
index 0000000..2156af5
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialParameters.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain;
+
+
+import android.os.Parcelable;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.google.auto.value.AutoValue;
+import de.cotech.hw.fido2.internal.cose.CoseIdentifiers.CoseAlg;
+
+
+@AutoValue
+public abstract class PublicKeyCredentialParameters implements Parcelable {
+ abstract Map rawParameters();
+
+ // Pretty ugly, but we can only parcel String keys in a Map type.
+ public Map parameters() {
+ HashMap result = new HashMap<>();
+ for (Map.Entry entry : rawParameters().entrySet()) {
+ result.put(PublicKeyCredentialType.fromString(entry.getKey()), entry.getValue());
+ }
+ return Collections.unmodifiableMap(result);
+ }
+
+ public static PublicKeyCredentialParameters createSingle(PublicKeyCredentialType type, CoseAlg algorithm) {
+ Map parameters = Collections.singletonMap(type.type, algorithm);
+ return new AutoValue_PublicKeyCredentialParameters(parameters);
+ }
+
+ public static PublicKeyCredentialParameters createDefaultEs256() {
+ return createSingle(PublicKeyCredentialType.PUBLIC_KEY, CoseAlg.ES256);
+ }
+
+ public static List createDefaultEs256List() {
+ return Collections.singletonList(createDefaultEs256());
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialRpEntity.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialRpEntity.java
new file mode 100644
index 0000000..accd3fe
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialRpEntity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain;
+
+
+import android.os.Parcelable;
+
+import androidx.annotation.Nullable;
+import com.google.auto.value.AutoValue;
+
+
+@AutoValue
+public abstract class PublicKeyCredentialRpEntity extends PublicKeyCredentialEntity implements Parcelable {
+ @Nullable
+ public abstract String id();
+
+ public static PublicKeyCredentialRpEntity create(String id, String name, @Nullable String icon) {
+ return new AutoValue_PublicKeyCredentialRpEntity(name, icon, id);
+ }
+
+ public PublicKeyCredentialRpEntity withId(String id) {
+ return create(id, name(), icon());
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialType.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialType.java
new file mode 100644
index 0000000..68f6602
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialType.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain;
+
+
+// https://www.w3.org/TR/webauthn/#enumdef-publickeycredentialtype
+public enum PublicKeyCredentialType {
+ PUBLIC_KEY("public-key");
+
+ public final String type;
+
+ PublicKeyCredentialType(String type) {
+ this.type = type;
+ }
+
+ public static PublicKeyCredentialType fromString(String type) {
+ if ("public-key".equals(type)) {
+ return PUBLIC_KEY;
+ }
+ return null;
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialUserEntity.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialUserEntity.java
new file mode 100644
index 0000000..c30d54b
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/PublicKeyCredentialUserEntity.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain;
+
+
+import android.os.Parcelable;
+
+import androidx.annotation.Nullable;
+import com.google.auto.value.AutoValue;
+
+
+@AutoValue
+public abstract class PublicKeyCredentialUserEntity extends PublicKeyCredentialEntity implements
+ Parcelable {
+ public abstract byte[] id();
+ @Nullable
+ public abstract String displayName();
+
+ public static PublicKeyCredentialUserEntity create(byte[] id, @Nullable String name,
+ @Nullable String displayName, @Nullable String icon) {
+ return new AutoValue_PublicKeyCredentialUserEntity(name, icon, id, displayName);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/UserVerificationRequirement.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/UserVerificationRequirement.java
new file mode 100644
index 0000000..16976cd
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/UserVerificationRequirement.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain;
+
+
+import androidx.annotation.CheckResult;
+import androidx.annotation.NonNull;
+
+
+public enum UserVerificationRequirement {
+ REQUIRED, PREFERRED, DISCOURAGED;
+
+ @NonNull
+ @CheckResult
+ public static UserVerificationRequirement fromString(String userVerification) {
+ if (userVerification == null) {
+ return PREFERRED;
+ }
+ switch (userVerification.toLowerCase()) {
+ case "required": return REQUIRED;
+ case "discouraged": return DISCOURAGED;
+ default: return PREFERRED;
+ }
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AttestationConveyancePreference.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AttestationConveyancePreference.java
new file mode 100644
index 0000000..780b8f0
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AttestationConveyancePreference.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain.create;
+
+
+public enum AttestationConveyancePreference {
+ NONE, INDIRECT, DIRECT;
+
+ public static AttestationConveyancePreference fromString(String userVerification) {
+ if (userVerification == null) {
+ return NONE;
+ }
+ switch (userVerification.toLowerCase()) {
+ case "direct": return DIRECT;
+ case "indirect": return INDIRECT;
+ default: return NONE;
+ }
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AttestationObject.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AttestationObject.java
new file mode 100644
index 0000000..ad359e4
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AttestationObject.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain.create;
+
+
+import com.google.auto.value.AutoValue;
+import de.cotech.hw.fido2.internal.ctap2.Ctap2Response;
+
+
+@AutoValue
+public abstract class AttestationObject extends Ctap2Response {
+ // fmt String Required The attestation statement format identifier.
+ public abstract String fmt();
+ // authData Byte Array Required The authenticator data object.
+ public abstract byte[] authData();
+ // attStmt Byte Array, the structure of which depends on the attestation statement format identifier Required The attestation statement, whose format is identified by the "fmt" object member. The client treats it as an opaque object. abstract List versions();
+ public abstract byte[] attStmt();
+
+
+ public static AttestationObject create(String fmt, byte[] authData, byte[] attStmt) {
+ return new AutoValue_AttestationObject(fmt, authData, attStmt);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AttestedCredentialData.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AttestedCredentialData.java
new file mode 100644
index 0000000..9d496f4
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AttestedCredentialData.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain.create;
+
+
+import com.google.auto.value.AutoValue;
+
+
+@AutoValue
+public abstract class AttestedCredentialData {
+ private static final byte[] LENGTH_AAGUID = new byte[16];
+
+ public abstract byte[] aaguid();
+ public abstract byte[] credentialId();
+ public abstract byte[] credentialPublicKey();
+
+ public static AttestedCredentialData create(byte[] aaguid, byte[] credentialId, byte[] credentialPublicKey) {
+ return new AutoValue_AttestedCredentialData(aaguid, credentialId, credentialPublicKey);
+ }
+
+ AttestedCredentialData withEmptyAaguid() {
+ return create(LENGTH_AAGUID, credentialId(), credentialPublicKey());
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AuthenticatorAttachment.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AuthenticatorAttachment.java
new file mode 100644
index 0000000..5343765
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AuthenticatorAttachment.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain.create;
+
+
+public enum AuthenticatorAttachment {
+ PLATFORM, CROSS_PLATFORM;
+
+ public static AuthenticatorAttachment fromString(String authenticatorAttachment) {
+ if (authenticatorAttachment == null) {
+ return null;
+ }
+ switch (authenticatorAttachment.toLowerCase()) {
+ case "platform": return PLATFORM;
+ case "cross-platform": return CROSS_PLATFORM;
+ }
+ return null;
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AuthenticatorAttestationResponse.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AuthenticatorAttestationResponse.java
new file mode 100644
index 0000000..4c67faa
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AuthenticatorAttestationResponse.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain.create;
+
+
+import com.google.auto.value.AutoValue;
+import de.cotech.hw.fido2.domain.AuthenticatorResponse;
+
+
+@AutoValue
+public abstract class AuthenticatorAttestationResponse extends AuthenticatorResponse {
+ public abstract byte[] attestationObject();
+
+ public static AuthenticatorAttestationResponse create(
+ byte[] clientData, byte[] attestationObject) {
+ return new AutoValue_AuthenticatorAttestationResponse(clientData, attestationObject);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AuthenticatorData.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AuthenticatorData.java
new file mode 100644
index 0000000..736ba3c
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AuthenticatorData.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain.create;
+
+
+import androidx.annotation.Nullable;
+import com.google.auto.value.AutoValue;
+
+
+@AutoValue
+public abstract class AuthenticatorData {
+ // Bit 0: User Present (UP) result.
+ // 1 means the user is present.
+ // 0 means the user is not present.
+ public static final byte FLAG_USER_PRESENT = 1;
+ // Bit 1: Reserved for future use (RFU1).
+ private static final byte FLAG_RFU_1 = 1<<1;
+ // Bit 2: User Verified (UV) result.
+ // 1 means the user is verified.
+ // 0 means the user is not verified.
+ private static final byte FLAG_USER_VERIFIED = 1<<2;
+ // Bits 3-5: Reserved for future use (RFU2).
+ private static final byte FLAG_RFU_2 = 1<<3;
+ private static final byte FLAG_RFU_3 = 1<<4;
+ private static final byte FLAG_RFU_4 = 1<<5;
+ // Bit 6: Attested credential data included (AT).
+ // Indicates whether the authenticator added attested credential data.
+ public static final byte FLAG_ATTESTED_CREDENTIAL_DATA = 1<<6;
+ // Bit 7: Extension data included (ED).
+ // Indicates if the authenticator data has extensions.
+ public static final byte FLAG_EXTENSION_DATA = (byte) (1<<7);
+
+ // rpIdHash 32 SHA-256 hash of the RP ID the credential is scoped to.
+ public abstract byte[] rpIdHash();
+
+ // flags 1 Flags (bit 0 is the least significant bit):
+ public abstract byte flags();
+
+ // signCount 4 Signature counter, 32-bit unsigned big-endian integer.
+ public abstract int sigCounter();
+
+ // attestedCredentialData variable (if present) attested credential data (if present). See §6.4.1 Attested Credential Data for details. Its length depends on the length of the credential ID and credential public key being attested.
+ @Nullable
+ public abstract AttestedCredentialData attestedCredentialData();
+
+ // extensions variable (if present) Extension-defined authenticator data. This is a CBOR [RFC7049] map with extension identifiers as keys, and authenticator extension outputs as values. See §9 WebAuthn Extensions for details.
+ @Nullable
+ public abstract byte[] extensions();
+
+ public boolean hasAttestedCredentialData() {
+ return (flags() & FLAG_ATTESTED_CREDENTIAL_DATA) != 0;
+ }
+
+ public boolean hasExtensionData() {
+ return (flags() & FLAG_EXTENSION_DATA) != 0;
+ }
+
+ public static AuthenticatorData create(byte[] rpIdHash, byte flags, int sigCounter,
+ AttestedCredentialData credentialData, byte[] extensions) {
+ return new AutoValue_AuthenticatorData(rpIdHash, flags, sigCounter, credentialData, extensions);
+ }
+
+ public AuthenticatorData withEmptyAaguid() {
+ AttestedCredentialData attestedCredentialData = attestedCredentialData();
+ if (attestedCredentialData != null) {
+ attestedCredentialData = attestedCredentialData.withEmptyAaguid();
+ }
+ return new AutoValue_AuthenticatorData(rpIdHash(), flags(), sigCounter(), attestedCredentialData, extensions());
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AuthenticatorSelectionCriteria.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AuthenticatorSelectionCriteria.java
new file mode 100644
index 0000000..f518c11
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/AuthenticatorSelectionCriteria.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain.create;
+
+
+import android.os.Parcelable;
+
+import androidx.annotation.Nullable;
+import com.google.auto.value.AutoValue;
+import de.cotech.hw.fido2.domain.UserVerificationRequirement;
+
+
+@AutoValue
+public abstract class AuthenticatorSelectionCriteria implements Parcelable {
+ @Nullable
+ public abstract AuthenticatorAttachment authenticatorAttachment();
+ public abstract boolean requireResidentKey();
+ public abstract UserVerificationRequirement userVerification();
+
+ public static AuthenticatorSelectionCriteria create(
+ @Nullable AuthenticatorAttachment authenticatorAttachment, boolean requireResidentKey,
+ UserVerificationRequirement userVerification) {
+ return new AutoValue_AuthenticatorSelectionCriteria(authenticatorAttachment,
+ requireResidentKey, userVerification);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/CredentialCreationData.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/CredentialCreationData.java
new file mode 100644
index 0000000..298f352
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/CredentialCreationData.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain.create;
+
+
+import com.google.auto.value.AutoValue;
+
+
+@AutoValue
+public abstract class CredentialCreationData {
+ public abstract AttestationObject attestationObjectResult();
+ public abstract byte[] clientDataJSONResult();
+ public abstract AttestationConveyancePreference attestationConveyancePreferenceOption();
+
+ public static CredentialCreationData create(
+ AttestationObject attestationObjectResult,
+ byte[] clientDataJSONResult,
+ AttestationConveyancePreference attestationConveyancePreferenceOption) {
+ return new AutoValue_CredentialCreationData(
+ attestationObjectResult, clientDataJSONResult, attestationConveyancePreferenceOption);
+ }
+
+ // Missing:
+ // public abstract AuthenticationExtensionsClientOutputs clientExtensionResults();
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/PublicKeyCredentialCreationOptions.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/PublicKeyCredentialCreationOptions.java
new file mode 100644
index 0000000..539c08a
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/create/PublicKeyCredentialCreationOptions.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain.create;
+
+
+import java.util.List;
+
+import android.os.Parcelable;
+
+import androidx.annotation.Nullable;
+import com.google.auto.value.AutoValue;
+import de.cotech.hw.fido2.domain.PublicKeyCredentialDescriptor;
+import de.cotech.hw.fido2.domain.PublicKeyCredentialParameters;
+import de.cotech.hw.fido2.domain.PublicKeyCredentialRpEntity;
+import de.cotech.hw.fido2.domain.PublicKeyCredentialUserEntity;
+
+
+@AutoValue
+public abstract class PublicKeyCredentialCreationOptions implements Parcelable {
+ public abstract PublicKeyCredentialRpEntity rp();
+ public abstract PublicKeyCredentialUserEntity user();
+ public abstract byte[] challenge();
+ public abstract List pubKeyCredParams();
+ @Nullable
+ public abstract Long timeout();
+ public abstract AuthenticatorSelectionCriteria authenticatorSelection();
+ @Nullable
+ public abstract List excludeCredentials();
+ public abstract AttestationConveyancePreference attestation();
+
+ public static PublicKeyCredentialCreationOptions create(
+ PublicKeyCredentialRpEntity rp,
+ PublicKeyCredentialUserEntity user,
+ byte[] challenge,
+ List pubKeyCredParams,
+ @Nullable Long timeout,
+ AuthenticatorSelectionCriteria authenticatorSelection,
+ @Nullable List excludeCredentials,
+ AttestationConveyancePreference attestation
+ ) {
+ return new AutoValue_PublicKeyCredentialCreationOptions(
+ rp, user, challenge, pubKeyCredParams, timeout,
+ authenticatorSelection, excludeCredentials, attestation);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/get/AssertionCreationData.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/get/AssertionCreationData.java
new file mode 100644
index 0000000..df7ae2f
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/get/AssertionCreationData.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain.get;
+
+
+import androidx.annotation.Nullable;
+import com.google.auto.value.AutoValue;
+
+
+@AutoValue
+public abstract class AssertionCreationData {
+ public abstract byte[] credentialIdResult();
+ public abstract byte[] clientDataJSONResult();
+ public abstract byte[] authenticatorDataResult();
+ public abstract byte[] signatureResult();
+ @Nullable
+ public abstract byte[] userHandleResult();
+
+ public static AssertionCreationData create(byte[] credentialId, byte[] clientDataJSON, byte[] authenticatorData, byte[] signature, @Nullable byte[] userHandle) {
+ return new AutoValue_AssertionCreationData(credentialId, clientDataJSON, authenticatorData, signature, userHandle);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/get/AuthenticatorAssertionResponse.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/get/AuthenticatorAssertionResponse.java
new file mode 100644
index 0000000..1a12217
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/get/AuthenticatorAssertionResponse.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain.get;
+
+
+import androidx.annotation.Nullable;
+import com.google.auto.value.AutoValue;
+import de.cotech.hw.fido2.domain.AuthenticatorResponse;
+
+
+@AutoValue
+public abstract class AuthenticatorAssertionResponse extends AuthenticatorResponse {
+ public abstract byte[] authenticatorData();
+ public abstract byte[] signature();
+ @Nullable
+ public abstract byte[] userHandle();
+
+ public static AuthenticatorAssertionResponse create(byte[] clientDataJson, byte[] authenticatorData, byte[] signature, @Nullable byte[] userHandle) {
+ return new AutoValue_AuthenticatorAssertionResponse(clientDataJson, authenticatorData, signature, userHandle);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/get/PublicKeyCredentialRequestOptions.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/get/PublicKeyCredentialRequestOptions.java
new file mode 100644
index 0000000..1dd9baa
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/domain/get/PublicKeyCredentialRequestOptions.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.domain.get;
+
+
+import java.util.List;
+
+import android.os.Parcelable;
+
+import androidx.annotation.Nullable;
+import com.google.auto.value.AutoValue;
+import de.cotech.hw.fido2.domain.PublicKeyCredentialDescriptor;
+import de.cotech.hw.fido2.domain.UserVerificationRequirement;
+
+
+@AutoValue
+public abstract class PublicKeyCredentialRequestOptions implements Parcelable {
+ public abstract byte[] challenge();
+ @Nullable
+ public abstract Long timeout();
+ @Nullable
+ public abstract String rpId();
+ @Nullable
+ public abstract List allowCredentials();
+ @Nullable
+ public abstract UserVerificationRequirement userVerification();
+
+ public static PublicKeyCredentialRequestOptions create(
+ byte[] challenge,
+ Long timeout,
+ String rpId,
+ List allowCredentials,
+ UserVerificationRequirement userVerification
+ ) {
+ return new AutoValue_PublicKeyCredentialRequestOptions(
+ challenge, timeout, rpId, allowCredentials, userVerification);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinBlockedException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinBlockedException.java
new file mode 100644
index 0000000..0058c27
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinBlockedException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.exceptions;
+
+
+import java.io.IOException;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+
+public class FidoClientPinBlockedException extends IOException {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public FidoClientPinBlockedException() {
+ super("This authenticator is blocked, it must be reset before use.");
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinInvalidException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinInvalidException.java
new file mode 100644
index 0000000..13bddd8
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinInvalidException.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.exceptions;
+
+
+import java.io.IOException;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+
+public class FidoClientPinInvalidException extends IOException {
+ public final int retriesLeft;
+
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public FidoClientPinInvalidException(int retriesLeft) {
+ super("Invalid PIN. " + getRetriesText(retriesLeft));
+ this.retriesLeft = retriesLeft;
+ }
+
+ public int getRetriesLeft() {
+ return retriesLeft;
+ }
+
+ private static String getRetriesText(int retriesLeft) {
+ if (retriesLeft == 0) {
+ return "Too many attempts, authenticator is now blocked.";
+ }
+ if (retriesLeft == 1) {
+ return "One attempt left.";
+ }
+ return retriesLeft + " attempts left.";
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinLastAttemptException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinLastAttemptException.java
new file mode 100644
index 0000000..3978ffd
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinLastAttemptException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.exceptions;
+
+
+import java.io.IOException;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+
+public class FidoClientPinLastAttemptException extends IOException {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public FidoClientPinLastAttemptException() {
+ super("Only one client PIN attempt left, need explicit confirmation.");
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinNotSetException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinNotSetException.java
new file mode 100644
index 0000000..532b624
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinNotSetException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.exceptions;
+
+
+import java.io.IOException;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+
+public class FidoClientPinNotSetException extends IOException {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public FidoClientPinNotSetException() {
+ super("User verification is required, but not set up on authenticator!");
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinNotSupportedException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinNotSupportedException.java
new file mode 100644
index 0000000..36bac61
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinNotSupportedException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.exceptions;
+
+
+import java.io.IOException;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+
+public class FidoClientPinNotSupportedException extends IOException {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public FidoClientPinNotSupportedException() {
+ super("User verification is required, but not supported by authenticator!");
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinRequiredException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinRequiredException.java
new file mode 100644
index 0000000..2cccfd7
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinRequiredException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.exceptions;
+
+
+import java.io.IOException;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+
+public class FidoClientPinRequiredException extends IOException {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public FidoClientPinRequiredException() {
+ super("User verification is required, but no PIN provided!");
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinTooShortException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinTooShortException.java
new file mode 100644
index 0000000..31f7923
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoClientPinTooShortException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.exceptions;
+
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+import java.io.IOException;
+
+
+public class FidoClientPinTooShortException extends IOException {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public FidoClientPinTooShortException() {
+ super("PIN must be 4 or more characters");
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoInvalidCredentialException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoInvalidCredentialException.java
new file mode 100644
index 0000000..cb3e0d0
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoInvalidCredentialException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.exceptions;
+
+
+import java.io.IOException;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+
+public class FidoInvalidCredentialException extends IOException {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public FidoInvalidCredentialException() {
+ super("No valid credentials.");
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoPresenceRequiredException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoPresenceRequiredException.java
new file mode 100644
index 0000000..c5d993a
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoPresenceRequiredException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.exceptions;
+
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import de.cotech.hw.exceptions.ConditionsNotSatisfiedException;
+
+
+public class FidoPresenceRequiredException extends ConditionsNotSatisfiedException {
+ public static final int SW_TEST_OF_USER_PRESENCE_REQUIRED = SW_CONDITIONS_NOT_SATISFIED;
+
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public FidoPresenceRequiredException() {
+ super("Security Key returned error, user presence is required for this operation");
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoResidentKeyNoCredentialException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoResidentKeyNoCredentialException.java
new file mode 100644
index 0000000..303c43b
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoResidentKeyNoCredentialException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.exceptions;
+
+
+import java.io.IOException;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+
+public class FidoResidentKeyNoCredentialException extends IOException {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public FidoResidentKeyNoCredentialException() {
+ super("No key available on authenticator for this relying party.");
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoResidentKeyNotSupportedException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoResidentKeyNotSupportedException.java
new file mode 100644
index 0000000..761e964
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoResidentKeyNotSupportedException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.exceptions;
+
+
+import java.io.IOException;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+
+public class FidoResidentKeyNotSupportedException extends IOException {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public FidoResidentKeyNotSupportedException() {
+ super("Requested resident key, but not supported by this authenticator.");
+ }
+}
diff --git a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SecurityKeyLostException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoSecurityError.java
similarity index 75%
rename from hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SecurityKeyLostException.java
rename to hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoSecurityError.java
index 5434d47..f90f6f2 100644
--- a/hwsecurity/core/src/main/java/de/cotech/hw/exceptions/SecurityKeyLostException.java
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoSecurityError.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2019 Confidential Technologies GmbH
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
*
* You can purchase a commercial license at https://hwsecurity.dev.
* Buying such a license is mandatory as soon as you develop commercial
@@ -22,18 +22,14 @@
* along with this program. If not, see .
*/
-package de.cotech.hw.exceptions;
+package de.cotech.hw.fido2.exceptions;
import java.io.IOException;
-public class SecurityKeyLostException extends IOException {
- public SecurityKeyLostException(Throwable cause) {
- super("Security Key is no longer connected.", cause);
- }
-
- public SecurityKeyLostException() {
- this(null);
+public class FidoSecurityError extends IOException {
+ public FidoSecurityError(String message) {
+ super(message);
}
}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoWrongKeyHandleException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoWrongKeyHandleException.java
new file mode 100644
index 0000000..787afac
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/exceptions/FidoWrongKeyHandleException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.exceptions;
+
+
+import de.cotech.hw.exceptions.WrongDataException;
+
+
+public class FidoWrongKeyHandleException extends WrongDataException {
+ public static final int SW_WRONG_KEY_HANDLE = WrongDataException.SW_WRONG_DATA;
+
+ public FidoWrongKeyHandleException() {
+ super("Security Key returned error WRONG_KEY_HANDLE (0x6A80)");
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/AuthenticationPin.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/AuthenticationPin.java
new file mode 100644
index 0000000..fb10e5a
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/AuthenticationPin.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal;
+
+
+import com.google.auto.value.AutoValue;
+import de.cotech.hw.fido2.internal.webauthn.WebauthnResponse;
+
+
+@AutoValue
+public abstract class AuthenticationPin implements WebauthnResponse {
+ public abstract String pin();
+
+ public static AuthenticationPin create(String pin) {
+ return new AutoValue_AuthenticationPin(pin);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/Fido2AppletConnection.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/Fido2AppletConnection.java
new file mode 100644
index 0000000..646948f
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/Fido2AppletConnection.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+import de.cotech.hw.SecurityKeyException;
+import de.cotech.hw.exceptions.AppletFileNotFoundException;
+import de.cotech.hw.exceptions.ClaNotSupportedException;
+import de.cotech.hw.exceptions.InsNotSupportedException;
+import de.cotech.hw.exceptions.SelectAppletException;
+import de.cotech.hw.exceptions.WrongRequestLengthException;
+import de.cotech.hw.fido2.exceptions.FidoPresenceRequiredException;
+import de.cotech.hw.fido2.exceptions.FidoWrongKeyHandleException;
+import de.cotech.hw.fido2.internal.ctap2.Ctap2Command;
+import de.cotech.hw.fido2.internal.ctap2.Ctap2CommandApduTransformer;
+import de.cotech.hw.fido2.internal.ctap2.Ctap2Exception;
+import de.cotech.hw.fido2.internal.ctap2.Ctap2Response;
+import de.cotech.hw.fido2.internal.ctap2.CtapErrorResponse;
+import de.cotech.hw.fido2.internal.ctap2.commands.getInfo.AuthenticatorGetInfo;
+import de.cotech.hw.fido2.internal.ctap2.commands.getInfo.AuthenticatorGetInfoResponse;
+import de.cotech.hw.fido2.internal.pinauth.PinToken;
+import de.cotech.hw.internal.iso7816.CommandApdu;
+import de.cotech.hw.internal.iso7816.ResponseApdu;
+import de.cotech.hw.internal.transport.SecurityKeyInfo.TransportType;
+import de.cotech.hw.internal.transport.Transport;
+import de.cotech.hw.util.Hex;
+import de.cotech.hw.util.HwTimber;
+
+
+@RestrictTo(Scope.LIBRARY_GROUP)
+public class Fido2AppletConnection {
+ private static final int APDU_SW1_RESPONSE_AVAILABLE = 0x61;
+ private static final int RESPONSE_SW1_INCORRECT_LENGTH = 0x6C;
+
+ private static final List FIDO_AID_PREFIXES = Arrays.asList(
+ // see to "FIDO U2F NFC protocol", Section 5. Applet selection
+ // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
+ Hex.decodeHexOrFail("A0000006472F0001"),
+ // Workaround for Solokey for firmware < 2.4.0: https://github.com/solokeys/solo/issues/213
+ Hex.decodeHexOrFail("A0000006472F000100"),
+ // old Yubico demo applet AID
+ Hex.decodeHexOrFail("A0000005271002")
+ );
+
+ @NonNull
+ private final Transport transport;
+ @NonNull
+ private final Fido2CommandApduFactory commandFactory;
+ @NonNull
+ private final Ctap2CommandApduTransformer ctap2CommandApduTransformer;
+
+ private boolean isFidoAppletConnected;
+ private AuthenticatorGetInfoResponse ctap2Info;
+ private boolean isForceCtap1;
+
+ private PinToken cachedPinToken;
+
+ public static Fido2AppletConnection getInstanceForTransport(@NonNull Transport transport) {
+ return new Fido2AppletConnection(transport, new Fido2CommandApduFactory(), new Ctap2CommandApduTransformer());
+ }
+
+ private Fido2AppletConnection(@NonNull Transport transport, @NonNull Fido2CommandApduFactory commandFactory,
+ @NonNull Ctap2CommandApduTransformer ctap2CommandApduTransformer) {
+ this.transport = transport;
+ this.commandFactory = commandFactory;
+ this.ctap2CommandApduTransformer = ctap2CommandApduTransformer;
+ }
+
+ // region connection management
+
+ public void connectIfNecessary() throws IOException {
+ if (isFidoAppletConnected) {
+ return;
+ }
+
+ connectToDevice();
+ }
+
+ private void connectToDevice() throws IOException {
+ try {
+ if (transport.getTransportType() == TransportType.USB_CTAPHID) {
+ HwTimber.d("Using USB U2F HID as a transport. No need to select AID.");
+ byte[] versionBytes = readVersion();
+ checkVersionOrThrow(versionBytes);
+ } else {
+ byte[] selectedAid = selectFilesFromPrefixOrFail();
+ HwTimber.d("Connected to AID %s", Hex.encodeHexString(selectedAid));
+ }
+
+ try {
+ ctap2Info = ctap2AuthenticatorGetInfo();
+ HwTimber.d("Call to AuthenticatorGetInfo returns valid response - using CTAP2");
+ HwTimber.d(ctap2Info.toString());
+ } catch (IOException e) {
+ HwTimber.d("Call to AuthenticatorGetInfo returned no valid response - using CTAP1");
+ }
+
+ isFidoAppletConnected = true;
+ } catch (IOException e) {
+ transport.release();
+ throw e;
+ }
+ }
+
+ private AuthenticatorGetInfoResponse ctap2AuthenticatorGetInfo() throws IOException {
+ return ctap2CommunicateOrThrow(AuthenticatorGetInfo.create());
+ }
+
+ public T ctap2CommunicateOrThrow(Ctap2Command ctap2Command)
+ throws IOException {
+ if (!isCtap2Capable()) {
+ HwTimber.w("Attempting to send CTAP2 command, but CTAP2 is not supported. " +
+ "This will probably cause an error.");
+ }
+ CommandApdu commandApdu = ctap2CommandApduTransformer.toCommandApdu(ctap2Command);
+ ResponseApdu responseApdu = communicateOrThrow(commandApdu);
+ Ctap2Response ctap2Response = ctap2ResponseFromResponseApdu(ctap2Command, responseApdu);
+ // noinspection unchecked, this is ensured in Ctap2ResponseTransformer
+ return (T) ctap2Response;
+ }
+
+ private Ctap2Response ctap2ResponseFromResponseApdu(Ctap2Command command, ResponseApdu responseApdu)
+ throws IOException {
+ byte[] data = responseApdu.getData();
+ if (data[0] != CtapErrorResponse.CTAP2_OK) {
+ throw new Ctap2Exception(CtapErrorResponse.create(data[0]));
+ }
+
+ byte[] responseData = de.cotech.hw.util.Arrays.copyOfRange(data, 1, data.length);
+ return command.getResponseFactory().createResponse(responseData);
+ }
+
+ private byte[] selectFilesFromPrefixOrFail() throws IOException {
+ for (byte[] fileAid : FIDO_AID_PREFIXES) {
+ byte[] initializedAid = selectFileOrFail(fileAid);
+ if (initializedAid != null) {
+ return initializedAid;
+ }
+ }
+ throw new SelectAppletException(FIDO_AID_PREFIXES, "FIDO U2F or CTAP2");
+ }
+
+ private void checkVersionOrThrow(byte[] versionBytes) throws IOException {
+ String version = new String(versionBytes, Charset.forName("ASCII"));
+
+ if ("U2F_V2".equals(version) || "FIDO_2_0".equals(version)) {
+ HwTimber.d("U2F applet answered correctly with version U2F_V2 or FIDO_2_0");
+ } else {
+ HwTimber.e("Applet did NOT answer with a correct version string!");
+ throw new IOException("Applet replied with incorrect version string!");
+ }
+ }
+
+ private byte[] selectFileOrFail(byte[] fileAid) throws IOException {
+ CommandApdu select = commandFactory.createSelectFileCommand(fileAid);
+
+ try {
+ ResponseApdu response = communicateOrThrow(select);
+
+ // "FIDO authenticator SHALL reply with its version string in the successful response"
+ // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
+ checkVersionOrThrow(response.getData());
+ return fileAid;
+ } catch (AppletFileNotFoundException e) {
+ return null;
+ }
+ }
+
+ // endregion
+
+ // region communication
+
+ // see "FIDO U2F Raw Message Formats", Section 3.3 Status Codes
+ // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
+ public ResponseApdu communicateOrThrow(CommandApdu commandApdu) throws IOException {
+ ResponseApdu response = communicate(commandApdu);
+
+ if (response.isSuccess()) {
+ return response;
+ }
+
+ switch (response.getSw()) {
+ case FidoPresenceRequiredException.SW_TEST_OF_USER_PRESENCE_REQUIRED:
+ throw new FidoPresenceRequiredException();
+ case FidoWrongKeyHandleException.SW_WRONG_KEY_HANDLE:
+ throw new FidoWrongKeyHandleException();
+ case AppletFileNotFoundException.SW_FILE_NOT_FOUND:
+ throw new AppletFileNotFoundException();
+ case ClaNotSupportedException.SW_CLA_NOT_SUPPORTED:
+ throw new ClaNotSupportedException();
+ case InsNotSupportedException.SW_INS_NOT_SUPPORTED:
+ throw new InsNotSupportedException();
+ case WrongRequestLengthException.SW_WRONG_REQUEST_LENGTH:
+ throw new WrongRequestLengthException();
+ default:
+ throw new SecurityKeyException("UNKNOWN", response.getSw());
+ }
+ }
+
+ // ISO/IEC 7816-4
+ private ResponseApdu communicate(CommandApdu commandApdu) throws IOException {
+ ResponseApdu lastResponse;
+
+ lastResponse = sendWithChaining(commandApdu);
+ if (lastResponse.getSw1() == RESPONSE_SW1_INCORRECT_LENGTH && lastResponse.getSw2() != 0) {
+ commandApdu = commandApdu.withNe(lastResponse.getSw2());
+ lastResponse = sendWithChaining(commandApdu);
+ }
+ lastResponse = readChainedResponseIfAvailable(lastResponse);
+
+ return lastResponse;
+ }
+
+ // ISO/IEC 7816-4
+ @NonNull
+ private ResponseApdu sendWithChaining(CommandApdu commandApdu) throws IOException {
+ /* CTAP2 Spec:
+ * "If the request was encoded using extended length APDU encoding,
+ * the authenticator MUST respond using the extended length APDU response format."
+ *
+ * "If the request was encoded using short APDU encoding,
+ * the authenticator MUST respond using ISO 7816-4 APDU chaining."
+ *
+ * https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#nfc-fragmentation
+ *
+ * In the best case, extended length is supported by device and authenticator, so we don't need to
+ * parse chained APDUs coming from the authenticator.
+ *
+ * We do *not* check for `transport.isExtendedLengthSupported()` here! There are phones (including
+ * the Nexus 5X, Nexus 6P) that return "false" to this, but some Security Keys (like Yubikey Neo) still
+ * require us to send extended APDUs. So what we do is, send an extended APDU, and if that doesn't
+ * work, fall back to a short one.
+ */
+ if (!transport.isExtendedLengthSupported()) {
+ HwTimber.w("Transport protocol does not support extended length. Probably an old device with NFC, such as Nexus 5X, Nexus 6P. We still try sending extended length!");
+ }
+
+ ResponseApdu response = transport.transceive(commandApdu.withExtendedApduNe());
+ if (response.getSw() != WrongRequestLengthException.SW_WRONG_REQUEST_LENGTH) {
+ return response;
+ } else {
+ HwTimber.d("Received WRONG_REQUEST_LENGTH error. Retrying with short APDU Ne.");
+ }
+
+ if (commandFactory.isSuitableForSingleShortApdu(commandApdu)) {
+ return transport.transceive(commandApdu.withShortApduNe());
+ }
+
+ ResponseApdu lastResponse = null;
+ List chainedApdus = commandFactory.createChainedApdus(commandApdu);
+ for (int i = 0, totalCommands = chainedApdus.size(); i < totalCommands; i++) {
+ CommandApdu chainedApdu = chainedApdus.get(i);
+ lastResponse = transport.transceive(chainedApdu);
+
+ boolean isLastCommand = (i == totalCommands - 1);
+ if (!isLastCommand && !lastResponse.isSuccess()) {
+ throw new IOException("Failed to chain apdu " +
+ "(" + i + "/" + (totalCommands - 1) + ", last SW: " + Integer.toHexString(lastResponse.getSw()) + ")");
+ }
+ }
+
+ if (lastResponse == null) {
+ throw new IllegalStateException();
+ }
+
+ return lastResponse;
+ }
+
+ // ISO/IEC 7816-4
+ @NonNull
+ private ResponseApdu readChainedResponseIfAvailable(ResponseApdu lastResponse) throws
+ IOException {
+ if (lastResponse.getSw1() != APDU_SW1_RESPONSE_AVAILABLE) {
+ return lastResponse;
+ }
+
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ result.write(lastResponse.getData());
+
+ do {
+ // GET RESPONSE ISO/IEC 7816-4 par.7.6.1
+ CommandApdu getResponse = commandFactory.createGetResponseCommand(lastResponse.getSw2());
+ lastResponse = transport.transceive(getResponse);
+ result.write(lastResponse.getData());
+ } while (lastResponse.getSw1() == APDU_SW1_RESPONSE_AVAILABLE);
+
+ result.write(lastResponse.getSw1());
+ result.write(lastResponse.getSw2());
+
+ return ResponseApdu.fromBytes(result.toByteArray());
+ }
+
+ // endregion
+
+ @NonNull
+ public Fido2CommandApduFactory getCommandFactory() {
+ return commandFactory;
+ }
+
+ /**
+ * GetVersion Request
+ * https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
+ *
+ * The FIDO Client can query the U2F token about the U2F protocol version that it implements.
+ *
+ * @return should return "U2F_V2"
+ */
+ private byte[] readVersion() throws IOException {
+ CommandApdu getDataUserIdCommand = commandFactory.createVersionCommand();
+ ResponseApdu responseApdu = communicateOrThrow(getDataUserIdCommand);
+ return responseApdu.getData();
+ }
+
+ public boolean isConnected() {
+ return transport.isConnected();
+ }
+
+ public boolean isSupportResidentKeys() {
+ return ctap2Info != null && ctap2Info.options().rk();
+ }
+
+ public boolean isSupportClientPin() {
+ return ctap2Info != null && ctap2Info.options().clientPin() != null;
+ }
+
+ public boolean isClientPinSet() {
+ if (ctap2Info == null) {
+ return false;
+ }
+ Boolean clientPin = ctap2Info.options().clientPin();
+ return clientPin != null && clientPin;
+ }
+
+ public boolean isSupportUserVerification() {
+ return ctap2Info != null && ctap2Info.options().uv() != null;
+ }
+
+ public boolean isSupportUserPresence() {
+ return ctap2Info != null && ctap2Info.options().up();
+ }
+
+ public boolean isCtap2Capable() {
+ return !isForceCtap1 && ctap2Info != null;
+ }
+
+ public void setForceCtap1(boolean isForceCtap1) {
+ this.isForceCtap1 = isForceCtap1;
+ }
+
+ @Nullable
+ public PinToken getCachedPinToken() {
+ return cachedPinToken;
+ }
+
+ public void setCachedPinToken(PinToken pinToken) {
+ this.cachedPinToken = pinToken;
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/Fido2CommandApduDescriber.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/Fido2CommandApduDescriber.java
new file mode 100644
index 0000000..0464472
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/Fido2CommandApduDescriber.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal;
+
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import de.cotech.hw.internal.iso7816.CommandApdu;
+import de.cotech.hw.internal.iso7816.CommandApduDescriber;
+import de.cotech.hw.util.Hex;
+
+
+@RestrictTo(Scope.LIBRARY_GROUP)
+public class Fido2CommandApduDescriber implements CommandApduDescriber {
+ @Override
+ public String describe(CommandApdu commandApdu) {
+ return Hex.encodeHexString(commandApdu.toBytes());
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/Fido2CommandApduFactory.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/Fido2CommandApduFactory.java
new file mode 100644
index 0000000..cedd366
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/Fido2CommandApduFactory.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import de.cotech.hw.internal.iso7816.CommandApdu;
+
+
+/**
+ * Follows "FIDO U2F Raw Message Formats" v1.2
+ * https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+public class Fido2CommandApduFactory {
+ private static final Fido2CommandApduDescriber DESCRIBER = new Fido2CommandApduDescriber();
+
+ private static final int MASK_CLA_CHAINING = 1 << 4;
+
+ private static final int CLA = 0x00;
+ private static final int INS_SELECT_FILE = 0xA4;
+ private static final int P1_SELECT_FILE = 0x04;
+ private static final int INS_GET_RESPONSE = 0xC0;
+
+ private static final int P1_EMPTY = 0x00;
+ private static final int P2_EMPTY = 0x00;
+
+ // "FIDO U2F Raw Message Formats", Section 3.1.1 Command and parameter values
+ // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
+ private static final int U2F_REGISTER = 0x01;
+ private static final int U2F_AUTHENTICATE = 0x02;
+ private static final int U2F_VERSION = 0x03;
+
+ private static final int U2F_AUTHENTICATE_P1_ENFORCE_USER_PRESENCE_AND_SIGN = 0x03;
+ private static final int U2F_AUTHENTICATE_P1_CHECK_ONLY = 0x07;
+ private static final int U2F_AUTHENTICATE_P1_DONT_ENFORCE_USER_PRESENCE_AND_SIGN = 0x08;
+
+
+ @NonNull
+ public CommandApdu createRegistrationCommand(byte[] data) {
+ return CommandApdu.create(CLA, U2F_REGISTER, U2F_AUTHENTICATE_P1_ENFORCE_USER_PRESENCE_AND_SIGN, P2_EMPTY, data).withDescriber(DESCRIBER);
+ }
+
+ @NonNull
+ public CommandApdu createAuthenticationCommand(byte[] data) {
+ return CommandApdu.create(CLA, U2F_AUTHENTICATE, U2F_AUTHENTICATE_P1_ENFORCE_USER_PRESENCE_AND_SIGN, P2_EMPTY, data).withDescriber(DESCRIBER);
+ }
+
+ @NonNull
+ public CommandApdu createVersionCommand() {
+ return CommandApdu.create(CLA, U2F_VERSION, P1_EMPTY, P2_EMPTY).withDescriber(DESCRIBER);
+ }
+
+ // ISO/IEC 7816-4
+ // SELECT command always as short APDU
+ @NonNull
+ public CommandApdu createSelectFileCommand(byte[] fileAid) {
+ return CommandApdu.create(CLA, INS_SELECT_FILE, P1_SELECT_FILE, P2_EMPTY, fileAid, CommandApdu.MAX_APDU_NE_SHORT).withDescriber(DESCRIBER);
+ }
+
+ // GET RESPONSE ISO/IEC 7816-4 par.7.6.1
+ @NonNull
+ public CommandApdu createGetResponseCommand(int lastResponseSw2) {
+ return CommandApdu.create(CLA, INS_GET_RESPONSE, P1_EMPTY, P2_EMPTY, lastResponseSw2).withDescriber(DESCRIBER);
+ }
+
+ // ISO/IEC 7816-4
+ @NonNull
+ public List createChainedApdus(CommandApdu apdu) {
+ ArrayList result = new ArrayList<>();
+
+ int offset = 0;
+ byte[] data = apdu.getData();
+ while (offset < data.length) {
+ int curLen = Math.min(CommandApdu.MAX_APDU_NC_SHORT, data.length - offset);
+ boolean last = offset + curLen >= data.length;
+ int cla = apdu.getCLA() + (last ? 0 : MASK_CLA_CHAINING);
+
+ CommandApdu cmd;
+ if (last) {
+ // TODO: check this!
+ int ne = Math.min(apdu.getNe(), CommandApdu.MAX_APDU_NE_SHORT);
+ cmd = CommandApdu.create(cla, apdu.getINS(), apdu.getP1(), apdu.getP2(), data, offset, curLen, ne, DESCRIBER);
+ } else {
+ cmd = CommandApdu.create(cla, apdu.getINS(), apdu.getP1(), apdu.getP2(), data, offset, curLen, 0, DESCRIBER);
+ }
+ result.add(cmd);
+
+ offset += curLen;
+ }
+
+ return result;
+ }
+
+ public boolean isSuitableForSingleShortApdu(CommandApdu apdu) {
+ return apdu.getNc() <= CommandApdu.MAX_APDU_NC_SHORT;
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/GenericFido2SecurityKeyDialogPresenter.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/GenericFido2SecurityKeyDialogPresenter.java
new file mode 100644
index 0000000..432c770
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/GenericFido2SecurityKeyDialogPresenter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal;
+
+import android.content.Context;
+
+import androidx.annotation.RestrictTo;
+
+import java.io.IOException;
+
+import de.cotech.hw.SecurityKey;
+import de.cotech.hw.SecurityKeyException;
+import de.cotech.hw.exceptions.SecurityKeyDisconnectedException;
+import de.cotech.hw.ui.R;
+import de.cotech.hw.secrets.ByteSecret;
+import de.cotech.hw.ui.SecurityKeyDialogOptions;
+import de.cotech.hw.ui.internal.SecurityKeyDialogPresenter;
+import de.cotech.hw.util.HwTimber;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class GenericFido2SecurityKeyDialogPresenter extends SecurityKeyDialogPresenter {
+
+ public GenericFido2SecurityKeyDialogPresenter(View view, Context context, SecurityKeyDialogOptions options) {
+ super(view, context, options);
+ }
+
+ @Override
+ public void handleError(IOException exception) {
+ HwTimber.d(exception);
+
+ switch (currentScreen) {
+ case NORMAL_SECURITY_KEY:
+ case NORMAL_SECURITY_KEY_HOLD: {
+ try {
+ throw exception;
+ } catch (SecurityKeyException e) {
+ gotoErrorScreenAndDelayedScreen(exception.getMessage(),
+ Screen.NORMAL_ERROR, Screen.NORMAL_SECURITY_KEY);
+ } catch (SecurityKeyDisconnectedException e) {
+ gotoScreen(Screen.NORMAL_SECURITY_KEY);
+ } catch (IOException e) {
+ view.updateErrorViewText(context.getString(R.string.hwsecurity_fido_error_internal, e.getMessage()));
+ gotoScreen(Screen.NORMAL_ERROR);
+ }
+ break;
+ }
+ default:
+ HwTimber.d("handleError unhandled screen: %s", currentScreen.name());
+ }
+ }
+
+ @Override
+ public void updateSecurityKeyPinUsingPuk(SecurityKey securityKey, ByteSecret puk, ByteSecret newPin) throws IOException {
+ throw new IOException("RESET_PIN mode is not supported in FIDO2 mode");
+ }
+
+ @Override
+ public boolean isSecurityKeyEmpty(SecurityKey securityKey) throws IOException {
+ HwTimber.e("SETUP mode is not supported in FIDO2 mode");
+ return true;
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/async/Ctap2Fido2OperationThread.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/async/Ctap2Fido2OperationThread.java
new file mode 100644
index 0000000..113598c
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/async/Ctap2Fido2OperationThread.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal.async;
+
+
+import java.io.IOException;
+
+import android.os.Handler;
+
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+import de.cotech.hw.fido2.Ctap2Callback;
+import de.cotech.hw.fido2.internal.Fido2AppletConnection;
+import de.cotech.hw.fido2.internal.ctap2.Ctap2Command;
+import de.cotech.hw.fido2.internal.ctap2.Ctap2Response;
+
+
+public class Ctap2Fido2OperationThread extends Fido2OperationThread {
+ private final Ctap2Command ctap2Command;
+ private final Ctap2Callback callback;
+
+ public Ctap2Fido2OperationThread(
+ Fido2AppletConnection fido2AppletConnection, Handler handler,
+ Ctap2Command ctap2Command, Ctap2Callback callback, int userPresenceCheckDelayMs) {
+ super(fido2AppletConnection, handler, userPresenceCheckDelayMs);
+ this.ctap2Command = ctap2Command;
+ this.callback = callback;
+ }
+
+ @WorkerThread
+ CR performOperation() throws IOException {
+ return fido2AppletConnection.ctap2CommunicateOrThrow(ctap2Command);
+ }
+
+ @Override
+ @UiThread
+ void deliverResponse(CR response) {
+ callback.onResponse(response);
+ }
+
+ @Override
+ @UiThread
+ void deliverIoException(IOException e) {
+ callback.onIoException(e);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/async/Fido2AsyncOperationManager.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/async/Fido2AsyncOperationManager.java
new file mode 100644
index 0000000..5cb9330
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/async/Fido2AsyncOperationManager.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal.async;
+
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.LifecycleOwner;
+import de.cotech.hw.fido2.internal.utils.AndroidUtils;
+
+
+@RestrictTo(Scope.LIBRARY_GROUP)
+public class Fido2AsyncOperationManager {
+ private final Object asyncOperationLock;
+ @VisibleForTesting
+ Fido2OperationThread> asyncOperationThread;
+
+ public Fido2AsyncOperationManager() {
+ asyncOperationLock = new Object();
+ }
+
+ @AnyThread
+ public void startAsyncOperation(LifecycleOwner lifecycleOwner, Fido2OperationThread> operationThread) {
+ synchronized (asyncOperationLock) {
+ if (asyncOperationThread != null) {
+ asyncOperationThread.interrupt();
+ asyncOperationThread = null;
+ }
+
+ asyncOperationThread = operationThread;
+ asyncOperationThread.setFido2AsyncOperationManager(this);
+ asyncOperationThread.start();
+ AndroidUtils.addLifecycleObserver(lifecycleOwner, asyncOperationThread);
+ }
+ }
+
+ @AnyThread
+ public void clearAsyncOperation() {
+ clearAsyncOperation(true, null);
+ }
+
+ @AnyThread
+ void clearAsyncOperation(boolean interrupt, Thread specificThread) {
+ synchronized (asyncOperationLock) {
+ if (specificThread != null && asyncOperationThread != specificThread) {
+ if (interrupt) {
+ specificThread.interrupt();
+ }
+ return;
+ }
+ if (asyncOperationThread != null && asyncOperationThread.isAlive() && interrupt) {
+ asyncOperationThread.interrupt();
+ }
+ asyncOperationThread = null;
+ }
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/async/Fido2OperationThread.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/async/Fido2OperationThread.java
new file mode 100644
index 0000000..ec72faa
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/async/Fido2OperationThread.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal.async;
+
+
+import java.io.IOException;
+
+import android.os.Handler;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+import androidx.lifecycle.Lifecycle.Event;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import de.cotech.hw.exceptions.SecurityKeyDisconnectedException;
+import de.cotech.hw.fido2.exceptions.FidoPresenceRequiredException;
+import de.cotech.hw.fido2.internal.Fido2AppletConnection;
+import de.cotech.hw.util.HwTimber;
+
+
+@RestrictTo(Scope.LIBRARY_GROUP)
+abstract class Fido2OperationThread extends Thread implements LifecycleObserver {
+ private Fido2AsyncOperationManager fido2AsyncOperationManager;
+ private final Handler handler;
+ private final int presenceCheckDelayMs;
+ final Fido2AppletConnection fido2AppletConnection;
+
+ Fido2OperationThread(Fido2AppletConnection fido2AppletConnection, Handler handler, int presenceCheckDelayMs) {
+ this.fido2AppletConnection = fido2AppletConnection;
+ this.handler = handler;
+ this.presenceCheckDelayMs = presenceCheckDelayMs;
+ }
+
+ @WorkerThread
+ void prepareOperation() throws InterruptedException {
+
+ }
+
+ @WorkerThread
+ abstract T performOperation() throws IOException, InterruptedException;
+ @UiThread
+ abstract void deliverResponse(T response);
+ @UiThread
+ abstract void deliverIoException(IOException e);
+
+ void setFido2AsyncOperationManager(Fido2AsyncOperationManager fido2AsyncOperationManager) {
+ this.fido2AsyncOperationManager = fido2AsyncOperationManager;
+ }
+
+ @Override
+ public void run() {
+ try {
+ prepareOperation();
+ } catch (InterruptedException e) {
+ fido2AsyncOperationManager.clearAsyncOperation(false, this);
+ return;
+ }
+ while (!isInterrupted() && fido2AppletConnection.isConnected()) {
+ try {
+ T response = performOperation();
+ postToHandler(() -> deliverResponse(response));
+ break;
+ } catch (InterruptedException e) {
+ HwTimber.e("Fido 2 operation was interrupted");
+ break;
+ } catch (SecurityKeyDisconnectedException e) {
+ HwTimber.e("Transport gone during fido 2 operation");
+ break;
+ } catch (FidoPresenceRequiredException e) {
+ try {
+ Thread.sleep(presenceCheckDelayMs);
+ } catch (InterruptedException e1) {
+ break;
+ }
+ } catch (IOException e) {
+ if (e.getCause() instanceof InterruptedException) {
+ HwTimber.e("Fido 2 operation was interrupted");
+ break;
+ }
+ postToHandler(() -> deliverIoException(e));
+ break;
+ }
+ }
+ fido2AsyncOperationManager.clearAsyncOperation(false, this);
+ }
+
+ private void postToHandler(Runnable runnable) {
+ if (isInterrupted()) {
+ return;
+ }
+ handler.post(() -> {
+ if (!isInterrupted()) {
+ runnable.run();
+ }
+ });
+ }
+
+ @OnLifecycleEvent(Event.ON_STOP)
+ public void onDestroy() {
+ if (isAlive() && !isInterrupted()) {
+ fido2AsyncOperationManager.clearAsyncOperation(true, this);
+ }
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/async/WebauthnFido2OperationThread.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/async/WebauthnFido2OperationThread.java
new file mode 100644
index 0000000..37e8fa1
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/async/WebauthnFido2OperationThread.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal.async;
+
+
+import java.io.IOException;
+
+import android.os.Handler;
+
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+import de.cotech.hw.fido2.WebauthnCallback;
+import de.cotech.hw.fido2.internal.Fido2AppletConnection;
+import de.cotech.hw.fido2.internal.operations.WebauthnSecurityKeyOperation;
+import de.cotech.hw.fido2.internal.operations.WebauthnSecurityKeyOperationFactory;
+import de.cotech.hw.fido2.internal.webauthn.WebauthnCommand;
+import de.cotech.hw.fido2.internal.webauthn.WebauthnResponse;
+
+
+public class WebauthnFido2OperationThread
+ extends Fido2OperationThread {
+ private final WC webauthnCommand;
+ private final WebauthnCallback callback;
+ private final WebauthnSecurityKeyOperation operation;
+
+ public WebauthnFido2OperationThread(
+ Fido2AppletConnection fido2AppletConnection,
+ WebauthnSecurityKeyOperationFactory operationFactory,
+ Handler handler, WebauthnCallback callback, WC webauthnCommand,
+ int userPresenceCheckDelayMs
+ ) {
+ super(fido2AppletConnection, handler, userPresenceCheckDelayMs);
+ this.callback = callback;
+ this.webauthnCommand = webauthnCommand;
+ this.operation = operationFactory.getOperation(webauthnCommand, fido2AppletConnection.isCtap2Capable());
+ }
+
+ @WorkerThread
+ WR performOperation() throws IOException {
+ return operation.performWebauthnSecurityKeyOperation(fido2AppletConnection, webauthnCommand);
+ }
+
+ @Override
+ @UiThread
+ void deliverResponse(WR response) {
+ callback.onResponse(response);
+ }
+
+ @Override
+ @UiThread
+ void deliverIoException(IOException e) {
+ callback.onIoException(e);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborAttestationObjectSerializer.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborAttestationObjectSerializer.java
new file mode 100644
index 0000000..92797d5
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborAttestationObjectSerializer.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal.cbor;
+
+
+import java.io.IOException;
+import java.util.List;
+
+import de.cotech.hw.fido2.internal.cbor_java.CborBuilder;
+import de.cotech.hw.fido2.internal.cbor_java.CborDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.CborException;
+import de.cotech.hw.fido2.internal.cbor_java.model.ByteString;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+import de.cotech.hw.fido2.internal.cbor_java.model.UnicodeString;
+import de.cotech.hw.fido2.domain.create.AttestationObject;
+
+
+public class CborAttestationObjectSerializer {
+ public byte[] serializeAttestationObject(AttestationObject attestationObject) throws IOException {
+ try {
+ return serializeAttestationObjectOrThrow(attestationObject);
+ } catch (CborException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private byte[] serializeAttestationObjectOrThrow(AttestationObject attestationObject)
+ throws CborException {
+ List cborData = new CborBuilder()
+ .addMap()
+ .put(CborConstants.FMT, new UnicodeString(attestationObject.fmt()))
+ .put(CborConstants.AUTH_DATA, new ByteString(attestationObject.authData()))
+ .put(CborConstants.ATT_STMT, CborDecoder.decode(attestationObject.attStmt()).get(0))
+ .end()
+ .build();
+ return CborUtils.writeCborDataToBytes(cborData);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborConstants.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborConstants.java
new file mode 100644
index 0000000..ac116fe
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborConstants.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal.cbor;
+
+
+import java.io.ByteArrayOutputStream;
+
+import de.cotech.hw.fido2.internal.cbor_java.CborEncoder;
+import de.cotech.hw.fido2.internal.cbor_java.CborException;
+import de.cotech.hw.fido2.internal.cbor_java.model.Map;
+import de.cotech.hw.fido2.internal.cbor_java.model.UnicodeString;
+
+
+public class CborConstants {
+ public static final UnicodeString FMT = new UnicodeString("fmt");
+ public static final UnicodeString AUTH_DATA = new UnicodeString("authData");
+ public static final UnicodeString ATT_STMT = new UnicodeString("attStmt");
+
+ public static final UnicodeString TYPE = new UnicodeString("type");
+ public static final UnicodeString ID = new UnicodeString("id");
+
+ public static final Map EMPTY_MAP = new Map();
+ public static final byte[] EMPTY_MAP_BYTES = emptyMap();
+
+ private CborConstants() { }
+
+ private static byte[] emptyMap() {
+ try {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ new CborEncoder(outputStream).encode(EMPTY_MAP);
+ return outputStream.toByteArray();
+ } catch (CborException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborCtap1AttestationStatementUtil.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborCtap1AttestationStatementUtil.java
new file mode 100644
index 0000000..f7b85d6
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborCtap1AttestationStatementUtil.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal.cbor;
+
+
+import java.util.List;
+
+import de.cotech.hw.fido2.internal.cbor_java.CborBuilder;
+import de.cotech.hw.fido2.internal.cbor_java.CborException;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+
+public class CborCtap1AttestationStatementUtil {
+ public static byte[] toAttestionStatement(byte[] x509certificate, byte[] signature) {
+ try {
+ List dataItems = new CborBuilder()
+ .addMap()
+ .put("sig", signature)
+ .putArray("x5c")
+ .add(x509certificate)
+ .end()
+ .end()
+ .build();
+ return CborUtils.writeCborDataToBytes(dataItems);
+ } catch (CborException e) {
+ // this operation will always work
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborPublicKeyCredentialDescriptorParser.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborPublicKeyCredentialDescriptorParser.java
new file mode 100644
index 0000000..081429f
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborPublicKeyCredentialDescriptorParser.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal.cbor;
+
+
+import java.io.IOException;
+import java.util.List;
+
+import de.cotech.hw.fido2.domain.PublicKeyCredentialDescriptor;
+import de.cotech.hw.fido2.domain.PublicKeyCredentialType;
+import de.cotech.hw.fido2.internal.cbor_java.CborDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.CborException;
+import de.cotech.hw.fido2.internal.cbor_java.model.ByteString;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+import de.cotech.hw.fido2.internal.cbor_java.model.Map;
+import de.cotech.hw.fido2.internal.cbor_java.model.UnicodeString;
+
+
+public class CborPublicKeyCredentialDescriptorParser {
+ public PublicKeyCredentialDescriptor parse(byte[] data) throws IOException {
+ try {
+ List dataItems = CborDecoder.decode(data);
+ if (dataItems.size() < 1) {
+ throw new IOException(
+ "Failed to parse PublicKeyCredentialDescriptor, expected 1 element!");
+ }
+ Map cborMap = (Map) dataItems.get(0);
+
+ PublicKeyCredentialType publicKeyCredentialType = PublicKeyCredentialType.fromString(
+ ((UnicodeString) cborMap.get(CborConstants.TYPE)).getString());
+ byte[] id = ((ByteString) cborMap.get(CborConstants.ID)).getBytes();
+
+ return PublicKeyCredentialDescriptor.create(publicKeyCredentialType, id, null);
+ } catch (ClassCastException e) {
+ throw new IOException("Failed to parse PublicKeyCredentialDescriptor!", e);
+ } catch (CborException e) {
+ throw new IOException("Failed to parse PublicKeyCredentialDescriptor!", e);
+ }
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborUtils.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborUtils.java
new file mode 100644
index 0000000..e512f4f
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor/CborUtils.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018-2020 Confidential Technologies GmbH
+ *
+ * You can purchase a commercial license at https://hwsecurity.dev.
+ * Buying such a license is mandatory as soon as you develop commercial
+ * activities involving this program without disclosing the source code
+ * of your own applications.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.cotech.hw.fido2.internal.cbor;
+
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import de.cotech.hw.fido2.internal.cbor_java.CborEncoder;
+import de.cotech.hw.fido2.internal.cbor_java.CborException;
+import de.cotech.hw.fido2.internal.cbor_java.model.Array;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+import de.cotech.hw.fido2.internal.cbor_java.model.UnicodeString;
+import de.cotech.hw.fido2.internal.cbor_java.model.UnsignedInteger;
+
+
+public class CborUtils {
+ public static byte[] writeCborDataToBytes(List cborData) throws CborException {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ new CborEncoder(outputStream).encode(cborData);
+ return outputStream.toByteArray();
+ }
+
+ public static byte[] writeCborDataToBytes(DataItem cborData) throws CborException {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ new CborEncoder(outputStream).encode(cborData);
+ return outputStream.toByteArray();
+ }
+
+ public static List cborArrayToIntegerArray(Array array) {
+ ArrayList result = new ArrayList<>();
+ for (DataItem versionDataItem : array.getDataItems()) {
+ UnsignedInteger unsignedInteger = (UnsignedInteger) versionDataItem;
+ result.add(unsignedInteger.getValue().intValue());
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ public static List cborArrayToStringArray(Array array) {
+ ArrayList result = new ArrayList<>();
+ for (DataItem versionDataItem : array.getDataItems()) {
+ UnicodeString string = (UnicodeString) versionDataItem;
+ result.add(string.getString());
+ }
+ return Collections.unmodifiableList(result);
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/CborBuilder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/CborBuilder.java
new file mode 100644
index 0000000..19107c6
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/CborBuilder.java
@@ -0,0 +1,129 @@
+package de.cotech.hw.fido2.internal.cbor_java;
+
+import java.math.BigInteger;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.cotech.hw.fido2.internal.cbor_java.builder.AbstractBuilder;
+import de.cotech.hw.fido2.internal.cbor_java.builder.ArrayBuilder;
+import de.cotech.hw.fido2.internal.cbor_java.builder.ByteStringBuilder;
+import de.cotech.hw.fido2.internal.cbor_java.builder.MapBuilder;
+import de.cotech.hw.fido2.internal.cbor_java.builder.UnicodeStringBuilder;
+import de.cotech.hw.fido2.internal.cbor_java.model.Array;
+import de.cotech.hw.fido2.internal.cbor_java.model.ByteString;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+import de.cotech.hw.fido2.internal.cbor_java.model.Map;
+import de.cotech.hw.fido2.internal.cbor_java.model.UnicodeString;
+
+public class CborBuilder extends AbstractBuilder {
+
+ private final List dataItems = new LinkedList<>();
+
+ public CborBuilder() {
+ super(null);
+ }
+
+ public CborBuilder reset() {
+ dataItems.clear();
+ return this;
+ }
+
+ public List build() {
+ return dataItems;
+ }
+
+ public CborBuilder add(DataItem dataItem) {
+ dataItems.add(dataItem);
+ return this;
+ }
+
+ public CborBuilder add(long value) {
+ add(convert(value));
+ return this;
+ }
+
+ public CborBuilder add(BigInteger value) {
+ add(convert(value));
+ return this;
+ }
+
+ public CborBuilder add(boolean value) {
+ add(convert(value));
+ return this;
+ }
+
+ public CborBuilder add(float value) {
+ add(convert(value));
+ return this;
+ }
+
+ public CborBuilder add(double value) {
+ add(convert(value));
+ return this;
+ }
+
+ public CborBuilder add(byte[] bytes) {
+ add(convert(bytes));
+ return this;
+ }
+
+ public ByteStringBuilder startByteString() {
+ return startByteString(null);
+ }
+
+ public ByteStringBuilder startByteString(byte[] bytes) {
+ add(new ByteString(bytes).setChunked(true));
+ return new ByteStringBuilder(this);
+ }
+
+ public CborBuilder add(String string) {
+ add(convert(string));
+ return this;
+ }
+
+ public UnicodeStringBuilder startString() {
+ return startString(null);
+ }
+
+ public UnicodeStringBuilder startString(String string) {
+ add(new UnicodeString(string).setChunked(true));
+ return new UnicodeStringBuilder(this);
+ }
+
+ public CborBuilder addTag(long value) {
+ add(tag(value));
+ return this;
+ }
+
+ public ArrayBuilder startArray() {
+ Array array = new Array();
+ array.setChunked(true);
+ add(array);
+ return new ArrayBuilder(this, array);
+ }
+
+ public ArrayBuilder addArray() {
+ Array array = new Array();
+ add(array);
+ return new ArrayBuilder(this, array);
+ }
+
+ public MapBuilder addMap() {
+ Map map = new Map();
+ add(map);
+ return new MapBuilder(this, map);
+ }
+
+ public MapBuilder startMap() {
+ Map map = new Map();
+ map.setChunked(true);
+ add(map);
+ return new MapBuilder(this, map);
+ }
+
+ @Override
+ protected void addChunk(DataItem dataItem) {
+ add(dataItem);
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/CborDecoder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/CborDecoder.java
new file mode 100644
index 0000000..5d734ea
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/CborDecoder.java
@@ -0,0 +1,288 @@
+package de.cotech.hw.fido2.internal.cbor_java;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+
+import de.cotech.hw.fido2.internal.cbor_java.decoder.ArrayDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.decoder.ByteStringDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.decoder.MapDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.decoder.NegativeIntegerDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.decoder.SpecialDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.decoder.TagDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.decoder.UnicodeStringDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.decoder.UnsignedIntegerDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.model.Array;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+import de.cotech.hw.fido2.internal.cbor_java.model.LanguageTaggedString;
+import de.cotech.hw.fido2.internal.cbor_java.model.MajorType;
+import de.cotech.hw.fido2.internal.cbor_java.model.Number;
+import de.cotech.hw.fido2.internal.cbor_java.model.RationalNumber;
+import de.cotech.hw.fido2.internal.cbor_java.model.Tag;
+import de.cotech.hw.fido2.internal.cbor_java.model.UnicodeString;
+
+/**
+ * Decoder for the CBOR format based.
+ */
+public class CborDecoder {
+
+ private final InputStream inputStream;
+ private final UnsignedIntegerDecoder unsignedIntegerDecoder;
+ private final NegativeIntegerDecoder negativeIntegerDecoder;
+ private final ByteStringDecoder byteStringDecoder;
+ private final UnicodeStringDecoder unicodeStringDecoder;
+ private final ArrayDecoder arrayDecoder;
+ private final MapDecoder mapDecoder;
+ private final TagDecoder tagDecoder;
+ private final SpecialDecoder specialDecoder;
+
+ private boolean autoDecodeInfinitiveArrays = true;
+ private boolean autoDecodeInfinitiveMaps = true;
+ private boolean autoDecodeInfinitiveByteStrings = true;
+ private boolean autoDecodeInfinitiveUnicodeStrings = true;
+ private boolean autoDecodeRationalNumbers = true;
+ private boolean autoDecodeLanguageTaggedStrings = true;
+ private boolean rejectDuplicateKeys = false;
+
+ /**
+ * Initialize a new decoder which reads the binary encoded data from an
+ * {@link InputStream}.
+ */
+ public CborDecoder(InputStream inputStream) {
+ Objects.requireNonNull(inputStream);
+ this.inputStream = inputStream;
+ unsignedIntegerDecoder = new UnsignedIntegerDecoder(this, inputStream);
+ negativeIntegerDecoder = new NegativeIntegerDecoder(this, inputStream);
+ byteStringDecoder = new ByteStringDecoder(this, inputStream);
+ unicodeStringDecoder = new UnicodeStringDecoder(this, inputStream);
+ arrayDecoder = new ArrayDecoder(this, inputStream);
+ mapDecoder = new MapDecoder(this, inputStream);
+ tagDecoder = new TagDecoder(this, inputStream);
+ specialDecoder = new SpecialDecoder(this, inputStream);
+ }
+
+ /**
+ * Convenience method to decode a byte array directly.
+ *
+ * @param bytes
+ * the CBOR encoded data
+ * @return a list of {@link DataItem}s
+ * @throws CborException
+ * if decoding failed
+ */
+ public static List decode(byte[] bytes) throws CborException {
+ return new CborDecoder(new ByteArrayInputStream(bytes)).decode();
+ }
+
+ /**
+ * Decode the {@link InputStream} to a list of {@link DataItem}s.
+ *
+ * @return the list of {@link DataItem}s
+ * @throws CborException
+ * if decoding failed
+ */
+ public List decode() throws CborException {
+ List dataItems = new LinkedList<>();
+ DataItem dataItem;
+ while ((dataItem = decodeNext()) != null) {
+ dataItems.add(dataItem);
+ }
+ return dataItems;
+ }
+
+ /**
+ * Streaming decoding of an input stream. On each decoded DataItem, the
+ * callback listener is invoked.
+ *
+ * @param dataItemListener
+ * the callback listener
+ * @throws CborException
+ * if decoding failed
+ */
+ public void decode(DataItemListener dataItemListener) throws CborException {
+ Objects.requireNonNull(dataItemListener);
+ DataItem dataItem = decodeNext();
+ while (dataItem != null) {
+ dataItemListener.onDataItem(dataItem);
+ dataItem = decodeNext();
+ }
+ }
+
+ /**
+ * Decodes exactly one DataItem from the input stream.
+ *
+ * @return a {@link DataItem} or null if end of stream has reached.
+ * @throws CborException
+ * if decoding failed
+ */
+ public DataItem decodeNext() throws CborException {
+ int symbol;
+ try {
+ symbol = inputStream.read();
+ } catch (IOException ioException) {
+ throw new CborException(ioException);
+ }
+ if (symbol == -1) {
+ return null;
+ }
+ switch (MajorType.ofByte(symbol)) {
+ case ARRAY:
+ return arrayDecoder.decode(symbol);
+ case BYTE_STRING:
+ return byteStringDecoder.decode(symbol);
+ case MAP:
+ return mapDecoder.decode(symbol);
+ case NEGATIVE_INTEGER:
+ return negativeIntegerDecoder.decode(symbol);
+ case UNICODE_STRING:
+ return unicodeStringDecoder.decode(symbol);
+ case UNSIGNED_INTEGER:
+ return unsignedIntegerDecoder.decode(symbol);
+ case SPECIAL:
+ return specialDecoder.decode(symbol);
+ case TAG:
+ Tag tag = tagDecoder.decode(symbol);
+ DataItem next = decodeNext();
+ if (next == null) {
+ throw new CborException("Unexpected end of stream: tag without following data item.");
+ } else {
+ if (autoDecodeRationalNumbers && tag.getValue() == 30) {
+ return decodeRationalNumber(next);
+ } else if (autoDecodeLanguageTaggedStrings && tag.getValue() == 38) {
+ return decodeLanguageTaggedString(next);
+ } else {
+ DataItem itemToTag = next;
+ while (itemToTag.hasTag())
+ itemToTag = itemToTag.getTag();
+ itemToTag.setTag(tag);
+ return next;
+ }
+ }
+ case INVALID:
+ default:
+ throw new CborException("Not implemented major type " + symbol);
+ }
+ }
+
+ private DataItem decodeLanguageTaggedString(DataItem dataItem) throws CborException {
+ if (!(dataItem instanceof Array)) {
+ throw new CborException("Error decoding LanguageTaggedString: not an array");
+ }
+
+ Array array = (Array) dataItem;
+
+ if (array.getDataItems().size() != 2) {
+ throw new CborException("Error decoding LanguageTaggedString: array size is not 2");
+ }
+
+ DataItem languageDataItem = array.getDataItems().get(0);
+
+ if (!(languageDataItem instanceof UnicodeString)) {
+ throw new CborException("Error decoding LanguageTaggedString: first data item is not an UnicodeString");
+ }
+
+ DataItem stringDataItem = array.getDataItems().get(1);
+
+ if (!(stringDataItem instanceof UnicodeString)) {
+ throw new CborException("Error decoding LanguageTaggedString: second data item is not an UnicodeString");
+ }
+
+ UnicodeString language = (UnicodeString) languageDataItem;
+ UnicodeString string = (UnicodeString) stringDataItem;
+
+ return new LanguageTaggedString(language, string);
+ }
+
+ private DataItem decodeRationalNumber(DataItem dataItem) throws CborException {
+ if (!(dataItem instanceof Array)) {
+ throw new CborException("Error decoding RationalNumber: not an array");
+ }
+
+ Array array = (Array) dataItem;
+
+ if (array.getDataItems().size() != 2) {
+ throw new CborException("Error decoding RationalNumber: array size is not 2");
+ }
+
+ DataItem numeratorDataItem = array.getDataItems().get(0);
+
+ if (!(numeratorDataItem instanceof Number)) {
+ throw new CborException("Error decoding RationalNumber: first data item is not a number");
+ }
+
+ DataItem denominatorDataItem = array.getDataItems().get(1);
+
+ if (!(denominatorDataItem instanceof Number)) {
+ throw new CborException("Error decoding RationalNumber: second data item is not a number");
+ }
+
+ Number numerator = (Number) numeratorDataItem;
+ Number denominator = (Number) denominatorDataItem;
+
+ return new RationalNumber(numerator, denominator);
+ }
+
+ public boolean isAutoDecodeInfinitiveArrays() {
+ return autoDecodeInfinitiveArrays;
+ }
+
+ public void setAutoDecodeInfinitiveArrays(boolean autoDecodeInfinitiveArrays) {
+ this.autoDecodeInfinitiveArrays = autoDecodeInfinitiveArrays;
+ }
+
+ public boolean isAutoDecodeInfinitiveMaps() {
+ return autoDecodeInfinitiveMaps;
+ }
+
+ public void setAutoDecodeInfinitiveMaps(boolean autoDecodeInfinitiveMaps) {
+ this.autoDecodeInfinitiveMaps = autoDecodeInfinitiveMaps;
+ }
+
+ public boolean isAutoDecodeInfinitiveByteStrings() {
+ return autoDecodeInfinitiveByteStrings;
+ }
+
+ public void setAutoDecodeInfinitiveByteStrings(
+ boolean autoDecodeInfinitiveByteStrings) {
+ this.autoDecodeInfinitiveByteStrings = autoDecodeInfinitiveByteStrings;
+ }
+
+ public boolean isAutoDecodeInfinitiveUnicodeStrings() {
+ return autoDecodeInfinitiveUnicodeStrings;
+ }
+
+ public void setAutoDecodeInfinitiveUnicodeStrings(
+ boolean autoDecodeInfinitiveUnicodeStrings) {
+ this.autoDecodeInfinitiveUnicodeStrings = autoDecodeInfinitiveUnicodeStrings;
+ }
+
+ public boolean isAutoDecodeRationalNumbers() {
+ return autoDecodeRationalNumbers;
+ }
+
+ public void setAutoDecodeRationalNumbers(
+ boolean autoDecodeRationalNumbers) {
+ this.autoDecodeRationalNumbers = autoDecodeRationalNumbers;
+ }
+
+ public boolean isAutoDecodeLanguageTaggedStrings() {
+ return autoDecodeLanguageTaggedStrings;
+ }
+
+ public void setAutoDecodeLanguageTaggedStrings(
+ boolean autoDecodeLanguageTaggedStrings) {
+ this.autoDecodeLanguageTaggedStrings = autoDecodeLanguageTaggedStrings;
+ }
+
+ public boolean isRejectDuplicateKeys() {
+ return rejectDuplicateKeys;
+ }
+
+ public void setRejectDuplicateKeys(boolean rejectDuplicateKeys) {
+ this.rejectDuplicateKeys = rejectDuplicateKeys;
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/CborEncoder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/CborEncoder.java
new file mode 100644
index 0000000..7c45906
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/CborEncoder.java
@@ -0,0 +1,121 @@
+package de.cotech.hw.fido2.internal.cbor_java;
+
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Objects;
+
+import de.cotech.hw.fido2.internal.cbor_java.encoder.ArrayEncoder;
+import de.cotech.hw.fido2.internal.cbor_java.encoder.ByteStringEncoder;
+import de.cotech.hw.fido2.internal.cbor_java.encoder.MapEncoder;
+import de.cotech.hw.fido2.internal.cbor_java.encoder.NegativeIntegerEncoder;
+import de.cotech.hw.fido2.internal.cbor_java.encoder.SpecialEncoder;
+import de.cotech.hw.fido2.internal.cbor_java.encoder.TagEncoder;
+import de.cotech.hw.fido2.internal.cbor_java.encoder.UnicodeStringEncoder;
+import de.cotech.hw.fido2.internal.cbor_java.encoder.UnsignedIntegerEncoder;
+import de.cotech.hw.fido2.internal.cbor_java.model.Array;
+import de.cotech.hw.fido2.internal.cbor_java.model.ByteString;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+import de.cotech.hw.fido2.internal.cbor_java.model.Map;
+import de.cotech.hw.fido2.internal.cbor_java.model.NegativeInteger;
+import de.cotech.hw.fido2.internal.cbor_java.model.SimpleValue;
+import de.cotech.hw.fido2.internal.cbor_java.model.Special;
+import de.cotech.hw.fido2.internal.cbor_java.model.Tag;
+import de.cotech.hw.fido2.internal.cbor_java.model.UnicodeString;
+import de.cotech.hw.fido2.internal.cbor_java.model.UnsignedInteger;
+
+/**
+ * Encoder for the CBOR format based.
+ */
+public class CborEncoder {
+
+ private final UnsignedIntegerEncoder unsignedIntegerEncoder;
+ private final NegativeIntegerEncoder negativeIntegerEncoder;
+ private final ByteStringEncoder byteStringEncoder;
+ private final UnicodeStringEncoder unicodeStringEncoder;
+ private final ArrayEncoder arrayEncoder;
+ private final MapEncoder mapEncoder;
+ private final TagEncoder tagEncoder;
+ private final SpecialEncoder specialEncoder;
+
+ /**
+ * Initialize a new encoder which writes the binary encoded data to an
+ * {@link OutputStream}.
+ */
+ public CborEncoder(OutputStream outputStream) {
+ Objects.requireNonNull(outputStream);
+ unsignedIntegerEncoder = new UnsignedIntegerEncoder(this, outputStream);
+ negativeIntegerEncoder = new NegativeIntegerEncoder(this, outputStream);
+ byteStringEncoder = new ByteStringEncoder(this, outputStream);
+ unicodeStringEncoder = new UnicodeStringEncoder(this, outputStream);
+ arrayEncoder = new ArrayEncoder(this, outputStream);
+ mapEncoder = new MapEncoder(this, outputStream);
+ tagEncoder = new TagEncoder(this, outputStream);
+ specialEncoder = new SpecialEncoder(this, outputStream);
+ }
+
+ /**
+ * Encode a list of {@link DataItem}s, also known as a stream.
+ *
+ * @param dataItems
+ * a list of {@link DataItem}s
+ * @throws CborException
+ * if the {@link DataItem}s could not be encoded or there was an
+ * problem with the {@link OutputStream}.
+ */
+ public void encode(List dataItems) throws CborException {
+ for (DataItem dataItem : dataItems) {
+ encode(dataItem);
+ }
+ }
+
+ /**
+ * Encode a single {@link DataItem}.
+ *
+ * @param dataItem
+ * the {@link DataItem} to encode. If null, encoder encodes a
+ * {@link SimpleValue} NULL value.
+ * @throws CborException
+ * if {@link DataItem} could not be encoded or there was an
+ * problem with the {@link OutputStream}.
+ */
+ public void encode(DataItem dataItem) throws CborException {
+ if (dataItem == null) {
+ dataItem = SimpleValue.NULL;
+ }
+
+ if (dataItem.hasTag()) {
+ Tag tagDi = dataItem.getTag();
+ tagEncoder.encode(tagDi);
+ }
+
+ switch (dataItem.getMajorType()) {
+ case UNSIGNED_INTEGER:
+ unsignedIntegerEncoder.encode((UnsignedInteger) dataItem);
+ break;
+ case NEGATIVE_INTEGER:
+ negativeIntegerEncoder.encode((NegativeInteger) dataItem);
+ break;
+ case BYTE_STRING:
+ byteStringEncoder.encode((ByteString) dataItem);
+ break;
+ case UNICODE_STRING:
+ unicodeStringEncoder.encode((UnicodeString) dataItem);
+ break;
+ case ARRAY:
+ arrayEncoder.encode((Array) dataItem);
+ break;
+ case MAP:
+ mapEncoder.encode((Map) dataItem);
+ break;
+ case SPECIAL:
+ specialEncoder.encode((Special) dataItem);
+ break;
+ case TAG:
+ tagEncoder.encode((Tag) dataItem);
+ break;
+ default:
+ throw new CborException("Unknown major type");
+ }
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/CborException.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/CborException.java
new file mode 100644
index 0000000..dab8291
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/CborException.java
@@ -0,0 +1,19 @@
+package de.cotech.hw.fido2.internal.cbor_java;
+
+public class CborException extends Exception {
+
+ private static final long serialVersionUID = 8839905301881841410L;
+
+ public CborException(String message) {
+ super(message);
+ }
+
+ public CborException(Throwable cause) {
+ super(cause);
+ }
+
+ public CborException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/DataItemListener.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/DataItemListener.java
new file mode 100644
index 0000000..7e1388d
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/DataItemListener.java
@@ -0,0 +1,15 @@
+package de.cotech.hw.fido2.internal.cbor_java;
+
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+
+/**
+ * Callback interface for a streaming {@link CborDecoder}.
+ */
+public interface DataItemListener {
+
+ /**
+ * Gets called on every decoded {@link DataItem}.
+ */
+ void onDataItem(DataItem dataItem);
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/AbstractBuilder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/AbstractBuilder.java
new file mode 100644
index 0000000..98bcc32
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/AbstractBuilder.java
@@ -0,0 +1,113 @@
+package de.cotech.hw.fido2.internal.cbor_java.builder;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+
+import de.cotech.hw.fido2.internal.cbor_java.CborException;
+import de.cotech.hw.fido2.internal.cbor_java.decoder.HalfPrecisionFloatDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.encoder.HalfPrecisionFloatEncoder;
+import de.cotech.hw.fido2.internal.cbor_java.model.ByteString;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+import de.cotech.hw.fido2.internal.cbor_java.model.DoublePrecisionFloat;
+import de.cotech.hw.fido2.internal.cbor_java.model.HalfPrecisionFloat;
+import de.cotech.hw.fido2.internal.cbor_java.model.NegativeInteger;
+import de.cotech.hw.fido2.internal.cbor_java.model.SimpleValue;
+import de.cotech.hw.fido2.internal.cbor_java.model.SinglePrecisionFloat;
+import de.cotech.hw.fido2.internal.cbor_java.model.Tag;
+import de.cotech.hw.fido2.internal.cbor_java.model.UnicodeString;
+import de.cotech.hw.fido2.internal.cbor_java.model.UnsignedInteger;
+
+public abstract class AbstractBuilder {
+
+ private final T parent;
+
+ public AbstractBuilder(T parent) {
+ this.parent = parent;
+ }
+
+ protected T getParent() {
+ return parent;
+ }
+
+ protected void addChunk(DataItem dataItem) {
+ throw new IllegalStateException();
+ }
+
+ protected DataItem convert(long value) {
+ if (value >= 0) {
+ return new UnsignedInteger(value);
+ } else {
+ return new NegativeInteger(value);
+ }
+ }
+
+ protected DataItem convert(BigInteger value) {
+ if (value.signum() == -1) {
+ return new NegativeInteger(value);
+ } else {
+ return new UnsignedInteger(value);
+ }
+ }
+
+ protected DataItem convert(boolean value) {
+ if (value) {
+ return SimpleValue.TRUE;
+ } else {
+ return SimpleValue.FALSE;
+ }
+ }
+
+ protected DataItem convert(byte[] bytes) {
+ return new ByteString(bytes);
+ }
+
+ protected DataItem convert(String string) {
+ return new UnicodeString(string);
+ }
+
+ protected DataItem convert(float value) {
+ if (isHalfPrecisionEnough(value)) {
+ return new HalfPrecisionFloat(value);
+ } else {
+ return new SinglePrecisionFloat(value);
+ }
+ }
+
+ protected DataItem convert(double value) {
+ return new DoublePrecisionFloat(value);
+ }
+
+ protected Tag tag(long value) {
+ return new Tag(value);
+ }
+
+ private boolean isHalfPrecisionEnough(float value) {
+ try {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ HalfPrecisionFloatEncoder encoder = getHalfPrecisionFloatEncoder(outputStream);
+ encoder.encode(new HalfPrecisionFloat(value));
+ byte[] bytes = outputStream.toByteArray();
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
+ HalfPrecisionFloatDecoder decoder = getHalfPrecisionFloatDecoder(inputStream);
+ if (inputStream.read() == -1) { // to skip type byte
+ throw new CborException("unexpected end of stream");
+ }
+ HalfPrecisionFloat halfPrecisionFloat = decoder.decode(0);
+ return value == halfPrecisionFloat.getValue();
+ } catch (CborException cborException) {
+ return false;
+ }
+ }
+
+ protected HalfPrecisionFloatEncoder getHalfPrecisionFloatEncoder(OutputStream outputStream) {
+ return new HalfPrecisionFloatEncoder(null, outputStream);
+ }
+
+ protected HalfPrecisionFloatDecoder getHalfPrecisionFloatDecoder(InputStream inputStream) {
+ return new HalfPrecisionFloatDecoder(null, inputStream);
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/ArrayBuilder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/ArrayBuilder.java
new file mode 100644
index 0000000..40e3608
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/ArrayBuilder.java
@@ -0,0 +1,86 @@
+package de.cotech.hw.fido2.internal.cbor_java.builder;
+
+import de.cotech.hw.fido2.internal.cbor_java.model.Array;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+import de.cotech.hw.fido2.internal.cbor_java.model.Map;
+import de.cotech.hw.fido2.internal.cbor_java.model.SimpleValue;
+
+public class ArrayBuilder> extends
+ AbstractBuilder {
+
+ private final Array array;
+
+ public ArrayBuilder(T parent, Array array) {
+ super(parent);
+ this.array = array;
+ }
+
+ public ArrayBuilder add(DataItem dataItem) {
+ array.add(dataItem);
+ return this;
+ }
+
+ public ArrayBuilder add(long value) {
+ add(convert(value));
+ return this;
+ }
+
+ public ArrayBuilder add(boolean value) {
+ add(convert(value));
+ return this;
+ }
+
+ public ArrayBuilder add(float value) {
+ add(convert(value));
+ return this;
+ }
+
+ public ArrayBuilder add(double value) {
+ add(convert(value));
+ return this;
+ }
+
+ public ArrayBuilder add(byte[] bytes) {
+ add(convert(bytes));
+ return this;
+ }
+
+ public ArrayBuilder add(String string) {
+ add(convert(string));
+ return this;
+ }
+
+ public ArrayBuilder> addArray() {
+ Array nestedArray = new Array();
+ add(nestedArray);
+ return new ArrayBuilder>(this, nestedArray);
+ }
+
+ public ArrayBuilder> startArray() {
+ Array nestedArray = new Array();
+ nestedArray.setChunked(true);
+ add(nestedArray);
+ return new ArrayBuilder>(this, nestedArray);
+ }
+
+ public MapBuilder> addMap() {
+ Map nestedMap = new Map();
+ add(nestedMap);
+ return new MapBuilder>(this, nestedMap);
+ }
+
+ public MapBuilder> startMap() {
+ Map nestedMap = new Map();
+ nestedMap.setChunked(true);
+ add(nestedMap);
+ return new MapBuilder>(this, nestedMap);
+ }
+
+ public T end() {
+ if (array.isChunked()) {
+ add(SimpleValue.BREAK);
+ }
+ return getParent();
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/ByteStringBuilder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/ByteStringBuilder.java
new file mode 100644
index 0000000..1301271
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/ByteStringBuilder.java
@@ -0,0 +1,22 @@
+package de.cotech.hw.fido2.internal.cbor_java.builder;
+
+import de.cotech.hw.fido2.internal.cbor_java.model.SimpleValue;
+
+public class ByteStringBuilder> extends
+ AbstractBuilder {
+
+ public ByteStringBuilder(T parent) {
+ super(parent);
+ }
+
+ public ByteStringBuilder add(byte[] bytes) {
+ getParent().addChunk(convert(bytes));
+ return this;
+ }
+
+ public T end() {
+ getParent().addChunk(SimpleValue.BREAK);
+ return getParent();
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/MapBuilder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/MapBuilder.java
new file mode 100644
index 0000000..fbe1796
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/MapBuilder.java
@@ -0,0 +1,155 @@
+package de.cotech.hw.fido2.internal.cbor_java.builder;
+
+import de.cotech.hw.fido2.internal.cbor_java.model.Array;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+import de.cotech.hw.fido2.internal.cbor_java.model.Map;
+
+public class MapBuilder> extends
+ AbstractBuilder {
+
+ private final Map map;
+
+ public MapBuilder(T parent, Map map) {
+ super(parent);
+ this.map = map;
+ }
+
+ public MapBuilder put(DataItem key, DataItem value) {
+ map.put(key, value);
+ return this;
+ }
+
+ public MapBuilder put(long key, long value) {
+ put(convert(key), convert(value));
+ return this;
+ }
+
+ public MapBuilder put(long key, boolean value) {
+ put(convert(key), convert(value));
+ return this;
+ }
+
+ public MapBuilder put(long key, float value) {
+ put(convert(key), convert(value));
+ return this;
+ }
+
+ public MapBuilder put(long key, double value) {
+ put(convert(key), convert(value));
+ return this;
+ }
+
+ public MapBuilder put(long key, byte[] value) {
+ put(convert(key), convert(value));
+ return this;
+ }
+
+ public MapBuilder put(long key, String value) {
+ put(convert(key), convert(value));
+ return this;
+ }
+
+ public MapBuilder put(String key, long value) {
+ put(convert(key), convert(value));
+ return this;
+ }
+
+ public MapBuilder put(String key, boolean value) {
+ put(convert(key), convert(value));
+ return this;
+ }
+
+ public MapBuilder put(String key, float value) {
+ put(convert(key), convert(value));
+ return this;
+ }
+
+ public MapBuilder put(String key, double value) {
+ put(convert(key), convert(value));
+ return this;
+ }
+
+ public MapBuilder put(String key, byte[] value) {
+ map.put(convert(key), convert(value));
+ return this;
+ }
+
+ public MapBuilder put(String key, String value) {
+ put(convert(key), convert(value));
+ return this;
+ }
+
+ public ArrayBuilder> putArray(DataItem key) {
+ Array array = new Array();
+ put(key, array);
+ return new ArrayBuilder<>(this, array);
+ }
+
+ public ArrayBuilder> putArray(long key) {
+ Array array = new Array();
+ put(convert(key), array);
+ return new ArrayBuilder<>(this, array);
+ }
+
+ public ArrayBuilder> putArray(String key) {
+ Array array = new Array();
+ put(convert(key), array);
+ return new ArrayBuilder<>(this, array);
+ }
+
+ public ArrayBuilder> startArray(DataItem key) {
+ Array array = new Array();
+ array.setChunked(true);
+ put(key, array);
+ return new ArrayBuilder<>(this, array);
+ }
+
+ public ArrayBuilder> startArray(long key) {
+ return startArray(convert(key));
+ }
+
+ public ArrayBuilder> startArray(String key) {
+ Array array = new Array();
+ array.setChunked(true);
+ put(convert(key), array);
+ return new ArrayBuilder<>(this, array);
+ }
+
+ public MapBuilder> putMap(DataItem key) {
+ Map nestedMap = new Map();
+ put(key, nestedMap);
+ return new MapBuilder<>(this, nestedMap);
+ }
+
+ public MapBuilder> putMap(long key) {
+ Map nestedMap = new Map();
+ put(convert(key), nestedMap);
+ return new MapBuilder<>(this, nestedMap);
+ }
+
+ public MapBuilder> putMap(String key) {
+ Map nestedMap = new Map();
+ put(convert(key), nestedMap);
+ return new MapBuilder<>(this, nestedMap);
+ }
+
+ public MapBuilder> startMap(DataItem key) {
+ Map nestedMap = new Map();
+ nestedMap.setChunked(true);
+ put(key, nestedMap);
+ return new MapBuilder<>(this, nestedMap);
+ }
+
+ public MapBuilder> startMap(long key) {
+ return startMap(convert(key));
+ }
+
+ public MapBuilder> startMap(String key) {
+ return startMap(convert(key));
+ }
+
+ public T end() {
+ return getParent();
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/UnicodeStringBuilder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/UnicodeStringBuilder.java
new file mode 100644
index 0000000..88f2afb
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/builder/UnicodeStringBuilder.java
@@ -0,0 +1,22 @@
+package de.cotech.hw.fido2.internal.cbor_java.builder;
+
+import de.cotech.hw.fido2.internal.cbor_java.model.SimpleValue;
+
+public class UnicodeStringBuilder> extends
+ AbstractBuilder {
+
+ public UnicodeStringBuilder(T parent) {
+ super(parent);
+ }
+
+ public UnicodeStringBuilder add(String string) {
+ getParent().addChunk(convert(string));
+ return this;
+ }
+
+ public T end() {
+ getParent().addChunk(SimpleValue.BREAK);
+ return getParent();
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/AbstractDecoder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/AbstractDecoder.java
new file mode 100644
index 0000000..5847268
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/AbstractDecoder.java
@@ -0,0 +1,119 @@
+package de.cotech.hw.fido2.internal.cbor_java.decoder;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+
+import de.cotech.hw.fido2.internal.cbor_java.CborDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.CborException;
+import de.cotech.hw.fido2.internal.cbor_java.model.AdditionalInformation;
+
+public abstract class AbstractDecoder {
+
+ protected static final int INFINITY = -1;
+
+ protected final InputStream inputStream;
+ protected final CborDecoder decoder;
+
+ public AbstractDecoder(CborDecoder decoder, InputStream inputStream) {
+ this.decoder = decoder;
+ this.inputStream = inputStream;
+ }
+
+ public abstract T decode(int initialByte) throws CborException;
+
+ protected int nextSymbol() throws CborException {
+ try {
+ int symbol = inputStream.read();
+ if (symbol == -1) {
+ throw new IOException("Unexpected end of stream");
+ }
+ return symbol;
+ } catch (IOException ioException) {
+ throw new CborException(ioException);
+ }
+ }
+
+ protected long getLength(int initialByte) throws CborException {
+ switch (AdditionalInformation.ofByte(initialByte)) {
+ case DIRECT:
+ return initialByte & 31;
+ case ONE_BYTE:
+ return nextSymbol();
+ case TWO_BYTES:
+ long twoByteValue = 0;
+ twoByteValue |= nextSymbol() << 8;
+ twoByteValue |= nextSymbol() << 0;
+ return twoByteValue;
+ case FOUR_BYTES:
+ long fourByteValue = 0L;
+ fourByteValue |= (long) nextSymbol() << 24;
+ fourByteValue |= (long) nextSymbol() << 16;
+ fourByteValue |= (long) nextSymbol() << 8;
+ fourByteValue |= (long) nextSymbol() << 0;
+ return fourByteValue;
+ case EIGHT_BYTES:
+ long eightByteValue = 0;
+ eightByteValue |= (long) nextSymbol() << 56;
+ eightByteValue |= (long) nextSymbol() << 48;
+ eightByteValue |= (long) nextSymbol() << 40;
+ eightByteValue |= (long) nextSymbol() << 32;
+ eightByteValue |= (long) nextSymbol() << 24;
+ eightByteValue |= (long) nextSymbol() << 16;
+ eightByteValue |= (long) nextSymbol() << 8;
+ eightByteValue |= (long) nextSymbol() << 0;
+ return eightByteValue;
+ case INDEFINITE:
+ return INFINITY;
+ case RESERVED:
+ default:
+ throw new CborException("Reserved additional information");
+ }
+ }
+
+ protected BigInteger getLengthAsBigInteger(int initialByte)
+ throws CborException {
+ switch (AdditionalInformation.ofByte(initialByte)) {
+ case DIRECT:
+ return BigInteger.valueOf(initialByte & 31);
+ case ONE_BYTE:
+ return BigInteger.valueOf(nextSymbol());
+ case TWO_BYTES:
+ long twoByteValue = 0;
+ twoByteValue |= nextSymbol() << 8;
+ twoByteValue |= nextSymbol() << 0;
+ return BigInteger.valueOf(twoByteValue);
+ case FOUR_BYTES:
+ long fourByteValue = 0L;
+ fourByteValue |= (long) nextSymbol() << 24;
+ fourByteValue |= (long) nextSymbol() << 16;
+ fourByteValue |= (long) nextSymbol() << 8;
+ fourByteValue |= (long) nextSymbol() << 0;
+ return BigInteger.valueOf(fourByteValue);
+ case EIGHT_BYTES:
+ BigInteger eightByteValue = BigInteger.ZERO;
+ eightByteValue = eightByteValue.or(BigInteger.valueOf(nextSymbol())
+ .shiftLeft(56));
+ eightByteValue = eightByteValue.or(BigInteger.valueOf(nextSymbol())
+ .shiftLeft(48));
+ eightByteValue = eightByteValue.or(BigInteger.valueOf(nextSymbol())
+ .shiftLeft(40));
+ eightByteValue = eightByteValue.or(BigInteger.valueOf(nextSymbol())
+ .shiftLeft(32));
+ eightByteValue = eightByteValue.or(BigInteger.valueOf(nextSymbol())
+ .shiftLeft(24));
+ eightByteValue = eightByteValue.or(BigInteger.valueOf(nextSymbol())
+ .shiftLeft(16));
+ eightByteValue = eightByteValue.or(BigInteger.valueOf(nextSymbol())
+ .shiftLeft(8));
+ eightByteValue = eightByteValue.or(BigInteger.valueOf(nextSymbol())
+ .shiftLeft(0));
+ return eightByteValue;
+ case INDEFINITE:
+ return BigInteger.valueOf(INFINITY);
+ case RESERVED:
+ default:
+ throw new CborException("Reserved additional information");
+ }
+ }
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/ArrayDecoder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/ArrayDecoder.java
new file mode 100644
index 0000000..65a9c7c
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/ArrayDecoder.java
@@ -0,0 +1,59 @@
+package de.cotech.hw.fido2.internal.cbor_java.decoder;
+
+import java.io.InputStream;
+
+import de.cotech.hw.fido2.internal.cbor_java.CborDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.CborException;
+import de.cotech.hw.fido2.internal.cbor_java.model.Array;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+import de.cotech.hw.fido2.internal.cbor_java.model.Special;
+
+public class ArrayDecoder extends AbstractDecoder {
+
+ public ArrayDecoder(CborDecoder decoder, InputStream inputStream) {
+ super(decoder, inputStream);
+ }
+
+ @Override
+ public Array decode(int initialByte) throws CborException {
+ long length = getLength(initialByte);
+ if (length == INFINITY) {
+ return decodeInfinitiveLength();
+ } else {
+ return decodeFixedLength(length);
+ }
+ }
+
+ private Array decodeInfinitiveLength() throws CborException {
+ Array array = new Array();
+ array.setChunked(true);
+ if (decoder.isAutoDecodeInfinitiveArrays()) {
+ DataItem dataItem;
+ for (;;) {
+ dataItem = decoder.decodeNext();
+ if (dataItem == null) {
+ throw new CborException("Unexpected end of stream");
+ }
+ if (Special.BREAK.equals(dataItem)) {
+ array.add(Special.BREAK);
+ break;
+ }
+ array.add(dataItem);
+ }
+ }
+ return array;
+ }
+
+ private Array decodeFixedLength(long length) throws CborException {
+ Array array = new Array();
+ for (long i = 0; i < length; i++) {
+ DataItem dataItem = decoder.decodeNext();
+ if (dataItem == null) {
+ throw new CborException("Unexpected end of stream");
+ }
+ array.add(dataItem);
+ }
+ return array;
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/ByteStringDecoder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/ByteStringDecoder.java
new file mode 100644
index 0000000..84c9075
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/ByteStringDecoder.java
@@ -0,0 +1,67 @@
+package de.cotech.hw.fido2.internal.cbor_java.decoder;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import de.cotech.hw.fido2.internal.cbor_java.CborDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.CborException;
+import de.cotech.hw.fido2.internal.cbor_java.model.ByteString;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+import de.cotech.hw.fido2.internal.cbor_java.model.MajorType;
+import de.cotech.hw.fido2.internal.cbor_java.model.Special;
+
+public class ByteStringDecoder extends AbstractDecoder {
+
+ public ByteStringDecoder(CborDecoder decoder, InputStream inputStream) {
+ super(decoder, inputStream);
+ }
+
+ @Override
+ public ByteString decode(int initialByte) throws CborException {
+ long length = getLength(initialByte);
+ if (length == INFINITY) {
+ if (decoder.isAutoDecodeInfinitiveByteStrings()) {
+ return decodeInfinitiveLength();
+ } else {
+ ByteString byteString = new ByteString(null);
+ byteString.setChunked(true);
+ return byteString;
+ }
+ } else {
+ return decodeFixedLength(length);
+ }
+ }
+
+ private ByteString decodeInfinitiveLength() throws CborException {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ for (;;) {
+ DataItem dataItem = decoder.decodeNext();
+ if (dataItem == null) {
+ throw new CborException("Unexpected end of stream");
+ }
+ MajorType majorType = dataItem.getMajorType();
+ if (Special.BREAK.equals(dataItem)) {
+ break;
+ } else if (majorType == MajorType.BYTE_STRING) {
+ ByteString byteString = (ByteString) dataItem;
+ byte[] byteArray = byteString.getBytes();
+ if (byteArray != null) {
+ bytes.write(byteArray, 0, byteArray.length);
+ }
+ } else {
+ throw new CborException("Unexpected major type "
+ + majorType);
+ }
+ }
+ return new ByteString(bytes.toByteArray());
+ }
+
+ private ByteString decodeFixedLength(long length) throws CborException {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream((int) length);
+ for (long i = 0; i < length; i++) {
+ bytes.write(nextSymbol());
+ }
+ return new ByteString(bytes.toByteArray());
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/DoublePrecisionFloatDecoder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/DoublePrecisionFloatDecoder.java
new file mode 100644
index 0000000..8618a19
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/DoublePrecisionFloatDecoder.java
@@ -0,0 +1,38 @@
+package de.cotech.hw.fido2.internal.cbor_java.decoder;
+
+import java.io.InputStream;
+
+import de.cotech.hw.fido2.internal.cbor_java.CborDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.CborException;
+import de.cotech.hw.fido2.internal.cbor_java.model.DoublePrecisionFloat;
+
+public class DoublePrecisionFloatDecoder extends
+ AbstractDecoder {
+
+ public DoublePrecisionFloatDecoder(CborDecoder decoder,
+ InputStream inputStream) {
+ super(decoder, inputStream);
+ }
+
+ @Override
+ public DoublePrecisionFloat decode(int initialByte) throws CborException {
+ long bits = 0;
+ bits |= nextSymbol() & 0xFF;
+ bits <<= 8;
+ bits |= nextSymbol() & 0xFF;
+ bits <<= 8;
+ bits |= nextSymbol() & 0xFF;
+ bits <<= 8;
+ bits |= nextSymbol() & 0xFF;
+ bits <<= 8;
+ bits |= nextSymbol() & 0xFF;
+ bits <<= 8;
+ bits |= nextSymbol() & 0xFF;
+ bits <<= 8;
+ bits |= nextSymbol() & 0xFF;
+ bits <<= 8;
+ bits |= nextSymbol() & 0xFF;
+ return new DoublePrecisionFloat(Double.longBitsToDouble(bits));
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/HalfPrecisionFloatDecoder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/HalfPrecisionFloatDecoder.java
new file mode 100644
index 0000000..e1f5aa6
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/HalfPrecisionFloatDecoder.java
@@ -0,0 +1,43 @@
+package de.cotech.hw.fido2.internal.cbor_java.decoder;
+
+import java.io.InputStream;
+
+import de.cotech.hw.fido2.internal.cbor_java.CborDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.CborException;
+import de.cotech.hw.fido2.internal.cbor_java.model.HalfPrecisionFloat;
+
+public class HalfPrecisionFloatDecoder extends
+ AbstractDecoder {
+
+ public HalfPrecisionFloatDecoder(CborDecoder decoder,
+ InputStream inputStream) {
+ super(decoder, inputStream);
+ }
+
+ @Override
+ public HalfPrecisionFloat decode(int initialByte) throws CborException {
+ int bits = nextSymbol() << 8 | nextSymbol();
+ return new HalfPrecisionFloat(toFloat(bits));
+ }
+
+ /**
+ * @see http://stackoverflow.com/a/5684578
+ */
+ private static float toFloat(int bits) {
+ int s = (bits & 0x8000) >> 15;
+ int e = (bits & 0x7C00) >> 10;
+ int f = bits & 0x03FF;
+
+ if (e == 0) {
+ return (float) ((s != 0 ? -1 : 1) * Math.pow(2, -14) * (f / Math
+ .pow(2, 10)));
+ } else if (e == 0x1F) {
+ return f != 0 ? Float.NaN
+ : (s != 0 ? -1 : 1) * Float.POSITIVE_INFINITY;
+ }
+
+ return (float) ((s != 0 ? -1 : 1) * Math.pow(2, e - 15) * (1 + f / Math
+ .pow(2, 10)));
+ }
+
+}
diff --git a/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/MapDecoder.java b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/MapDecoder.java
new file mode 100644
index 0000000..5d3d6ef
--- /dev/null
+++ b/hwsecurity/fido2/src/main/java/de/cotech/hw/fido2/internal/cbor_java/decoder/MapDecoder.java
@@ -0,0 +1,65 @@
+package de.cotech.hw.fido2.internal.cbor_java.decoder;
+
+import java.io.InputStream;
+
+import de.cotech.hw.fido2.internal.cbor_java.CborDecoder;
+import de.cotech.hw.fido2.internal.cbor_java.CborException;
+import de.cotech.hw.fido2.internal.cbor_java.model.DataItem;
+import de.cotech.hw.fido2.internal.cbor_java.model.Map;
+import de.cotech.hw.fido2.internal.cbor_java.model.Special;
+
+public class MapDecoder extends AbstractDecoder