mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
Merge "Identity Credential changes for Android 12" am: ef90842503 am: 409331de91 am: 3b83d41967
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/1464242 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Ib6012072f2682bcb31c5aead9cb8f01e560a3560
This commit is contained in:
@@ -287,7 +287,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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -263,7 +263,9 @@ 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
|
||||
@@ -271,6 +273,17 @@ interface IWritableIdentityCredential {
|
||||
* ; 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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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|.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
194
identity/aidl/vts/AuthenticationKeyTests.cpp
Normal file
194
identity/aidl/vts/AuthenticationKeyTests.cpp
Normal file
@@ -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
|
||||
169
identity/aidl/vts/DeleteCredentialTests.cpp
Normal file
169
identity/aidl/vts/DeleteCredentialTests.cpp
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
146
identity/aidl/vts/ProveOwnershipTests.cpp
Normal file
146
identity/aidl/vts/ProveOwnershipTests.cpp
Normal file
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
204
identity/aidl/vts/TestCredentialTests.cpp
Normal file
204
identity/aidl/vts/TestCredentialTests.cpp
Normal file
@@ -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
|
||||
232
identity/aidl/vts/UpdateCredentialTests.cpp
Normal file
232
identity/aidl/vts/UpdateCredentialTests.cpp
Normal file
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -14,15 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "VtsIdentityTestUtils"
|
||||
#define LOG_TAG "Util"
|
||||
|
||||
#include "VtsIdentityTestUtils.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <aidl/Gtest.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <keymaster/km_openssl/openssl_utils.h>
|
||||
#include <keymasterV4_1/attestation_record.h>
|
||||
#include <charconv>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace android::hardware::identity::test_utils {
|
||||
@@ -35,6 +38,7 @@ using 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(
|
||||
@@ -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 {
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <future>
|
||||
#include <map>
|
||||
|
||||
#include "VtsIdentityTestUtils.h"
|
||||
#include "Util.h"
|
||||
|
||||
namespace android::hardware::identity {
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <future>
|
||||
#include <map>
|
||||
|
||||
#include "VtsIdentityTestUtils.h"
|
||||
#include "Util.h"
|
||||
|
||||
namespace android::hardware::identity {
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user