| // Copyright 2022, 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. |
| |
| //! Fuzzes unsafe APIs of libkeystore2 module |
| |
| #![feature(slice_internals)] |
| #![no_main] |
| |
| use core::slice::memchr; |
| use keystore2::{legacy_blob::LegacyBlobLoader, utils::ui_opts_2_compat}; |
| use keystore2_aaid::get_aaid; |
| use keystore2_apc_compat::ApcHal; |
| use keystore2_crypto::{ |
| aes_gcm_decrypt, aes_gcm_encrypt, ec_key_generate_key, ec_key_get0_public_key, |
| ec_key_marshal_private_key, ec_key_parse_private_key, ec_point_oct_to_point, |
| ec_point_point_to_oct, ecdh_compute_key, generate_random_data, hkdf_expand, hkdf_extract, |
| hmac_sha256, parse_subject_from_certificate, Password, ZVec, |
| }; |
| use keystore2_selinux::{check_access, getpidcon, setcon, Backend, Context, KeystoreKeyBackend}; |
| use keystore2_vintf::{get_aidl_instances, get_hidl_instances}; |
| use libfuzzer_sys::{arbitrary::Arbitrary, fuzz_target}; |
| use std::{ffi::CString, sync::Arc}; |
| |
| // Avoid allocating too much memory and crashing the fuzzer. |
| const MAX_SIZE_MODIFIER: usize = 1024; |
| |
| /// CString does not contain any internal 0 bytes |
| fn get_valid_cstring_data(data: &[u8]) -> &[u8] { |
| match memchr::memchr(0, data) { |
| Some(idx) => &data[0..idx], |
| None => data, |
| } |
| } |
| |
| #[derive(Arbitrary, Debug)] |
| enum FuzzCommand<'a> { |
| DecodeAlias { |
| string: String, |
| }, |
| TryFrom { |
| vector_data: Vec<u8>, |
| }, |
| GenerateRandomData { |
| size: usize, |
| }, |
| HmacSha256 { |
| key_hmac: &'a [u8], |
| msg: &'a [u8], |
| }, |
| AesGcmDecrypt { |
| data: &'a [u8], |
| iv: &'a [u8], |
| tag: &'a [u8], |
| key_aes_decrypt: &'a [u8], |
| }, |
| AesGcmEecrypt { |
| plaintext: &'a [u8], |
| key_aes_encrypt: &'a [u8], |
| }, |
| Password { |
| pw: &'a [u8], |
| salt: &'a [u8], |
| key_length: usize, |
| }, |
| HkdfExtract { |
| hkdf_secret: &'a [u8], |
| hkdf_salt: &'a [u8], |
| }, |
| HkdfExpand { |
| out_len: usize, |
| hkdf_prk: &'a [u8], |
| hkdf_info: &'a [u8], |
| }, |
| PublicPrivateKey { |
| ec_priv_buf: &'a [u8], |
| ec_oct_buf: &'a [u8], |
| }, |
| ParseSubjectFromCertificate { |
| parse_buf: &'a [u8], |
| }, |
| GetHidlInstances { |
| hidl_package: &'a str, |
| major_version: usize, |
| minor_version: usize, |
| hidl_interface_name: &'a str, |
| }, |
| GetAidlInstances { |
| aidl_package: &'a str, |
| version: usize, |
| aidl_interface_name: &'a str, |
| }, |
| GetAaid { |
| aaid_uid: u32, |
| }, |
| Hal { |
| opt: i32, |
| prompt_text: &'a str, |
| locale: &'a str, |
| extra_data: &'a [u8], |
| }, |
| Context { |
| context: &'a str, |
| }, |
| Backend { |
| namespace: &'a str, |
| }, |
| GetPidCon { |
| pid: i32, |
| }, |
| CheckAccess { |
| source: &'a [u8], |
| target: &'a [u8], |
| tclass: &'a str, |
| perm: &'a str, |
| }, |
| SetCon { |
| set_target: &'a [u8], |
| }, |
| } |
| |
| fuzz_target!(|commands: Vec<FuzzCommand>| { |
| for command in commands { |
| match command { |
| FuzzCommand::DecodeAlias { string } => { |
| let _res = LegacyBlobLoader::decode_alias(&string); |
| } |
| FuzzCommand::TryFrom { vector_data } => { |
| let _res = ZVec::try_from(vector_data); |
| } |
| FuzzCommand::GenerateRandomData { size } => { |
| let _res = generate_random_data(size % MAX_SIZE_MODIFIER); |
| } |
| FuzzCommand::HmacSha256 { key_hmac, msg } => { |
| let _res = hmac_sha256(key_hmac, msg); |
| } |
| FuzzCommand::AesGcmDecrypt { data, iv, tag, key_aes_decrypt } => { |
| let _res = aes_gcm_decrypt(data, iv, tag, key_aes_decrypt); |
| } |
| FuzzCommand::AesGcmEecrypt { plaintext, key_aes_encrypt } => { |
| let _res = aes_gcm_encrypt(plaintext, key_aes_encrypt); |
| } |
| FuzzCommand::Password { pw, salt, key_length } => { |
| let _res = Password::from(pw).derive_key(salt, key_length % MAX_SIZE_MODIFIER); |
| } |
| FuzzCommand::HkdfExtract { hkdf_secret, hkdf_salt } => { |
| let _res = hkdf_extract(hkdf_secret, hkdf_salt); |
| } |
| FuzzCommand::HkdfExpand { out_len, hkdf_prk, hkdf_info } => { |
| let _res = hkdf_expand(out_len % MAX_SIZE_MODIFIER, hkdf_prk, hkdf_info); |
| } |
| FuzzCommand::PublicPrivateKey { ec_priv_buf, ec_oct_buf } => { |
| let check_private_key = { |
| let mut check_private_key = ec_key_parse_private_key(ec_priv_buf); |
| if check_private_key.is_err() { |
| check_private_key = ec_key_generate_key(); |
| }; |
| check_private_key |
| }; |
| let check_ecpoint = ec_point_oct_to_point(ec_oct_buf); |
| if check_private_key.is_ok() { |
| let private_key = check_private_key.unwrap(); |
| ec_key_get0_public_key(&private_key); |
| let _res = ec_key_marshal_private_key(&private_key); |
| |
| if check_ecpoint.is_ok() { |
| let public_key = check_ecpoint.unwrap(); |
| let _res = ec_point_point_to_oct(public_key.get_point()); |
| let _res = ecdh_compute_key(public_key.get_point(), &private_key); |
| } |
| } |
| } |
| FuzzCommand::ParseSubjectFromCertificate { parse_buf } => { |
| let _res = parse_subject_from_certificate(parse_buf); |
| } |
| FuzzCommand::GetHidlInstances { |
| hidl_package, |
| major_version, |
| minor_version, |
| hidl_interface_name, |
| } => { |
| get_hidl_instances(hidl_package, major_version, minor_version, hidl_interface_name); |
| } |
| FuzzCommand::GetAidlInstances { aidl_package, version, aidl_interface_name } => { |
| get_aidl_instances(aidl_package, version, aidl_interface_name); |
| } |
| FuzzCommand::GetAaid { aaid_uid } => { |
| let _res = get_aaid(aaid_uid); |
| } |
| FuzzCommand::Hal { opt, prompt_text, locale, extra_data } => { |
| let hal = ApcHal::try_get_service(); |
| if hal.is_some() { |
| let hal = Arc::new(hal.unwrap()); |
| let apc_compat_options = ui_opts_2_compat(opt); |
| let prompt_text = |
| std::str::from_utf8(get_valid_cstring_data(prompt_text.as_bytes())) |
| .unwrap(); |
| let locale = |
| std::str::from_utf8(get_valid_cstring_data(locale.as_bytes())).unwrap(); |
| let _res = hal.prompt_user_confirmation( |
| prompt_text, |
| extra_data, |
| locale, |
| apc_compat_options, |
| move |_, _, _| {}, |
| ); |
| } |
| } |
| FuzzCommand::Context { context } => { |
| let _res = Context::new(context); |
| } |
| FuzzCommand::Backend { namespace } => { |
| let backend = KeystoreKeyBackend::new(); |
| if let Ok(backend) = backend { |
| let _res = backend.lookup(namespace); |
| } |
| } |
| FuzzCommand::GetPidCon { pid } => { |
| let _res = getpidcon(pid); |
| } |
| FuzzCommand::CheckAccess { source, target, tclass, perm } => { |
| let source = get_valid_cstring_data(source); |
| let target = get_valid_cstring_data(target); |
| let _res = check_access( |
| &CString::new(source).unwrap(), |
| &CString::new(target).unwrap(), |
| tclass, |
| perm, |
| ); |
| } |
| FuzzCommand::SetCon { set_target } => { |
| let _res = setcon(&CString::new(get_valid_cstring_data(set_target)).unwrap()); |
| } |
| } |
| } |
| }); |