Merge "Added keymint_attestation_fuzzer" into main am: 02a086bdaa

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/3066743

Change-Id: Id5c46cf92626e9f9ee13c3644b0128306dc09281
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Treehugger Robot
2024-05-28 07:02:14 +00:00
committed by Automerger Merge Worker
4 changed files with 537 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "hardware_interfaces_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["hardware_interfaces_license"],
default_team: "trendy_team_android_hardware_backed_security",
}
cc_defaults {
name: "keymint_fuzzer_defaults",
static_libs: [
"libhidlbase",
"libkeymint_support",
],
shared_libs: [
"libbase",
"libcrypto",
"libutils",
"libhardware",
"libbinder_ndk",
"liblog",
],
defaults: [
"keymint_use_latest_hal_aidl_ndk_shared",
],
include_dirs: [
"hardware/interfaces/security/keymint/support/include",
"frameworks/native/libs/binder/ndk/include_platform",
],
}
cc_fuzz {
name: "keymint_attestation_fuzzer",
srcs: [
"keymint_attestation_fuzzer.cpp",
],
defaults: [
"keymint_fuzzer_defaults",
],
}

View File

@@ -0,0 +1,53 @@
# Fuzzers for libkeymint_support
## Plugin Design Considerations
The fuzzer plugins for libkeymint_support are designed based on the understanding of the source code and try to achieve the following:
#### Maximize code coverage
The configuration parameters are not hardcoded, but instead selected based on incoming data. This ensures more code paths are reached by the fuzzers.
#### Maximize utilization of input data
The plugins feed the entire input data to the module. This ensures that the plugins tolerate any kind of input (empty, huge, malformed, etc) and dont `exit()` on any input and thereby increasing the chance of identifying vulnerabilities.
## Table of contents
+ [keymint_attestation_fuzzer](#KeyMintAttestation)
# <a name="KeyMintAttestation"></a> Fuzzer for KeyMintAttestation
KeyMintAttestation supports the following parameters:
1. PaddingMode(parameter name: "padding")
2. Digest(parameter name: "digest")
3. Index(parameter name: "idx")
4. Timestamp(parameter name: "timestamp")
5. AuthSet(parameter name: "authSet")
6. IssuerSubjectName(parameter name: "issuerSubjectName")
7. AttestationChallenge(parameter name: "challenge")
8. AttestationApplicationId(parameter name: "id")
9. EcCurve(parameter name: "ecCurve")
10. BlockMode(parameter name: "blockmode")
11. minMacLength(parameter name: "minMacLength")
12. macLength(parameter name: "macLength")
| Parameter| Valid Values| Configured Value|
|------------- |--------------| -------------------- |
|`padding`| `PaddingMode` |Value obtained from FuzzedDataProvider|
|`digest`| `Digest` |Value obtained from FuzzedDataProvider|
|`idx`| `size_t` |Value obtained from FuzzedDataProvider|
|`timestamp`| `uint64_t` |Value obtained from FuzzedDataProvider|
|`authSet`| `uint32_t` |Value obtained from FuzzedDataProvider|
|`issuerSubjectName`| `uint8_t` |Value obtained from FuzzedDataProvider|
|`AttestationChallenge`| `string` |Value obtained from FuzzedDataProvider|
|`AttestationApplicationId`| `string` |Value obtained from FuzzedDataProvider|
|`blockmode`| `BlockMode` |Value obtained from FuzzedDataProvider|
|`minMacLength`| `uint32_t` |Value obtained from FuzzedDataProvider|
|`macLength`| `uint32_t` |Value obtained from FuzzedDataProvider|
#### Steps to run
1. Build the fuzzer
```
$ mm -j$(nproc) keymint_attestation_fuzzer
```
2. Run on device
```
$ adb sync data
$ adb shell /data/fuzz/arm64/keymint_attestation_fuzzer/keymint_attestation_fuzzer
```

View File

@@ -0,0 +1,165 @@
/*
* Copyright (C) 2024 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.
*
*/
#include <aidl/android/hardware/security/keymint/AttestationKey.h>
#include <aidl/android/hardware/security/keymint/KeyCreationResult.h>
#include <android/binder_manager.h>
#include <keymint_common.h>
#include <keymint_support/attestation_record.h>
#include <keymint_support/openssl_utils.h>
#include <utils/Log.h>
namespace android::hardware::security::keymint_support::fuzzer {
using namespace android;
using AStatus = ::ndk::ScopedAStatus;
std::shared_ptr<IKeyMintDevice> gKeyMint = nullptr;
constexpr size_t kMaxBytes = 256;
const std::string kServiceName = "android.hardware.security.keymint.IKeyMintDevice/default";
class KeyMintAttestationFuzzer {
public:
KeyMintAttestationFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
void process();
private:
KeyCreationResult generateKey(const AuthorizationSet& keyDesc,
const std::optional<AttestationKey>& attestKey,
vector<uint8_t>* keyBlob,
vector<KeyCharacteristics>* keyCharacteristics,
vector<Certificate>* certChain);
X509_Ptr parseCertificateBlob(const vector<uint8_t>& blob);
ASN1_OCTET_STRING* getAttestationRecord(const X509* certificate);
bool verifyAttestationRecord(const vector<uint8_t>& attestationCert);
FuzzedDataProvider mFdp;
};
KeyCreationResult KeyMintAttestationFuzzer::generateKey(
const AuthorizationSet& keyDesc, const std::optional<AttestationKey>& attestKey,
vector<uint8_t>* keyBlob, vector<KeyCharacteristics>* keyCharacteristics,
vector<Certificate>* certChain) {
KeyCreationResult creationResult;
AStatus result = gKeyMint->generateKey(keyDesc.vector_data(), attestKey, &creationResult);
if (result.isOk() && creationResult.keyBlob.size() > 0) {
*keyBlob = std::move(creationResult.keyBlob);
*keyCharacteristics = std::move(creationResult.keyCharacteristics);
*certChain = std::move(creationResult.certificateChain);
}
return creationResult;
}
X509_Ptr KeyMintAttestationFuzzer::parseCertificateBlob(const vector<uint8_t>& blob) {
const uint8_t* data = blob.data();
return X509_Ptr(d2i_X509(nullptr, &data, blob.size()));
}
ASN1_OCTET_STRING* KeyMintAttestationFuzzer::getAttestationRecord(const X509* certificate) {
ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */));
if (!oid.get()) {
return nullptr;
}
int32_t location = X509_get_ext_by_OBJ(certificate, oid.get(), -1 /* search from beginning */);
if (location == -1) {
return nullptr;
}
X509_EXTENSION* attestRecordExt = X509_get_ext(certificate, location);
if (!attestRecordExt) {
return nullptr;
}
ASN1_OCTET_STRING* attestRecord = X509_EXTENSION_get_data(attestRecordExt);
return attestRecord;
}
bool KeyMintAttestationFuzzer::verifyAttestationRecord(const vector<uint8_t>& attestationCert) {
X509_Ptr cert(parseCertificateBlob(attestationCert));
if (!cert.get()) {
return false;
}
ASN1_OCTET_STRING* attestRecord = getAttestationRecord(cert.get());
if (!attestRecord) {
return false;
}
AuthorizationSet attestationSwEnforced;
AuthorizationSet attestationHwEnforced;
SecurityLevel attestationSecurityLevel;
SecurityLevel keymintSecurityLevel;
vector<uint8_t> attestationChallenge;
vector<uint8_t> attestationUniqueId;
uint32_t attestationVersion;
uint32_t keymintVersion;
auto error = parse_attestation_record(attestRecord->data, attestRecord->length,
&attestationVersion, &attestationSecurityLevel,
&keymintVersion, &keymintSecurityLevel,
&attestationChallenge, &attestationSwEnforced,
&attestationHwEnforced, &attestationUniqueId);
if (error != ErrorCode::OK) {
return false;
}
VerifiedBoot verifiedBootState;
vector<uint8_t> verifiedBootKey;
vector<uint8_t> verifiedBootHash;
bool device_locked;
error = parse_root_of_trust(attestRecord->data, attestRecord->length, &verifiedBootKey,
&verifiedBootState, &device_locked, &verifiedBootHash);
if (error != ErrorCode::OK) {
return false;
}
return true;
}
void KeyMintAttestationFuzzer::process() {
AttestationKey attestKey;
vector<Certificate> attestKeyCertChain;
vector<KeyCharacteristics> attestKeyCharacteristics;
generateKey(createAuthSetForAttestKey(&mFdp), {}, &attestKey.keyBlob, &attestKeyCharacteristics,
&attestKeyCertChain);
vector<Certificate> attestedKeyCertChain;
vector<KeyCharacteristics> attestedKeyCharacteristics;
vector<uint8_t> attestedKeyBlob;
attestKey.issuerSubjectName = mFdp.ConsumeBytes<uint8_t>(kMaxBytes);
generateKey(createAuthorizationSet(&mFdp), attestKey, &attestedKeyBlob,
&attestedKeyCharacteristics, &attestedKeyCertChain);
if (attestedKeyCertChain.size() > 0) {
size_t leafCert = attestedKeyCertChain.size() - 1;
verifyAttestationRecord(attestedKeyCertChain[leafCert].encodedCertificate);
}
}
extern "C" int LLVMFuzzerInitialize(int /* *argc */, char /* ***argv */) {
::ndk::SpAIBinder binder(AServiceManager_waitForService(kServiceName.c_str()));
gKeyMint = std::move(IKeyMintDevice::fromBinder(binder));
LOG_ALWAYS_FATAL_IF(!gKeyMint, "Failed to get IKeyMintDevice instance.");
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
KeyMintAttestationFuzzer kmAttestationFuzzer(data, size);
kmAttestationFuzzer.process();
return 0;
}
} // namespace android::hardware::security::keymint_support::fuzzer

