From 007129a7fd16dad09d313a865afc50e5e0b28f0d Mon Sep 17 00:00:00 2001 From: Akhilesh Sanikop Date: Tue, 30 Apr 2024 13:59:21 +0530 Subject: [PATCH] Added keymint_attestation_fuzzer exec/s: 2 Test: ./keymint_attestation_fuzzer Bug: 337985606 Change-Id: I2c0d56e1af97190a5eb06a34e15bcd93123f39a6 --- security/keymint/support/fuzzer/Android.bp | 59 ++++ security/keymint/support/fuzzer/README.md | 53 ++++ .../fuzzer/keymint_attestation_fuzzer.cpp | 165 +++++++++++ .../keymint/support/fuzzer/keymint_common.h | 260 ++++++++++++++++++ 4 files changed, 537 insertions(+) create mode 100644 security/keymint/support/fuzzer/Android.bp create mode 100644 security/keymint/support/fuzzer/README.md create mode 100644 security/keymint/support/fuzzer/keymint_attestation_fuzzer.cpp create mode 100644 security/keymint/support/fuzzer/keymint_common.h diff --git a/security/keymint/support/fuzzer/Android.bp b/security/keymint/support/fuzzer/Android.bp new file mode 100644 index 0000000000..e851186751 --- /dev/null +++ b/security/keymint/support/fuzzer/Android.bp @@ -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", + ], +} diff --git a/security/keymint/support/fuzzer/README.md b/security/keymint/support/fuzzer/README.md new file mode 100644 index 0000000000..913dd72d2d --- /dev/null +++ b/security/keymint/support/fuzzer/README.md @@ -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) + +# 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 +``` diff --git a/security/keymint/support/fuzzer/keymint_attestation_fuzzer.cpp b/security/keymint/support/fuzzer/keymint_attestation_fuzzer.cpp new file mode 100644 index 0000000000..bd781acb30 --- /dev/null +++ b/security/keymint/support/fuzzer/keymint_attestation_fuzzer.cpp @@ -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 +#include +#include +#include +#include +#include +#include + +namespace android::hardware::security::keymint_support::fuzzer { +using namespace android; +using AStatus = ::ndk::ScopedAStatus; +std::shared_ptr 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& attestKey, + vector* keyBlob, + vector* keyCharacteristics, + vector* certChain); + X509_Ptr parseCertificateBlob(const vector& blob); + ASN1_OCTET_STRING* getAttestationRecord(const X509* certificate); + bool verifyAttestationRecord(const vector& attestationCert); + FuzzedDataProvider mFdp; +}; + +KeyCreationResult KeyMintAttestationFuzzer::generateKey( + const AuthorizationSet& keyDesc, const std::optional& attestKey, + vector* keyBlob, vector* keyCharacteristics, + vector* 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& 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& 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 attestationChallenge; + vector 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 verifiedBootKey; + vector 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 attestKeyCertChain; + vector attestKeyCharacteristics; + generateKey(createAuthSetForAttestKey(&mFdp), {}, &attestKey.keyBlob, &attestKeyCharacteristics, + &attestKeyCertChain); + + vector attestedKeyCertChain; + vector attestedKeyCharacteristics; + vector attestedKeyBlob; + attestKey.issuerSubjectName = mFdp.ConsumeBytes(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 diff --git a/security/keymint/support/fuzzer/keymint_common.h b/security/keymint/support/fuzzer/keymint_common.h new file mode 100644 index 0000000000..5c30e387f0 --- /dev/null +++ b/security/keymint/support/fuzzer/keymint_common.h @@ -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 +#include +#include +#include +#include +#include + +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(); + 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(AuthSet::RSA_KEY, AuthSet::BLOCK_MODE); + uint64_t timestamp = dataProvider->ConsumeIntegral(); + 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(); + 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(); + 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__