Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Implement HMAC-Secret extension #5

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

BryanJacobs
Copy link

@BryanJacobs BryanJacobs commented Sep 3, 2022

This adds the ability for Android apps (not web pages) to use the
hwsecurity library to:

  • register credentials with the FIDO2 hmac-secret extension enabled
  • pass salts in to authenticators with getAssertion calls
  • retrieve decrypted results back from the authenticator

It also lays the basic groundwork for supporting PIN Protocol 2,
defined as a mandatory part of FIDO2 CTAP2.1.

Example usage for create:

        val extensionParams = listOf(
            ExtensionParameter.create(
                Common.HMAC_SECRET_EXTENSION,
                BooleanExtensionParameterValue.create(true)
            )
        )

        val registerRequest = PublicKeyCredentialCreationOptions.create(
            [...],
            extensionParams
        )

Example usage for getAssertion:

private fun showAuthenticateDialog(hmacSalt1: ByteArray, hmacSalt2: ByteArray?) {
        val extensionParams = if (hmacSalts == null) null else listOf(
            ExtensionParameter.create(
                Common.HMAC_SECRET_EXTENSION,
                HmacSecretExtensionParameterValue.create(
                    hmacSalt1,
                    hmacSalt2
                )
            )
        )

        val authenticateRequest = PublicKeyCredentialRequestOptions.create(
           [...],
            extensionParams
        )

[...]

    override fun onGetAssertionResponse(publicKeyCredential: PublicKeyCredential) {
        val res = publicKeyCredential.response() as AuthenticatorAssertionResponse
        
        val hmacSecretReply: ByteArray = res.hmacSecretData()
    }

This adds the ability for Android apps (not web pages) to use the
hwsecurity library to:

- register credentials with the FIDO2 hmac-secret extension enabled
- pass salts in to authenticators with getAssertion calls
- retrieve decrypted results back from the authenticator

It also lays the basic groundwork for supporting PIN Protocol 2,
defined as a mandatory part of FIDO2 CTAP2.1.
@@ -27,5 +27,5 @@ allprojects {

ext {
compileSdkVersion = 29
hwSdkVersionName = '4.4.0'
hwSdkVersionName = '4.5.0'
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change doesn't break any non-"internal" classes, per se, but it does add fields and features.

import com.google.auto.value.AutoValue;

@AutoValue
public abstract class ExtensionParameter implements Parcelable {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class (and change) lays the groundwork for supporting other extensions such as CredProtect, mandatory in CTAP2.1 and quite desirable for an application using resident keys.

@SuppressWarnings("mutable")
public abstract byte[] salt2();

public static HmacSecretExtensionParameterValue create(byte[] salt1, @Nullable byte[] salt2) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the unencrypted values so they must always be 32 bytes long - even when PIN Protocol 2 will add a 16-byte IV to the front, that happens around these values and not within them.

@@ -69,7 +70,7 @@ public abstract class AuthenticatorData {
// 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
@SuppressWarnings("mutable")
public abstract byte[] extensions();
public abstract Map extensions();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not useful to have the extensions type (never actually exposed in app-visible objects) be a byte array because some of its contents depend on authenticator state objects like the shared secret :-(.

// Handle parameters which require assertion-specific data here
Object value = extensionParameters.value();
if (value instanceof HmacSecretExtensionParameterValue) {
HmacSecretExtensionParameterValue param = (HmacSecretExtensionParameterValue) value;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Significant change here: serialize hmac-secret extension parameters to the CTAP2 CBOR request.

throw new IOException("hmac-secret response is not a byte string");
}
byte[] hmacSecretBytes = ((ByteString) hmacSecretValue).getBytes();
decryptedHmacSecretValue = authenticatorGetAssertion.pinToken().decrypt(hmacSecretBytes);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return side of the hmac-secret getAssertion flow, and the reason why I did all this wiring of the PinToken: the hmac-secret response value must be decrypted using the platform<->authenticator shared secret in order to be useful.

That's done here in the response factory.


import de.cotech.hw.fido2.internal.Fido2AppletConnection;

public interface PinProtocol {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes here aren't, strictly speaking, necessary for hmac-secret support. But if you ever want to implement the vastly superior CTAP2.1 PIN Protocol 2, you're going to need changes like these to do so.

I've used the CTAP2.1 terminology for the PIN operations - authenticate, encrypt, decrypt - instead of the implementation-specific ones that were here before.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant