This library provides a FIDO2 roaming authenticator and CTAP1/2 implementation for Android devices. It leverages modern Android OS security concepts to provide easy support for resident keys with strong authentication via biometric user verification or clientPIN functionality compliant with the FIDO2 specification, and so, legacy U2F specification.
The library contains 2 main components, i.e. the Authenticator
class and the TransactionManager
. The Authenticator
implements the authenticator main logic and functionality and can be used directly in any app via the utilities and auxiliary classes existent within the library.
The TransactionManager
role is to provide an easy way to bind diverse transport layers to the Authenticator
and handle the CTAP messages and handshakes between a remote client and the authenticator. Currently the TransactionManager
is focused on handling well the HID CTAP protocol. The physical transport of this can be implemented either via USB connections or the Bluetooth HID device profile and this is up to the calling applications to decide, configure and setup.
An example in this sense is for instance the WioKey application which uses the Bluetooth HID device profile available in Android 9.0+ for the communication. We recommend the latter version of implementing HID for an improved user experience and easy interaction support with major desktop platforms (Windows 10, macOS, Linux) out of the box.
For security, the resident keys are protected by default by the Android KeyStore and strong authentication via AndroidX BiometricPrompt or clientPIN user verfication. Jump to security for more details.
You can use JitPack to include this module in your Android project, or you can include the source code.
Add this in your root build.gradle:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Add this to your dependencies list:
dependencies {
implementation 'com.github.wiosense:rauth-android:master-SNAPSHOT'
}
$ cd ~/your/project/src/directory
$ git clone https://github.com/wiosense/rauth-android.git
In Android Studio: File -> New -> Import Module
and then point it at the rauth-android
directory.
In Android Studio: File -> Project Structure -> App -> Dependencies -> + -> Module Dependency
Select the roaming-authenticator
module. After a Gradle sync, you should be able to use the de.wiosense.webauthn
package.
The Authenticator
class implements the operations described in the Authenticator API section of Client To Authenticator Protocol (CTAP), and also provides APIs to manage the credentials that are contained in the Android Keystore.
You must first instantiate an authenticator object, which is safe to instantiate multiple times.
//Authenticator(Context ctx, boolean strongboxRequired)
Authenticator roamingAuthenticator = new Authenticator(appContext, true);
Here the strongboxRequired flag chooses whether to enforce the usage of a StrongBox Keymaster HSM. Not all phones support this, but you can easily check at runtime by calling:
boolean hasStrongbox = currentContext.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE);
It's important to note that all the following calls are blocking unless stated and shouldn't be executed in the UI thread of the app. Also, from this point on, all mentions to CBOR objects are related to this CBOR implementation.
You can poll the internal state and capabilities of the authenticator by calling the Authenticator.getInfo()
method. This returns a GetInfoResult
object that can be put into CBOR formatted byte array by calling GetInfoResult.asCBOR
.
You can create a new credential by passing a MakeCredentialOptions
object to the Authenticator.makeCredential
method. Or alternatively, you can also pass a Map
CBOR object that is formatted according to the CTAP spec, this is usually handled by the internals of the TransactionManager
.
This will return an AttestationObject
which you can inspect and retrieve a byte array formatter CBOR like so:
byte[] attestation = attestationObject.asCBOR();
The returned object can be of any of three subclasses depending on the options that were passed to Authenticator.makeCredentials
:
NoneAttestation
PackedBasicAttestation
PackedSelfAttestation
You can make an authentication request by passing a GetAssertionOptions
object to the Authenticator.getAssertion
method. Or alternatively, you can also pass a Map
CBOR object that is formatted according to the CTAP spec, this is usually handleded by the internals of the TransactionManager
.
This will return an AssertionObject
which you can inspect and retrieve a byte array formatter CBOR like so:
byte[] assertion = assertionObject.asCBOR();
The Authenticator.getPinResult
method can be called with a ClientPINOptions
object. However, it is important to point out that this particular method has a series of subcommands that internally are handled differently, but all return the same ClientPINResult
object that can be serialized like the previous results objects. This command is usually used as a handshake, which needs to be handled by the user of the authenticator.
Apart from using the CTAP2 method to manage the PIN, the following APIs are provided for internal management:
Authenticator.selfSetPin
which can set a PIN or override a previously set one after user verificationAuthenticator.resetPin
which gracefully removes a previously set PIN after user verificationAuthenticator.isPinSet
which returns a boolean specifying whether a PIN is currently set or not.
Authenticator.getAllCredentials
returns a list of all the credentials that are currently stored in the authenticator, as well as their relying parties and other relevant information.
Authenticator.deleteCredential
deletes the credential object PublicKeyCredentialSource
that is passed to it, after user verification.
Authenticator.deleteAllCredentials
deletes the credential database after user verification.
Authenticator.resetAuthenticator
can be called to completely wipe the credential database as well as the pin, after user verification.
The TransactionManager
class oversees all communications going over the Bluetooth connection and is in charge of translating and formatting incoming/outgoing messages in the CTAP1/2 protocols so they can be interpreted by the Authenticator
class.
The transaction manager needs to be instantiated with a valid Authenticator
object and a valid activity to pass events like biometric requests to the UI:
//TransactionManager(FragmentActivity activity, Authenticator authenticator)
TransactionManager transactionManager = new TransactionManager(currentActivity,
roamingAuthenticator);
To service the messages coming from the Bluetooth connection, the transanction manager needs to be called from the BluetoothHidDevice.Callback
that is registered to the inputHost
, which can be done in the following manner:
BluetoothHidDevice inputHost;
/*
* Perform application registration and initialization
*/
BluetoothHidDevice.Callback callback = new BluetoothHidDevice.Callback() {
/*
* Override other methods
*/
@Override
public void onInterruptData(BluetoothDevice device , byte reportId, byte[] data) {
super.onInterruptData(device, reportId, data);
transactionManager.handleReport(data, (rawReports) -> {
for (byte[] report : rawReports) {
inputHost.sendReport(device, reportId, report);
}
})
}
}
NOTE: In cases where the current activity is recreated, you must update the internal TransactionManager activity by calling transactionManager.updateActivity(newActivity)
.
The application is open source is released under the terms of BSD3 license.
Some parts of the code were modified from other open source projects (see INFO), being marked with their original license terms and copyright information.
This library aims to provide a software replacement of hardware security keys by leveraging the modern hardware and security components of Android OS and phones. To this extent all sensitive data is stored on the phone in dedicated secured components and no sensitive information (private keys, user account information) is exchanged with any 3rd parties. It is meant to provide a core authenticator component for FIDO2 strong authentication via roaming authenticators on Android.
If you identify any security problems or vulnerabilities within this library, please contact vulnerability@wiosense.de with a security disclosure and report of the identified issues.
We practice a fortnight (2 weeks) security disclosure policy, time in which we will try to address the problems and provide a fix. Therefore we kindly ask you to delay any planned public vulnerability disclosure either until 2 weeks have passed or a fix has been issued to allow for this process to take place.
Contributions are of course welcome!
The authenticator operations described earlier handled within the TransactionManager
are by default protected by strong authentication enforced via screen lock mechanisms (e.g. PIN, pattern, Fingerprint, Face Unlock). It is the responsibility of the caller to make sure such settings exist to enable the authenticator functionality. The credentials database and the client PIN locker are authenticator resident by default and leverage the modern Android OS security hardware capabilities storing the sensitive key materials by default either within a Trusted Execution Environment (TEE) or a dedicated hardware Secure Element (SE) which Android OS asserts to harden and prevent the extraction of the key materials. For a quick distinction between the two check this article out.