diff options
48 files changed, 1925 insertions, 287 deletions
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index a8d9a1b14d..66417c212f 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -268,7 +268,7 @@ </hal> <hal format="aidl" optional="true"> <name>android.hardware.identity</name> - <version>1-2</version> + <version>1-3</version> <interface> <name>IIdentityCredentialStore</name> <instance>default</instance> 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 7e3002d70a..d8a8128c8e 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 447203faa6..2685525044 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 e1296e05e8..f8d5a9e7e6 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 88104d9577..a097895b81 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 @@ interface IIdentityCredential { 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 5dafb76d1c..c6fb3c889e 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 c5ac9d6340..a713462940 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 24ec26afdb..c9c2b9fec6 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 af00f3bb62..aaf1e20f1d 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 dfc1ad0681..695fb3fb26 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 702334d0b6..d23f88cac8 100644 --- a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl +++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl @@ -19,6 +19,7 @@ package android.hardware.identity; 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 @@ interface IIdentityCredential { * 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 @@ interface IIdentityCredential { * * - 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 @@ interface IIdentityCredential { * 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 33e25b1adf..638be796c4 100644 --- a/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl +++ b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl @@ -104,6 +104,11 @@ import android.hardware.identity.CipherSuite; * 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 @@ interface IIdentityCredentialStore { * 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 c48cb6682e..5f878eece2 100644 --- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl +++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl @@ -263,14 +263,27 @@ interface IWritableIdentityCredential { * * 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 + * bstr ; credentialPrivKey, the private key for credentialKey + * ; 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 @@ interface IWritableIdentityCredential { * @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 7f342d087a..874464849d 100644 --- a/identity/aidl/default/Android.bp +++ b/identity/aidl/default/Android.bp @@ -12,6 +12,7 @@ cc_library_static { cflags: [ "-Wall", "-Wextra", + "-Wno-deprecated-declarations", ], shared_libs: [ "liblog", @@ -28,8 +29,8 @@ cc_library_static { "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 @@ cc_binary { "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 @@ cc_binary { "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 3f2ec8b763..8ec4cc9b58 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 @@ bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], 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 @@ bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], } // 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 @@ bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const 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 eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const 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 @@ bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE], 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 de6762fc2e..287ffb82e1 100644 --- a/identity/aidl/default/FakeSecureHardwareProxy.cpp +++ b/identity/aidl/default/FakeSecureHardwareProxy.cpp @@ -67,6 +67,13 @@ bool FakeSecureHardwareProvisioningProxy::initialize(bool testCredential) { 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 @@ optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishAddingEntri 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 @@ bool FakeSecureHardwarePresentationProxy::initialize(bool testCredential, string 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::finishRetrieval() } 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 b858dd409d..6852c1a979 100644 --- a/identity/aidl/default/FakeSecureHardwareProxy.h +++ b/identity/aidl/default/FakeSecureHardwareProxy.h @@ -32,6 +32,9 @@ class FakeSecureHardwareProvisioningProxy : public SecureHardwareProvisioningPro 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 @@ class FakeSecureHardwarePresentationProxy : public SecureHardwarePresentationPro 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 0000000000..5149792b7f --- /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 270fcfa8d0..94779971b1 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 @@ int IdentityCredential::initialize() { 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 @@ int IdentityCredential::initialize() { 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 @@ ndk::ScopedAStatus IdentityCredential::deleteCredential( 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 @@ ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair( 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 228182160a..9913b86869 100644 --- a/identity/aidl/default/common/IdentityCredential.h +++ b/identity/aidl/default/common/IdentityCredential.h @@ -45,9 +45,11 @@ using ::std::vector; 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 @@ class IdentityCredential : public BnIdentityCredential { // 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 @@ class IdentityCredential : public BnIdentityCredential { 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 @@ class IdentityCredential : public BnIdentityCredential { // 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 13f91aacf9..e6b5466096 100644 --- a/identity/aidl/default/common/IdentityCredentialStore.cpp +++ b/identity/aidl/default/common/IdentityCredentialStore.cpp @@ -63,7 +63,7 @@ ndk::ScopedAStatus IdentityCredentialStore::getCredential( 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 b89ad8781f..a1ed1ef03b 100644 --- a/identity/aidl/default/common/SecureHardwareProxy.h +++ b/identity/aidl/default/common/SecureHardwareProxy.h @@ -64,6 +64,9 @@ class SecureHardwareProvisioningProxy : public RefBase { 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 @@ class SecureHardwarePresentationProxy : public RefBase { 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 1328f3629e..2d897c7d8b 100644 --- a/identity/aidl/default/common/WritableIdentityCredential.cpp +++ b/identity/aidl/default/common/WritableIdentityCredential.cpp @@ -40,7 +40,20 @@ using namespace ::android::hardware::identity; 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 c6f0628cae..36ad4300d1 100644 --- a/identity/aidl/default/common/WritableIdentityCredential.h +++ b/identity/aidl/default/common/WritableIdentityCredential.h @@ -36,16 +36,22 @@ using ::std::vector; 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 37d5b81c21..a074250901 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 ec049b1c0d..fe131eb8b7 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 eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize) { 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 @@ void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize, 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 @@ void eicCborAppend(EicCbor* cbor, const uint8_t* data, size_t size) { 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 4686b38447..9c0f531e4a 100644 --- a/identity/aidl/default/libeic/EicCbor.h +++ b/identity/aidl/default/libeic/EicCbor.h @@ -53,6 +53,9 @@ typedef struct { 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 eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize); 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 da4dabf879..d4fcf0e1bb 100644 --- a/identity/aidl/default/libeic/EicOps.h +++ b/identity/aidl/default/libeic/EicOps.h @@ -207,14 +207,17 @@ bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const // 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 d3f5556f66..5e9a280d09 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 @@ bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* // 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 @@ bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* doc 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 @@ bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* doc 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 eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMac } 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 @@ bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType, 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 @@ bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType, 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 d79896212e..7cad068772 100644 --- a/identity/aidl/default/libeic/EicPresentation.h +++ b/identity/aidl/default/libeic/EicPresentation.h @@ -31,6 +31,8 @@ extern "C" { #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 @@ typedef struct { // 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 @@ bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMac // 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 f16605cfad..3b4148e571 100644 --- a/identity/aidl/default/libeic/EicProvisioning.c +++ b/identity/aidl/default/libeic/EicProvisioning.c @@ -26,10 +26,84 @@ bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential) { 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 @@ bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControl 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 eicProvisioningFinishAddingEntries( } 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 @@ bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* do 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 836d16e444..f064787b1b 100644 --- a/identity/aidl/default/libeic/EicProvisioning.h +++ b/identity/aidl/default/libeic/EicProvisioning.h @@ -31,7 +31,7 @@ extern "C" { #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 @@ typedef struct { 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 @@ typedef struct { // 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 @@ bool eicProvisioningFinishAddingEntries( // 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 03966de549..f1b1bcf552 100644 --- a/identity/aidl/vts/Android.bp +++ b/identity/aidl/vts/Android.bp @@ -4,13 +4,21 @@ cc_test { "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 @@ cc_test { "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 0000000000..bda3e70413 --- /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 0000000000..1d300673eb --- /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 index cdecb97989..5798b4c28c 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 ::android::hardware::keymaster::VerificationToken; 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 @@ TEST_P(IdentityAidl, hardwareInformation) { 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 @@ tuple<bool, string, vector<uint8_t>, vector<uint8_t>> extractFromTestCredentialD 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 @@ tuple<bool, string, vector<uint8_t>, vector<uint8_t>> extractFromTestCredentialD 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 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) { // 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 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) { 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 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) { 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 0000000000..d1a3d39223 --- /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 0a9fdc0aa8..7656c8edfc 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 @@ vector<uint8_t> generateReaderCert(const vector<uint8_t>& publicKey, 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 0000000000..d53de3b073 --- /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 0000000000..9c5ca55cf2 --- /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 327493c9a0..ef89d1cafd 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 @@ void UserAuthTests::provisionData() { 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 index 3b106514b4..1148cb0b60 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 <aidl/Gtest.h> #include <android-base/logging.h> + +#include <aidl/Gtest.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 std::vector; using ::android::sp; using ::android::String16; +using ::android::base::StringPrintf; using ::android::binder::Status; using ::keymaster::X509_Ptr; @@ -86,7 +90,7 @@ optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal, 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 index 85c24f86b5..80e52a21da 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 5529853009..e12fe051d5 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 1629a0c952..cc63c482f1 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 { diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h index 3aa5bb641e..3b91de6dcc 100644 --- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h +++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h @@ -18,6 +18,7 @@ #define IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_ #include <cstdint> +#include <map> #include <optional> #include <string> #include <tuple> @@ -29,11 +30,12 @@ namespace hardware { namespace identity { namespace support { +using ::std::map; using ::std::optional; +using ::std::pair; using ::std::string; using ::std::tuple; using ::std::vector; -using ::std::pair; // The semantic tag for a bstr which includes Encoded CBOR (RFC 7049, section 2.4) const int kSemanticTagEncodedCbor = 24; @@ -221,6 +223,11 @@ optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x // optional<pair<time_t, time_t>> certificateGetValidity(const vector<uint8_t>& x509Certificate); +// Looks for an extension with OID in |oidStr| which must be an stored as an OCTET STRING. +// +optional<vector<uint8_t>> certificateGetExtension(const vector<uint8_t>& x509Certificate, + const string& oidStr); + // Generates a X.509 certificate for |publicKey| (which must be in the format // returned by ecKeyPairGetPublicKey()). // @@ -230,7 +237,8 @@ optional<pair<time_t, time_t>> certificateGetValidity(const vector<uint8_t>& x50 optional<vector<uint8_t>> ecPublicKeyGenerateCertificate( const vector<uint8_t>& publicKey, const vector<uint8_t>& signingKey, const string& serialDecimal, const string& issuer, const string& subject, - time_t validityNotBefore, time_t validityNotAfter); + time_t validityNotBefore, time_t validityNotAfter, + const map<string, vector<uint8_t>>& extensions); // Performs Elliptic-curve Diffie-Helman using |publicKey| (which must be in the // format returned by ecKeyPairGetPublicKey()) and |privateKey| (which must be diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp index 093120d032..38348ac1b0 100644 --- a/identity/support/src/IdentityCredentialSupport.cpp +++ b/identity/support/src/IdentityCredentialSupport.cpp @@ -344,15 +344,22 @@ string cborPrettyPrint(const vector<uint8_t>& encodedCbor, size_t maxBStrSize, // Crypto functionality / abstraction. // --------------------------------------------------------------------------- -struct EVP_CIPHER_CTX_Deleter { - void operator()(EVP_CIPHER_CTX* ctx) const { - if (ctx != nullptr) { - EVP_CIPHER_CTX_free(ctx); - } - } -}; - -using EvpCipherCtxPtr = unique_ptr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_Deleter>; +using EvpCipherCtxPtr = bssl::UniquePtr<EVP_CIPHER_CTX>; +using EC_KEY_Ptr = bssl::UniquePtr<EC_KEY>; +using EVP_PKEY_Ptr = bssl::UniquePtr<EVP_PKEY>; +using EVP_PKEY_CTX_Ptr = bssl::UniquePtr<EVP_PKEY_CTX>; +using EC_GROUP_Ptr = bssl::UniquePtr<EC_GROUP>; +using EC_POINT_Ptr = bssl::UniquePtr<EC_POINT>; +using ECDSA_SIG_Ptr = bssl::UniquePtr<ECDSA_SIG>; +using X509_Ptr = bssl::UniquePtr<X509>; +using PKCS12_Ptr = bssl::UniquePtr<PKCS12>; +using BIGNUM_Ptr = bssl::UniquePtr<BIGNUM>; +using ASN1_INTEGER_Ptr = bssl::UniquePtr<ASN1_INTEGER>; +using ASN1_TIME_Ptr = bssl::UniquePtr<ASN1_TIME>; +using ASN1_OCTET_STRING_Ptr = bssl::UniquePtr<ASN1_OCTET_STRING>; +using ASN1_OBJECT_Ptr = bssl::UniquePtr<ASN1_OBJECT>; +using X509_NAME_Ptr = bssl::UniquePtr<X509_NAME>; +using X509_EXTENSION_Ptr = bssl::UniquePtr<X509_EXTENSION>; // bool getRandom(size_t numBytes, vector<uint8_t>& output) { optional<vector<uint8_t>> getRandom(size_t numBytes) { @@ -534,115 +541,6 @@ optional<vector<uint8_t>> encryptAes128Gcm(const vector<uint8_t>& key, const vec return encryptedData; } -struct EC_KEY_Deleter { - void operator()(EC_KEY* key) const { - if (key != nullptr) { - EC_KEY_free(key); - } - } -}; -using EC_KEY_Ptr = unique_ptr<EC_KEY, EC_KEY_Deleter>; - -struct EVP_PKEY_Deleter { - void operator()(EVP_PKEY* key) const { - if (key != nullptr) { - EVP_PKEY_free(key); - } - } -}; -using EVP_PKEY_Ptr = unique_ptr<EVP_PKEY, EVP_PKEY_Deleter>; - -struct EVP_PKEY_CTX_Deleter { - void operator()(EVP_PKEY_CTX* ctx) const { - if (ctx != nullptr) { - EVP_PKEY_CTX_free(ctx); - } - } -}; -using EVP_PKEY_CTX_Ptr = unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Deleter>; - -struct EC_GROUP_Deleter { - void operator()(EC_GROUP* group) const { - if (group != nullptr) { - EC_GROUP_free(group); - } - } -}; -using EC_GROUP_Ptr = unique_ptr<EC_GROUP, EC_GROUP_Deleter>; - -struct EC_POINT_Deleter { - void operator()(EC_POINT* point) const { - if (point != nullptr) { - EC_POINT_free(point); - } - } -}; - -using EC_POINT_Ptr = unique_ptr<EC_POINT, EC_POINT_Deleter>; - -struct ECDSA_SIG_Deleter { - void operator()(ECDSA_SIG* sig) const { - if (sig != nullptr) { - ECDSA_SIG_free(sig); - } - } -}; -using ECDSA_SIG_Ptr = unique_ptr<ECDSA_SIG, ECDSA_SIG_Deleter>; - -struct X509_Deleter { - void operator()(X509* x509) const { - if (x509 != nullptr) { - X509_free(x509); - } - } -}; -using X509_Ptr = unique_ptr<X509, X509_Deleter>; - -struct PKCS12_Deleter { - void operator()(PKCS12* pkcs12) const { - if (pkcs12 != nullptr) { - PKCS12_free(pkcs12); - } - } -}; -using PKCS12_Ptr = unique_ptr<PKCS12, PKCS12_Deleter>; - -struct BIGNUM_Deleter { - void operator()(BIGNUM* bignum) const { - if (bignum != nullptr) { - BN_free(bignum); - } - } -}; -using BIGNUM_Ptr = unique_ptr<BIGNUM, BIGNUM_Deleter>; - -struct ASN1_INTEGER_Deleter { - void operator()(ASN1_INTEGER* value) const { - if (value != nullptr) { - ASN1_INTEGER_free(value); - } - } -}; -using ASN1_INTEGER_Ptr = unique_ptr<ASN1_INTEGER, ASN1_INTEGER_Deleter>; - -struct ASN1_TIME_Deleter { - void operator()(ASN1_TIME* value) const { - if (value != nullptr) { - ASN1_TIME_free(value); - } - } -}; -using ASN1_TIME_Ptr = unique_ptr<ASN1_TIME, ASN1_TIME_Deleter>; - -struct X509_NAME_Deleter { - void operator()(X509_NAME* value) const { - if (value != nullptr) { - X509_NAME_free(value); - } - } -}; -using X509_NAME_Ptr = unique_ptr<X509_NAME, X509_NAME_Deleter>; - vector<uint8_t> certificateChainJoin(const vector<vector<uint8_t>>& certificateChain) { vector<uint8_t> ret; for (const vector<uint8_t>& certificate : certificateChain) { @@ -1221,8 +1119,19 @@ optional<vector<uint8_t>> ecKeyPairGetPrivateKey(const vector<uint8_t>& keyPair) return {}; } vector<uint8_t> privateKey; - privateKey.resize(BN_num_bytes(bignum)); - BN_bn2bin(bignum, privateKey.data()); + + // Note that this may return fewer than 32 bytes so pad with zeroes since we + // want to always return 32 bytes. + size_t numBytes = BN_num_bytes(bignum); + if (numBytes > 32) { + LOG(ERROR) << "Size is " << numBytes << ", expected this to be 32 or less"; + return {}; + } + privateKey.resize(32); + for (size_t n = 0; n < 32 - numBytes; n++) { + privateKey[n] = 0x00; + } + BN_bn2bin(bignum, privateKey.data() + 32 - numBytes); return privateKey; } @@ -1379,7 +1288,8 @@ optional<vector<uint8_t>> ecKeyPairGetPkcs12(const vector<uint8_t>& keyPair, con optional<vector<uint8_t>> ecPublicKeyGenerateCertificate( const vector<uint8_t>& publicKey, const vector<uint8_t>& signingKey, const string& serialDecimal, const string& issuer, const string& subject, - time_t validityNotBefore, time_t validityNotAfter) { + time_t validityNotBefore, time_t validityNotAfter, + const map<string, vector<uint8_t>>& extensions) { auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) != @@ -1482,6 +1392,32 @@ optional<vector<uint8_t>> ecPublicKeyGenerateCertificate( return {}; } + for (auto const& [oidStr, blob] : extensions) { + ASN1_OBJECT_Ptr oid( + OBJ_txt2obj(oidStr.c_str(), 1)); // accept numerical dotted string form only + if (!oid.get()) { + LOG(ERROR) << "Error setting OID"; + return {}; + } + ASN1_OCTET_STRING_Ptr octetString(ASN1_OCTET_STRING_new()); + if (!ASN1_OCTET_STRING_set(octetString.get(), blob.data(), blob.size())) { + LOG(ERROR) << "Error setting octet string for extension"; + return {}; + } + + X509_EXTENSION_Ptr extension = X509_EXTENSION_Ptr(X509_EXTENSION_new()); + extension.reset(X509_EXTENSION_create_by_OBJ(nullptr, oid.get(), 0 /* not critical */, + octetString.get())); + if (!extension.get()) { + LOG(ERROR) << "Error setting extension"; + return {}; + } + if (!X509_add_ext(x509.get(), extension.get(), -1)) { + LOG(ERROR) << "Error adding extension"; + return {}; + } + } + if (X509_sign(x509.get(), privPkey.get(), EVP_sha256()) == 0) { LOG(ERROR) << "Error signing X509 certificate"; return {}; @@ -1650,6 +1586,44 @@ optional<vector<uint8_t>> certificateChainGetTopMostKey(const vector<uint8_t>& c return publicKey; } +optional<vector<uint8_t>> certificateGetExtension(const vector<uint8_t>& x509Certificate, + const string& oidStr) { + vector<X509_Ptr> certs; + if (!parseX509Certificates(x509Certificate, certs)) { + return {}; + } + if (certs.size() < 1) { + LOG(ERROR) << "No certificates in chain"; + return {}; + } + + ASN1_OBJECT_Ptr oid( + OBJ_txt2obj(oidStr.c_str(), 1)); // accept numerical dotted string form only + if (!oid.get()) { + LOG(ERROR) << "Error setting OID"; + return {}; + } + + int location = X509_get_ext_by_OBJ(certs[0].get(), oid.get(), -1 /* search from beginning */); + if (location == -1) { + return {}; + } + + X509_EXTENSION* ext = X509_get_ext(certs[0].get(), location); + if (ext == nullptr) { + return {}; + } + + ASN1_OCTET_STRING* octetString = X509_EXTENSION_get_data(ext); + if (octetString == nullptr) { + return {}; + } + vector<uint8_t> result; + result.resize(octetString->length); + memcpy(result.data(), octetString->data, octetString->length); + return result; +} + optional<pair<size_t, size_t>> certificateFindPublicKey(const vector<uint8_t>& x509Certificate) { vector<X509_Ptr> certs; if (!parseX509Certificates(x509Certificate, certs)) { diff --git a/identity/support/tests/IdentityCredentialSupportTest.cpp b/identity/support/tests/IdentityCredentialSupportTest.cpp index 266f263203..509133cbc9 100644 --- a/identity/support/tests/IdentityCredentialSupportTest.cpp +++ b/identity/support/tests/IdentityCredentialSupportTest.cpp @@ -271,7 +271,7 @@ vector<uint8_t> generateCertChain(size_t numCerts) { optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value()); optional<vector<uint8_t>> cert = support::ecPublicKeyGenerateCertificate( - pubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0); + pubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0, {}); certs.push_back(cert.value()); } return support::certificateChainJoin(certs); @@ -338,7 +338,7 @@ TEST(IdentityCredentialSupport, CertificateChain) { ASSERT_TRUE(pubKey); optional<vector<uint8_t>> cert = support::ecPublicKeyGenerateCertificate( - pubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0); + pubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0, {}); optional<vector<uint8_t>> extractedPubKey = support::certificateChainGetTopMostKey(cert.value()); @@ -358,7 +358,7 @@ TEST(IdentityCredentialSupport, CertificateChain) { optional<vector<uint8_t>> otherPubKey = support::ecKeyPairGetPublicKey(keyPair.value()); ASSERT_TRUE(otherPubKey); optional<vector<uint8_t>> otherCert = support::ecPublicKeyGenerateCertificate( - otherPubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0); + otherPubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0, {}); // Now both cert and otherCert are two distinct certificates. Let's make a // chain and check that certificateChainSplit() works as expected. |