openwallet-foundation-labs / identity-credential

Apache License 2.0
156 stars 80 forks source link

Support other kinds of keystores #269

Closed davidz25 closed 1 year ago

davidz25 commented 1 year ago

Currently we use Hardware-Backed Android KeyStore for CredentialKey and DeviceKey which means that the private part of those keys never leave Secure Hardware. Concretely the Secure Hardware is where KeyMint (KeyMaster in older Android versions) is running and this is typically the Trusted Execution Environment but could also be something else such as a Secure Element. This approach provides a number of security guarantees and is consistent with NOTE 1 of ISO/IEC 18013-5:2021. Crucially this feature is available on almost all Android phones today.

In some cases however it is desirable to use another Secure Area for managing keys so it would be useful to have a small abstraction for how we use EC keys, for example

public interface KeystoreImplementation {
  // Creates CredentialKey, returns platform-specific attestation 
  //
  // With Android KeyStore, an attestation according to
  // https://source.android.com/docs/security/features/keystore/attestation#key-attestation
  // is returned and the given challenge is included in the Android-specific X.509 extension.
  //
  X509Certificate[] createCredentialKey(String alias, byte[] challenge, int curve);
  void deleteCredentialKey(String alias);
  byte[] ecdsaSignWithCredentialKey(String alias, byte[] dataToSign, int algorithm);  // returns DER encoded signature

  // Creates DeviceKey which can be used in MSO, returns a X509 certificate signed by CredentialKey
  //
  X509Certificate createDeviceKey(String alias, String credentialKeyAlias, int curve);
  void deleteDeviceKey(String alias);
  byte[] ecdsaSignWithDeviceKey(String alias, byte[] dataToSign, int algorithm);  // returns DER encoded signature

}

It's possible that each implementation needs user interaction for unlocking a key, this can be done by concrete implementations. For example the implementation for Android KeyStore would be

public class AndroidKeystoreKeystoreImplementation extends KeystoreImplementation {
  // Creates a new KeystoreImplementation using Android Keystore
  //
  AndroidKeystoreKeystoreImplementation(boolean useStrongBox, KeyNeedsUnlockingListener unlockListener) { ... }

  // <snip implementation of interface>

  public interface KeyNeedsUnlockingListener {
    // Called when a key needs unlocking
    void keyNeedsUnlocking(CryptoObject crytptoObject);
  }
}

The application would then construct an IdentityCredentialStore by passing in a KeystoreImplementation object. Our reference apps would use AndroidKeystoreKeystoreImplementation and set up a listener for unlocking keys.

You could also have SoftwareBackedKeystoreImplementation which would allow to backup/restore keys or it could run in a non-Android environment (useful for testing etc.)

Or you could have MyOwnTrustedAppKeystoreImplementation which implements the interface by communicating with e.g. a JavaCard applet on a Secure Element using OMAPI. Such an implementation could have its own listener that could be used to ask the use for a PIN for unlocking which could then be sent to the Secure Environment.

We'd need to change the core IdentityCredential API a little bit (today it exposes CryptoObject directly, we'd have to change that) to make this work but I don't think it would be a very big change and we've been wanting to do that anyway.

davidz25 commented 1 year ago

Btw, the https://github.com/google/identity-credential/tree/keystore-abstraction branch contains work towards this feature. It's not quite ready for review or finished but the bulk of the credential storage and keystore abstraction work is there.

davidz25 commented 1 year ago

The work in the branch is almost complete but it's too big to land as a single PR so going to land this in pieces. PR #288 is the first part which introduces storage and keystore abstractions.

davidz25 commented 1 year ago

Been making a lot of progress here, the chunk of functionality is landing as PR #297. After that, just a couple of PRs for presentation using this new store (code is already written but needs PR #293 to land first) and migration from the old credential store (apps will be able to safely migrate devices in the field to use this).