Identity Credential changes for Android 12
- Add IIdentityCredential.deleteCredentialWithChallenge()
- Deprecate IIdentityCredential.deleteCredential()
- Add IIdentityCredential.proveOwership()
- Add IIdentityCredential.updateCredential()
- Add ProofOfBinding CBOR to AuthenticationKey X.509 certificate
- Document which API versions new methods/features appeared in.
- Mention need to declare android.hardware.identity_credential system
feature (w/ feature version number) and do this for the default
implementation.
Bug: 170146643
Test: atest VtsHalIdentityTargetTest
Change-Id: Ib47c7caa5f3d6fff6919f019eee44a735dba9cf8
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/Certificate.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/Certificate.aidl
index 7e3002d..d8a8128 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/Certificate.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/Certificate.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
// 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.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file 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
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/CipherSuite.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/CipherSuite.aidl
index 447203f..2685525 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/CipherSuite.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/CipherSuite.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
// 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.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file 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
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/HardwareInformation.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/HardwareInformation.aidl
index e1296e0..f8d5a9e 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/HardwareInformation.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/HardwareInformation.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
// 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.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file 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
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 88104d9..a097895 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
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
// 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.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file 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
@@ -18,6 +33,9 @@
package android.hardware.identity;
@VintfStability
interface IIdentityCredential {
+ /**
+ * @deprecated use deleteCredentalWithChallenge() instead.
+ */
byte[] deleteCredential();
byte[] createEphemeralKeyPair();
void setReaderEphemeralPublicKey(in byte[] publicKey);
@@ -29,4 +47,7 @@
android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
void setRequestedNamespaces(in android.hardware.identity.RequestNamespace[] requestNamespaces);
void setVerificationToken(in android.hardware.keymaster.VerificationToken verificationToken);
+ byte[] deleteCredentialWithChallenge(in byte[] challenge);
+ byte[] proveOwnership(in byte[] challenge);
+ android.hardware.identity.IWritableIdentityCredential updateCredential();
}
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredentialStore.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredentialStore.aidl
index 5dafb76..c6fb3c8 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredentialStore.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredentialStore.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
// 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.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file 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
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 c5ac9d6..a713462 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
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
// 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.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file 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
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
index 24ec26a..c9c2b9f 100644
--- 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
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
// 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.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file 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
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
index af00f3b..aaf1e20 100644
--- 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
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
// 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.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file 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
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/SecureAccessControlProfile.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/SecureAccessControlProfile.aidl
index dfc1ad0..695fb3f 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/SecureAccessControlProfile.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/SecureAccessControlProfile.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
// 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.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file 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
diff --git a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
index 702334d..d23f88c 100644
--- a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
@@ -19,6 +19,7 @@
import android.hardware.identity.Certificate;
import android.hardware.identity.RequestNamespace;
import android.hardware.identity.SecureAccessControlProfile;
+import android.hardware.identity.IWritableIdentityCredential;
import android.hardware.keymaster.HardwareAuthToken;
import android.hardware.keymaster.VerificationToken;
@@ -40,7 +41,11 @@
* After this method has been called, the persistent storage used for credentialData should
* be deleted.
*
- * @return a COSE_Sign1 signature described above.
+ * This method was deprecated in API version 3 because there's no challenge so freshness
+ * can't be checked. Use deleteCredentalWithChallenge() instead.
+ *
+ * @return a COSE_Sign1 signature described above
+ * @deprecated use deleteCredentalWithChallenge() instead.
*/
byte[] deleteCredential();
@@ -353,6 +358,18 @@
*
* - subjectPublicKeyInfo: must contain attested public key.
*
+ * As of API version 3, the certificate shall also have an X.509 extension at
+ * OID 1.3.6.1.4.1.11129.2.1.26 which shall contain an OCTET STRING with the
+ * bytes of the CBOR with the following CDDL:
+ *
+ * ProofOfBinding = [
+ * "ProofOfBinding",
+ * bstr, // Contains SHA-256(ProofOfProvisioning)
+ * ]
+ *
+ * This CBOR enables an issuer to determine the exact state of the credential it
+ * returns issuer-signed data for.
+ *
* @param out signingKeyBlob contains an AES-GCM-ENC(storageKey, R, signingKey, docType)
* where signingKey is an EC private key in uncompressed form. That is, the returned
* blob is an encrypted copy of the newly-generated private signing key.
@@ -381,4 +398,63 @@
* The verification token. This token is only valid if the timestamp field is non-zero.
*/
void setVerificationToken(in VerificationToken verificationToken);
+
+ /**
+ * Delete a credential.
+ *
+ * This method returns a COSE_Sign1 data structure signed by CredentialKey
+ * with payload set to the ProofOfDeletion CBOR below:
+ *
+ * ProofOfDeletion = [
+ * "ProofOfDeletion", ; tstr
+ * tstr, ; DocType
+ * bstr, ; Challenge
+ * bool ; true if this is a test credential, should
+ * ; always be false.
+ * ]
+ *
+ * After this method has been called, the persistent storage used for credentialData should
+ * be deleted.
+ *
+ * This method was introduced in API version 3.
+ *
+ * @param challenge a challenge set by the issuer to ensure freshness. Maximum size is 32 bytes
+ * and it may be empty. Fails with STATUS_INVALID_DATA if bigger than 32 bytes.
+ * @return a COSE_Sign1 signature described above.
+ */
+ byte[] deleteCredentialWithChallenge(in byte[] challenge);
+
+ /**
+ * Prove ownership of credential.
+ *
+ * This method returns a COSE_Sign1 data structure signed by CredentialKey with payload
+ * set to the ProofOfOwnership CBOR below.
+ *
+ * ProofOfOwnership = [
+ * "ProofOfOwnership", ; tstr
+ * tstr, ; DocType
+ * bstr, ; Challenge
+ * bool ; true if this is a test credential, should
+ * ; always be false.
+ * ]
+ *
+ * This method was introduced in API version 3.
+ *
+ * @param challenge a challenge set by the issuer to ensure freshness. Maximum size is 32 bytes
+ * and it may be empty. Fails with STATUS_INVALID_DATA if bigger than 32 bytes.
+ * @return a COSE_Sign1 signature described above.
+ */
+ byte[] proveOwnership(in byte[] challenge);
+
+ /**
+ * Called to start updating the credential with new data items.
+ *
+ * If the getAttestationCertificate() method is called on the returned object
+ * it fails with the error STATUS_FAILED.
+ *
+ * This method was introduced in API version 3.
+ *
+ * @return an IWritableIdentityCredential
+ */
+ IWritableIdentityCredential updateCredential();
}
diff --git a/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
index 33e25b1..638be79 100644
--- a/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
+++ b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
@@ -104,6 +104,11 @@
* All binder calls in the HAL may return a ServiceSpecificException with statuses from the
* STATUS_* integers defined in this interface. Each method states which status can be returned
* and under which circumstances.
+ *
+ * The API described here is API version 3 which corresponds to feature version 202101
+ * of the android.security.identity Framework API. An XML file declaring the feature
+ * android.hardware.identity_credential (or android.hardware.identity_credential.direct_access
+ * if implementing the Direct Access HAL) should be included declaring this feature version.
*/
@VintfStability
interface IIdentityCredentialStore {
@@ -230,6 +235,9 @@
* return argument of the same name in finishAddingEntries(), in
* IWritableIdentityCredential.
*
+ * Note that the format of credentialData may depend on the feature version.
+ * Implementations must support credentialData created by an earlier feature version.
+ *
* @return an IIdentityCredential interface that provides operations on the Credential.
*/
IIdentityCredential getCredential(in CipherSuite cipherSuite, in byte[] credentialData);
diff --git a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index c48cb66..5f878ee 100644
--- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -263,7 +263,9 @@
*
* where HBK is a unique hardware-bound key that has never existed outside of the secure
* environment (except it's all zeroes if testCredential is True) and CredentialKeys is
- * the CBOR-encoded structure (in CDDL notation):
+ * the CBOR-encoded structure (in CDDL notation) given below.
+ *
+ * In API versions 1 and 2 it was the following
*
* CredentialKeys = [
* bstr, ; storageKey, a 128-bit AES key
@@ -271,6 +273,17 @@
* ; in uncompressed form
* ]
*
+ * In API version 3 or later it must be the following
+ *
+ * CredentialKeys = [
+ * bstr, ; storageKey, a 128-bit AES key
+ * bstr ; credentialPrivKey, the private key for credentialKey
+ * ; in uncompressed form
+ * bstr ; SHA-256(ProofOfProvisioning)
+ * ]
+ *
+ * Additional elements may be added to the CredentialKeys array in future versions.
+ *
* @param out proofOfProvisioningSignature proves to the IA that the credential was imported
* into the secure hardware without alteration or error. When the final addEntry() call is
* made (when the number of provisioned entries equals the sum of the items in
@@ -321,4 +334,5 @@
* @param expectedProofOfProvisioningSize the expected size of ProofOfProvisioning.
*/
void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize);
+
}
diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp
index 7f342d0..8744648 100644
--- a/identity/aidl/default/Android.bp
+++ b/identity/aidl/default/Android.bp
@@ -12,6 +12,7 @@
cflags: [
"-Wall",
"-Wextra",
+ "-Wno-deprecated-declarations",
],
shared_libs: [
"liblog",
@@ -28,8 +29,8 @@
"libsoft_attestation_cert",
"libpuresoftkeymasterdevice",
"android.hardware.identity-support-lib",
- "android.hardware.identity-ndk_platform",
- "android.hardware.keymaster-ndk_platform",
+ "android.hardware.identity-unstable-ndk_platform",
+ "android.hardware.keymaster-unstable-ndk_platform",
],
}
@@ -88,8 +89,8 @@
"libsoft_attestation_cert",
"libpuresoftkeymasterdevice",
"android.hardware.identity-support-lib",
- "android.hardware.identity-ndk_platform",
- "android.hardware.keymaster-ndk_platform",
+ "android.hardware.identity-unstable-ndk_platform",
+ "android.hardware.keymaster-unstable-ndk_platform",
"android.hardware.identity-libeic-hal-common",
"android.hardware.identity-libeic-library",
],
@@ -97,4 +98,14 @@
"service.cpp",
"FakeSecureHardwareProxy.cpp",
],
+ required: [
+ "android.hardware.identity_credential.xml",
+ ],
+}
+
+prebuilt_etc {
+ name: "android.hardware.identity_credential.xml",
+ sub_dir: "permissions",
+ vendor: true,
+ src: "android.hardware.identity_credential.xml",
}
diff --git a/identity/aidl/default/EicOpsImpl.cc b/identity/aidl/default/EicOpsImpl.cc
index 3f2ec8b..8ec4cc9 100644
--- a/identity/aidl/default/EicOpsImpl.cc
+++ b/identity/aidl/default/EicOpsImpl.cc
@@ -45,6 +45,7 @@
#include "EicOps.h"
+using ::std::map;
using ::std::optional;
using ::std::string;
using ::std::tuple;
@@ -212,7 +213,8 @@
return false;
}
if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
- eicDebug("Private key is not %zd bytes long as expected", (size_t)EIC_P256_PRIV_KEY_SIZE);
+ eicDebug("Private key is %zd bytes, expected %zd", privKey.value().size(),
+ (size_t)EIC_P256_PRIV_KEY_SIZE);
return false;
}
@@ -224,7 +226,7 @@
}
// ecKeyPairGetPublicKey() returns 0x04 | x | y, we don't want the leading 0x04.
if (pubKey.value().size() != EIC_P256_PUB_KEY_SIZE + 1) {
- eicDebug("Private key is %zd bytes long, expected %zd", pubKey.value().size(),
+ eicDebug("Public key is %zd bytes long, expected %zd", pubKey.value().size(),
(size_t)EIC_P256_PRIV_KEY_SIZE + 1);
return false;
}
@@ -272,7 +274,8 @@
return false;
}
if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
- eicDebug("Private key is not %zd bytes long as expected", (size_t)EIC_P256_PRIV_KEY_SIZE);
+ eicDebug("Private key is %zd bytes, expected %zd", privKey.value().size(),
+ (size_t)EIC_P256_PRIV_KEY_SIZE);
return false;
}
@@ -284,8 +287,8 @@
bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
const char* issuerName, const char* subjectName, time_t validityNotBefore,
- time_t validityNotAfter, uint8_t* cert,
- size_t* certSize) { // inout
+ time_t validityNotAfter, const uint8_t* proofOfBinding,
+ size_t proofOfBindingSize, uint8_t* cert, size_t* certSize) { // inout
vector<uint8_t> signingKeyVec(EIC_P256_PRIV_KEY_SIZE);
memcpy(signingKeyVec.data(), signingKey, EIC_P256_PRIV_KEY_SIZE);
@@ -293,12 +296,18 @@
pubKeyVec[0] = 0x04;
memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE);
- std::string serialDecimal = android::base::StringPrintf("%d", serial);
+ string serialDecimal = android::base::StringPrintf("%d", serial);
+
+ map<string, vector<uint8_t>> extensions;
+ if (proofOfBinding != nullptr) {
+ vector<uint8_t> proofOfBindingVec(proofOfBinding, proofOfBinding + proofOfBindingSize);
+ extensions["1.3.6.1.4.1.11129.2.1.26"] = proofOfBindingVec;
+ }
optional<vector<uint8_t>> certVec =
android::hardware::identity::support::ecPublicKeyGenerateCertificate(
pubKeyVec, signingKeyVec, serialDecimal, issuerName, subjectName,
- validityNotBefore, validityNotAfter);
+ validityNotBefore, validityNotAfter, extensions);
if (!certVec) {
eicDebug("Error generating certificate");
return false;
diff --git a/identity/aidl/default/FakeSecureHardwareProxy.cpp b/identity/aidl/default/FakeSecureHardwareProxy.cpp
index de6762f..287ffb8 100644
--- a/identity/aidl/default/FakeSecureHardwareProxy.cpp
+++ b/identity/aidl/default/FakeSecureHardwareProxy.cpp
@@ -67,6 +67,13 @@
return eicProvisioningInit(&ctx_, testCredential);
}
+bool FakeSecureHardwareProvisioningProxy::initializeForUpdate(
+ bool testCredential, string docType, vector<uint8_t> encryptedCredentialKeys) {
+ return eicProvisioningInitForUpdate(&ctx_, testCredential, docType.c_str(),
+ encryptedCredentialKeys.data(),
+ encryptedCredentialKeys.size());
+}
+
// Returns public key certificate.
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::createCredentialKey(
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
@@ -140,14 +147,16 @@
return signatureOfToBeSigned;
}
-// Returns encryptedCredentialKeys (80 bytes).
+// Returns encryptedCredentialKeys.
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishGetCredentialData(
const string& docType) {
- vector<uint8_t> encryptedCredentialKeys(80);
+ vector<uint8_t> encryptedCredentialKeys(116);
+ size_t size = encryptedCredentialKeys.size();
if (!eicProvisioningFinishGetCredentialData(&ctx_, docType.c_str(),
- encryptedCredentialKeys.data())) {
+ encryptedCredentialKeys.data(), &size)) {
return {};
}
+ encryptedCredentialKeys.resize(size);
return encryptedCredentialKeys;
}
@@ -162,7 +171,7 @@
LOG(INFO) << "FakeSecureHardwarePresentationProxy created, sizeof(EicPresentation): "
<< sizeof(EicPresentation);
return eicPresentationInit(&ctx_, testCredential, docType.c_str(),
- encryptedCredentialKeys.data());
+ encryptedCredentialKeys.data(), encryptedCredentialKeys.size());
}
// Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
@@ -312,13 +321,27 @@
}
optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::deleteCredential(
- const string& docType, size_t proofOfDeletionCborSize) {
+ const string& docType, const vector<uint8_t>& challenge, bool includeChallenge,
+ size_t proofOfDeletionCborSize) {
vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
- if (!eicPresentationDeleteCredential(&ctx_, docType.c_str(), proofOfDeletionCborSize,
+ if (!eicPresentationDeleteCredential(&ctx_, docType.c_str(), challenge.data(), challenge.size(),
+ includeChallenge, proofOfDeletionCborSize,
signatureOfToBeSigned.data())) {
return {};
}
return signatureOfToBeSigned;
}
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::proveOwnership(
+ const string& docType, bool testCredential, const vector<uint8_t>& challenge,
+ size_t proofOfOwnershipCborSize) {
+ vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
+ if (!eicPresentationProveOwnership(&ctx_, docType.c_str(), testCredential, challenge.data(),
+ challenge.size(), proofOfOwnershipCborSize,
+ signatureOfToBeSigned.data())) {
+ return {};
+ }
+ return signatureOfToBeSigned;
+}
+
} // namespace android::hardware::identity
diff --git a/identity/aidl/default/FakeSecureHardwareProxy.h b/identity/aidl/default/FakeSecureHardwareProxy.h
index b858dd4..6852c1a 100644
--- a/identity/aidl/default/FakeSecureHardwareProxy.h
+++ b/identity/aidl/default/FakeSecureHardwareProxy.h
@@ -32,6 +32,9 @@
bool initialize(bool testCredential) override;
+ bool initializeForUpdate(bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) override;
+
bool shutdown() override;
// Returns public key certificate.
@@ -122,8 +125,14 @@
optional<vector<uint8_t>> finishRetrieval() override;
optional<vector<uint8_t>> deleteCredential(const string& docType,
+ const vector<uint8_t>& challenge,
+ bool includeChallenge,
size_t proofOfDeletionCborSize) override;
+ optional<vector<uint8_t>> proveOwnership(const string& docType, bool testCredential,
+ const vector<uint8_t>& challenge,
+ size_t proofOfOwnershipCborSize) override;
+
bool shutdown() override;
protected:
diff --git a/identity/aidl/default/android.hardware.identity_credential.xml b/identity/aidl/default/android.hardware.identity_credential.xml
new file mode 100644
index 0000000..5149792
--- /dev/null
+++ b/identity/aidl/default/android.hardware.identity_credential.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 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.
+-->
+<permissions>
+ <feature name="android.hardware.identity_credential" version="202101" />
+</permissions>
diff --git a/identity/aidl/default/common/IdentityCredential.cpp b/identity/aidl/default/common/IdentityCredential.cpp
index 270fcfa..9477997 100644
--- a/identity/aidl/default/common/IdentityCredential.cpp
+++ b/identity/aidl/default/common/IdentityCredential.cpp
@@ -30,6 +30,7 @@
#include <cppbor_parse.h>
#include "FakeSecureHardwareProxy.h"
+#include "WritableIdentityCredential.h"
namespace aidl::android::hardware::identity {
@@ -70,14 +71,8 @@
docType_ = docTypeItem->value();
testCredential_ = testCredentialItem->value();
- const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
-
- if (encryptedCredentialKeys.size() != 80) {
- LOG(ERROR) << "Unexpected size for encrypted CredentialKeys";
- return IIdentityCredentialStore::STATUS_INVALID_DATA;
- }
-
- if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys)) {
+ encryptedCredentialKeys_ = encryptedCredentialKeysItem->value();
+ if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys_)) {
LOG(ERROR) << "hwProxy->initialize failed";
return false;
}
@@ -87,12 +82,32 @@
ndk::ScopedAStatus IdentityCredential::deleteCredential(
vector<uint8_t>* outProofOfDeletionSignature) {
+ return deleteCredentialCommon({}, false, outProofOfDeletionSignature);
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredentialWithChallenge(
+ const vector<uint8_t>& challenge, vector<uint8_t>* outProofOfDeletionSignature) {
+ return deleteCredentialCommon(challenge, true, outProofOfDeletionSignature);
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredentialCommon(
+ const vector<uint8_t>& challenge, bool includeChallenge,
+ vector<uint8_t>* outProofOfDeletionSignature) {
+ if (challenge.size() > 32) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
+ }
+
cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
+ if (includeChallenge) {
+ array = {"ProofOfDeletion", docType_, challenge, testCredential_};
+ }
+
vector<uint8_t> proofOfDeletionCbor = array.encode();
vector<uint8_t> podDigest = support::sha256(proofOfDeletionCbor);
- optional<vector<uint8_t>> signatureOfToBeSigned =
- hwProxy_->deleteCredential(docType_, proofOfDeletionCbor.size());
+ optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->deleteCredential(
+ docType_, challenge, includeChallenge, proofOfDeletionCbor.size());
if (!signatureOfToBeSigned) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED, "Error signing ProofOfDeletion"));
@@ -111,6 +126,38 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus IdentityCredential::proveOwnership(
+ const vector<uint8_t>& challenge, vector<uint8_t>* outProofOfOwnershipSignature) {
+ if (challenge.size() > 32) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
+ }
+
+ cppbor::Array array;
+ array = {"ProofOfOwnership", docType_, challenge, testCredential_};
+ vector<uint8_t> proofOfOwnershipCbor = array.encode();
+ vector<uint8_t> podDigest = support::sha256(proofOfOwnershipCbor);
+
+ optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->proveOwnership(
+ docType_, testCredential_, challenge, proofOfOwnershipCbor.size());
+ if (!signatureOfToBeSigned) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing ProofOfOwnership"));
+ }
+
+ optional<vector<uint8_t>> signature =
+ support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
+ proofOfOwnershipCbor, // data
+ {}); // certificateChain
+ if (!signature) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
+ }
+
+ *outProofOfOwnershipSignature = signature.value();
+ return ndk::ScopedAStatus::ok();
+}
+
ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<uint8_t>* outKeyPair) {
optional<vector<uint8_t>> ephemeralPriv = hwProxy_->createEphemeralKeyPair();
if (!ephemeralPriv) {
@@ -833,4 +880,19 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus IdentityCredential::updateCredential(
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
+ sp<SecureHardwareProvisioningProxy> hwProxy = hwProxyFactory_->createProvisioningProxy();
+ shared_ptr<WritableIdentityCredential> wc =
+ ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType_,
+ testCredential_);
+ if (!wc->initializeForUpdate(encryptedCredentialKeys_)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error initializing WritableIdentityCredential for update"));
+ }
+ *outWritableCredential = wc;
+ return ndk::ScopedAStatus::ok();
+}
+
} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/common/IdentityCredential.h b/identity/aidl/default/common/IdentityCredential.h
index 2281821..9913b86 100644
--- a/identity/aidl/default/common/IdentityCredential.h
+++ b/identity/aidl/default/common/IdentityCredential.h
@@ -45,9 +45,11 @@
class IdentityCredential : public BnIdentityCredential {
public:
- IdentityCredential(sp<SecureHardwarePresentationProxy> hwProxy,
+ IdentityCredential(sp<SecureHardwareProxyFactory> hwProxyFactory,
+ sp<SecureHardwarePresentationProxy> hwProxy,
const vector<uint8_t>& credentialData)
- : hwProxy_(hwProxy),
+ : hwProxyFactory_(hwProxyFactory),
+ hwProxy_(hwProxy),
credentialData_(credentialData),
numStartRetrievalCalls_(0),
expectedDeviceNameSpacesSize_(0) {}
@@ -58,6 +60,11 @@
// Methods from IIdentityCredential follow.
ndk::ScopedAStatus deleteCredential(vector<uint8_t>* outProofOfDeletionSignature) override;
+ ndk::ScopedAStatus deleteCredentialWithChallenge(
+ const vector<uint8_t>& challenge,
+ vector<uint8_t>* outProofOfDeletionSignature) override;
+ ndk::ScopedAStatus proveOwnership(const vector<uint8_t>& challenge,
+ vector<uint8_t>* outProofOfOwnershipSignature) override;
ndk::ScopedAStatus createEphemeralKeyPair(vector<uint8_t>* outKeyPair) override;
ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override;
@@ -79,8 +86,16 @@
ndk::ScopedAStatus generateSigningKeyPair(vector<uint8_t>* outSigningKeyBlob,
Certificate* outSigningKeyCertificate) override;
+ ndk::ScopedAStatus updateCredential(
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) override;
+
private:
+ ndk::ScopedAStatus deleteCredentialCommon(const vector<uint8_t>& challenge,
+ bool includeChallenge,
+ vector<uint8_t>* outProofOfDeletionSignature);
+
// Set by constructor
+ sp<SecureHardwareProxyFactory> hwProxyFactory_;
sp<SecureHardwarePresentationProxy> hwProxy_;
vector<uint8_t> credentialData_;
int numStartRetrievalCalls_;
@@ -88,6 +103,7 @@
// Set by initialize()
string docType_;
bool testCredential_;
+ vector<uint8_t> encryptedCredentialKeys_;
// Set by createEphemeralKeyPair()
vector<uint8_t> ephemeralPublicKey_;
diff --git a/identity/aidl/default/common/IdentityCredentialStore.cpp b/identity/aidl/default/common/IdentityCredentialStore.cpp
index 13f91aa..e6b5466 100644
--- a/identity/aidl/default/common/IdentityCredentialStore.cpp
+++ b/identity/aidl/default/common/IdentityCredentialStore.cpp
@@ -63,7 +63,7 @@
sp<SecureHardwarePresentationProxy> hwProxy = hwProxyFactory_->createPresentationProxy();
shared_ptr<IdentityCredential> credential =
- ndk::SharedRefBase::make<IdentityCredential>(hwProxy, credentialData);
+ ndk::SharedRefBase::make<IdentityCredential>(hwProxyFactory_, hwProxy, credentialData);
auto ret = credential->initialize();
if (ret != IIdentityCredentialStore::STATUS_OK) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
diff --git a/identity/aidl/default/common/SecureHardwareProxy.h b/identity/aidl/default/common/SecureHardwareProxy.h
index b89ad87..a1ed1ef 100644
--- a/identity/aidl/default/common/SecureHardwareProxy.h
+++ b/identity/aidl/default/common/SecureHardwareProxy.h
@@ -64,6 +64,9 @@
virtual bool initialize(bool testCredential) = 0;
+ virtual bool initializeForUpdate(bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) = 0;
+
// Returns public key certificate chain with attestation.
//
// This must return an entire certificate chain and its implementation must
@@ -164,8 +167,14 @@
virtual optional<vector<uint8_t>> finishRetrieval();
virtual optional<vector<uint8_t>> deleteCredential(const string& docType,
+ const vector<uint8_t>& challenge,
+ bool includeChallenge,
size_t proofOfDeletionCborSize) = 0;
+ virtual optional<vector<uint8_t>> proveOwnership(const string& docType, bool testCredential,
+ const vector<uint8_t>& challenge,
+ size_t proofOfOwnershipCborSize) = 0;
+
virtual bool shutdown() = 0;
};
diff --git a/identity/aidl/default/common/WritableIdentityCredential.cpp b/identity/aidl/default/common/WritableIdentityCredential.cpp
index 1328f36..2d897c7 100644
--- a/identity/aidl/default/common/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/common/WritableIdentityCredential.cpp
@@ -40,7 +40,20 @@
bool WritableIdentityCredential::initialize() {
if (!hwProxy_->initialize(testCredential_)) {
- LOG(ERROR) << "hwProxy->initialize failed";
+ LOG(ERROR) << "hwProxy->initialize() failed";
+ return false;
+ }
+ startPersonalizationCalled_ = false;
+ firstEntry_ = true;
+
+ return true;
+}
+
+// Used when updating a credential. Returns false on failure.
+bool WritableIdentityCredential::initializeForUpdate(
+ const vector<uint8_t>& encryptedCredentialKeys) {
+ if (!hwProxy_->initializeForUpdate(testCredential_, docType_, encryptedCredentialKeys)) {
+ LOG(ERROR) << "hwProxy->initializeForUpdate() failed";
return false;
}
startPersonalizationCalled_ = false;
diff --git a/identity/aidl/default/common/WritableIdentityCredential.h b/identity/aidl/default/common/WritableIdentityCredential.h
index c6f0628..36ad430 100644
--- a/identity/aidl/default/common/WritableIdentityCredential.h
+++ b/identity/aidl/default/common/WritableIdentityCredential.h
@@ -36,16 +36,22 @@
class WritableIdentityCredential : public BnWritableIdentityCredential {
public:
+ // For a new credential, call initialize() right after construction.
+ //
+ // For an updated credential, call initializeForUpdate() right after construction.
+ //
WritableIdentityCredential(sp<SecureHardwareProvisioningProxy> hwProxy, const string& docType,
bool testCredential)
: hwProxy_(hwProxy), docType_(docType), testCredential_(testCredential) {}
~WritableIdentityCredential();
- // Creates the Credential Key. Returns false on failure. Must be called
- // right after construction.
+ // Creates the Credential Key. Returns false on failure.
bool initialize();
+ // Used when updating a credential. Returns false on failure.
+ bool initializeForUpdate(const vector<uint8_t>& encryptedCredentialKeys);
+
// Methods from IWritableIdentityCredential follow.
ndk::ScopedAStatus getAttestationCertificate(const vector<uint8_t>& attestationApplicationId,
const vector<uint8_t>& attestationChallenge,
diff --git a/identity/aidl/default/identity-default.xml b/identity/aidl/default/identity-default.xml
index 37d5b81..a074250 100644
--- a/identity/aidl/default/identity-default.xml
+++ b/identity/aidl/default/identity-default.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.identity</name>
- <version>2</version>
+ <version>3</version>
<interface>
<name>IIdentityCredentialStore</name>
<instance>default</instance>
diff --git a/identity/aidl/default/libeic/EicCbor.c b/identity/aidl/default/libeic/EicCbor.c
index ec049b1..fe131eb 100644
--- a/identity/aidl/default/libeic/EicCbor.c
+++ b/identity/aidl/default/libeic/EicCbor.c
@@ -17,6 +17,7 @@
#include "EicCbor.h"
void eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize) {
+ eicMemSet(cbor, '\0', sizeof(EicCbor));
cbor->size = 0;
cbor->bufferSize = bufferSize;
cbor->buffer = buffer;
@@ -26,6 +27,7 @@
void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
const uint8_t* hmacKey, size_t hmacKeySize) {
+ eicMemSet(cbor, '\0', sizeof(EicCbor));
cbor->size = 0;
cbor->bufferSize = bufferSize;
cbor->buffer = buffer;
@@ -33,6 +35,10 @@
eicOpsHmacSha256Init(&cbor->digester.hmacSha256, hmacKey, hmacKeySize);
}
+void eicCborEnableSecondaryDigesterSha256(EicCbor* cbor, EicSha256Ctx* sha256) {
+ cbor->secondaryDigesterSha256 = sha256;
+}
+
void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
switch (cbor->digestType) {
case EIC_CBOR_DIGEST_TYPE_SHA256:
@@ -53,6 +59,9 @@
eicOpsHmacSha256Update(&cbor->digester.hmacSha256, data, size);
break;
}
+ if (cbor->secondaryDigesterSha256 != NULL) {
+ eicOpsSha256Update(cbor->secondaryDigesterSha256, data, size);
+ }
if (cbor->size >= cbor->bufferSize) {
cbor->size += size;
diff --git a/identity/aidl/default/libeic/EicCbor.h b/identity/aidl/default/libeic/EicCbor.h
index 4686b38..9c0f531 100644
--- a/identity/aidl/default/libeic/EicCbor.h
+++ b/identity/aidl/default/libeic/EicCbor.h
@@ -53,6 +53,9 @@
EicHmacSha256Ctx hmacSha256;
} digester;
+ // The secondary digester, may be unset.
+ EicSha256Ctx* secondaryDigesterSha256;
+
// The buffer used for building up CBOR or NULL if bufferSize is 0.
uint8_t* buffer;
} EicCbor;
@@ -70,6 +73,14 @@
void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
const uint8_t* hmacKey, size_t hmacKeySize);
+/* Enables a secondary digester.
+ *
+ * May be enabled midway through processing, this can be used to e.g. calculate
+ * a digest of Sig_structure (for COSE_Sign1) and a separate digest of its
+ * payload.
+ */
+void eicCborEnableSecondaryDigesterSha256(EicCbor* cbor, EicSha256Ctx* sha256);
+
/* Finishes building CBOR and returns the digest. */
void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
diff --git a/identity/aidl/default/libeic/EicOps.h b/identity/aidl/default/libeic/EicOps.h
index da4dabf..d4fcf0e 100644
--- a/identity/aidl/default/libeic/EicOps.h
+++ b/identity/aidl/default/libeic/EicOps.h
@@ -207,14 +207,17 @@
// Generate an X.509 certificate for the key identified by |publicKey| which
// must be of the form returned by eicOpsCreateEcKey().
//
+// If proofOfBinding is not NULL, it will be included as an OCTET_STRING
+// X.509 extension at OID 1.3.6.1.4.1.11129.2.1.26.
+//
// The certificate will be signed by the key identified by |signingKey| which
// must be of the form returned by eicOpsCreateEcKey().
//
bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
const char* issuerName, const char* subjectName, time_t validityNotBefore,
- time_t validityNotAfter, uint8_t* cert,
- size_t* certSize); // inout
+ time_t validityNotAfter, const uint8_t* proofOfBinding,
+ size_t proofOfBindingSize, uint8_t* cert, size_t* certSize); // inout
// Uses |privateKey| to create an ECDSA signature of some data (the SHA-256 must
// be given by |digestOfData|). Returns the signature in |signature|.
diff --git a/identity/aidl/default/libeic/EicPresentation.c b/identity/aidl/default/libeic/EicPresentation.c
index d3f5556..5e9a280 100644
--- a/identity/aidl/default/libeic/EicPresentation.c
+++ b/identity/aidl/default/libeic/EicPresentation.c
@@ -19,13 +19,28 @@
#include <inttypes.h>
bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
- const uint8_t encryptedCredentialKeys[80]) {
- uint8_t credentialKeys[52];
+ const uint8_t* encryptedCredentialKeys,
+ size_t encryptedCredentialKeysSize) {
+ uint8_t credentialKeys[86];
+ bool expectPopSha256 = false;
+
+ // For feature version 202009 it's 52 bytes long and for feature version 202101 it's 86
+ // bytes (the additional data is the ProofOfProvisioning SHA-256). We need
+ // to support loading all feature versions.
+ //
+ if (encryptedCredentialKeysSize == 52 + 28) {
+ /* do nothing */
+ } else if (encryptedCredentialKeysSize == 86 + 28) {
+ expectPopSha256 = true;
+ } else {
+ eicDebug("Unexpected size %zd for encryptedCredentialKeys", encryptedCredentialKeysSize);
+ return false;
+ }
eicMemSet(ctx, '\0', sizeof(EicPresentation));
if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
- 80,
+ encryptedCredentialKeysSize,
// DocType is the additionalAuthenticatedData
(const uint8_t*)docType, eicStrLen(docType), credentialKeys)) {
eicDebug("Error decrypting CredentialKeys");
@@ -34,25 +49,42 @@
// It's supposed to look like this;
//
+ // Feature version 202009:
+ //
// CredentialKeys = [
// bstr, ; storageKey, a 128-bit AES key
- // bstr ; credentialPrivKey, the private key for credentialKey
+ // bstr, ; credentialPrivKey, the private key for credentialKey
// ]
//
- // where storageKey is 16 bytes and credentialPrivateKey is 32 bytes.
+ // Feature version 202101:
//
- // So the first two bytes will be 0x82 0x50 indicating resp. an array of two elements
- // and a bstr of 16 elements. Sixteen bytes later (offset 18 and 19) there will be
- // a bstr of 32 bytes. It's encoded as two bytes 0x58 and 0x20.
+ // CredentialKeys = [
+ // bstr, ; storageKey, a 128-bit AES key
+ // bstr, ; credentialPrivKey, the private key for credentialKey
+ // bstr ; proofOfProvisioning SHA-256
+ // ]
//
- if (credentialKeys[0] != 0x82 || credentialKeys[1] != 0x50 || credentialKeys[18] != 0x58 ||
- credentialKeys[19] != 0x20) {
+ // where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and proofOfProvisioning
+ // SHA-256 is 32 bytes.
+ //
+ if (credentialKeys[0] != (expectPopSha256 ? 0x83 : 0x82) || // array of two or three elements
+ credentialKeys[1] != 0x50 || // 16-byte bstr
+ credentialKeys[18] != 0x58 || credentialKeys[19] != 0x20) { // 32-byte bstr
eicDebug("Invalid CBOR for CredentialKeys");
return false;
}
+ if (expectPopSha256) {
+ if (credentialKeys[52] != 0x58 || credentialKeys[53] != 0x20) { // 32-byte bstr
+ eicDebug("Invalid CBOR for CredentialKeys");
+ return false;
+ }
+ }
eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20, EIC_P256_PRIV_KEY_SIZE);
ctx->testCredential = testCredential;
+ if (expectPopSha256) {
+ eicMemCpy(ctx->proofOfProvisioningSha256, credentialKeys + 54, EIC_SHA256_DIGEST_SIZE);
+ }
return true;
}
@@ -61,6 +93,35 @@
uint8_t signingKeyBlob[60]) {
uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
uint8_t signingKeyPub[EIC_P256_PUB_KEY_SIZE];
+ uint8_t cborBuf[64];
+
+ // Generate the ProofOfBinding CBOR to include in the X.509 certificate in
+ // IdentityCredentialAuthenticationKeyExtension CBOR. This CBOR is defined
+ // by the following CDDL
+ //
+ // ProofOfBinding = [
+ // "ProofOfBinding",
+ // bstr, // Contains the SHA-256 of ProofOfProvisioning
+ // ]
+ //
+ // This array may grow in the future if other information needs to be
+ // conveyed.
+ //
+ // The bytes of ProofOfBinding is is represented as an OCTET_STRING
+ // and stored at OID 1.3.6.1.4.1.11129.2.1.26.
+ //
+
+ EicCbor cbor;
+ eicCborInit(&cbor, cborBuf, sizeof cborBuf);
+ eicCborAppendArray(&cbor, 2);
+ eicCborAppendString(&cbor, "ProofOfBinding");
+ eicCborAppendByteString(&cbor, ctx->proofOfProvisioningSha256, EIC_SHA256_DIGEST_SIZE);
+ if (cbor.size > sizeof(cborBuf)) {
+ eicDebug("Exceeded buffer size");
+ return false;
+ }
+ const uint8_t* proofOfBinding = cborBuf;
+ size_t proofOfBindingSize = cbor.size;
if (!eicOpsCreateEcKey(signingKeyPriv, signingKeyPub)) {
eicDebug("Error creating signing key");
@@ -73,7 +134,8 @@
if (!eicOpsSignEcKey(signingKeyPub, ctx->credentialPrivateKey, 1,
"Android Identity Credential Key", // issuer CN
"Android Identity Credential Authentication Key", // subject CN
- validityNotBefore, validityNotAfter, publicKeyCert, publicKeyCertSize)) {
+ validityNotBefore, validityNotAfter, proofOfBinding, proofOfBindingSize,
+ publicKeyCert, publicKeyCertSize)) {
eicDebug("Error creating certificate for signing key");
return false;
}
@@ -674,7 +736,8 @@
}
bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
- size_t proofOfDeletionCborSize,
+ const uint8_t* challenge, size_t challengeSize,
+ bool includeChallenge, size_t proofOfDeletionCborSize,
uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
EicCbor cbor;
@@ -712,9 +775,12 @@
eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfDeletionCborSize);
// Finally, the CBOR that we're actually signing.
- eicCborAppendArray(&cbor, 3);
+ eicCborAppendArray(&cbor, includeChallenge ? 4 : 3);
eicCborAppendString(&cbor, "ProofOfDeletion");
eicCborAppendString(&cbor, docType);
+ if (includeChallenge) {
+ eicCborAppendByteString(&cbor, challenge, challengeSize);
+ }
eicCborAppendBool(&cbor, ctx->testCredential);
uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
@@ -726,3 +792,59 @@
return true;
}
+
+bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, bool testCredential,
+ const uint8_t* challenge, size_t challengeSize,
+ size_t proofOfOwnershipCborSize,
+ uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+ EicCbor cbor;
+
+ eicCborInit(&cbor, NULL, 0);
+
+ // What we're going to sign is the COSE ToBeSigned structure which
+ // looks like the following:
+ //
+ // Sig_structure = [
+ // context : "Signature" / "Signature1" / "CounterSignature",
+ // body_protected : empty_or_serialized_map,
+ // ? sign_protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ //
+ eicCborAppendArray(&cbor, 4);
+ eicCborAppendString(&cbor, "Signature1");
+
+ // The COSE Encoded protected headers is just a single field with
+ // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
+ // hard-code the CBOR encoding:
+ static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+ eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
+ sizeof(coseEncodedProtectedHeaders));
+
+ // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+ // so external_aad is the empty bstr
+ static const uint8_t externalAad[0] = {};
+ eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time.
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfOwnershipCborSize);
+
+ // Finally, the CBOR that we're actually signing.
+ eicCborAppendArray(&cbor, 4);
+ eicCborAppendString(&cbor, "ProofOfOwnership");
+ eicCborAppendString(&cbor, docType);
+ eicCborAppendByteString(&cbor, challenge, challengeSize);
+ eicCborAppendBool(&cbor, testCredential);
+
+ uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
+ eicCborFinal(&cbor, cborSha256);
+ if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
+ eicDebug("Error signing proofOfDeletion");
+ return false;
+ }
+
+ return true;
+}
diff --git a/identity/aidl/default/libeic/EicPresentation.h b/identity/aidl/default/libeic/EicPresentation.h
index d798962..7cad068 100644
--- a/identity/aidl/default/libeic/EicPresentation.h
+++ b/identity/aidl/default/libeic/EicPresentation.h
@@ -31,6 +31,8 @@
#define EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE 65
typedef struct {
+ int featureLevel;
+
uint8_t storageKey[EIC_AES_128_KEY_SIZE];
uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
@@ -79,12 +81,17 @@
// SHA-256 for AdditionalData, updated for each entry.
uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
+ // SHA-256 of ProofOfProvisioning. Set to NUL-bytes or initialized from CredentialKeys data
+ // if credential was created with feature version 202101 or later.
+ uint8_t proofOfProvisioningSha256[EIC_SHA256_DIGEST_SIZE];
+
size_t expectedCborSizeAtEnd;
EicCbor cbor;
} EicPresentation;
bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
- const uint8_t encryptedCredentialKeys[80]);
+ const uint8_t* encryptedCredentialKeys,
+ size_t encryptedCredentialKeysSize);
bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now,
uint8_t* publicKeyCert, size_t* publicKeyCertSize,
@@ -219,9 +226,19 @@
// where content is set to the ProofOfDeletion CBOR.
//
bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
- size_t proofOfDeletionCborSize,
+ const uint8_t* challenge, size_t challengeSize,
+ bool includeChallenge, size_t proofOfDeletionCborSize,
uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
+// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
+// where content is set to the ProofOfOwnership CBOR.
+//
+bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, bool testCredential,
+ const uint8_t* challenge, size_t challengeSize,
+ size_t proofOfOwnershipCborSize,
+ uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
#ifdef __cplusplus
}
#endif
diff --git a/identity/aidl/default/libeic/EicProvisioning.c b/identity/aidl/default/libeic/EicProvisioning.c
index f16605c..3b4148e 100644
--- a/identity/aidl/default/libeic/EicProvisioning.c
+++ b/identity/aidl/default/libeic/EicProvisioning.c
@@ -26,10 +26,84 @@
return true;
}
+bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential, const char* docType,
+ const uint8_t* encryptedCredentialKeys,
+ size_t encryptedCredentialKeysSize) {
+ uint8_t credentialKeys[86];
+
+ // For feature version 202009 it's 52 bytes long and for feature version 202101 it's 86
+ // bytes (the additional data is the ProofOfProvisioning SHA-256). We need
+ // to support loading all feature versions.
+ //
+ bool expectPopSha256 = false;
+ if (encryptedCredentialKeysSize == 52 + 28) {
+ /* do nothing */
+ } else if (encryptedCredentialKeysSize == 86 + 28) {
+ expectPopSha256 = true;
+ } else {
+ eicDebug("Unexpected size %zd for encryptedCredentialKeys", encryptedCredentialKeysSize);
+ return false;
+ }
+
+ eicMemSet(ctx, '\0', sizeof(EicProvisioning));
+ ctx->testCredential = testCredential;
+
+ if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
+ encryptedCredentialKeysSize,
+ // DocType is the additionalAuthenticatedData
+ (const uint8_t*)docType, eicStrLen(docType), credentialKeys)) {
+ eicDebug("Error decrypting CredentialKeys");
+ return false;
+ }
+
+ // It's supposed to look like this;
+ //
+ // Feature version 202009:
+ //
+ // CredentialKeys = [
+ // bstr, ; storageKey, a 128-bit AES key
+ // bstr, ; credentialPrivKey, the private key for credentialKey
+ // ]
+ //
+ // Feature version 202101:
+ //
+ // CredentialKeys = [
+ // bstr, ; storageKey, a 128-bit AES key
+ // bstr, ; credentialPrivKey, the private key for credentialKey
+ // bstr ; proofOfProvisioning SHA-256
+ // ]
+ //
+ // where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and proofOfProvisioning
+ // SHA-256 is 32 bytes.
+ //
+ if (credentialKeys[0] != (expectPopSha256 ? 0x83 : 0x82) || // array of two or three elements
+ credentialKeys[1] != 0x50 || // 16-byte bstr
+ credentialKeys[18] != 0x58 || credentialKeys[19] != 0x20) { // 32-byte bstr
+ eicDebug("Invalid CBOR for CredentialKeys");
+ return false;
+ }
+ if (expectPopSha256) {
+ if (credentialKeys[52] != 0x58 || credentialKeys[53] != 0x20) { // 32-byte bstr
+ eicDebug("Invalid CBOR for CredentialKeys");
+ return false;
+ }
+ }
+ eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
+ eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20, EIC_P256_PRIV_KEY_SIZE);
+ // Note: We don't care about the previous ProofOfProvisioning SHA-256
+ ctx->isUpdate = true;
+ return true;
+}
+
bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
size_t challengeSize, const uint8_t* applicationId,
size_t applicationIdSize, uint8_t* publicKeyCert,
size_t* publicKeyCertSize) {
+ if (ctx->isUpdate) {
+ eicDebug("Cannot create CredentialKey on update");
+ return false;
+ }
+
if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge, challengeSize,
applicationId, applicationIdSize, ctx->testCredential,
publicKeyCert, publicKeyCertSize)) {
@@ -96,6 +170,9 @@
eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedProofOfProvisioningSize);
ctx->expectedCborSizeAtEnd = expectedProofOfProvisioningSize + ctx->cbor.size;
+ eicOpsSha256Init(&ctx->proofOfProvisioningDigester);
+ eicCborEnableSecondaryDigesterSha256(&ctx->cbor, &ctx->proofOfProvisioningDigester);
+
eicCborAppendArray(&ctx->cbor, 5);
eicCborAppendString(&ctx->cbor, "ProofOfProvisioning");
eicCborAppendString(&ctx->cbor, docType);
@@ -260,14 +337,23 @@
}
bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
- uint8_t encryptedCredentialKeys[80]) {
+ uint8_t* encryptedCredentialKeys,
+ size_t* encryptedCredentialKeysSize) {
EicCbor cbor;
- uint8_t cborBuf[52];
+ uint8_t cborBuf[86];
+
+ if (*encryptedCredentialKeysSize < 86 + 28) {
+ eicDebug("encryptedCredentialKeysSize is %zd which is insufficient");
+ return false;
+ }
eicCborInit(&cbor, cborBuf, sizeof(cborBuf));
- eicCborAppendArray(&cbor, 2);
+ eicCborAppendArray(&cbor, 3);
eicCborAppendByteString(&cbor, ctx->storageKey, EIC_AES_128_KEY_SIZE);
eicCborAppendByteString(&cbor, ctx->credentialPrivateKey, EIC_P256_PRIV_KEY_SIZE);
+ uint8_t popSha256[EIC_SHA256_DIGEST_SIZE];
+ eicOpsSha256Final(&ctx->proofOfProvisioningDigester, popSha256);
+ eicCborAppendByteString(&cbor, popSha256, EIC_SHA256_DIGEST_SIZE);
if (cbor.size > sizeof(cborBuf)) {
eicDebug("Exceeded buffer size");
return false;
@@ -285,6 +371,7 @@
eicDebug("Error encrypting CredentialKeys");
return false;
}
+ *encryptedCredentialKeysSize = cbor.size + 28;
return true;
}
diff --git a/identity/aidl/default/libeic/EicProvisioning.h b/identity/aidl/default/libeic/EicProvisioning.h
index 836d16e..f064787 100644
--- a/identity/aidl/default/libeic/EicProvisioning.h
+++ b/identity/aidl/default/libeic/EicProvisioning.h
@@ -31,7 +31,7 @@
#define EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS 32
typedef struct {
- // Set by eicCreateCredentialKey.
+ // Set by eicCreateCredentialKey() OR eicProvisioningInitForUpdate()
uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
int numEntryCounts;
@@ -43,6 +43,7 @@
size_t curEntrySize;
size_t curEntryNumBytesReceived;
+ // Set by eicProvisioningInit() OR eicProvisioningInitForUpdate()
uint8_t storageKey[EIC_AES_128_KEY_SIZE];
size_t expectedCborSizeAtEnd;
@@ -50,13 +51,23 @@
// SHA-256 for AdditionalData, updated for each entry.
uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
+ // Digester just for ProofOfProvisioning (without Sig_structure).
+ EicSha256Ctx proofOfProvisioningDigester;
+
EicCbor cbor;
bool testCredential;
+
+ // Set to true if this is an update.
+ bool isUpdate;
} EicProvisioning;
bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential);
+bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential, const char* docType,
+ const uint8_t* encryptedCredentialKeys,
+ size_t encryptedCredentialKeysSize);
+
bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
size_t challengeSize, const uint8_t* applicationId,
size_t applicationIdSize, uint8_t* publicKeyCert,
@@ -107,14 +118,18 @@
// CredentialKeys = [
// bstr, ; storageKey, a 128-bit AES key
// bstr ; credentialPrivKey, the private key for credentialKey
+// bstr ; SHA-256(ProofOfProvisioning)
// ]
//
+// for feature version 202101. For feature version 202009 the third field was not present.
+//
// Since |storageKey| is 16 bytes and |credentialPrivKey| is 32 bytes, the
-// encoded CBOR for CredentialKeys is 52 bytes and consequently
-// |encryptedCredentialKeys| will be 52 + 28 = 80 bytes.
+// encoded CBOR for CredentialKeys is 86 bytes and consequently
+// |encryptedCredentialKeys| will be no longer than 86 + 28 = 114 bytes.
//
bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
- uint8_t encryptedCredentialKeys[80]);
+ uint8_t* encryptedCredentialKeys,
+ size_t* encryptedCredentialKeysSize);
#ifdef __cplusplus
}
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index 03966de..f1b1bcf 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -4,13 +4,21 @@
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
],
+ cflags: [
+ "-Wno-deprecated-declarations",
+ ],
srcs: [
- "VtsHalIdentityEndToEndTest.cpp",
"VtsIWritableIdentityCredentialTests.cpp",
- "VtsIdentityTestUtils.cpp",
+ "Util.cpp",
"VtsAttestationTests.cpp",
"UserAuthTests.cpp",
"ReaderAuthTests.cpp",
+ "DeleteCredentialTests.cpp",
+ "ProveOwnershipTests.cpp",
+ "UpdateCredentialTests.cpp",
+ "EndToEndTests.cpp",
+ "TestCredentialTests.cpp",
+ "AuthenticationKeyTests.cpp",
],
shared_libs: [
"libbinder",
@@ -22,9 +30,9 @@
"libpuresoftkeymasterdevice",
"android.hardware.keymaster@4.0",
"android.hardware.identity-support-lib",
- "android.hardware.identity-cpp",
- "android.hardware.keymaster-cpp",
- "android.hardware.keymaster-ndk_platform",
+ "android.hardware.identity-unstable-cpp",
+ "android.hardware.keymaster-unstable-cpp",
+ "android.hardware.keymaster-unstable-ndk_platform",
"libkeymaster4support",
"libkeymaster4_1support",
],
diff --git a/identity/aidl/vts/AuthenticationKeyTests.cpp b/identity/aidl/vts/AuthenticationKeyTests.cpp
new file mode 100644
index 0000000..bda3e70
--- /dev/null
+++ b/identity/aidl/vts/AuthenticationKeyTests.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "TestCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class AuthenticationKeyTests : public testing::TestWithParam<string> {
+ public:
+ virtual void SetUp() override {
+ string halInstanceName = GetParam();
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(halInstanceName.c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ halApiVersion_ = credentialStore_->getInterfaceVersion();
+ }
+
+ sp<IIdentityCredentialStore> credentialStore_;
+ int halApiVersion_;
+};
+
+TEST_P(AuthenticationKeyTests, proofOfProvisionInAuthKeyCert) {
+ if (halApiVersion_ < 3) {
+ GTEST_SKIP() << "Need HAL API version 3, have " << halApiVersion_;
+ }
+
+ string docType = "org.iso.18013-5.2019.mdl";
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credentialStore_
+ ->createCredential(docType,
+ true, // testCredential
+ &wc)
+ .isOk());
+
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain)
+ .isOk());
+
+ optional<vector<uint8_t>> optCredentialPubKey =
+ support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+ ASSERT_TRUE(optCredentialPubKey);
+ vector<uint8_t> credentialPubKey;
+ credentialPubKey = optCredentialPubKey.value();
+
+ size_t proofOfProvisioningSize = 112;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+ {1} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: open access - don't care about the returned SACP
+ SecureAccessControlProfile sacp;
+ ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+ // Single entry - don't care about the returned encrypted data
+ vector<uint8_t> encryptedData;
+ vector<uint8_t> tstrLastName = cppbor::Tstr("Turing").encode();
+ ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Last name", tstrLastName.size()).isOk());
+ ASSERT_TRUE(wc->addEntryValue(tstrLastName, &encryptedData).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ vector<uint8_t> credentialData;
+ Status status = wc->finishAddingEntries(&credentialData, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+
+ optional<vector<uint8_t>> proofOfProvisioning =
+ support::coseSignGetPayload(proofOfProvisioningSignature);
+ ASSERT_TRUE(proofOfProvisioning);
+ string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {});
+ EXPECT_EQ(
+ "[\n"
+ " 'ProofOfProvisioning',\n"
+ " 'org.iso.18013-5.2019.mdl',\n"
+ " [\n"
+ " {\n"
+ " 'id' : 1,\n"
+ " },\n"
+ " ],\n"
+ " {\n"
+ " 'ns' : [\n"
+ " {\n"
+ " 'name' : 'Last name',\n"
+ " 'value' : 'Turing',\n"
+ " 'accessControlProfiles' : [1, ],\n"
+ " },\n"
+ " ],\n"
+ " },\n"
+ " true,\n"
+ "]",
+ cborPretty);
+ // Make sure it's signed by the CredentialKey in the returned cert chain.
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+ {}, // Additional data
+ credentialPubKey));
+
+ // Now get a credential and have it create AuthenticationKey so we can check
+ // the certificate.
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData, &credential)
+ .isOk());
+ vector<uint8_t> signingKeyBlob;
+ Certificate signingKeyCertificate;
+ ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+ optional<vector<uint8_t>> signingPubKey =
+ support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
+ EXPECT_TRUE(signingPubKey);
+
+ // SHA-256(ProofOfProvisioning) is embedded in CBOR with the following CDDL
+ //
+ // ProofOfBinding = [
+ // "ProofOfBinding",
+ // bstr, // Contains the SHA-256 of ProofOfProvisioning
+ // ]
+ //
+ // Check that.
+ //
+ optional<vector<uint8_t>> proofOfBinding = support::certificateGetExtension(
+ signingKeyCertificate.encodedCertificate, "1.3.6.1.4.1.11129.2.1.26");
+ ASSERT_TRUE(proofOfBinding);
+ auto [item, _, message] = cppbor::parse(proofOfBinding.value());
+ ASSERT_NE(item, nullptr) << message;
+ const cppbor::Array* arrayItem = item->asArray();
+ ASSERT_NE(arrayItem, nullptr);
+ ASSERT_EQ(arrayItem->size(), 2);
+ const cppbor::Tstr* strItem = (*arrayItem)[0]->asTstr();
+ ASSERT_NE(strItem, nullptr);
+ EXPECT_EQ(strItem->value(), "ProofOfBinding");
+ const cppbor::Bstr* popSha256Item = (*arrayItem)[1]->asBstr();
+ ASSERT_NE(popSha256Item, nullptr);
+ EXPECT_EQ(popSha256Item->value(), support::sha256(proofOfProvisioning.value()));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AuthenticationKeyTests);
+INSTANTIATE_TEST_SUITE_P(
+ Identity, AuthenticationKeyTests,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/DeleteCredentialTests.cpp b/identity/aidl/vts/DeleteCredentialTests.cpp
new file mode 100644
index 0000000..1d30067
--- /dev/null
+++ b/identity/aidl/vts/DeleteCredentialTests.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "DeleteCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class DeleteCredentialTests : public testing::TestWithParam<string> {
+ public:
+ virtual void SetUp() override {
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(GetParam().c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ halApiVersion_ = credentialStore_->getInterfaceVersion();
+ }
+
+ void provisionData();
+
+ // Set by provisionData
+ vector<uint8_t> credentialData_;
+ vector<uint8_t> credentialPubKey_;
+
+ sp<IIdentityCredentialStore> credentialStore_;
+ int halApiVersion_;
+};
+
+void DeleteCredentialTests::provisionData() {
+ string docType = "org.iso.18013-5.2019.mdl";
+ bool testCredential = true;
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain)
+ .isOk());
+
+ optional<vector<uint8_t>> optCredentialPubKey =
+ support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+ ASSERT_TRUE(optCredentialPubKey);
+ credentialPubKey_ = optCredentialPubKey.value();
+
+ size_t proofOfProvisioningSize = 106;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+ {1} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: open access - don't care about the returned SACP
+ SecureAccessControlProfile sacp;
+ ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+ // Single entry - don't care about the returned encrypted data
+ ASSERT_TRUE(wc->beginAddEntry({0}, "ns", "Some Data", 1).isOk());
+ vector<uint8_t> encryptedData;
+ ASSERT_TRUE(wc->addEntryValue({9}, &encryptedData).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+}
+
+TEST_P(DeleteCredentialTests, Delete) {
+ provisionData();
+
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData_, &credential)
+ .isOk());
+
+ vector<uint8_t> proofOfDeletionSignature;
+ ASSERT_TRUE(credential->deleteCredential(&proofOfDeletionSignature).isOk());
+ optional<vector<uint8_t>> proofOfDeletion =
+ support::coseSignGetPayload(proofOfDeletionSignature);
+ ASSERT_TRUE(proofOfDeletion);
+ string cborPretty = support::cborPrettyPrint(proofOfDeletion.value(), 32, {});
+ EXPECT_EQ("['ProofOfDeletion', 'org.iso.18013-5.2019.mdl', true, ]", cborPretty);
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfDeletionSignature, {}, // Additional data
+ credentialPubKey_));
+}
+
+TEST_P(DeleteCredentialTests, DeleteWithChallenge) {
+ if (halApiVersion_ < 3) {
+ GTEST_SKIP() << "Need HAL API version 3, have " << halApiVersion_;
+ }
+
+ provisionData();
+
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData_, &credential)
+ .isOk());
+
+ vector<uint8_t> challenge = {65, 66, 67};
+ vector<uint8_t> proofOfDeletionSignature;
+ ASSERT_TRUE(
+ credential->deleteCredentialWithChallenge(challenge, &proofOfDeletionSignature).isOk());
+ optional<vector<uint8_t>> proofOfDeletion =
+ support::coseSignGetPayload(proofOfDeletionSignature);
+ ASSERT_TRUE(proofOfDeletion);
+ string cborPretty = support::cborPrettyPrint(proofOfDeletion.value(), 32, {});
+ EXPECT_EQ("['ProofOfDeletion', 'org.iso.18013-5.2019.mdl', {0x41, 0x42, 0x43}, true, ]",
+ cborPretty);
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfDeletionSignature, {}, // Additional data
+ credentialPubKey_));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DeleteCredentialTests);
+INSTANTIATE_TEST_SUITE_P(
+ Identity, DeleteCredentialTests,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp b/identity/aidl/vts/EndToEndTests.cpp
similarity index 93%
rename from identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
rename to identity/aidl/vts/EndToEndTests.cpp
index cdecb97..5798b4c 100644
--- a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
+++ b/identity/aidl/vts/EndToEndTests.cpp
@@ -29,7 +29,7 @@
#include <map>
#include <tuple>
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
namespace android::hardware::identity {
@@ -50,18 +50,20 @@
using test_utils::validateAttestationCertificate;
-class IdentityAidl : public testing::TestWithParam<std::string> {
+class EndToEndTests : public testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
String16(GetParam().c_str()));
ASSERT_NE(credentialStore_, nullptr);
+ halApiVersion_ = credentialStore_->getInterfaceVersion();
}
sp<IIdentityCredentialStore> credentialStore_;
+ int halApiVersion_;
};
-TEST_P(IdentityAidl, hardwareInformation) {
+TEST_P(EndToEndTests, hardwareInformation) {
HardwareInformation info;
ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
ASSERT_GT(info.credentialStoreName.size(), 0);
@@ -69,20 +71,21 @@
ASSERT_GE(info.dataChunkSize, 256);
}
-tuple<bool, string, vector<uint8_t>, vector<uint8_t>> extractFromTestCredentialData(
- const vector<uint8_t>& credentialData) {
+tuple<bool, string, vector<uint8_t>, vector<uint8_t>, vector<uint8_t>>
+extractFromTestCredentialData(const vector<uint8_t>& credentialData) {
string docType;
vector<uint8_t> storageKey;
vector<uint8_t> credentialPrivKey;
+ vector<uint8_t> sha256Pop;
auto [item, _, message] = cppbor::parse(credentialData);
if (item == nullptr) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
const cppbor::Array* arrayItem = item->asArray();
if (arrayItem == nullptr || arrayItem->size() != 3) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
@@ -92,7 +95,7 @@
const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
if (docTypeItem == nullptr || testCredentialItem == nullptr ||
encryptedCredentialKeysItem == nullptr) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
docType = docTypeItem->value();
@@ -103,28 +106,38 @@
optional<vector<uint8_t>> decryptedCredentialKeys =
support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
if (!decryptedCredentialKeys) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
if (dckItem == nullptr) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
const cppbor::Array* dckArrayItem = dckItem->asArray();
- if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ if (dckArrayItem == nullptr) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+ }
+ if (dckArrayItem->size() < 2) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
storageKey = storageKeyItem->value();
credentialPrivKey = credentialPrivKeyItem->value();
- return make_tuple(true, docType, storageKey, credentialPrivKey);
+ if (dckArrayItem->size() == 3) {
+ const cppbor::Bstr* sha256PopItem = (*dckArrayItem)[2]->asBstr();
+ if (sha256PopItem == nullptr) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+ }
+ sha256Pop = sha256PopItem->value();
+ }
+ return make_tuple(true, docType, storageKey, credentialPrivKey, sha256Pop);
}
-TEST_P(IdentityAidl, createAndRetrieveCredential) {
+TEST_P(EndToEndTests, createAndRetrieveCredential) {
// First, generate a key-pair for the reader since its public key will be
// part of the request data.
vector<uint8_t> readerKey;
@@ -277,8 +290,9 @@
// Extract doctype, storage key, and credentialPrivKey from credentialData... this works
// only because we asked for a test-credential meaning that the HBK is all zeroes.
- auto [exSuccess, exDocType, exStorageKey, exCredentialPrivKey] =
+ auto [exSuccess, exDocType, exStorageKey, exCredentialPrivKey, exSha256Pop] =
extractFromTestCredentialData(credentialData);
+
ASSERT_TRUE(exSuccess);
ASSERT_EQ(exDocType, "org.iso.18013-5.2019.mdl");
// ... check that the public key derived from the private key matches what was
@@ -291,6 +305,13 @@
ASSERT_TRUE(exCredentialPubKey);
ASSERT_EQ(exCredentialPubKey.value(), credentialPubKey.value());
+ // Starting with API version 3 (feature version 202101) we require SHA-256(ProofOfProvisioning)
+ // to be in CredentialKeys (which is stored encrypted in CredentialData). Check
+ // that it's there with the expected value.
+ if (halApiVersion_ >= 3) {
+ ASSERT_EQ(exSha256Pop, support::sha256(proofOfProvisioning.value()));
+ }
+
// Now that the credential has been provisioned, read it back and check the
// correct data is returned.
sp<IIdentityCredential> credential;
@@ -498,13 +519,11 @@
EXPECT_EQ(mac, calculatedMac);
}
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(IdentityAidl);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EndToEndTests);
INSTANTIATE_TEST_SUITE_P(
- Identity, IdentityAidl,
+ Identity, EndToEndTests,
testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
android::PrintInstanceNameToString);
-// INSTANTIATE_TEST_SUITE_P(Identity, IdentityAidl,
-// testing::Values("android.hardware.identity.IIdentityCredentialStore/default"));
} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/ProveOwnershipTests.cpp b/identity/aidl/vts/ProveOwnershipTests.cpp
new file mode 100644
index 0000000..d1a3d39
--- /dev/null
+++ b/identity/aidl/vts/ProveOwnershipTests.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "ProveOwnershipTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class ProveOwnershipTests : public testing::TestWithParam<string> {
+ public:
+ virtual void SetUp() override {
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(GetParam().c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ halApiVersion_ = credentialStore_->getInterfaceVersion();
+ }
+
+ void provisionData();
+
+ // Set by provisionData
+ vector<uint8_t> credentialData_;
+ vector<uint8_t> credentialPubKey_;
+
+ sp<IIdentityCredentialStore> credentialStore_;
+ int halApiVersion_;
+};
+
+void ProveOwnershipTests::provisionData() {
+ string docType = "org.iso.18013-5.2019.mdl";
+ bool testCredential = true;
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain)
+ .isOk());
+
+ optional<vector<uint8_t>> optCredentialPubKey =
+ support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+ ASSERT_TRUE(optCredentialPubKey);
+ credentialPubKey_ = optCredentialPubKey.value();
+
+ size_t proofOfProvisioningSize = 106;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+ {1} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: open access - don't care about the returned SACP
+ SecureAccessControlProfile sacp;
+ ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+ // Single entry - don't care about the returned encrypted data
+ ASSERT_TRUE(wc->beginAddEntry({0}, "ns", "Some Data", 1).isOk());
+ vector<uint8_t> encryptedData;
+ ASSERT_TRUE(wc->addEntryValue({9}, &encryptedData).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+}
+
+TEST_P(ProveOwnershipTests, proveOwnership) {
+ if (halApiVersion_ < 3) {
+ GTEST_SKIP() << "Need HAL API version 3, have " << halApiVersion_;
+ }
+
+ provisionData();
+
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData_, &credential)
+ .isOk());
+
+ vector<uint8_t> challenge = {17, 18};
+ vector<uint8_t> proofOfOwnershipSignature;
+ ASSERT_TRUE(credential->proveOwnership(challenge, &proofOfOwnershipSignature).isOk());
+ optional<vector<uint8_t>> proofOfOwnership =
+ support::coseSignGetPayload(proofOfOwnershipSignature);
+ ASSERT_TRUE(proofOfOwnership);
+ string cborPretty = support::cborPrettyPrint(proofOfOwnership.value(), 32, {});
+ EXPECT_EQ("['ProofOfOwnership', 'org.iso.18013-5.2019.mdl', {0x11, 0x12}, true, ]", cborPretty);
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfOwnershipSignature, {}, // Additional data
+ credentialPubKey_));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ProveOwnershipTests);
+INSTANTIATE_TEST_SUITE_P(
+ Identity, ProveOwnershipTests,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/ReaderAuthTests.cpp b/identity/aidl/vts/ReaderAuthTests.cpp
index 0a9fdc0..7656c8e 100644
--- a/identity/aidl/vts/ReaderAuthTests.cpp
+++ b/identity/aidl/vts/ReaderAuthTests.cpp
@@ -32,7 +32,7 @@
#include <map>
#include <utility>
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
namespace android::hardware::identity {
@@ -123,9 +123,9 @@
const vector<uint8_t>& signingKey) {
time_t validityNotBefore = 0;
time_t validityNotAfter = 0xffffffff;
- optional<vector<uint8_t>> cert =
- support::ecPublicKeyGenerateCertificate(publicKey, signingKey, "24601", "Issuer",
- "Subject", validityNotBefore, validityNotAfter);
+ optional<vector<uint8_t>> cert = support::ecPublicKeyGenerateCertificate(
+ publicKey, signingKey, "24601", "Issuer", "Subject", validityNotBefore,
+ validityNotAfter, {});
return cert.value();
}
diff --git a/identity/aidl/vts/TestCredentialTests.cpp b/identity/aidl/vts/TestCredentialTests.cpp
new file mode 100644
index 0000000..d53de3b
--- /dev/null
+++ b/identity/aidl/vts/TestCredentialTests.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "TestCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class TestCredentialTests : public testing::TestWithParam<string> {
+ public:
+ virtual void SetUp() override {
+ string halInstanceName = GetParam();
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(halInstanceName.c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ halApiVersion_ = credentialStore_->getInterfaceVersion();
+ }
+
+ sp<IIdentityCredentialStore> credentialStore_;
+ int halApiVersion_;
+};
+
+TEST_P(TestCredentialTests, testCredential) {
+ string docType = "org.iso.18013-5.2019.mdl";
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credentialStore_
+ ->createCredential(docType,
+ true, // testCredential
+ &wc)
+ .isOk());
+
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain)
+ .isOk());
+
+ optional<vector<uint8_t>> optCredentialPubKey =
+ support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+ ASSERT_TRUE(optCredentialPubKey);
+ vector<uint8_t> credentialPubKey;
+ credentialPubKey = optCredentialPubKey.value();
+
+ size_t proofOfProvisioningSize = 112;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+ {1} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: open access - don't care about the returned SACP
+ SecureAccessControlProfile sacp;
+ ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+ // Single entry - don't care about the returned encrypted data
+ vector<uint8_t> encryptedData;
+ vector<uint8_t> tstrLastName = cppbor::Tstr("Turing").encode();
+ ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Last name", tstrLastName.size()).isOk());
+ ASSERT_TRUE(wc->addEntryValue(tstrLastName, &encryptedData).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ vector<uint8_t> credentialData;
+ Status status = wc->finishAddingEntries(&credentialData, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+
+ optional<vector<uint8_t>> proofOfProvisioning =
+ support::coseSignGetPayload(proofOfProvisioningSignature);
+ ASSERT_TRUE(proofOfProvisioning);
+ string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {});
+ EXPECT_EQ(
+ "[\n"
+ " 'ProofOfProvisioning',\n"
+ " 'org.iso.18013-5.2019.mdl',\n"
+ " [\n"
+ " {\n"
+ " 'id' : 1,\n"
+ " },\n"
+ " ],\n"
+ " {\n"
+ " 'ns' : [\n"
+ " {\n"
+ " 'name' : 'Last name',\n"
+ " 'value' : 'Turing',\n"
+ " 'accessControlProfiles' : [1, ],\n"
+ " },\n"
+ " ],\n"
+ " },\n"
+ " true,\n"
+ "]",
+ cborPretty);
+ // Make sure it's signed by the CredentialKey in the returned cert chain.
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+ {}, // Additional data
+ credentialPubKey));
+
+ // Now analyze credentialData..
+ auto [item, _, message] = cppbor::parse(credentialData);
+ ASSERT_NE(item, nullptr);
+ const cppbor::Array* arrayItem = item->asArray();
+ ASSERT_NE(arrayItem, nullptr);
+ ASSERT_EQ(arrayItem->size(), 3);
+ const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
+ const cppbor::Bool* testCredentialItem =
+ ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
+ : nullptr);
+ EXPECT_EQ(docTypeItem->value(), docType);
+ EXPECT_EQ(testCredentialItem->value(), true);
+
+ vector<uint8_t> hardwareBoundKey = support::getTestHardwareBoundKey();
+ const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
+ const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
+ const vector<uint8_t> docTypeVec(docType.begin(), docType.end());
+ optional<vector<uint8_t>> decryptedCredentialKeys =
+ support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
+ ASSERT_TRUE(decryptedCredentialKeys);
+ auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
+ ASSERT_NE(dckItem, nullptr) << dckMessage;
+ const cppbor::Array* dckArrayItem = dckItem->asArray();
+ ASSERT_NE(dckArrayItem, nullptr);
+ // In HAL API version 1 and 2 this array has two items, in version 3 and later it has three.
+ if (halApiVersion_ < 3) {
+ ASSERT_EQ(dckArrayItem->size(), 2);
+ } else {
+ ASSERT_EQ(dckArrayItem->size(), 3);
+ }
+ const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
+ const vector<uint8_t> storageKey = storageKeyItem->value();
+ // const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
+ // const vector<uint8_t> credentialPrivKey = credentialPrivKeyItem->value();
+
+ // Check storageKey can be used to decrypt |encryptedData| to |tstrLastName|
+ vector<uint8_t> additionalData = cppbor::Map()
+ .add("Namespace", "ns")
+ .add("Name", "Last name")
+ .add("AccessControlProfileIds", cppbor::Array().add(1))
+ .encode();
+ optional<vector<uint8_t>> decryptedDataItemValue =
+ support::decryptAes128Gcm(storageKey, encryptedData, additionalData);
+ ASSERT_TRUE(decryptedDataItemValue);
+ EXPECT_EQ(decryptedDataItemValue.value(), tstrLastName);
+
+ // Check that SHA-256(ProofOfProvisioning) matches (only in HAL API version 3)
+ if (halApiVersion_ >= 3) {
+ const cppbor::Bstr* popSha256Item = (*dckArrayItem)[2]->asBstr();
+ const vector<uint8_t> popSha256 = popSha256Item->value();
+ ASSERT_EQ(popSha256, support::sha256(proofOfProvisioning.value()));
+ }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestCredentialTests);
+INSTANTIATE_TEST_SUITE_P(
+ Identity, TestCredentialTests,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/UpdateCredentialTests.cpp b/identity/aidl/vts/UpdateCredentialTests.cpp
new file mode 100644
index 0000000..9c5ca55
--- /dev/null
+++ b/identity/aidl/vts/UpdateCredentialTests.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "UpdateCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class UpdateCredentialTests : public testing::TestWithParam<string> {
+ public:
+ virtual void SetUp() override {
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(GetParam().c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ halApiVersion_ = credentialStore_->getInterfaceVersion();
+ }
+
+ void provisionData();
+
+ // Set by provisionData
+ vector<uint8_t> credentialData_;
+ vector<uint8_t> credentialPubKey_;
+
+ sp<IIdentityCredentialStore> credentialStore_;
+ int halApiVersion_;
+};
+
+void UpdateCredentialTests::provisionData() {
+ string docType = "org.iso.18013-5.2019.mdl";
+ bool testCredential = true;
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain)
+ .isOk());
+
+ optional<vector<uint8_t>> optCredentialPubKey =
+ support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+ ASSERT_TRUE(optCredentialPubKey);
+ credentialPubKey_ = optCredentialPubKey.value();
+
+ size_t proofOfProvisioningSize = 112;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+ {1} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: open access - don't care about the returned SACP
+ SecureAccessControlProfile sacp;
+ ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+ // Single entry - don't care about the returned encrypted data
+ vector<uint8_t> encryptedData;
+ vector<uint8_t> tstrLastName = cppbor::Tstr("Prince").encode();
+ ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Last name", tstrLastName.size()).isOk());
+ ASSERT_TRUE(wc->addEntryValue(tstrLastName, &encryptedData).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+
+ optional<vector<uint8_t>> proofOfProvisioning =
+ support::coseSignGetPayload(proofOfProvisioningSignature);
+ ASSERT_TRUE(proofOfProvisioning);
+ string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {});
+ EXPECT_EQ(
+ "[\n"
+ " 'ProofOfProvisioning',\n"
+ " 'org.iso.18013-5.2019.mdl',\n"
+ " [\n"
+ " {\n"
+ " 'id' : 1,\n"
+ " },\n"
+ " ],\n"
+ " {\n"
+ " 'ns' : [\n"
+ " {\n"
+ " 'name' : 'Last name',\n"
+ " 'value' : 'Prince',\n"
+ " 'accessControlProfiles' : [1, ],\n"
+ " },\n"
+ " ],\n"
+ " },\n"
+ " true,\n"
+ "]",
+ cborPretty);
+ // Make sure it's signed by the CredentialKey in the returned cert chain.
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+ {}, // Additional data
+ credentialPubKey_));
+}
+
+TEST_P(UpdateCredentialTests, updateCredential) {
+ if (halApiVersion_ < 3) {
+ GTEST_SKIP() << "Need HAL API version 3, have " << halApiVersion_;
+ }
+
+ provisionData();
+
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData_, &credential)
+ .isOk());
+
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credential->updateCredential(&wc).isOk());
+
+ // Getting an attestation cert should fail (because it's an update).
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ Status result = wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain);
+ ASSERT_FALSE(result.isOk());
+ EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
+ EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode());
+
+ // Now provision some new data...
+ //
+ size_t proofOfProvisioningSize = 117;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+ {1} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: open access - don't care about the returned SACP
+ SecureAccessControlProfile sacp;
+ ASSERT_TRUE(wc->addAccessControlProfile(2, {}, false, 0, 0, &sacp).isOk());
+
+ // Single entry - don't care about the returned encrypted data
+ vector<uint8_t> encryptedData;
+ vector<uint8_t> tstrLastName = cppbor::Tstr("T.A.F.K.A.P").encode();
+ ASSERT_TRUE(wc->beginAddEntry({2}, "ns", "Last name", tstrLastName.size()).isOk());
+ ASSERT_TRUE(wc->addEntryValue(tstrLastName, &encryptedData).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+ optional<vector<uint8_t>> proofOfProvisioning =
+ support::coseSignGetPayload(proofOfProvisioningSignature);
+ ASSERT_TRUE(proofOfProvisioning);
+ string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {});
+ EXPECT_EQ(
+ "[\n"
+ " 'ProofOfProvisioning',\n"
+ " 'org.iso.18013-5.2019.mdl',\n"
+ " [\n"
+ " {\n"
+ " 'id' : 2,\n"
+ " },\n"
+ " ],\n"
+ " {\n"
+ " 'ns' : [\n"
+ " {\n"
+ " 'name' : 'Last name',\n"
+ " 'value' : 'T.A.F.K.A.P',\n"
+ " 'accessControlProfiles' : [2, ],\n"
+ " },\n"
+ " ],\n"
+ " },\n"
+ " true,\n"
+ "]",
+ cborPretty);
+ // Make sure it's signed by the same CredentialKey we originally provisioned with.
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+ {}, // Additional data
+ credentialPubKey_));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UpdateCredentialTests);
+INSTANTIATE_TEST_SUITE_P(
+ Identity, UpdateCredentialTests,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/UserAuthTests.cpp b/identity/aidl/vts/UserAuthTests.cpp
index 327493c..ef89d1c 100644
--- a/identity/aidl/vts/UserAuthTests.cpp
+++ b/identity/aidl/vts/UserAuthTests.cpp
@@ -32,7 +32,7 @@
#include <map>
#include <utility>
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
namespace android::hardware::identity {
@@ -145,7 +145,7 @@
EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
}
-// From ReaderAuthTest.cpp - TODO: consolidate with VtsIdentityTestUtils.h
+// From ReaderAuthTest.cpp - TODO: consolidate with Util.h
pair<vector<uint8_t>, vector<uint8_t>> generateReaderKey();
vector<uint8_t> generateReaderCert(const vector<uint8_t>& publicKey,
const vector<uint8_t>& signingKey);
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.cpp b/identity/aidl/vts/Util.cpp
similarity index 98%
rename from identity/aidl/vts/VtsIdentityTestUtils.cpp
rename to identity/aidl/vts/Util.cpp
index 3b10651..1148cb0 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.cpp
+++ b/identity/aidl/vts/Util.cpp
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-#define LOG_TAG "VtsIdentityTestUtils"
+#define LOG_TAG "Util"
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
+
+#include <android-base/logging.h>
#include <aidl/Gtest.h>
-#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <keymaster/km_openssl/openssl_utils.h>
#include <keymasterV4_1/attestation_record.h>
#include <charconv>
+
#include <map>
namespace android::hardware::identity::test_utils {
@@ -35,6 +38,7 @@
using ::android::sp;
using ::android::String16;
+using ::android::base::StringPrintf;
using ::android::binder::Status;
using ::keymaster::X509_Ptr;
@@ -86,7 +90,7 @@
return support::ecPublicKeyGenerateCertificate(readerPublicKey.value(), readerKey.value(),
serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
+ validityNotBefore, validityNotAfter, {});
}
optional<vector<SecureAccessControlProfile>> addAccessControlProfiles(
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.h b/identity/aidl/vts/Util.h
similarity index 99%
rename from identity/aidl/vts/VtsIdentityTestUtils.h
rename to identity/aidl/vts/Util.h
index 85c24f8..80e52a2 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.h
+++ b/identity/aidl/vts/Util.h
@@ -21,6 +21,7 @@
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
#include <cppbor.h>
#include <cppbor_parse.h>
+#include <gtest/gtest.h>
namespace android::hardware::identity::test_utils {
diff --git a/identity/aidl/vts/VtsAttestationTests.cpp b/identity/aidl/vts/VtsAttestationTests.cpp
index 5529853..e12fe05 100644
--- a/identity/aidl/vts/VtsAttestationTests.cpp
+++ b/identity/aidl/vts/VtsAttestationTests.cpp
@@ -29,7 +29,7 @@
#include <future>
#include <map>
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
namespace android::hardware::identity {
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
index 1629a0c..cc63c48 100644
--- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
+++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
@@ -29,7 +29,7 @@
#include <future>
#include <map>
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
namespace android::hardware::identity {