View File

@@ -0,0 +1,260 @@
/*
* Copyright (C) 2024 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.
*
*/
#ifndef __KEYMINT_COMMON_H__
#define __KEYMINT_COMMON_H__
#include <aidl/android/hardware/security/keymint/BlockMode.h>
#include <aidl/android/hardware/security/keymint/Digest.h>
#include <aidl/android/hardware/security/keymint/EcCurve.h>
#include <aidl/android/hardware/security/keymint/PaddingMode.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <keymint_support/authorization_set.h>
namespace android::hardware::security::keymint_support::fuzzer {
using namespace aidl::android::hardware::security::keymint;
constexpr uint32_t kStringSize = 64;
constexpr uint32_t k3DesKeySize = 168;
constexpr uint32_t kSymmKeySize = 256;
constexpr uint32_t kRsaKeySize = 2048;
constexpr uint32_t kPublicExponent = 65537;
constexpr EcCurve kCurve[] = {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521,
EcCurve::CURVE_25519};
constexpr PaddingMode kPaddingMode[] = {
PaddingMode::NONE,
PaddingMode::RSA_OAEP,
PaddingMode::RSA_PSS,
PaddingMode::RSA_PKCS1_1_5_ENCRYPT,
PaddingMode::RSA_PKCS1_1_5_SIGN,
PaddingMode::PKCS7,
};
constexpr Digest kDigest[] = {
Digest::NONE, Digest::MD5, Digest::SHA1, Digest::SHA_2_224,
Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512,
};
constexpr BlockMode kBlockMode[] = {
BlockMode::ECB,
BlockMode::CBC,
BlockMode::CTR,
BlockMode::GCM,
};
enum AttestAuthSet : uint32_t {
RSA_ATTEST_KEY = 0u,
ECDSA_ATTEST_KEY,
};
enum AuthSet : uint32_t {
RSA_KEY = 0u,
RSA_SIGNING_KEY,
RSA_ENCRYPTION_KEY,
ECDSA_SIGNING_CURVE,
AES_ENCRYPTION_KEY,
TRIPLE_DES,
HMAC,
NO_DIGEST,
ECB_MODE,
GSM_MODE_MIN_MAC,
GSM_MODE_MAC,
BLOCK_MODE,
};
AuthorizationSet createAuthSetForAttestKey(FuzzedDataProvider* dataProvider) {
uint32_t attestAuthSet = dataProvider->ConsumeBool() ? AttestAuthSet::RSA_ATTEST_KEY
: AttestAuthSet::ECDSA_ATTEST_KEY;
uint64_t timestamp = dataProvider->ConsumeIntegral<uint64_t>();
Digest digest = dataProvider->PickValueInArray(kDigest);
PaddingMode padding = dataProvider->PickValueInArray(kPaddingMode);
std::string challenge = dataProvider->ConsumeRandomLengthString(kStringSize);
std::string id = dataProvider->ConsumeRandomLengthString(kStringSize);
switch (attestAuthSet) {
case RSA_ATTEST_KEY: {
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.RsaKey(kRsaKeySize, kPublicExponent)
.Digest(digest)
.Padding(padding)
.AttestKey()
.AttestationChallenge(challenge)
.AttestationApplicationId(id)
.SetDefaultValidity()
.Authorization(TAG_CREATION_DATETIME, timestamp)
.Authorization(TAG_INCLUDE_UNIQUE_ID)
.Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY);
} break;
case ECDSA_ATTEST_KEY: {
EcCurve ecCurve = dataProvider->PickValueInArray(kCurve);
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.EcdsaKey(ecCurve)
.AttestKey()
.Digest(digest)
.AttestationChallenge(challenge)
.AttestationApplicationId(id)
.SetDefaultValidity()
.Authorization(TAG_CREATION_DATETIME, timestamp)
.Authorization(TAG_INCLUDE_UNIQUE_ID)
.Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY);
} break;
default:
break;
};
return AuthorizationSetBuilder();
}
AuthorizationSet createAuthorizationSet(FuzzedDataProvider* dataProvider) {
uint32_t authSet =
dataProvider->ConsumeIntegralInRange<uint32_t>(AuthSet::RSA_KEY, AuthSet::BLOCK_MODE);
uint64_t timestamp = dataProvider->ConsumeIntegral<uint64_t>();
Digest digest = dataProvider->PickValueInArray(kDigest);
PaddingMode padding = dataProvider->PickValueInArray(kPaddingMode);
std::string challenge = dataProvider->ConsumeRandomLengthString(kStringSize);
std::string id = dataProvider->ConsumeRandomLengthString(kStringSize);
switch (authSet) {
case RSA_KEY: {
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.RsaKey(kRsaKeySize, kPublicExponent)
.Digest(digest)
.Padding(padding)
.AttestKey()
.AttestationChallenge(challenge)
.AttestationApplicationId(id)
.SetDefaultValidity()
.Authorization(TAG_CREATION_DATETIME, timestamp)
.Authorization(TAG_INCLUDE_UNIQUE_ID);
} break;
case RSA_SIGNING_KEY: {
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.RsaSigningKey(kRsaKeySize, kPublicExponent)
.Digest(digest)
.Padding(padding)
.AttestationChallenge(challenge)
.AttestationApplicationId(id)
.SetDefaultValidity()
.Authorization(TAG_CREATION_DATETIME, timestamp)
.Authorization(TAG_INCLUDE_UNIQUE_ID);
} break;
case RSA_ENCRYPTION_KEY: {
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.RsaEncryptionKey(kRsaKeySize, kPublicExponent)
.Digest(digest)
.Padding(padding)
.AttestationChallenge(challenge)
.AttestationApplicationId(id)
.SetDefaultValidity()
.Authorization(TAG_CREATION_DATETIME, timestamp)
.Authorization(TAG_INCLUDE_UNIQUE_ID);
} break;
case ECDSA_SIGNING_CURVE: {
EcCurve ecCurve = dataProvider->PickValueInArray(kCurve);
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.EcdsaSigningKey(ecCurve)
.Digest(digest)
.AttestationChallenge(challenge)
.AttestationApplicationId(id)
.SetDefaultValidity()
.Authorization(TAG_CREATION_DATETIME, timestamp)
.Authorization(TAG_INCLUDE_UNIQUE_ID);
} break;
case AES_ENCRYPTION_KEY: {
BlockMode blockmode = dataProvider->PickValueInArray(kBlockMode);
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.AesEncryptionKey(kSymmKeySize)
.BlockMode(blockmode)
.Digest(digest)
.Padding(padding);
} break;
case TRIPLE_DES: {
BlockMode blockmode = dataProvider->PickValueInArray(kBlockMode);
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.TripleDesEncryptionKey(k3DesKeySize)
.BlockMode(blockmode)
.Digest(digest)
.Padding(padding)
.EcbMode()
.SetDefaultValidity();
} break;
case HMAC: {
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.HmacKey(kSymmKeySize)
.Digest(digest)
.Padding(padding);
} break;
case NO_DIGEST: {
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.AesEncryptionKey(kSymmKeySize)
.NoDigestOrPadding()
.Digest(digest)
.Padding(padding);
} break;
case ECB_MODE: {
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.AesEncryptionKey(kSymmKeySize)
.EcbMode()
.Digest(digest)
.Padding(padding);
} break;
case GSM_MODE_MIN_MAC: {
uint32_t minMacLength = dataProvider->ConsumeIntegral<uint32_t>();
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.AesEncryptionKey(kSymmKeySize)
.GcmModeMinMacLen(minMacLength)
.Digest(digest)
.Padding(padding);
} break;
case GSM_MODE_MAC: {
uint32_t macLength = dataProvider->ConsumeIntegral<uint32_t>();
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.AesEncryptionKey(kSymmKeySize)
.GcmModeMacLen(macLength)
.Digest(digest)
.Padding(padding);
} break;
case BLOCK_MODE: {
BlockMode blockmode = dataProvider->PickValueInArray(kBlockMode);
return AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.AesEncryptionKey(kSymmKeySize)
.BlockMode(blockmode)
.Digest(digest)
.Padding(padding);
} break;
default:
break;
};
return AuthorizationSetBuilder();
}
} // namespace android::hardware::security::keymint_support::fuzzer
#endif // __KEYMINT_COMMON_H__