diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp index 7298c7df44..14aef8ee40 100644 --- a/identity/aidl/Android.bp +++ b/identity/aidl/Android.bp @@ -18,5 +18,8 @@ aidl_interface { }, }, }, - versions: ["1"], + versions: [ + "1", + "2", + ], } diff --git a/identity/aidl/aidl_api/android.hardware.identity/2/.hash b/identity/aidl/aidl_api/android.hardware.identity/2/.hash new file mode 100644 index 0000000000..036ce8425a --- /dev/null +++ b/identity/aidl/aidl_api/android.hardware.identity/2/.hash @@ -0,0 +1 @@ +194e04be642728623d65ec8321a3764fdea52ae0 diff --git a/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/Certificate.aidl b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/Certificate.aidl new file mode 100644 index 0000000000..7e3002d70a --- /dev/null +++ b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/Certificate.aidl @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.identity; +@VintfStability +parcelable Certificate { + byte[] encodedCertificate; +} diff --git a/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/CipherSuite.aidl b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/CipherSuite.aidl new file mode 100644 index 0000000000..447203faa6 --- /dev/null +++ b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/CipherSuite.aidl @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.identity; +@Backing(type="int") @VintfStability +enum CipherSuite { + CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1, +} diff --git a/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/HardwareInformation.aidl b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/HardwareInformation.aidl new file mode 100644 index 0000000000..e1296e05e8 --- /dev/null +++ b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/HardwareInformation.aidl @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.identity; +@VintfStability +parcelable HardwareInformation { + @utf8InCpp String credentialStoreName; + @utf8InCpp String credentialStoreAuthorName; + int dataChunkSize; + boolean isDirectAccess; + @utf8InCpp String[] supportedDocTypes; +} diff --git a/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/IIdentityCredential.aidl new file mode 100644 index 0000000000..88104d9577 --- /dev/null +++ b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/IIdentityCredential.aidl @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.identity; +@VintfStability +interface IIdentityCredential { + byte[] deleteCredential(); + byte[] createEphemeralKeyPair(); + void setReaderEphemeralPublicKey(in byte[] publicKey); + long createAuthChallenge(); + void startRetrieval(in android.hardware.identity.SecureAccessControlProfile[] accessControlProfiles, in android.hardware.keymaster.HardwareAuthToken authToken, in byte[] itemsRequest, in byte[] signingKeyBlob, in byte[] sessionTranscript, in byte[] readerSignature, in int[] requestCounts); + void startRetrieveEntryValue(in @utf8InCpp String nameSpace, in @utf8InCpp String name, in int entrySize, in int[] accessControlProfileIds); + byte[] retrieveEntryValue(in byte[] encryptedContent); + void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces); + android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob); + void setRequestedNamespaces(in android.hardware.identity.RequestNamespace[] requestNamespaces); + void setVerificationToken(in android.hardware.keymaster.VerificationToken verificationToken); +} diff --git a/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/IIdentityCredentialStore.aidl b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/IIdentityCredentialStore.aidl new file mode 100644 index 0000000000..5dafb76d1c --- /dev/null +++ b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/IIdentityCredentialStore.aidl @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.identity; +@VintfStability +interface IIdentityCredentialStore { + android.hardware.identity.HardwareInformation getHardwareInformation(); + android.hardware.identity.IWritableIdentityCredential createCredential(in @utf8InCpp String docType, in boolean testCredential); + android.hardware.identity.IIdentityCredential getCredential(in android.hardware.identity.CipherSuite cipherSuite, in byte[] credentialData); + const int STATUS_OK = 0; + const int STATUS_FAILED = 1; + const int STATUS_CIPHER_SUITE_NOT_SUPPORTED = 2; + const int STATUS_INVALID_DATA = 3; + const int STATUS_INVALID_AUTH_TOKEN = 4; + const int STATUS_INVALID_ITEMS_REQUEST_MESSAGE = 5; + const int STATUS_READER_SIGNATURE_CHECK_FAILED = 6; + const int STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 7; + const int STATUS_USER_AUTHENTICATION_FAILED = 8; + const int STATUS_READER_AUTHENTICATION_FAILED = 9; + const int STATUS_NO_ACCESS_CONTROL_PROFILES = 10; + const int STATUS_NOT_IN_REQUEST_MESSAGE = 11; + const int STATUS_SESSION_TRANSCRIPT_MISMATCH = 12; +} diff --git a/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/IWritableIdentityCredential.aidl new file mode 100644 index 0000000000..c5ac9d6340 --- /dev/null +++ b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/IWritableIdentityCredential.aidl @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.identity; +@VintfStability +interface IWritableIdentityCredential { + android.hardware.identity.Certificate[] getAttestationCertificate(in byte[] attestationApplicationId, in byte[] attestationChallenge); + void startPersonalization(in int accessControlProfileCount, in int[] entryCounts); + android.hardware.identity.SecureAccessControlProfile addAccessControlProfile(in int id, in android.hardware.identity.Certificate readerCertificate, in boolean userAuthenticationRequired, in long timeoutMillis, in long secureUserId); + void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace, in @utf8InCpp String name, in int entrySize); + byte[] addEntryValue(in byte[] content); + void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature); + void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize); +} diff --git a/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/RequestDataItem.aidl b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/RequestDataItem.aidl new file mode 100644 index 0000000000..24ec26afdb --- /dev/null +++ b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/RequestDataItem.aidl @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.identity; +@VintfStability +parcelable RequestDataItem { + @utf8InCpp String name; + long size; + int[] accessControlProfileIds; +} diff --git a/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/RequestNamespace.aidl b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/RequestNamespace.aidl new file mode 100644 index 0000000000..af00f3bb62 --- /dev/null +++ b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/RequestNamespace.aidl @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.identity; +@VintfStability +parcelable RequestNamespace { + @utf8InCpp String namespaceName; + android.hardware.identity.RequestDataItem[] items; +} diff --git a/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/SecureAccessControlProfile.aidl b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/SecureAccessControlProfile.aidl new file mode 100644 index 0000000000..dfc1ad0681 --- /dev/null +++ b/identity/aidl/aidl_api/android.hardware.identity/2/android/hardware/identity/SecureAccessControlProfile.aidl @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.identity; +@VintfStability +parcelable SecureAccessControlProfile { + int id; + android.hardware.identity.Certificate readerCertificate; + boolean userAuthenticationRequired; + long timeoutMillis; + long secureUserId; + byte[] mac; +} diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl index 58b90b54be..88104d9577 100644 --- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl +++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl @@ -27,4 +27,6 @@ interface IIdentityCredential { byte[] retrieveEntryValue(in byte[] encryptedContent); void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces); android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob); + void setRequestedNamespaces(in android.hardware.identity.RequestNamespace[] requestNamespaces); + void setVerificationToken(in android.hardware.keymaster.VerificationToken verificationToken); } diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl index 32f283cc18..c5ac9d6340 100644 --- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl +++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl @@ -24,4 +24,5 @@ interface IWritableIdentityCredential { void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace, in @utf8InCpp String name, in int entrySize); byte[] addEntryValue(in byte[] content); void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature); + void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize); } diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestDataItem.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestDataItem.aidl new file mode 100644 index 0000000000..24ec26afdb --- /dev/null +++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestDataItem.aidl @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.identity; +@VintfStability +parcelable RequestDataItem { + @utf8InCpp String name; + long size; + int[] accessControlProfileIds; +} diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestNamespace.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestNamespace.aidl new file mode 100644 index 0000000000..af00f3bb62 --- /dev/null +++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestNamespace.aidl @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.identity; +@VintfStability +parcelable RequestNamespace { + @utf8InCpp String namespaceName; + android.hardware.identity.RequestDataItem[] items; +} diff --git a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl index 7d14f03b1f..d7f47e82c0 100644 --- a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl +++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl @@ -17,8 +17,10 @@ package android.hardware.identity; import android.hardware.identity.Certificate; +import android.hardware.identity.RequestNamespace; import android.hardware.identity.SecureAccessControlProfile; import android.hardware.keymaster.HardwareAuthToken; +import android.hardware.keymaster.VerificationToken; @VintfStability interface IIdentityCredential { @@ -70,10 +72,11 @@ interface IIdentityCredential { /** * Creates a challenge value to be used for proving successful user authentication. This - * is included in the authToken passed to the startRetrieval() method. + * is included in the authToken passed to the startRetrieval() method and the + * verificationToken passed to the setVerificationToken() method. * * This method may only be called once per instance. If called more than once, STATUS_FAILED - * will be returned. + * will be returned. If user authentication is not needed, this method may not be called. * * @return challenge, a non-zero number. */ @@ -82,6 +85,9 @@ interface IIdentityCredential { /** * Start an entry retrieval process. * + * The setRequestedNamespaces() and setVerificationToken() methods will be called before + * this method is called. + * * This method be called after createEphemeralKeyPair(), setReaderEphemeralPublicKey(), * createAuthChallenge() and before startRetrieveEntry(). This method call is followed by * multiple calls of startRetrieveEntryValue(), retrieveEntryValue(), and finally @@ -93,7 +99,19 @@ interface IIdentityCredential { * must be identical for each startRetrieval() invocation. If this is not the case, this call * fails with the STATUS_SESSION_TRANSCRIPT_MISMATCH error. * - * If the provided authToken is not valid this method fails with STATUS_INVALID_AUTH_TOKEN. + * If either authToken or verificationToken (as passed with setVerificationToken()) + * is not valid this method fails with STATUS_INVALID_AUTH_TOKEN. Note that valid tokens + * are only passed if they are actually needed and available (this can be detected by + * the timestamp being set to zero). For example, if no data items with access control + * profiles using user authentication are requested, the tokens are not filled in. + * It's also possible that no usable auth token is actually available (it could be the user + * never unlocked the device within the timeouts in the access control profiles) and + * in this case the tokens aren't filled in either. + * + * For test credentials (identified by the testCredential boolean in the CredentialData + * CBOR created at provisioning time), the |mac| field in both the authToken and + * verificationToken should not be checked against the shared HMAC key (see IKeyMasterDevice + * for details). This is to enable VTS tests to check for correct behavior. * * Each of the provided accessControlProfiles is checked in this call. If they are not * all valid, the call fails with STATUS_INVALID_DATA. @@ -176,7 +194,8 @@ interface IIdentityCredential { * * @param authToken * The authentication token that proves the user was authenticated, as required - * by one or more of the provided accessControlProfiles. See above. + * by one or more of the provided accessControlProfiles. This token is only valid + * if the timestamp field is non-zero. See above. * * @param itemsRequest * If non-empty, contains request data that is signed by the reader. See above. @@ -274,7 +293,7 @@ interface IIdentityCredential { * "DeviceAuthentication", * SessionTranscript, * DocType, - * DeviceNameSpaceBytes, + * DeviceNameSpacesBytes, * ] * * DocType = tstr @@ -343,4 +362,25 @@ interface IIdentityCredential { * @return an X.509 certificate for the new signing key, signed by the credential key. */ Certificate generateSigningKeyPair(out byte[] signingKeyBlob); + + /** + * Sets the namespaces and data items (including their size and access control profiles) + * which will be requested. This method must be called before startRetrieval() is called. + * + * This information is provided to make it possible for a HAL implementation to + * incrementally build up cryptographically authenticated data which includes the + * DeviceNameSpaces CBOR. + * + * @param requestNamespaces Namespaces and data items which will be requested. + */ + void setRequestedNamespaces(in RequestNamespace[] requestNamespaces); + + /** + * Sets the VerificationToken. This method must be called before startRetrieval() is + * called. This token uses the same challenge as returned by createAuthChallenge(). + * + * @param verificationToken + * The verification token. This token is only valid if the timestamp field is non-zero. + */ + void setVerificationToken(in VerificationToken verificationToken); } diff --git a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl index 07486e6001..b7ad283bf8 100644 --- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl +++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl @@ -120,6 +120,8 @@ interface IWritableIdentityCredential { * * startPersonalization must not be called more than once. * + * The setExpectedProofOfProvisioningSize() method will be called before this method. + * * @param accessControlProfileCount specifies the number of access control profiles that will * be provisioned with addAccessControlProfile(). * @@ -288,4 +290,16 @@ interface IWritableIdentityCredential { */ void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature); + + /** + * Sets the expected size of the ProofOfProvisioning returned by finishAddingEntries(). This + * method must be called before startPersonalization() is called. + * + * This information is provided to make it possible for a HAL implementation to + * incrementally build up cryptographically authenticated data which includes the + * ProofOfProvisioning CBOR. + * + * @param expectedProofOfProvisioningSize the expected size of ProofOfProvisioning. + */ + void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize); } diff --git a/identity/aidl/android/hardware/identity/RequestDataItem.aidl b/identity/aidl/android/hardware/identity/RequestDataItem.aidl new file mode 100644 index 0000000000..05bc7624c9 --- /dev/null +++ b/identity/aidl/android/hardware/identity/RequestDataItem.aidl @@ -0,0 +1,38 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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. + */ + +package android.hardware.identity; + +@VintfStability +parcelable RequestDataItem { + /** + * The data item name being requested, for example "driving_privileges". + */ + @utf8InCpp String name; + + /** + * The size of the data item value. + * + * Data item values are always encoded as CBOR so this is the length of + * the CBOR encoding of the value. + */ + long size; + + /** + * The access control profile ids this data item is configured with. + */ + int[] accessControlProfileIds; +} diff --git a/identity/aidl/android/hardware/identity/RequestNamespace.aidl b/identity/aidl/android/hardware/identity/RequestNamespace.aidl new file mode 100644 index 0000000000..4d615067e3 --- /dev/null +++ b/identity/aidl/android/hardware/identity/RequestNamespace.aidl @@ -0,0 +1,33 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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. + */ + +package android.hardware.identity; + +import android.hardware.identity.RequestDataItem; + +@VintfStability +parcelable RequestNamespace { + /** + * The name of the namespace that items are being requested from, for + * example "org.iso.18013.5.1". + */ + @utf8InCpp String namespaceName; + + /** + * The data items requested. + */ + RequestDataItem[] items; +} diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/IdentityCredential.cpp index 341fae6e93..8a00d221b7 100644 --- a/identity/aidl/default/IdentityCredential.cpp +++ b/identity/aidl/default/IdentityCredential.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -32,6 +33,7 @@ namespace aidl::android::hardware::identity { using ::aidl::android::hardware::keymaster::Timestamp; +using ::android::base::StringPrintf; using ::std::optional; using namespace ::android::hardware::identity; @@ -196,15 +198,8 @@ bool checkReaderAuthentication(const SecureAccessControlProfile& profile, return false; } -Timestamp clockGetTime() { - struct timespec time; - clock_gettime(CLOCK_MONOTONIC, &time); - Timestamp ts; - ts.milliSeconds = time.tv_sec * 1000 + time.tv_nsec / 1000000; - return ts; -} - bool checkUserAuthentication(const SecureAccessControlProfile& profile, + const VerificationToken& verificationToken, const HardwareAuthToken& authToken, uint64_t authChallenge) { if (profile.secureUserId != authToken.userId) { LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId @@ -212,6 +207,15 @@ bool checkUserAuthentication(const SecureAccessControlProfile& profile, return false; } + if (verificationToken.timestamp.milliSeconds == 0) { + LOG(ERROR) << "VerificationToken is not set"; + return false; + } + if (authToken.timestamp.milliSeconds == 0) { + LOG(ERROR) << "AuthToken is not set"; + return false; + } + if (profile.timeoutMillis == 0) { if (authToken.challenge == 0) { LOG(ERROR) << "No challenge in authToken"; @@ -225,19 +229,11 @@ bool checkUserAuthentication(const SecureAccessControlProfile& profile, return true; } - // Note that the Epoch for timestamps in HardwareAuthToken is at the - // discretion of the vendor: + // Timeout-based user auth follows. The verification token conveys what the + // time is right now in the environment which generated the auth token. This + // is what makes it possible to do timeout-based checks. // - // "[...] since some starting point (generally the most recent device - // boot) which all of the applications within one secure environment - // must agree upon." - // - // Therefore, if this software implementation is used on a device which isn't - // the emulator then the assumption that the epoch is the same as used in - // clockGetTime above will not hold. This is OK as this software - // implementation should never be used on a real device. - // - Timestamp now = clockGetTime(); + const Timestamp now = verificationToken.timestamp; if (authToken.timestamp.milliSeconds > now.milliSeconds) { LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp.milliSeconds << ") is in the future (now: " << now.milliSeconds << ")"; @@ -253,6 +249,18 @@ bool checkUserAuthentication(const SecureAccessControlProfile& profile, return true; } +ndk::ScopedAStatus IdentityCredential::setRequestedNamespaces( + const vector& requestNamespaces) { + requestNamespaces_ = requestNamespaces; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus IdentityCredential::setVerificationToken( + const VerificationToken& verificationToken) { + verificationToken_ = verificationToken; + return ndk::ScopedAStatus::ok(); +} + ndk::ScopedAStatus IdentityCredential::startRetrieval( const vector& accessControlProfiles, const HardwareAuthToken& authToken, const vector& itemsRequestS, @@ -451,7 +459,7 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval( "Type mismatch in nameSpaces map")); } string requestedNamespace = nsKey->value(); - vector requestedKeys; + set requestedKeys; for (size_t m = 0; m < nsInnerMap->size(); m++) { const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m]; const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr(); @@ -463,13 +471,13 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval( IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE, "Type mismatch in value in nameSpaces map")); } - requestedKeys.push_back(nameItem->value()); + requestedKeys.insert(nameItem->value()); } requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys; } } - // Finally, validate all the access control profiles in the requestData. + // Validate all the access control profiles in the requestData. bool haveAuthToken = (authToken.mac.size() > 0); for (const auto& profile : accessControlProfiles) { if (!secureAccessControlProfileCheckMac(profile, storageKey_)) { @@ -479,7 +487,8 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval( } int accessControlCheck = IIdentityCredentialStore::STATUS_OK; if (profile.userAuthenticationRequired) { - if (!haveAuthToken || !checkUserAuthentication(profile, authToken, authChallenge_)) { + if (!haveAuthToken || + !checkUserAuthentication(profile, verificationToken_, authToken, authChallenge_)) { accessControlCheck = IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED; } } else if (profile.readerCertificate.encodedCertificate.size() > 0) { @@ -500,10 +509,118 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval( itemsRequest_ = itemsRequest; signingKeyBlob_ = byteStringToUnsigned(signingKeyBlobS); + // Finally, calculate the size of DeviceNameSpaces. We need to know it ahead of time. + expectedDeviceNameSpacesSize_ = calcDeviceNameSpacesSize(); + numStartRetrievalCalls_ += 1; return ndk::ScopedAStatus::ok(); } +size_t cborNumBytesForLength(size_t length) { + if (length < 24) { + return 0; + } else if (length <= 0xff) { + return 1; + } else if (length <= 0xffff) { + return 2; + } else if (length <= 0xffffffff) { + return 4; + } + return 8; +} + +size_t cborNumBytesForTstr(const string& value) { + return 1 + cborNumBytesForLength(value.size()) + value.size(); +} + +size_t IdentityCredential::calcDeviceNameSpacesSize() { + /* + * This is how DeviceNameSpaces is defined: + * + * DeviceNameSpaces = { + * * NameSpace => DeviceSignedItems + * } + * DeviceSignedItems = { + * + DataItemName => DataItemValue + * } + * + * Namespace = tstr + * DataItemName = tstr + * DataItemValue = any + * + * This function will calculate its length using knowledge of how CBOR is + * encoded. + */ + size_t ret = 0; + size_t numNamespacesWithValues = 0; + for (const RequestNamespace& rns : requestNamespaces_) { + vector itemsToInclude; + + for (const RequestDataItem& rdi : rns.items) { + // If we have a CBOR request message, skip if item isn't in it + if (itemsRequest_.size() > 0) { + const auto& it = requestedNameSpacesAndNames_.find(rns.namespaceName); + if (it == requestedNameSpacesAndNames_.end()) { + continue; + } + const set& dataItemNames = it->second; + if (dataItemNames.find(rdi.name) == dataItemNames.end()) { + continue; + } + } + + // Access is granted if at least one of the profiles grants access. + // + // If an item is configured without any profiles, access is denied. + // + bool authorized = false; + for (auto id : rdi.accessControlProfileIds) { + auto it = profileIdToAccessCheckResult_.find(id); + if (it != profileIdToAccessCheckResult_.end()) { + int accessControlForProfile = it->second; + if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) { + authorized = true; + break; + } + } + } + if (!authorized) { + continue; + } + + itemsToInclude.push_back(rdi); + } + + // If no entries are to be in the namespace, we don't include it... + if (itemsToInclude.size() == 0) { + continue; + } + + // Key: NameSpace + ret += cborNumBytesForTstr(rns.namespaceName); + + // Value: Open the DeviceSignedItems map + ret += 1 + cborNumBytesForLength(itemsToInclude.size()); + + for (const RequestDataItem& item : itemsToInclude) { + // Key: DataItemName + ret += cborNumBytesForTstr(item.name); + + // Value: DataItemValue - entryData.size is the length of serialized CBOR so we use + // that. + ret += item.size; + } + + numNamespacesWithValues++; + } + + // Now that we now the nunber of namespaces with values, we know how many + // bytes the DeviceNamespaces map in the beginning is going to take up. + ret += 1 + cborNumBytesForLength(numNamespacesWithValues); + + return ret; +} + ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue( const string& nameSpace, const string& name, int32_t entrySize, const vector& accessControlProfileIds) { @@ -562,8 +679,8 @@ ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue( IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE, "Name space was not requested in startRetrieval")); } - const auto& dataItemNames = it->second; - if (std::find(dataItemNames.begin(), dataItemNames.end(), name) == dataItemNames.end()) { + const set& dataItemNames = it->second; + if (dataItemNames.find(name) == dataItemNames.end()) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE, "Data item name in name space was not requested in startRetrieval")); @@ -608,7 +725,6 @@ ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue( ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector& encryptedContentS, vector* outContent) { auto encryptedContent = byteStringToUnsigned(encryptedContentS); - optional> content = support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_); if (!content) { @@ -659,6 +775,17 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector* outMac, } vector encodedDeviceNameSpaces = deviceNameSpacesMap_.encode(); + if (encodedDeviceNameSpaces.size() != expectedDeviceNameSpacesSize_) { + LOG(ERROR) << "encodedDeviceNameSpaces is " << encodedDeviceNameSpaces.size() << " bytes, " + << "was expecting " << expectedDeviceNameSpacesSize_; + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + StringPrintf( + "Unexpected CBOR size %zd for encodedDeviceNameSpaces, was expecting %zd", + encodedDeviceNameSpaces.size(), expectedDeviceNameSpacesSize_) + .c_str())); + } + // If there's no signing key or no sessionTranscript or no reader ephemeral // public key, we return the empty MAC. optional> mac; diff --git a/identity/aidl/default/IdentityCredential.h b/identity/aidl/default/IdentityCredential.h index fc29254f4b..40070c0074 100644 --- a/identity/aidl/default/IdentityCredential.h +++ b/identity/aidl/default/IdentityCredential.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -31,16 +32,19 @@ namespace aidl::android::hardware::identity { using ::aidl::android::hardware::keymaster::HardwareAuthToken; +using ::aidl::android::hardware::keymaster::VerificationToken; using ::std::map; +using ::std::set; using ::std::string; using ::std::vector; -using MapStringToVectorOfStrings = map>; - class IdentityCredential : public BnIdentityCredential { public: IdentityCredential(const vector& credentialData) - : credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {} + : credentialData_(credentialData), + numStartRetrievalCalls_(0), + authChallenge_(0), + expectedDeviceNameSpacesSize_(0) {} // Parses and decrypts credentialData_, return a status code from // IIdentityCredentialStore. Must be called right after construction. @@ -51,6 +55,9 @@ class IdentityCredential : public BnIdentityCredential { ndk::ScopedAStatus createEphemeralKeyPair(vector* outKeyPair) override; ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector& publicKey) override; ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override; + ndk::ScopedAStatus setRequestedNamespaces( + const vector& requestNamespaces) override; + ndk::ScopedAStatus setVerificationToken(const VerificationToken& verificationToken) override; ndk::ScopedAStatus startRetrieval( const vector& accessControlProfiles, const HardwareAuthToken& authToken, const vector& itemsRequest, @@ -86,6 +93,12 @@ class IdentityCredential : public BnIdentityCredential { // Set by createAuthChallenge() uint64_t authChallenge_; + // Set by setRequestedNamespaces() + vector requestNamespaces_; + + // Set by setVerificationToken(). + VerificationToken verificationToken_; + // Set at startRetrieval() time. map profileIdToAccessCheckResult_; vector signingKeyBlob_; @@ -93,16 +106,21 @@ class IdentityCredential : public BnIdentityCredential { std::unique_ptr sessionTranscriptItem_; vector itemsRequest_; vector requestCountsRemaining_; - MapStringToVectorOfStrings requestedNameSpacesAndNames_; + map> requestedNameSpacesAndNames_; cppbor::Map deviceNameSpacesMap_; cppbor::Map currentNameSpaceDeviceNameSpacesMap_; + // Calculated at startRetrieval() time. + size_t expectedDeviceNameSpacesSize_; + // Set at startRetrieveEntryValue() time. string currentNameSpace_; string currentName_; size_t entryRemainingBytes_; vector entryValue_; vector entryAdditionalData_; + + size_t calcDeviceNameSpacesSize(); }; } // namespace aidl::android::hardware::identity diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp index 52cd49600f..7732c33a16 100644 --- a/identity/aidl/default/WritableIdentityCredential.cpp +++ b/identity/aidl/default/WritableIdentityCredential.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -34,6 +35,7 @@ namespace aidl::android::hardware::identity { +using ::android::base::StringPrintf; using ::std::optional; using namespace ::android::hardware::identity; @@ -105,6 +107,12 @@ ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate( return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus WritableIdentityCredential::setExpectedProofOfProvisioningSize( + int32_t expectedProofOfProvisioningSize) { + expectedProofOfProvisioningSize_ = expectedProofOfProvisioningSize; + return ndk::ScopedAStatus::ok(); +} + ndk::ScopedAStatus WritableIdentityCredential::startPersonalization( int32_t accessControlProfileCount, const vector& entryCounts) { if (startPersonalizationCalled_) { @@ -255,7 +263,7 @@ ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry( } ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector& contentS, - vector* outEncryptedContent) { + vector* outEncryptedContentS) { auto content = byteStringToUnsigned(contentS); size_t contentSize = content.size(); @@ -311,7 +319,7 @@ ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector encodedCbor = popArray.encode(); + if (encodedCbor.size() != expectedProofOfProvisioningSize_) { + LOG(ERROR) << "CBOR for proofOfProvisioning is " << encodedCbor.size() << " bytes, " + << "was expecting " << expectedProofOfProvisioningSize_; + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + StringPrintf("Unexpected CBOR size %zd for proofOfProvisioning, was expecting %zd", + encodedCbor.size(), expectedProofOfProvisioningSize_) + .c_str())); + } + optional> signature = support::coseSignEcDsa(credentialPrivKey_, encodedCbor, // payload {}, // additionalData diff --git a/identity/aidl/default/WritableIdentityCredential.h b/identity/aidl/default/WritableIdentityCredential.h index cb91f7ba86..05104d7016 100644 --- a/identity/aidl/default/WritableIdentityCredential.h +++ b/identity/aidl/default/WritableIdentityCredential.h @@ -43,6 +43,9 @@ class WritableIdentityCredential : public BnWritableIdentityCredential { const vector& attestationChallenge, vector* outCertificateChain) override; + ndk::ScopedAStatus setExpectedProofOfProvisioningSize( + int32_t expectedProofOfProvisioningSize) override; + ndk::ScopedAStatus startPersonalization(int32_t accessControlProfileCount, const vector& entryCounts) override; @@ -62,7 +65,7 @@ class WritableIdentityCredential : public BnWritableIdentityCredential { vector* outCredentialData, vector* outProofOfProvisioningSignature) override; - // private: + private: string docType_; bool testCredential_; @@ -82,6 +85,7 @@ class WritableIdentityCredential : public BnWritableIdentityCredential { cppbor::Array signedDataAccessControlProfiles_; cppbor::Map signedDataNamespaces_; cppbor::Array signedDataCurrentNamespace_; + size_t expectedProofOfProvisioningSize_; // This field is initialized in addAccessControlProfile set accessControlProfileIds_; diff --git a/identity/aidl/default/service.cpp b/identity/aidl/default/service.cpp index f05c615001..bf95df523a 100644 --- a/identity/aidl/default/service.cpp +++ b/identity/aidl/default/service.cpp @@ -22,9 +22,14 @@ #include "IdentityCredentialStore.h" +using ::android::base::InitLogging; +using ::android::base::StderrLogger; + using aidl::android::hardware::identity::IdentityCredentialStore; -int main() { +int main(int /*argc*/, char* argv[]) { + InitLogging(argv, StderrLogger); + ABinderProcess_setThreadPoolMaxThreadCount(0); std::shared_ptr store = ndk::SharedRefBase::make(); diff --git a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp index 8a4e8a7fa1..17145b4154 100644 --- a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp +++ b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp @@ -43,6 +43,7 @@ using ::android::String16; using ::android::binder::Status; using ::android::hardware::keymaster::HardwareAuthToken; +using ::android::hardware::keymaster::VerificationToken; class IdentityAidl : public testing::TestWithParam { public: @@ -82,7 +83,20 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) { // Profile 1 (no authentication) {1, {}, false, 0}}; + // It doesn't matter since no user auth is needed in this particular test, + // but for good measure, clear out the tokens we pass to the HAL. HardwareAuthToken authToken; + VerificationToken verificationToken; + authToken.challenge = 0; + authToken.userId = 0; + authToken.authenticatorId = 0; + authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE; + authToken.timestamp.milliSeconds = 0; + authToken.mac.clear(); + verificationToken.challenge = 0; + verificationToken.timestamp.milliSeconds = 0; + verificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE; + verificationToken.mac.clear(); // Here's the actual test data: const vector testEntries = { @@ -112,6 +126,11 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) { // TODO: set it to something random and check it's in the cert chain ASSERT_GE(attData.attestationCertificate.size(), 2); + // This is kinda of a hack but we need to give the size of + // ProofOfProvisioning that we'll expect to receive. + const int32_t expectedProofOfProvisioningSize = 262861 - 326 + readerCertificate.value().size(); + // OK to fail, not available in v1 HAL + writableCredential->setExpectedProofOfProvisioningSize(expectedProofOfProvisioningSize); ASSERT_TRUE( writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts) .isOk()); @@ -268,6 +287,11 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) { Certificate signingKeyCertificate; ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk()); + vector requestedNamespaces = test_utils::buildRequestNamespaces(testEntries); + // OK to fail, not available in v1 HAL + credential->setRequestedNamespaces(requestedNamespaces).isOk(); + // OK to fail, not available in v1 HAL + credential->setVerificationToken(verificationToken); ASSERT_TRUE(credential ->startRetrieval(secureProfiles.value(), authToken, itemsRequestBytes, signingKeyBlob, sessionTranscriptBytes, diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp index b68fbb54f1..724aaa1643 100644 --- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp +++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp @@ -279,14 +279,17 @@ TEST_P(IdentityCredentialTests, verifyOneProfileAndEntryPass) { EXPECT_TRUE(attData.result.isOk()) << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl; - const vector entryCounts = {1u}; - writableCredential->startPersonalization(1, entryCounts); - ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() - << endl; - optional> readerCertificate1 = test_utils::GenerateReaderCertificate("123456"); ASSERT_TRUE(readerCertificate1); + const vector entryCounts = {1u}; + size_t expectedPoPSize = 186 + readerCertificate1.value().size(); + // OK to fail, not available in v1 HAL + writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize); + result = writableCredential->startPersonalization(1, entryCounts); + ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() + << endl; + const vector testProfiles = {{1, readerCertificate1.value(), true, 1}}; optional> secureProfiles = @@ -374,7 +377,11 @@ TEST_P(IdentityCredentialTests, verifyManyProfilesAndEntriesPass) { {2, readerCertificate2.value(), true, 2}, }; const vector entryCounts = {1u, 3u, 1u, 1u, 2u}; - writableCredential->startPersonalization(testProfiles.size(), entryCounts); + size_t expectedPoPSize = + 525021 + readerCertificate1.value().size() + readerCertificate2.value().size(); + // OK to fail, not available in v1 HAL + writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize); + result = writableCredential->startPersonalization(testProfiles.size(), entryCounts); ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() << endl; @@ -518,11 +525,6 @@ TEST_P(IdentityCredentialTests, verifyEmptyNameSpaceMixedWithNonEmptyWorks) { ASSERT_TRUE(attData.result.isOk()) << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl; - const vector entryCounts = {2u, 2u}; - writableCredential->startPersonalization(3, entryCounts); - ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() - << endl; - optional> readerCertificate1 = test_utils::GenerateReaderCertificate("123456"); ASSERT_TRUE(readerCertificate1); @@ -530,6 +532,16 @@ TEST_P(IdentityCredentialTests, verifyEmptyNameSpaceMixedWithNonEmptyWorks) { test_utils::GenerateReaderCertificate("123456987987987987987987"); ASSERT_TRUE(readerCertificate2); + const vector entryCounts = {2u, 2u}; + size_t expectedPoPSize = + 377 + readerCertificate1.value().size() + readerCertificate2.value().size(); + ; + // OK to fail, not available in v1 HAL + writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize); + writableCredential->startPersonalization(3, entryCounts); + ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() + << endl; + const vector testProfiles = {{0, readerCertificate1.value(), false, 0}, {1, readerCertificate2.value(), true, 1}, {2, {}, false, 0}}; diff --git a/identity/aidl/vts/VtsIdentityTestUtils.cpp b/identity/aidl/vts/VtsIdentityTestUtils.cpp index 3aeebc66b6..48e47dc735 100644 --- a/identity/aidl/vts/VtsIdentityTestUtils.cpp +++ b/identity/aidl/vts/VtsIdentityTestUtils.cpp @@ -176,4 +176,28 @@ void SetImageData(vector& image) { } } +vector buildRequestNamespaces(const vector entries) { + vector ret; + RequestNamespace curNs; + for (const TestEntryData& testEntry : entries) { + if (testEntry.nameSpace != curNs.namespaceName) { + if (curNs.namespaceName.size() > 0) { + ret.push_back(curNs); + } + curNs.namespaceName = testEntry.nameSpace; + curNs.items.clear(); + } + + RequestDataItem item; + item.name = testEntry.name; + item.size = testEntry.valueCbor.size(); + item.accessControlProfileIds = testEntry.profileIds; + curNs.items.push_back(item); + } + if (curNs.namespaceName.size() > 0) { + ret.push_back(curNs); + } + return ret; +} + } // namespace android::hardware::identity::test_utils diff --git a/identity/aidl/vts/VtsIdentityTestUtils.h b/identity/aidl/vts/VtsIdentityTestUtils.h index 043ccd6905..9e1f35271d 100644 --- a/identity/aidl/vts/VtsIdentityTestUtils.h +++ b/identity/aidl/vts/VtsIdentityTestUtils.h @@ -113,6 +113,8 @@ bool ValidateAttestationCertificate(vector& inputCertificates); void SetImageData(vector& image); +vector buildRequestNamespaces(const vector entries); + } // namespace android::hardware::identity::test_utils #endif // VTS_IDENTITY_TEST_UTILS_H diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h index 61645f853e..f585d6269e 100644 --- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h +++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h @@ -18,6 +18,8 @@ #define HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_UTILS_H_ #include +#include +#include namespace android { namespace hardware { @@ -52,6 +54,15 @@ inline static hidl_vec blob2hidlVec(const std::vector& blob) { HardwareAuthToken hidlVec2AuthToken(const hidl_vec& buffer); hidl_vec authToken2HidlVec(const HardwareAuthToken& token); +// Serializes and deserializes a verification token. This format is private and +// not stable between releases and should not be persisted to disk. +// +// Currently doesn't support the |parametersVerified| field, will fail if set. +// +std::optional deserializeVerificationToken( + const std::vector& serializedToken); +std::optional> serializeVerificationToken(const VerificationToken& token); + uint32_t getOsVersion(); uint32_t getOsPatchlevel(); diff --git a/keymaster/4.0/support/keymaster_utils.cpp b/keymaster/4.0/support/keymaster_utils.cpp index 850a7767bf..366cd0e553 100644 --- a/keymaster/4.0/support/keymaster_utils.cpp +++ b/keymaster/4.0/support/keymaster_utils.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -110,6 +111,80 @@ HardwareAuthToken hidlVec2AuthToken(const hidl_vec& buffer) { return token; } +void appendUint64(std::vector& vec, uint64_t value) { + for (size_t n = 0; n < sizeof(uint64_t); n++) { + uint8_t byte = (value >> (n * 8)) & 0xff; + vec.push_back(byte); + } +} + +uint64_t extractUint64(const std::vector& data, size_t offset) { + uint64_t value = 0; + for (size_t n = 0; n < sizeof(uint64_t); n++) { + uint8_t byte = data[offset + n]; + value |= byte << (n * 8); + } + return value; +} + +void appendUint32(std::vector& vec, uint32_t value) { + for (size_t n = 0; n < sizeof(uint32_t); n++) { + uint8_t byte = (value >> (n * 8)) & 0xff; + vec.push_back(byte); + } +} + +uint32_t extractUint32(const std::vector& data, size_t offset) { + uint32_t value = 0; + for (size_t n = 0; n < sizeof(uint32_t); n++) { + uint8_t byte = data[offset + n]; + value |= byte << (n * 8); + } + return value; +} + +std::optional> serializeVerificationToken(const VerificationToken& token) { + if (token.parametersVerified.size() > 0) { + LOG(ERROR) << "Serializing verification tokens with parametersVerified is not supported"; + return {}; + } + if (!(token.mac.size() == 0 || token.mac.size() == 32)) { + LOG(ERROR) << "Unexpected MAC size " << token.mac.size() << ", expected 0 or 32"; + return {}; + } + std::vector serializedToken; + appendUint64(serializedToken, token.challenge); + appendUint64(serializedToken, token.timestamp); + appendUint32(serializedToken, uint32_t(token.securityLevel)); + appendUint32(serializedToken, token.mac.size()); + serializedToken.insert(serializedToken.end(), token.mac.begin(), token.mac.end()); + return serializedToken; +} + +std::optional deserializeVerificationToken( + const std::vector& serializedToken) { + if (serializedToken.size() < 24) { + LOG(ERROR) << "Unexpected serialized VerificationToken size " << serializedToken.size() + << ", expected at least 24 bytes"; + return {}; + } + VerificationToken token; + token.challenge = extractUint64(serializedToken, 0); + token.timestamp = extractUint64(serializedToken, 8); + token.securityLevel = SecurityLevel(extractUint32(serializedToken, 16)); + size_t macSize = extractUint32(serializedToken, 20); + size_t expectedSerializedSize = 24 + macSize; + if (serializedToken.size() != expectedSerializedSize) { + LOG(ERROR) << "Unexpected serialized VerificationToken size " << serializedToken.size() + << ", expected " << expectedSerializedSize; + return {}; + } + if (macSize > 0) { + token.mac = std::vector(serializedToken.begin() + 24, serializedToken.end()); + } + return token; +} + namespace { constexpr char kPlatformVersionProp[] = "ro.build.version.release"; diff --git a/keymaster/aidl/Android.bp b/keymaster/aidl/Android.bp index 3011da67db..56a3ca99e7 100644 --- a/keymaster/aidl/Android.bp +++ b/keymaster/aidl/Android.bp @@ -15,5 +15,8 @@ aidl_interface { }, }, }, - versions: ["1"], + versions: [ + "1", + "2", + ], } diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/2/.hash b/keymaster/aidl/aidl_api/android.hardware.keymaster/2/.hash new file mode 100644 index 0000000000..9d5974e920 --- /dev/null +++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/2/.hash @@ -0,0 +1 @@ +91ab0be1887410935f564e3938ff12c5f5f8c59d diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/HardwareAuthToken.aidl new file mode 100644 index 0000000000..db1df2b050 --- /dev/null +++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/HardwareAuthToken.aidl @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.keymaster; +@VintfStability +parcelable HardwareAuthToken { + long challenge; + long userId; + long authenticatorId; + android.hardware.keymaster.HardwareAuthenticatorType authenticatorType; + android.hardware.keymaster.Timestamp timestamp; + byte[] mac; +} diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/HardwareAuthenticatorType.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/HardwareAuthenticatorType.aidl new file mode 100644 index 0000000000..924567f95b --- /dev/null +++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/HardwareAuthenticatorType.aidl @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.keymaster; +@Backing(type="int") @VintfStability +enum HardwareAuthenticatorType { + NONE = 0, + PASSWORD = 1, + FINGERPRINT = 2, + ANY = -1, +} diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/SecurityLevel.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/SecurityLevel.aidl new file mode 100644 index 0000000000..127c1bf7cd --- /dev/null +++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/SecurityLevel.aidl @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.keymaster; +@Backing(type="int") @VintfStability +enum SecurityLevel { + SOFTWARE = 0, + TRUSTED_ENVIRONMENT = 1, + STRONGBOX = 2, +} diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/Timestamp.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/Timestamp.aidl new file mode 100644 index 0000000000..45fa1aed13 --- /dev/null +++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/Timestamp.aidl @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.keymaster; +@VintfStability +parcelable Timestamp { + long milliSeconds; +} diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/VerificationToken.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/VerificationToken.aidl new file mode 100644 index 0000000000..0633765cac --- /dev/null +++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/2/android/hardware/keymaster/VerificationToken.aidl @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.keymaster; +@VintfStability +parcelable VerificationToken { + long challenge; + android.hardware.keymaster.Timestamp timestamp; + android.hardware.keymaster.SecurityLevel securityLevel; + byte[] mac; +} diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/SecurityLevel.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/SecurityLevel.aidl new file mode 100644 index 0000000000..127c1bf7cd --- /dev/null +++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/SecurityLevel.aidl @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.keymaster; +@Backing(type="int") @VintfStability +enum SecurityLevel { + SOFTWARE = 0, + TRUSTED_ENVIRONMENT = 1, + STRONGBOX = 2, +} diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl new file mode 100644 index 0000000000..0633765cac --- /dev/null +++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.keymaster; +@VintfStability +parcelable VerificationToken { + long challenge; + android.hardware.keymaster.Timestamp timestamp; + android.hardware.keymaster.SecurityLevel securityLevel; + byte[] mac; +} diff --git a/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl b/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl new file mode 100644 index 0000000000..f1297831c1 --- /dev/null +++ b/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl @@ -0,0 +1,32 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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. + */ + +package android.hardware.keymaster; + +/** + * Device security levels. + */ +@VintfStability +@Backing(type="int") +enum SecurityLevel { + SOFTWARE = 0, + TRUSTED_ENVIRONMENT = 1, + /** + * STRONGBOX specifies that the secure hardware satisfies the requirements specified in CDD + * 9.11.2. + */ + STRONGBOX = 2, +} diff --git a/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl new file mode 100644 index 0000000000..eff9ca6f0c --- /dev/null +++ b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl @@ -0,0 +1,69 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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. + */ + +package android.hardware.keymaster; + +import android.hardware.keymaster.SecurityLevel; +import android.hardware.keymaster.Timestamp; +import android.hardware.keymaster.HardwareAuthenticatorType; + +/** + * VerificationToken instances are used for secure environments to authenticate one another. + * + * This version of the parcelable currently don't use the parametersVerified field since it's not + * needed for time-based verification. This can be added in a later version, if needed. + */ +@VintfStability +parcelable VerificationToken { + /** + * The operation handle, used to ensure freshness. + */ + long challenge; + + /** + * The current time of the secure environment that generates the VerificationToken. This can be + * checked against auth tokens generated by the same secure environment, which avoids needing to + * synchronize clocks. + */ + Timestamp timestamp; + + /** + * SecurityLevel of the secure environment that generated the token. + */ + SecurityLevel securityLevel; + + /** + * 32-byte HMAC-SHA256 of the above values, computed as: + * + * HMAC(H, + * "Auth Verification" || challenge || timestamp || securityLevel || parametersVerified) + * + * where: + * + * ``HMAC'' is the shared HMAC key (see computeSharedHmac() in IKeymaster). + * + * ``||'' represents concatenation + * + * The representation of challenge and timestamp is as 64-bit unsigned integers in big-endian + * order. securityLevel is represented as a 32-bit unsigned integer in big-endian order. + * + * If parametersVerified is non-empty, the representation of parametersVerified is an ASN.1 DER + * encoded representation of the values. The ASN.1 schema used is the AuthorizationList schema + * from the Keystore attestation documentation. If parametersVerified is empty, it is simply + * omitted from the HMAC computation. + */ + byte[] mac; +}