mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-02 06:22:53 +00:00
Merge "Identity Credential changes for Android 12" am: ef90842503 am: 409331de91 am: 3b83d41967 am: 0f8e9d1b6e
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/1464242 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Idf678b762d8be079d7ab6c15603842e734a59da7
This commit is contained in:
@@ -287,7 +287,7 @@
|
|||||||
</hal>
|
</hal>
|
||||||
<hal format="aidl" optional="true">
|
<hal format="aidl" optional="true">
|
||||||
<name>android.hardware.identity</name>
|
<name>android.hardware.identity</name>
|
||||||
<version>1-2</version>
|
<version>1-3</version>
|
||||||
<interface>
|
<interface>
|
||||||
<name>IIdentityCredentialStore</name>
|
<name>IIdentityCredentialStore</name>
|
||||||
<instance>default</instance>
|
<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 IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
|
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||||
// edit this file. It looks like you are doing that because you have modified
|
// two cases:
|
||||||
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
|
// 1). this is a frozen version file - do not edit this in any case.
|
||||||
// from an interface or a field from a parcelable and it broke the build. That
|
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||||
// breakage is intended.
|
// 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
|
// 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
|
// 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
|
// 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 IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
|
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||||
// edit this file. It looks like you are doing that because you have modified
|
// two cases:
|
||||||
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
|
// 1). this is a frozen version file - do not edit this in any case.
|
||||||
// from an interface or a field from a parcelable and it broke the build. That
|
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||||
// breakage is intended.
|
// 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
|
// 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
|
// 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
|
// 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 IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
|
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||||
// edit this file. It looks like you are doing that because you have modified
|
// two cases:
|
||||||
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
|
// 1). this is a frozen version file - do not edit this in any case.
|
||||||
// from an interface or a field from a parcelable and it broke the build. That
|
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||||
// breakage is intended.
|
// 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
|
// 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
|
// 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
|
// 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 IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
|
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||||
// edit this file. It looks like you are doing that because you have modified
|
// two cases:
|
||||||
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
|
// 1). this is a frozen version file - do not edit this in any case.
|
||||||
// from an interface or a field from a parcelable and it broke the build. That
|
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||||
// breakage is intended.
|
// 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
|
// 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
|
// 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
|
// independently updatable components of the system. If a device is shipped
|
||||||
@@ -18,6 +33,9 @@
|
|||||||
package android.hardware.identity;
|
package android.hardware.identity;
|
||||||
@VintfStability
|
@VintfStability
|
||||||
interface IIdentityCredential {
|
interface IIdentityCredential {
|
||||||
|
/**
|
||||||
|
* @deprecated use deleteCredentalWithChallenge() instead.
|
||||||
|
*/
|
||||||
byte[] deleteCredential();
|
byte[] deleteCredential();
|
||||||
byte[] createEphemeralKeyPair();
|
byte[] createEphemeralKeyPair();
|
||||||
void setReaderEphemeralPublicKey(in byte[] publicKey);
|
void setReaderEphemeralPublicKey(in byte[] publicKey);
|
||||||
@@ -29,4 +47,7 @@ interface IIdentityCredential {
|
|||||||
android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
|
android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
|
||||||
void setRequestedNamespaces(in android.hardware.identity.RequestNamespace[] requestNamespaces);
|
void setRequestedNamespaces(in android.hardware.identity.RequestNamespace[] requestNamespaces);
|
||||||
void setVerificationToken(in android.hardware.keymaster.VerificationToken verificationToken);
|
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 IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
|
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||||
// edit this file. It looks like you are doing that because you have modified
|
// two cases:
|
||||||
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
|
// 1). this is a frozen version file - do not edit this in any case.
|
||||||
// from an interface or a field from a parcelable and it broke the build. That
|
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||||
// breakage is intended.
|
// 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
|
// 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
|
// 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
|
// 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 IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
|
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||||
// edit this file. It looks like you are doing that because you have modified
|
// two cases:
|
||||||
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
|
// 1). this is a frozen version file - do not edit this in any case.
|
||||||
// from an interface or a field from a parcelable and it broke the build. That
|
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||||
// breakage is intended.
|
// 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
|
// 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
|
// 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
|
// 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 IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
|
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||||
// edit this file. It looks like you are doing that because you have modified
|
// two cases:
|
||||||
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
|
// 1). this is a frozen version file - do not edit this in any case.
|
||||||
// from an interface or a field from a parcelable and it broke the build. That
|
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||||
// breakage is intended.
|
// 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
|
// 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
|
// 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
|
// 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 IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
|
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||||
// edit this file. It looks like you are doing that because you have modified
|
// two cases:
|
||||||
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
|
// 1). this is a frozen version file - do not edit this in any case.
|
||||||
// from an interface or a field from a parcelable and it broke the build. That
|
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||||
// breakage is intended.
|
// 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
|
// 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
|
// 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
|
// 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 IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
|
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||||
// edit this file. It looks like you are doing that because you have modified
|
// two cases:
|
||||||
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
|
// 1). this is a frozen version file - do not edit this in any case.
|
||||||
// from an interface or a field from a parcelable and it broke the build. That
|
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||||
// breakage is intended.
|
// 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
|
// 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
|
// 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
|
// 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.Certificate;
|
||||||
import android.hardware.identity.RequestNamespace;
|
import android.hardware.identity.RequestNamespace;
|
||||||
import android.hardware.identity.SecureAccessControlProfile;
|
import android.hardware.identity.SecureAccessControlProfile;
|
||||||
|
import android.hardware.identity.IWritableIdentityCredential;
|
||||||
import android.hardware.keymaster.HardwareAuthToken;
|
import android.hardware.keymaster.HardwareAuthToken;
|
||||||
import android.hardware.keymaster.VerificationToken;
|
import android.hardware.keymaster.VerificationToken;
|
||||||
|
|
||||||
@@ -40,7 +41,11 @@ interface IIdentityCredential {
|
|||||||
* After this method has been called, the persistent storage used for credentialData should
|
* After this method has been called, the persistent storage used for credentialData should
|
||||||
* be deleted.
|
* 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();
|
byte[] deleteCredential();
|
||||||
|
|
||||||
@@ -353,6 +358,18 @@ interface IIdentityCredential {
|
|||||||
*
|
*
|
||||||
* - subjectPublicKeyInfo: must contain attested public key.
|
* - 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)
|
* @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
|
* 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.
|
* 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.
|
* The verification token. This token is only valid if the timestamp field is non-zero.
|
||||||
*/
|
*/
|
||||||
void setVerificationToken(in VerificationToken verificationToken);
|
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
|
* 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
|
* STATUS_* integers defined in this interface. Each method states which status can be returned
|
||||||
* and under which circumstances.
|
* 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
|
@VintfStability
|
||||||
interface IIdentityCredentialStore {
|
interface IIdentityCredentialStore {
|
||||||
@@ -230,6 +235,9 @@ interface IIdentityCredentialStore {
|
|||||||
* return argument of the same name in finishAddingEntries(), in
|
* return argument of the same name in finishAddingEntries(), in
|
||||||
* IWritableIdentityCredential.
|
* 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.
|
* @return an IIdentityCredential interface that provides operations on the Credential.
|
||||||
*/
|
*/
|
||||||
IIdentityCredential getCredential(in CipherSuite cipherSuite, in byte[] credentialData);
|
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
|
* 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
|
* 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 = [
|
* CredentialKeys = [
|
||||||
* bstr, ; storageKey, a 128-bit AES key
|
* bstr, ; storageKey, a 128-bit AES key
|
||||||
@@ -271,6 +273,17 @@ interface IWritableIdentityCredential {
|
|||||||
* ; in uncompressed form
|
* ; 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
|
* @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
|
* 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
|
* 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.
|
* @param expectedProofOfProvisioningSize the expected size of ProofOfProvisioning.
|
||||||
*/
|
*/
|
||||||
void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize);
|
void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ cc_library_static {
|
|||||||
cflags: [
|
cflags: [
|
||||||
"-Wall",
|
"-Wall",
|
||||||
"-Wextra",
|
"-Wextra",
|
||||||
|
"-Wno-deprecated-declarations",
|
||||||
],
|
],
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
"liblog",
|
"liblog",
|
||||||
@@ -28,8 +29,8 @@ cc_library_static {
|
|||||||
"libsoft_attestation_cert",
|
"libsoft_attestation_cert",
|
||||||
"libpuresoftkeymasterdevice",
|
"libpuresoftkeymasterdevice",
|
||||||
"android.hardware.identity-support-lib",
|
"android.hardware.identity-support-lib",
|
||||||
"android.hardware.identity-ndk_platform",
|
"android.hardware.identity-unstable-ndk_platform",
|
||||||
"android.hardware.keymaster-ndk_platform",
|
"android.hardware.keymaster-unstable-ndk_platform",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +89,8 @@ cc_binary {
|
|||||||
"libsoft_attestation_cert",
|
"libsoft_attestation_cert",
|
||||||
"libpuresoftkeymasterdevice",
|
"libpuresoftkeymasterdevice",
|
||||||
"android.hardware.identity-support-lib",
|
"android.hardware.identity-support-lib",
|
||||||
"android.hardware.identity-ndk_platform",
|
"android.hardware.identity-unstable-ndk_platform",
|
||||||
"android.hardware.keymaster-ndk_platform",
|
"android.hardware.keymaster-unstable-ndk_platform",
|
||||||
"android.hardware.identity-libeic-hal-common",
|
"android.hardware.identity-libeic-hal-common",
|
||||||
"android.hardware.identity-libeic-library",
|
"android.hardware.identity-libeic-library",
|
||||||
],
|
],
|
||||||
@@ -97,4 +98,14 @@ cc_binary {
|
|||||||
"service.cpp",
|
"service.cpp",
|
||||||
"FakeSecureHardwareProxy.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"
|
#include "EicOps.h"
|
||||||
|
|
||||||
|
using ::std::map;
|
||||||
using ::std::optional;
|
using ::std::optional;
|
||||||
using ::std::string;
|
using ::std::string;
|
||||||
using ::std::tuple;
|
using ::std::tuple;
|
||||||
@@ -212,7 +213,8 @@ bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
|
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;
|
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.
|
// ecKeyPairGetPublicKey() returns 0x04 | x | y, we don't want the leading 0x04.
|
||||||
if (pubKey.value().size() != EIC_P256_PUB_KEY_SIZE + 1) {
|
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);
|
(size_t)EIC_P256_PRIV_KEY_SIZE + 1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -272,7 +274,8 @@ bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
|
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;
|
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],
|
bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
|
||||||
const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
|
const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
|
||||||
const char* issuerName, const char* subjectName, time_t validityNotBefore,
|
const char* issuerName, const char* subjectName, time_t validityNotBefore,
|
||||||
time_t validityNotAfter, uint8_t* cert,
|
time_t validityNotAfter, const uint8_t* proofOfBinding,
|
||||||
size_t* certSize) { // inout
|
size_t proofOfBindingSize, uint8_t* cert, size_t* certSize) { // inout
|
||||||
vector<uint8_t> signingKeyVec(EIC_P256_PRIV_KEY_SIZE);
|
vector<uint8_t> signingKeyVec(EIC_P256_PRIV_KEY_SIZE);
|
||||||
memcpy(signingKeyVec.data(), signingKey, 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;
|
pubKeyVec[0] = 0x04;
|
||||||
memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE);
|
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 =
|
optional<vector<uint8_t>> certVec =
|
||||||
android::hardware::identity::support::ecPublicKeyGenerateCertificate(
|
android::hardware::identity::support::ecPublicKeyGenerateCertificate(
|
||||||
pubKeyVec, signingKeyVec, serialDecimal, issuerName, subjectName,
|
pubKeyVec, signingKeyVec, serialDecimal, issuerName, subjectName,
|
||||||
validityNotBefore, validityNotAfter);
|
validityNotBefore, validityNotAfter, extensions);
|
||||||
if (!certVec) {
|
if (!certVec) {
|
||||||
eicDebug("Error generating certificate");
|
eicDebug("Error generating certificate");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -67,6 +67,13 @@ bool FakeSecureHardwareProvisioningProxy::initialize(bool testCredential) {
|
|||||||
return eicProvisioningInit(&ctx_, 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.
|
// Returns public key certificate.
|
||||||
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::createCredentialKey(
|
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::createCredentialKey(
|
||||||
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
|
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
|
||||||
@@ -140,14 +147,16 @@ optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishAddingEntri
|
|||||||
return signatureOfToBeSigned;
|
return signatureOfToBeSigned;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns encryptedCredentialKeys (80 bytes).
|
// Returns encryptedCredentialKeys.
|
||||||
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishGetCredentialData(
|
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishGetCredentialData(
|
||||||
const string& docType) {
|
const string& docType) {
|
||||||
vector<uint8_t> encryptedCredentialKeys(80);
|
vector<uint8_t> encryptedCredentialKeys(116);
|
||||||
|
size_t size = encryptedCredentialKeys.size();
|
||||||
if (!eicProvisioningFinishGetCredentialData(&ctx_, docType.c_str(),
|
if (!eicProvisioningFinishGetCredentialData(&ctx_, docType.c_str(),
|
||||||
encryptedCredentialKeys.data())) {
|
encryptedCredentialKeys.data(), &size)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
encryptedCredentialKeys.resize(size);
|
||||||
return encryptedCredentialKeys;
|
return encryptedCredentialKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +171,7 @@ bool FakeSecureHardwarePresentationProxy::initialize(bool testCredential, string
|
|||||||
LOG(INFO) << "FakeSecureHardwarePresentationProxy created, sizeof(EicPresentation): "
|
LOG(INFO) << "FakeSecureHardwarePresentationProxy created, sizeof(EicPresentation): "
|
||||||
<< sizeof(EicPresentation);
|
<< sizeof(EicPresentation);
|
||||||
return eicPresentationInit(&ctx_, testCredential, docType.c_str(),
|
return eicPresentationInit(&ctx_, testCredential, docType.c_str(),
|
||||||
encryptedCredentialKeys.data());
|
encryptedCredentialKeys.data(), encryptedCredentialKeys.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
|
// Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
|
||||||
@@ -312,13 +321,27 @@ optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::finishRetrieval()
|
|||||||
}
|
}
|
||||||
|
|
||||||
optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::deleteCredential(
|
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);
|
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())) {
|
signatureOfToBeSigned.data())) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return signatureOfToBeSigned;
|
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
|
} // namespace android::hardware::identity
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ class FakeSecureHardwareProvisioningProxy : public SecureHardwareProvisioningPro
|
|||||||
|
|
||||||
bool initialize(bool testCredential) override;
|
bool initialize(bool testCredential) override;
|
||||||
|
|
||||||
|
bool initializeForUpdate(bool testCredential, string docType,
|
||||||
|
vector<uint8_t> encryptedCredentialKeys) override;
|
||||||
|
|
||||||
bool shutdown() override;
|
bool shutdown() override;
|
||||||
|
|
||||||
// Returns public key certificate.
|
// Returns public key certificate.
|
||||||
@@ -122,8 +125,14 @@ class FakeSecureHardwarePresentationProxy : public SecureHardwarePresentationPro
|
|||||||
optional<vector<uint8_t>> finishRetrieval() override;
|
optional<vector<uint8_t>> finishRetrieval() override;
|
||||||
|
|
||||||
optional<vector<uint8_t>> deleteCredential(const string& docType,
|
optional<vector<uint8_t>> deleteCredential(const string& docType,
|
||||||
|
const vector<uint8_t>& challenge,
|
||||||
|
bool includeChallenge,
|
||||||
size_t proofOfDeletionCborSize) override;
|
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;
|
bool shutdown() override;
|
||||||
|
|
||||||
protected:
|
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 <cppbor_parse.h>
|
||||||
|
|
||||||
#include "FakeSecureHardwareProxy.h"
|
#include "FakeSecureHardwareProxy.h"
|
||||||
|
#include "WritableIdentityCredential.h"
|
||||||
|
|
||||||
namespace aidl::android::hardware::identity {
|
namespace aidl::android::hardware::identity {
|
||||||
|
|
||||||
@@ -70,14 +71,8 @@ int IdentityCredential::initialize() {
|
|||||||
docType_ = docTypeItem->value();
|
docType_ = docTypeItem->value();
|
||||||
testCredential_ = testCredentialItem->value();
|
testCredential_ = testCredentialItem->value();
|
||||||
|
|
||||||
const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
|
encryptedCredentialKeys_ = encryptedCredentialKeysItem->value();
|
||||||
|
if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys_)) {
|
||||||
if (encryptedCredentialKeys.size() != 80) {
|
|
||||||
LOG(ERROR) << "Unexpected size for encrypted CredentialKeys";
|
|
||||||
return IIdentityCredentialStore::STATUS_INVALID_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys)) {
|
|
||||||
LOG(ERROR) << "hwProxy->initialize failed";
|
LOG(ERROR) << "hwProxy->initialize failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -87,12 +82,32 @@ int IdentityCredential::initialize() {
|
|||||||
|
|
||||||
ndk::ScopedAStatus IdentityCredential::deleteCredential(
|
ndk::ScopedAStatus IdentityCredential::deleteCredential(
|
||||||
vector<uint8_t>* outProofOfDeletionSignature) {
|
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_};
|
cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
|
||||||
|
if (includeChallenge) {
|
||||||
|
array = {"ProofOfDeletion", docType_, challenge, testCredential_};
|
||||||
|
}
|
||||||
|
|
||||||
vector<uint8_t> proofOfDeletionCbor = array.encode();
|
vector<uint8_t> proofOfDeletionCbor = array.encode();
|
||||||
vector<uint8_t> podDigest = support::sha256(proofOfDeletionCbor);
|
vector<uint8_t> podDigest = support::sha256(proofOfDeletionCbor);
|
||||||
|
|
||||||
optional<vector<uint8_t>> signatureOfToBeSigned =
|
optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->deleteCredential(
|
||||||
hwProxy_->deleteCredential(docType_, proofOfDeletionCbor.size());
|
docType_, challenge, includeChallenge, proofOfDeletionCbor.size());
|
||||||
if (!signatureOfToBeSigned) {
|
if (!signatureOfToBeSigned) {
|
||||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||||
IIdentityCredentialStore::STATUS_FAILED, "Error signing ProofOfDeletion"));
|
IIdentityCredentialStore::STATUS_FAILED, "Error signing ProofOfDeletion"));
|
||||||
@@ -111,6 +126,38 @@ ndk::ScopedAStatus IdentityCredential::deleteCredential(
|
|||||||
return ndk::ScopedAStatus::ok();
|
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) {
|
ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<uint8_t>* outKeyPair) {
|
||||||
optional<vector<uint8_t>> ephemeralPriv = hwProxy_->createEphemeralKeyPair();
|
optional<vector<uint8_t>> ephemeralPriv = hwProxy_->createEphemeralKeyPair();
|
||||||
if (!ephemeralPriv) {
|
if (!ephemeralPriv) {
|
||||||
@@ -833,4 +880,19 @@ ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
|
|||||||
return ndk::ScopedAStatus::ok();
|
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
|
} // namespace aidl::android::hardware::identity
|
||||||
|
|||||||
@@ -45,9 +45,11 @@ using ::std::vector;
|
|||||||
|
|
||||||
class IdentityCredential : public BnIdentityCredential {
|
class IdentityCredential : public BnIdentityCredential {
|
||||||
public:
|
public:
|
||||||
IdentityCredential(sp<SecureHardwarePresentationProxy> hwProxy,
|
IdentityCredential(sp<SecureHardwareProxyFactory> hwProxyFactory,
|
||||||
|
sp<SecureHardwarePresentationProxy> hwProxy,
|
||||||
const vector<uint8_t>& credentialData)
|
const vector<uint8_t>& credentialData)
|
||||||
: hwProxy_(hwProxy),
|
: hwProxyFactory_(hwProxyFactory),
|
||||||
|
hwProxy_(hwProxy),
|
||||||
credentialData_(credentialData),
|
credentialData_(credentialData),
|
||||||
numStartRetrievalCalls_(0),
|
numStartRetrievalCalls_(0),
|
||||||
expectedDeviceNameSpacesSize_(0) {}
|
expectedDeviceNameSpacesSize_(0) {}
|
||||||
@@ -58,6 +60,11 @@ class IdentityCredential : public BnIdentityCredential {
|
|||||||
|
|
||||||
// Methods from IIdentityCredential follow.
|
// Methods from IIdentityCredential follow.
|
||||||
ndk::ScopedAStatus deleteCredential(vector<uint8_t>* outProofOfDeletionSignature) override;
|
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 createEphemeralKeyPair(vector<uint8_t>* outKeyPair) override;
|
||||||
ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
|
ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
|
||||||
ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override;
|
ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override;
|
||||||
@@ -79,8 +86,16 @@ class IdentityCredential : public BnIdentityCredential {
|
|||||||
ndk::ScopedAStatus generateSigningKeyPair(vector<uint8_t>* outSigningKeyBlob,
|
ndk::ScopedAStatus generateSigningKeyPair(vector<uint8_t>* outSigningKeyBlob,
|
||||||
Certificate* outSigningKeyCertificate) override;
|
Certificate* outSigningKeyCertificate) override;
|
||||||
|
|
||||||
|
ndk::ScopedAStatus updateCredential(
|
||||||
|
shared_ptr<IWritableIdentityCredential>* outWritableCredential) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ndk::ScopedAStatus deleteCredentialCommon(const vector<uint8_t>& challenge,
|
||||||
|
bool includeChallenge,
|
||||||
|
vector<uint8_t>* outProofOfDeletionSignature);
|
||||||
|
|
||||||
// Set by constructor
|
// Set by constructor
|
||||||
|
sp<SecureHardwareProxyFactory> hwProxyFactory_;
|
||||||
sp<SecureHardwarePresentationProxy> hwProxy_;
|
sp<SecureHardwarePresentationProxy> hwProxy_;
|
||||||
vector<uint8_t> credentialData_;
|
vector<uint8_t> credentialData_;
|
||||||
int numStartRetrievalCalls_;
|
int numStartRetrievalCalls_;
|
||||||
@@ -88,6 +103,7 @@ class IdentityCredential : public BnIdentityCredential {
|
|||||||
// Set by initialize()
|
// Set by initialize()
|
||||||
string docType_;
|
string docType_;
|
||||||
bool testCredential_;
|
bool testCredential_;
|
||||||
|
vector<uint8_t> encryptedCredentialKeys_;
|
||||||
|
|
||||||
// Set by createEphemeralKeyPair()
|
// Set by createEphemeralKeyPair()
|
||||||
vector<uint8_t> ephemeralPublicKey_;
|
vector<uint8_t> ephemeralPublicKey_;
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ ndk::ScopedAStatus IdentityCredentialStore::getCredential(
|
|||||||
|
|
||||||
sp<SecureHardwarePresentationProxy> hwProxy = hwProxyFactory_->createPresentationProxy();
|
sp<SecureHardwarePresentationProxy> hwProxy = hwProxyFactory_->createPresentationProxy();
|
||||||
shared_ptr<IdentityCredential> credential =
|
shared_ptr<IdentityCredential> credential =
|
||||||
ndk::SharedRefBase::make<IdentityCredential>(hwProxy, credentialData);
|
ndk::SharedRefBase::make<IdentityCredential>(hwProxyFactory_, hwProxy, credentialData);
|
||||||
auto ret = credential->initialize();
|
auto ret = credential->initialize();
|
||||||
if (ret != IIdentityCredentialStore::STATUS_OK) {
|
if (ret != IIdentityCredentialStore::STATUS_OK) {
|
||||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ class SecureHardwareProvisioningProxy : public RefBase {
|
|||||||
|
|
||||||
virtual bool initialize(bool testCredential) = 0;
|
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.
|
// Returns public key certificate chain with attestation.
|
||||||
//
|
//
|
||||||
// This must return an entire certificate chain and its implementation must
|
// 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>> finishRetrieval();
|
||||||
|
|
||||||
virtual optional<vector<uint8_t>> deleteCredential(const string& docType,
|
virtual optional<vector<uint8_t>> deleteCredential(const string& docType,
|
||||||
|
const vector<uint8_t>& challenge,
|
||||||
|
bool includeChallenge,
|
||||||
size_t proofOfDeletionCborSize) = 0;
|
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;
|
virtual bool shutdown() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,20 @@ using namespace ::android::hardware::identity;
|
|||||||
|
|
||||||
bool WritableIdentityCredential::initialize() {
|
bool WritableIdentityCredential::initialize() {
|
||||||
if (!hwProxy_->initialize(testCredential_)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
startPersonalizationCalled_ = false;
|
startPersonalizationCalled_ = false;
|
||||||
|
|||||||
@@ -36,16 +36,22 @@ using ::std::vector;
|
|||||||
|
|
||||||
class WritableIdentityCredential : public BnWritableIdentityCredential {
|
class WritableIdentityCredential : public BnWritableIdentityCredential {
|
||||||
public:
|
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,
|
WritableIdentityCredential(sp<SecureHardwareProvisioningProxy> hwProxy, const string& docType,
|
||||||
bool testCredential)
|
bool testCredential)
|
||||||
: hwProxy_(hwProxy), docType_(docType), testCredential_(testCredential) {}
|
: hwProxy_(hwProxy), docType_(docType), testCredential_(testCredential) {}
|
||||||
|
|
||||||
~WritableIdentityCredential();
|
~WritableIdentityCredential();
|
||||||
|
|
||||||
// Creates the Credential Key. Returns false on failure. Must be called
|
// Creates the Credential Key. Returns false on failure.
|
||||||
// right after construction.
|
|
||||||
bool initialize();
|
bool initialize();
|
||||||
|
|
||||||
|
// Used when updating a credential. Returns false on failure.
|
||||||
|
bool initializeForUpdate(const vector<uint8_t>& encryptedCredentialKeys);
|
||||||
|
|
||||||
// Methods from IWritableIdentityCredential follow.
|
// Methods from IWritableIdentityCredential follow.
|
||||||
ndk::ScopedAStatus getAttestationCertificate(const vector<uint8_t>& attestationApplicationId,
|
ndk::ScopedAStatus getAttestationCertificate(const vector<uint8_t>& attestationApplicationId,
|
||||||
const vector<uint8_t>& attestationChallenge,
|
const vector<uint8_t>& attestationChallenge,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<manifest version="1.0" type="device">
|
<manifest version="1.0" type="device">
|
||||||
<hal format="aidl">
|
<hal format="aidl">
|
||||||
<name>android.hardware.identity</name>
|
<name>android.hardware.identity</name>
|
||||||
<version>2</version>
|
<version>3</version>
|
||||||
<interface>
|
<interface>
|
||||||
<name>IIdentityCredentialStore</name>
|
<name>IIdentityCredentialStore</name>
|
||||||
<instance>default</instance>
|
<instance>default</instance>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "EicCbor.h"
|
#include "EicCbor.h"
|
||||||
|
|
||||||
void eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize) {
|
void eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize) {
|
||||||
|
eicMemSet(cbor, '\0', sizeof(EicCbor));
|
||||||
cbor->size = 0;
|
cbor->size = 0;
|
||||||
cbor->bufferSize = bufferSize;
|
cbor->bufferSize = bufferSize;
|
||||||
cbor->buffer = buffer;
|
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,
|
void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
|
||||||
const uint8_t* hmacKey, size_t hmacKeySize) {
|
const uint8_t* hmacKey, size_t hmacKeySize) {
|
||||||
|
eicMemSet(cbor, '\0', sizeof(EicCbor));
|
||||||
cbor->size = 0;
|
cbor->size = 0;
|
||||||
cbor->bufferSize = bufferSize;
|
cbor->bufferSize = bufferSize;
|
||||||
cbor->buffer = buffer;
|
cbor->buffer = buffer;
|
||||||
@@ -33,6 +35,10 @@ void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
|
|||||||
eicOpsHmacSha256Init(&cbor->digester.hmacSha256, hmacKey, hmacKeySize);
|
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]) {
|
void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
|
||||||
switch (cbor->digestType) {
|
switch (cbor->digestType) {
|
||||||
case EIC_CBOR_DIGEST_TYPE_SHA256:
|
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);
|
eicOpsHmacSha256Update(&cbor->digester.hmacSha256, data, size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (cbor->secondaryDigesterSha256 != NULL) {
|
||||||
|
eicOpsSha256Update(cbor->secondaryDigesterSha256, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
if (cbor->size >= cbor->bufferSize) {
|
if (cbor->size >= cbor->bufferSize) {
|
||||||
cbor->size += size;
|
cbor->size += size;
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ typedef struct {
|
|||||||
EicHmacSha256Ctx hmacSha256;
|
EicHmacSha256Ctx hmacSha256;
|
||||||
} digester;
|
} digester;
|
||||||
|
|
||||||
|
// The secondary digester, may be unset.
|
||||||
|
EicSha256Ctx* secondaryDigesterSha256;
|
||||||
|
|
||||||
// The buffer used for building up CBOR or NULL if bufferSize is 0.
|
// The buffer used for building up CBOR or NULL if bufferSize is 0.
|
||||||
uint8_t* buffer;
|
uint8_t* buffer;
|
||||||
} EicCbor;
|
} 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,
|
void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
|
||||||
const uint8_t* hmacKey, size_t hmacKeySize);
|
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. */
|
/* Finishes building CBOR and returns the digest. */
|
||||||
void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
|
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
|
// Generate an X.509 certificate for the key identified by |publicKey| which
|
||||||
// must be of the form returned by eicOpsCreateEcKey().
|
// 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
|
// The certificate will be signed by the key identified by |signingKey| which
|
||||||
// must be of the form returned by eicOpsCreateEcKey().
|
// must be of the form returned by eicOpsCreateEcKey().
|
||||||
//
|
//
|
||||||
bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
|
bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
|
||||||
const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
|
const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
|
||||||
const char* issuerName, const char* subjectName, time_t validityNotBefore,
|
const char* issuerName, const char* subjectName, time_t validityNotBefore,
|
||||||
time_t validityNotAfter, uint8_t* cert,
|
time_t validityNotAfter, const uint8_t* proofOfBinding,
|
||||||
size_t* certSize); // inout
|
size_t proofOfBindingSize, uint8_t* cert, size_t* certSize); // inout
|
||||||
|
|
||||||
// Uses |privateKey| to create an ECDSA signature of some data (the SHA-256 must
|
// Uses |privateKey| to create an ECDSA signature of some data (the SHA-256 must
|
||||||
// be given by |digestOfData|). Returns the signature in |signature|.
|
// be given by |digestOfData|). Returns the signature in |signature|.
|
||||||
|
|||||||
@@ -19,13 +19,28 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
|
bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
|
||||||
const uint8_t encryptedCredentialKeys[80]) {
|
const uint8_t* encryptedCredentialKeys,
|
||||||
uint8_t credentialKeys[52];
|
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));
|
eicMemSet(ctx, '\0', sizeof(EicPresentation));
|
||||||
|
|
||||||
if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
|
if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
|
||||||
80,
|
encryptedCredentialKeysSize,
|
||||||
// DocType is the additionalAuthenticatedData
|
// DocType is the additionalAuthenticatedData
|
||||||
(const uint8_t*)docType, eicStrLen(docType), credentialKeys)) {
|
(const uint8_t*)docType, eicStrLen(docType), credentialKeys)) {
|
||||||
eicDebug("Error decrypting CredentialKeys");
|
eicDebug("Error decrypting CredentialKeys");
|
||||||
@@ -34,25 +49,42 @@ bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char*
|
|||||||
|
|
||||||
// It's supposed to look like this;
|
// It's supposed to look like this;
|
||||||
//
|
//
|
||||||
|
// Feature version 202009:
|
||||||
|
//
|
||||||
// CredentialKeys = [
|
// CredentialKeys = [
|
||||||
// bstr, ; storageKey, a 128-bit AES key
|
// 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
|
// CredentialKeys = [
|
||||||
// and a bstr of 16 elements. Sixteen bytes later (offset 18 and 19) there will be
|
// bstr, ; storageKey, a 128-bit AES key
|
||||||
// a bstr of 32 bytes. It's encoded as two bytes 0x58 and 0x20.
|
// bstr, ; credentialPrivKey, the private key for credentialKey
|
||||||
|
// bstr ; proofOfProvisioning SHA-256
|
||||||
|
// ]
|
||||||
//
|
//
|
||||||
if (credentialKeys[0] != 0x82 || credentialKeys[1] != 0x50 || credentialKeys[18] != 0x58 ||
|
// where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and proofOfProvisioning
|
||||||
credentialKeys[19] != 0x20) {
|
// 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");
|
eicDebug("Invalid CBOR for CredentialKeys");
|
||||||
return false;
|
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->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
|
||||||
eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20, EIC_P256_PRIV_KEY_SIZE);
|
eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20, EIC_P256_PRIV_KEY_SIZE);
|
||||||
ctx->testCredential = testCredential;
|
ctx->testCredential = testCredential;
|
||||||
|
if (expectPopSha256) {
|
||||||
|
eicMemCpy(ctx->proofOfProvisioningSha256, credentialKeys + 54, EIC_SHA256_DIGEST_SIZE);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +93,35 @@ bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* doc
|
|||||||
uint8_t signingKeyBlob[60]) {
|
uint8_t signingKeyBlob[60]) {
|
||||||
uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
|
uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
|
||||||
uint8_t signingKeyPub[EIC_P256_PUB_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)) {
|
if (!eicOpsCreateEcKey(signingKeyPriv, signingKeyPub)) {
|
||||||
eicDebug("Error creating signing key");
|
eicDebug("Error creating signing key");
|
||||||
@@ -73,7 +134,8 @@ bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* doc
|
|||||||
if (!eicOpsSignEcKey(signingKeyPub, ctx->credentialPrivateKey, 1,
|
if (!eicOpsSignEcKey(signingKeyPub, ctx->credentialPrivateKey, 1,
|
||||||
"Android Identity Credential Key", // issuer CN
|
"Android Identity Credential Key", // issuer CN
|
||||||
"Android Identity Credential Authentication Key", // subject 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");
|
eicDebug("Error creating certificate for signing key");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -674,7 +736,8 @@ bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMac
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
|
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]) {
|
uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
|
||||||
EicCbor cbor;
|
EicCbor cbor;
|
||||||
|
|
||||||
@@ -712,9 +775,12 @@ bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
|
|||||||
eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfDeletionCborSize);
|
eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfDeletionCborSize);
|
||||||
|
|
||||||
// Finally, the CBOR that we're actually signing.
|
// Finally, the CBOR that we're actually signing.
|
||||||
eicCborAppendArray(&cbor, 3);
|
eicCborAppendArray(&cbor, includeChallenge ? 4 : 3);
|
||||||
eicCborAppendString(&cbor, "ProofOfDeletion");
|
eicCborAppendString(&cbor, "ProofOfDeletion");
|
||||||
eicCborAppendString(&cbor, docType);
|
eicCborAppendString(&cbor, docType);
|
||||||
|
if (includeChallenge) {
|
||||||
|
eicCborAppendByteString(&cbor, challenge, challengeSize);
|
||||||
|
}
|
||||||
eicCborAppendBool(&cbor, ctx->testCredential);
|
eicCborAppendBool(&cbor, ctx->testCredential);
|
||||||
|
|
||||||
uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
|
uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
|
||||||
@@ -726,3 +792,59 @@ bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
|
|||||||
|
|
||||||
return true;
|
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
|
#define EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE 65
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
int featureLevel;
|
||||||
|
|
||||||
uint8_t storageKey[EIC_AES_128_KEY_SIZE];
|
uint8_t storageKey[EIC_AES_128_KEY_SIZE];
|
||||||
uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
|
uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
|
||||||
|
|
||||||
@@ -79,12 +81,17 @@ typedef struct {
|
|||||||
// SHA-256 for AdditionalData, updated for each entry.
|
// SHA-256 for AdditionalData, updated for each entry.
|
||||||
uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
|
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;
|
size_t expectedCborSizeAtEnd;
|
||||||
EicCbor cbor;
|
EicCbor cbor;
|
||||||
} EicPresentation;
|
} EicPresentation;
|
||||||
|
|
||||||
bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
|
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,
|
bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now,
|
||||||
uint8_t* publicKeyCert, size_t* publicKeyCertSize,
|
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.
|
// where content is set to the ProofOfDeletion CBOR.
|
||||||
//
|
//
|
||||||
bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
|
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]);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -26,10 +26,84 @@ bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential) {
|
|||||||
return true;
|
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,
|
bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
|
||||||
size_t challengeSize, const uint8_t* applicationId,
|
size_t challengeSize, const uint8_t* applicationId,
|
||||||
size_t applicationIdSize, uint8_t* publicKeyCert,
|
size_t applicationIdSize, uint8_t* publicKeyCert,
|
||||||
size_t* publicKeyCertSize) {
|
size_t* publicKeyCertSize) {
|
||||||
|
if (ctx->isUpdate) {
|
||||||
|
eicDebug("Cannot create CredentialKey on update");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge, challengeSize,
|
if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge, challengeSize,
|
||||||
applicationId, applicationIdSize, ctx->testCredential,
|
applicationId, applicationIdSize, ctx->testCredential,
|
||||||
publicKeyCert, publicKeyCertSize)) {
|
publicKeyCert, publicKeyCertSize)) {
|
||||||
@@ -96,6 +170,9 @@ bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControl
|
|||||||
eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedProofOfProvisioningSize);
|
eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedProofOfProvisioningSize);
|
||||||
ctx->expectedCborSizeAtEnd = expectedProofOfProvisioningSize + ctx->cbor.size;
|
ctx->expectedCborSizeAtEnd = expectedProofOfProvisioningSize + ctx->cbor.size;
|
||||||
|
|
||||||
|
eicOpsSha256Init(&ctx->proofOfProvisioningDigester);
|
||||||
|
eicCborEnableSecondaryDigesterSha256(&ctx->cbor, &ctx->proofOfProvisioningDigester);
|
||||||
|
|
||||||
eicCborAppendArray(&ctx->cbor, 5);
|
eicCborAppendArray(&ctx->cbor, 5);
|
||||||
eicCborAppendString(&ctx->cbor, "ProofOfProvisioning");
|
eicCborAppendString(&ctx->cbor, "ProofOfProvisioning");
|
||||||
eicCborAppendString(&ctx->cbor, docType);
|
eicCborAppendString(&ctx->cbor, docType);
|
||||||
@@ -260,14 +337,23 @@ bool eicProvisioningFinishAddingEntries(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
|
bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
|
||||||
uint8_t encryptedCredentialKeys[80]) {
|
uint8_t* encryptedCredentialKeys,
|
||||||
|
size_t* encryptedCredentialKeysSize) {
|
||||||
EicCbor cbor;
|
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));
|
eicCborInit(&cbor, cborBuf, sizeof(cborBuf));
|
||||||
eicCborAppendArray(&cbor, 2);
|
eicCborAppendArray(&cbor, 3);
|
||||||
eicCborAppendByteString(&cbor, ctx->storageKey, EIC_AES_128_KEY_SIZE);
|
eicCborAppendByteString(&cbor, ctx->storageKey, EIC_AES_128_KEY_SIZE);
|
||||||
eicCborAppendByteString(&cbor, ctx->credentialPrivateKey, EIC_P256_PRIV_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)) {
|
if (cbor.size > sizeof(cborBuf)) {
|
||||||
eicDebug("Exceeded buffer size");
|
eicDebug("Exceeded buffer size");
|
||||||
return false;
|
return false;
|
||||||
@@ -285,6 +371,7 @@ bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* do
|
|||||||
eicDebug("Error encrypting CredentialKeys");
|
eicDebug("Error encrypting CredentialKeys");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
*encryptedCredentialKeysSize = cbor.size + 28;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ extern "C" {
|
|||||||
#define EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS 32
|
#define EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS 32
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// Set by eicCreateCredentialKey.
|
// Set by eicCreateCredentialKey() OR eicProvisioningInitForUpdate()
|
||||||
uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
|
uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
|
||||||
|
|
||||||
int numEntryCounts;
|
int numEntryCounts;
|
||||||
@@ -43,6 +43,7 @@ typedef struct {
|
|||||||
size_t curEntrySize;
|
size_t curEntrySize;
|
||||||
size_t curEntryNumBytesReceived;
|
size_t curEntryNumBytesReceived;
|
||||||
|
|
||||||
|
// Set by eicProvisioningInit() OR eicProvisioningInitForUpdate()
|
||||||
uint8_t storageKey[EIC_AES_128_KEY_SIZE];
|
uint8_t storageKey[EIC_AES_128_KEY_SIZE];
|
||||||
|
|
||||||
size_t expectedCborSizeAtEnd;
|
size_t expectedCborSizeAtEnd;
|
||||||
@@ -50,13 +51,23 @@ typedef struct {
|
|||||||
// SHA-256 for AdditionalData, updated for each entry.
|
// SHA-256 for AdditionalData, updated for each entry.
|
||||||
uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
|
uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
|
||||||
|
|
||||||
|
// Digester just for ProofOfProvisioning (without Sig_structure).
|
||||||
|
EicSha256Ctx proofOfProvisioningDigester;
|
||||||
|
|
||||||
EicCbor cbor;
|
EicCbor cbor;
|
||||||
|
|
||||||
bool testCredential;
|
bool testCredential;
|
||||||
|
|
||||||
|
// Set to true if this is an update.
|
||||||
|
bool isUpdate;
|
||||||
} EicProvisioning;
|
} EicProvisioning;
|
||||||
|
|
||||||
bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential);
|
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,
|
bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
|
||||||
size_t challengeSize, const uint8_t* applicationId,
|
size_t challengeSize, const uint8_t* applicationId,
|
||||||
size_t applicationIdSize, uint8_t* publicKeyCert,
|
size_t applicationIdSize, uint8_t* publicKeyCert,
|
||||||
@@ -107,14 +118,18 @@ bool eicProvisioningFinishAddingEntries(
|
|||||||
// CredentialKeys = [
|
// CredentialKeys = [
|
||||||
// bstr, ; storageKey, a 128-bit AES key
|
// bstr, ; storageKey, a 128-bit AES key
|
||||||
// bstr ; credentialPrivKey, the private key for credentialKey
|
// 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
|
// Since |storageKey| is 16 bytes and |credentialPrivKey| is 32 bytes, the
|
||||||
// encoded CBOR for CredentialKeys is 52 bytes and consequently
|
// encoded CBOR for CredentialKeys is 86 bytes and consequently
|
||||||
// |encryptedCredentialKeys| will be 52 + 28 = 80 bytes.
|
// |encryptedCredentialKeys| will be no longer than 86 + 28 = 114 bytes.
|
||||||
//
|
//
|
||||||
bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
|
bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
|
||||||
uint8_t encryptedCredentialKeys[80]);
|
uint8_t* encryptedCredentialKeys,
|
||||||
|
size_t* encryptedCredentialKeysSize);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,21 @@ cc_test {
|
|||||||
"VtsHalTargetTestDefaults",
|
"VtsHalTargetTestDefaults",
|
||||||
"use_libaidlvintf_gtest_helper_static",
|
"use_libaidlvintf_gtest_helper_static",
|
||||||
],
|
],
|
||||||
|
cflags: [
|
||||||
|
"-Wno-deprecated-declarations",
|
||||||
|
],
|
||||||
srcs: [
|
srcs: [
|
||||||
"VtsHalIdentityEndToEndTest.cpp",
|
|
||||||
"VtsIWritableIdentityCredentialTests.cpp",
|
"VtsIWritableIdentityCredentialTests.cpp",
|
||||||
"VtsIdentityTestUtils.cpp",
|
"Util.cpp",
|
||||||
"VtsAttestationTests.cpp",
|
"VtsAttestationTests.cpp",
|
||||||
"UserAuthTests.cpp",
|
"UserAuthTests.cpp",
|
||||||
"ReaderAuthTests.cpp",
|
"ReaderAuthTests.cpp",
|
||||||
|
"DeleteCredentialTests.cpp",
|
||||||
|
"ProveOwnershipTests.cpp",
|
||||||
|
"UpdateCredentialTests.cpp",
|
||||||
|
"EndToEndTests.cpp",
|
||||||
|
"TestCredentialTests.cpp",
|
||||||
|
"AuthenticationKeyTests.cpp",
|
||||||
],
|
],
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
"libbinder",
|
"libbinder",
|
||||||
@@ -22,9 +30,9 @@ cc_test {
|
|||||||
"libpuresoftkeymasterdevice",
|
"libpuresoftkeymasterdevice",
|
||||||
"android.hardware.keymaster@4.0",
|
"android.hardware.keymaster@4.0",
|
||||||
"android.hardware.identity-support-lib",
|
"android.hardware.identity-support-lib",
|
||||||
"android.hardware.identity-cpp",
|
"android.hardware.identity-unstable-cpp",
|
||||||
"android.hardware.keymaster-cpp",
|
"android.hardware.keymaster-unstable-cpp",
|
||||||
"android.hardware.keymaster-ndk_platform",
|
"android.hardware.keymaster-unstable-ndk_platform",
|
||||||
"libkeymaster4support",
|
"libkeymaster4support",
|
||||||
"libkeymaster4_1support",
|
"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 <map>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include "VtsIdentityTestUtils.h"
|
#include "Util.h"
|
||||||
|
|
||||||
namespace android::hardware::identity {
|
namespace android::hardware::identity {
|
||||||
|
|
||||||
@@ -50,18 +50,20 @@ using ::android::hardware::keymaster::VerificationToken;
|
|||||||
|
|
||||||
using test_utils::validateAttestationCertificate;
|
using test_utils::validateAttestationCertificate;
|
||||||
|
|
||||||
class IdentityAidl : public testing::TestWithParam<std::string> {
|
class EndToEndTests : public testing::TestWithParam<std::string> {
|
||||||
public:
|
public:
|
||||||
virtual void SetUp() override {
|
virtual void SetUp() override {
|
||||||
credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
|
credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
|
||||||
String16(GetParam().c_str()));
|
String16(GetParam().c_str()));
|
||||||
ASSERT_NE(credentialStore_, nullptr);
|
ASSERT_NE(credentialStore_, nullptr);
|
||||||
|
halApiVersion_ = credentialStore_->getInterfaceVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
sp<IIdentityCredentialStore> credentialStore_;
|
sp<IIdentityCredentialStore> credentialStore_;
|
||||||
|
int halApiVersion_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_P(IdentityAidl, hardwareInformation) {
|
TEST_P(EndToEndTests, hardwareInformation) {
|
||||||
HardwareInformation info;
|
HardwareInformation info;
|
||||||
ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
|
ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
|
||||||
ASSERT_GT(info.credentialStoreName.size(), 0);
|
ASSERT_GT(info.credentialStoreName.size(), 0);
|
||||||
@@ -69,20 +71,21 @@ TEST_P(IdentityAidl, hardwareInformation) {
|
|||||||
ASSERT_GE(info.dataChunkSize, 256);
|
ASSERT_GE(info.dataChunkSize, 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
tuple<bool, string, vector<uint8_t>, vector<uint8_t>> extractFromTestCredentialData(
|
tuple<bool, string, vector<uint8_t>, vector<uint8_t>, vector<uint8_t>>
|
||||||
const vector<uint8_t>& credentialData) {
|
extractFromTestCredentialData(const vector<uint8_t>& credentialData) {
|
||||||
string docType;
|
string docType;
|
||||||
vector<uint8_t> storageKey;
|
vector<uint8_t> storageKey;
|
||||||
vector<uint8_t> credentialPrivKey;
|
vector<uint8_t> credentialPrivKey;
|
||||||
|
vector<uint8_t> sha256Pop;
|
||||||
|
|
||||||
auto [item, _, message] = cppbor::parse(credentialData);
|
auto [item, _, message] = cppbor::parse(credentialData);
|
||||||
if (item == nullptr) {
|
if (item == nullptr) {
|
||||||
return make_tuple(false, docType, storageKey, credentialPrivKey);
|
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cppbor::Array* arrayItem = item->asArray();
|
const cppbor::Array* arrayItem = item->asArray();
|
||||||
if (arrayItem == nullptr || arrayItem->size() != 3) {
|
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();
|
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();
|
const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
|
||||||
if (docTypeItem == nullptr || testCredentialItem == nullptr ||
|
if (docTypeItem == nullptr || testCredentialItem == nullptr ||
|
||||||
encryptedCredentialKeysItem == nullptr) {
|
encryptedCredentialKeysItem == nullptr) {
|
||||||
return make_tuple(false, docType, storageKey, credentialPrivKey);
|
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
|
||||||
}
|
}
|
||||||
|
|
||||||
docType = docTypeItem->value();
|
docType = docTypeItem->value();
|
||||||
@@ -103,28 +106,38 @@ tuple<bool, string, vector<uint8_t>, vector<uint8_t>> extractFromTestCredentialD
|
|||||||
optional<vector<uint8_t>> decryptedCredentialKeys =
|
optional<vector<uint8_t>> decryptedCredentialKeys =
|
||||||
support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
|
support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
|
||||||
if (!decryptedCredentialKeys) {
|
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());
|
auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
|
||||||
if (dckItem == nullptr) {
|
if (dckItem == nullptr) {
|
||||||
return make_tuple(false, docType, storageKey, credentialPrivKey);
|
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
|
||||||
}
|
}
|
||||||
const cppbor::Array* dckArrayItem = dckItem->asArray();
|
const cppbor::Array* dckArrayItem = dckItem->asArray();
|
||||||
if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
|
if (dckArrayItem == nullptr) {
|
||||||
return make_tuple(false, docType, storageKey, credentialPrivKey);
|
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* storageKeyItem = (*dckArrayItem)[0]->asBstr();
|
||||||
const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
|
const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
|
||||||
if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
|
if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
|
||||||
return make_tuple(false, docType, storageKey, credentialPrivKey);
|
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
|
||||||
}
|
}
|
||||||
storageKey = storageKeyItem->value();
|
storageKey = storageKeyItem->value();
|
||||||
credentialPrivKey = credentialPrivKeyItem->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
|
// First, generate a key-pair for the reader since its public key will be
|
||||||
// part of the request data.
|
// part of the request data.
|
||||||
vector<uint8_t> readerKey;
|
vector<uint8_t> readerKey;
|
||||||
@@ -277,8 +290,9 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
|
|||||||
|
|
||||||
// Extract doctype, storage key, and credentialPrivKey from credentialData... this works
|
// 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.
|
// 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);
|
extractFromTestCredentialData(credentialData);
|
||||||
|
|
||||||
ASSERT_TRUE(exSuccess);
|
ASSERT_TRUE(exSuccess);
|
||||||
ASSERT_EQ(exDocType, "org.iso.18013-5.2019.mdl");
|
ASSERT_EQ(exDocType, "org.iso.18013-5.2019.mdl");
|
||||||
// ... check that the public key derived from the private key matches what was
|
// ... 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_TRUE(exCredentialPubKey);
|
||||||
ASSERT_EQ(exCredentialPubKey.value(), credentialPubKey.value());
|
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
|
// Now that the credential has been provisioned, read it back and check the
|
||||||
// correct data is returned.
|
// correct data is returned.
|
||||||
sp<IIdentityCredential> credential;
|
sp<IIdentityCredential> credential;
|
||||||
@@ -498,13 +519,11 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
|
|||||||
EXPECT_EQ(mac, calculatedMac);
|
EXPECT_EQ(mac, calculatedMac);
|
||||||
}
|
}
|
||||||
|
|
||||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(IdentityAidl);
|
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EndToEndTests);
|
||||||
INSTANTIATE_TEST_SUITE_P(
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
Identity, IdentityAidl,
|
Identity, EndToEndTests,
|
||||||
testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
|
testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
|
||||||
android::PrintInstanceNameToString);
|
android::PrintInstanceNameToString);
|
||||||
// INSTANTIATE_TEST_SUITE_P(Identity, IdentityAidl,
|
|
||||||
// testing::Values("android.hardware.identity.IIdentityCredentialStore/default"));
|
|
||||||
|
|
||||||
} // namespace android::hardware::identity
|
} // 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 <map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "VtsIdentityTestUtils.h"
|
#include "Util.h"
|
||||||
|
|
||||||
namespace android::hardware::identity {
|
namespace android::hardware::identity {
|
||||||
|
|
||||||
@@ -123,9 +123,9 @@ vector<uint8_t> generateReaderCert(const vector<uint8_t>& publicKey,
|
|||||||
const vector<uint8_t>& signingKey) {
|
const vector<uint8_t>& signingKey) {
|
||||||
time_t validityNotBefore = 0;
|
time_t validityNotBefore = 0;
|
||||||
time_t validityNotAfter = 0xffffffff;
|
time_t validityNotAfter = 0xffffffff;
|
||||||
optional<vector<uint8_t>> cert =
|
optional<vector<uint8_t>> cert = support::ecPublicKeyGenerateCertificate(
|
||||||
support::ecPublicKeyGenerateCertificate(publicKey, signingKey, "24601", "Issuer",
|
publicKey, signingKey, "24601", "Issuer", "Subject", validityNotBefore,
|
||||||
"Subject", validityNotBefore, validityNotAfter);
|
validityNotAfter, {});
|
||||||
return cert.value();
|
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 <map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "VtsIdentityTestUtils.h"
|
#include "Util.h"
|
||||||
|
|
||||||
namespace android::hardware::identity {
|
namespace android::hardware::identity {
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ void UserAuthTests::provisionData() {
|
|||||||
EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
|
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();
|
pair<vector<uint8_t>, vector<uint8_t>> generateReaderKey();
|
||||||
vector<uint8_t> generateReaderCert(const vector<uint8_t>& publicKey,
|
vector<uint8_t> generateReaderCert(const vector<uint8_t>& publicKey,
|
||||||
const vector<uint8_t>& signingKey);
|
const vector<uint8_t>& signingKey);
|
||||||
|
|||||||
@@ -14,15 +14,18 @@
|
|||||||
* limitations under the License.
|
* 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 <aidl/Gtest.h>
|
||||||
#include <android-base/logging.h>
|
#include <android-base/stringprintf.h>
|
||||||
#include <keymaster/km_openssl/openssl_utils.h>
|
#include <keymaster/km_openssl/openssl_utils.h>
|
||||||
#include <keymasterV4_1/attestation_record.h>
|
#include <keymasterV4_1/attestation_record.h>
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
namespace android::hardware::identity::test_utils {
|
namespace android::hardware::identity::test_utils {
|
||||||
@@ -35,6 +38,7 @@ using std::vector;
|
|||||||
|
|
||||||
using ::android::sp;
|
using ::android::sp;
|
||||||
using ::android::String16;
|
using ::android::String16;
|
||||||
|
using ::android::base::StringPrintf;
|
||||||
using ::android::binder::Status;
|
using ::android::binder::Status;
|
||||||
using ::keymaster::X509_Ptr;
|
using ::keymaster::X509_Ptr;
|
||||||
|
|
||||||
@@ -86,7 +90,7 @@ optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal,
|
|||||||
|
|
||||||
return support::ecPublicKeyGenerateCertificate(readerPublicKey.value(), readerKey.value(),
|
return support::ecPublicKeyGenerateCertificate(readerPublicKey.value(), readerKey.value(),
|
||||||
serialDecimal, issuer, subject,
|
serialDecimal, issuer, subject,
|
||||||
validityNotBefore, validityNotAfter);
|
validityNotBefore, validityNotAfter, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<vector<SecureAccessControlProfile>> addAccessControlProfiles(
|
optional<vector<SecureAccessControlProfile>> addAccessControlProfiles(
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||||
#include <cppbor.h>
|
#include <cppbor.h>
|
||||||
#include <cppbor_parse.h>
|
#include <cppbor_parse.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
namespace android::hardware::identity::test_utils {
|
namespace android::hardware::identity::test_utils {
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
#include <future>
|
#include <future>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "VtsIdentityTestUtils.h"
|
#include "Util.h"
|
||||||
|
|
||||||
namespace android::hardware::identity {
|
namespace android::hardware::identity {
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
#include <future>
|
#include <future>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "VtsIdentityTestUtils.h"
|
#include "Util.h"
|
||||||
|
|
||||||
namespace android::hardware::identity {
|
namespace android::hardware::identity {
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#define IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_
|
#define IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
@@ -29,11 +30,12 @@ namespace hardware {
|
|||||||
namespace identity {
|
namespace identity {
|
||||||
namespace support {
|
namespace support {
|
||||||
|
|
||||||
|
using ::std::map;
|
||||||
using ::std::optional;
|
using ::std::optional;
|
||||||
|
using ::std::pair;
|
||||||
using ::std::string;
|
using ::std::string;
|
||||||
using ::std::tuple;
|
using ::std::tuple;
|
||||||
using ::std::vector;
|
using ::std::vector;
|
||||||
using ::std::pair;
|
|
||||||
|
|
||||||
// The semantic tag for a bstr which includes Encoded CBOR (RFC 7049, section 2.4)
|
// The semantic tag for a bstr which includes Encoded CBOR (RFC 7049, section 2.4)
|
||||||
const int kSemanticTagEncodedCbor = 24;
|
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);
|
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
|
// Generates a X.509 certificate for |publicKey| (which must be in the format
|
||||||
// returned by ecKeyPairGetPublicKey()).
|
// returned by ecKeyPairGetPublicKey()).
|
||||||
//
|
//
|
||||||
@@ -230,7 +237,8 @@ optional<pair<time_t, time_t>> certificateGetValidity(const vector<uint8_t>& x50
|
|||||||
optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
|
optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
|
||||||
const vector<uint8_t>& publicKey, const vector<uint8_t>& signingKey,
|
const vector<uint8_t>& publicKey, const vector<uint8_t>& signingKey,
|
||||||
const string& serialDecimal, const string& issuer, const string& subject,
|
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
|
// Performs Elliptic-curve Diffie-Helman using |publicKey| (which must be in the
|
||||||
// format returned by ecKeyPairGetPublicKey()) and |privateKey| (which must be
|
// 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.
|
// Crypto functionality / abstraction.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct EVP_CIPHER_CTX_Deleter {
|
using EvpCipherCtxPtr = bssl::UniquePtr<EVP_CIPHER_CTX>;
|
||||||
void operator()(EVP_CIPHER_CTX* ctx) const {
|
using EC_KEY_Ptr = bssl::UniquePtr<EC_KEY>;
|
||||||
if (ctx != nullptr) {
|
using EVP_PKEY_Ptr = bssl::UniquePtr<EVP_PKEY>;
|
||||||
EVP_CIPHER_CTX_free(ctx);
|
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 EvpCipherCtxPtr = unique_ptr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_Deleter>;
|
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) {
|
// bool getRandom(size_t numBytes, vector<uint8_t>& output) {
|
||||||
optional<vector<uint8_t>> getRandom(size_t numBytes) {
|
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;
|
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> certificateChainJoin(const vector<vector<uint8_t>>& certificateChain) {
|
||||||
vector<uint8_t> ret;
|
vector<uint8_t> ret;
|
||||||
for (const vector<uint8_t>& certificate : certificateChain) {
|
for (const vector<uint8_t>& certificate : certificateChain) {
|
||||||
@@ -1221,8 +1119,19 @@ optional<vector<uint8_t>> ecKeyPairGetPrivateKey(const vector<uint8_t>& keyPair)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
vector<uint8_t> privateKey;
|
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;
|
return privateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1379,7 +1288,8 @@ optional<vector<uint8_t>> ecKeyPairGetPkcs12(const vector<uint8_t>& keyPair, con
|
|||||||
optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
|
optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
|
||||||
const vector<uint8_t>& publicKey, const vector<uint8_t>& signingKey,
|
const vector<uint8_t>& publicKey, const vector<uint8_t>& signingKey,
|
||||||
const string& serialDecimal, const string& issuer, const string& subject,
|
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 group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
|
||||||
auto point = EC_POINT_Ptr(EC_POINT_new(group.get()));
|
auto point = EC_POINT_Ptr(EC_POINT_new(group.get()));
|
||||||
if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) !=
|
if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) !=
|
||||||
@@ -1482,6 +1392,32 @@ optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
|
|||||||
return {};
|
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) {
|
if (X509_sign(x509.get(), privPkey.get(), EVP_sha256()) == 0) {
|
||||||
LOG(ERROR) << "Error signing X509 certificate";
|
LOG(ERROR) << "Error signing X509 certificate";
|
||||||
return {};
|
return {};
|
||||||
@@ -1650,6 +1586,44 @@ optional<vector<uint8_t>> certificateChainGetTopMostKey(const vector<uint8_t>& c
|
|||||||
return publicKey;
|
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) {
|
optional<pair<size_t, size_t>> certificateFindPublicKey(const vector<uint8_t>& x509Certificate) {
|
||||||
vector<X509_Ptr> certs;
|
vector<X509_Ptr> certs;
|
||||||
if (!parseX509Certificates(x509Certificate, 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>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
|
||||||
|
|
||||||
optional<vector<uint8_t>> cert = support::ecPublicKeyGenerateCertificate(
|
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());
|
certs.push_back(cert.value());
|
||||||
}
|
}
|
||||||
return support::certificateChainJoin(certs);
|
return support::certificateChainJoin(certs);
|
||||||
@@ -338,7 +338,7 @@ TEST(IdentityCredentialSupport, CertificateChain) {
|
|||||||
ASSERT_TRUE(pubKey);
|
ASSERT_TRUE(pubKey);
|
||||||
|
|
||||||
optional<vector<uint8_t>> cert = support::ecPublicKeyGenerateCertificate(
|
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 =
|
optional<vector<uint8_t>> extractedPubKey =
|
||||||
support::certificateChainGetTopMostKey(cert.value());
|
support::certificateChainGetTopMostKey(cert.value());
|
||||||
@@ -358,7 +358,7 @@ TEST(IdentityCredentialSupport, CertificateChain) {
|
|||||||
optional<vector<uint8_t>> otherPubKey = support::ecKeyPairGetPublicKey(keyPair.value());
|
optional<vector<uint8_t>> otherPubKey = support::ecKeyPairGetPublicKey(keyPair.value());
|
||||||
ASSERT_TRUE(otherPubKey);
|
ASSERT_TRUE(otherPubKey);
|
||||||
optional<vector<uint8_t>> otherCert = support::ecPublicKeyGenerateCertificate(
|
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
|
// Now both cert and otherCert are two distinct certificates. Let's make a
|
||||||
// chain and check that certificateChainSplit() works as expected.
|
// chain and check that certificateChainSplit() works as expected.
|
||||||
|
|||||||
Reference in New Issue
Block a user