1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Fuzzes unsafe APIs of libkeystore2 module
16 
17 #![no_main]
18 
19 use keystore2::{legacy_blob::LegacyBlobLoader, utils::ui_opts_2_compat};
20 use keystore2_aaid::get_aaid;
21 use keystore2_apc_compat::ApcHal;
22 use keystore2_crypto::{
23     aes_gcm_decrypt, aes_gcm_encrypt, ec_key_generate_key, ec_key_get0_public_key,
24     ec_key_marshal_private_key, ec_key_parse_private_key, ec_point_oct_to_point,
25     ec_point_point_to_oct, ecdh_compute_key, generate_random_data, hkdf_expand, hkdf_extract,
26     hmac_sha256, parse_subject_from_certificate, Password, ZVec,
27 };
28 use keystore2_hal_names::get_hidl_instances;
29 use keystore2_selinux::{check_access, getpidcon, setcon, Backend, Context, KeystoreKeyBackend};
30 use libfuzzer_sys::{arbitrary::Arbitrary, fuzz_target};
31 use std::{ffi::CString, sync::Arc};
32 
33 // Avoid allocating too much memory and crashing the fuzzer.
34 const MAX_SIZE_MODIFIER: usize = 1024;
35 
36 /// CString does not contain any internal 0 bytes
get_valid_cstring_data(data: &[u8]) -> &[u8]37 fn get_valid_cstring_data(data: &[u8]) -> &[u8] {
38     match data.iter().position(|&b| b == 0) {
39         Some(idx) => &data[0..idx],
40         None => data,
41     }
42 }
43 
44 #[derive(Arbitrary, Debug)]
45 enum FuzzCommand<'a> {
46     DecodeAlias {
47         string: String,
48     },
49     TryFrom {
50         vector_data: Vec<u8>,
51     },
52     GenerateRandomData {
53         size: usize,
54     },
55     HmacSha256 {
56         key_hmac: &'a [u8],
57         msg: &'a [u8],
58     },
59     AesGcmDecrypt {
60         data: &'a [u8],
61         iv: &'a [u8],
62         tag: &'a [u8],
63         key_aes_decrypt: &'a [u8],
64     },
65     AesGcmEecrypt {
66         plaintext: &'a [u8],
67         key_aes_encrypt: &'a [u8],
68     },
69     Password {
70         pw: &'a [u8],
71         salt: &'a [u8],
72         key_length: usize,
73     },
74     HkdfExtract {
75         hkdf_secret: &'a [u8],
76         hkdf_salt: &'a [u8],
77     },
78     HkdfExpand {
79         out_len: usize,
80         hkdf_prk: &'a [u8],
81         hkdf_info: &'a [u8],
82     },
83     PublicPrivateKey {
84         ec_priv_buf: &'a [u8],
85         ec_oct_buf: &'a [u8],
86     },
87     ParseSubjectFromCertificate {
88         parse_buf: &'a [u8],
89     },
90     GetHidlInstances {
91         hidl_package: &'a str,
92         major_version: usize,
93         minor_version: usize,
94         hidl_interface_name: &'a str,
95     },
96     GetAaid {
97         aaid_uid: u32,
98     },
99     Hal {
100         opt: i32,
101         prompt_text: &'a str,
102         locale: &'a str,
103         extra_data: &'a [u8],
104     },
105     Context {
106         context: &'a str,
107     },
108     Backend {
109         namespace: &'a str,
110     },
111     GetPidCon {
112         pid: i32,
113     },
114     CheckAccess {
115         source: &'a [u8],
116         target: &'a [u8],
117         tclass: &'a str,
118         perm: &'a str,
119     },
120     SetCon {
121         set_target: &'a [u8],
122     },
123 }
124 
125 fuzz_target!(|commands: Vec<FuzzCommand>| {
126     for command in commands {
127         match command {
128             FuzzCommand::DecodeAlias { string } => {
129                 let _res = LegacyBlobLoader::decode_alias(&string);
130             }
131             FuzzCommand::TryFrom { vector_data } => {
132                 let _res = ZVec::try_from(vector_data);
133             }
134             FuzzCommand::GenerateRandomData { size } => {
135                 let _res = generate_random_data(size % MAX_SIZE_MODIFIER);
136             }
137             FuzzCommand::HmacSha256 { key_hmac, msg } => {
138                 let _res = hmac_sha256(key_hmac, msg);
139             }
140             FuzzCommand::AesGcmDecrypt { data, iv, tag, key_aes_decrypt } => {
141                 let _res = aes_gcm_decrypt(data, iv, tag, key_aes_decrypt);
142             }
143             FuzzCommand::AesGcmEecrypt { plaintext, key_aes_encrypt } => {
144                 let _res = aes_gcm_encrypt(plaintext, key_aes_encrypt);
145             }
146             FuzzCommand::Password { pw, salt, key_length } => {
147                 let _res =
148                     Password::from(pw).derive_key_pbkdf2(salt, key_length % MAX_SIZE_MODIFIER);
149             }
150             FuzzCommand::HkdfExtract { hkdf_secret, hkdf_salt } => {
151                 let _res = hkdf_extract(hkdf_secret, hkdf_salt);
152             }
153             FuzzCommand::HkdfExpand { out_len, hkdf_prk, hkdf_info } => {
154                 let _res = hkdf_expand(out_len % MAX_SIZE_MODIFIER, hkdf_prk, hkdf_info);
155             }
156             FuzzCommand::PublicPrivateKey { ec_priv_buf, ec_oct_buf } => {
157                 let check_private_key = {
158                     let mut check_private_key = ec_key_parse_private_key(ec_priv_buf);
159                     if check_private_key.is_err() {
160                         check_private_key = ec_key_generate_key();
161                     };
162                     check_private_key
163                 };
164                 let check_ecpoint = ec_point_oct_to_point(ec_oct_buf);
165                 if check_private_key.is_ok() {
166                     let private_key = check_private_key.unwrap();
167                     ec_key_get0_public_key(&private_key);
168                     let _res = ec_key_marshal_private_key(&private_key);
169 
170                     if check_ecpoint.is_ok() {
171                         let public_key = check_ecpoint.unwrap();
172                         let _res = ec_point_point_to_oct(public_key.get_point());
173                         let _res = ecdh_compute_key(public_key.get_point(), &private_key);
174                     }
175                 }
176             }
177             FuzzCommand::ParseSubjectFromCertificate { parse_buf } => {
178                 let _res = parse_subject_from_certificate(parse_buf);
179             }
180             FuzzCommand::GetHidlInstances {
181                 hidl_package,
182                 major_version,
183                 minor_version,
184                 hidl_interface_name,
185             } => {
186                 get_hidl_instances(hidl_package, major_version, minor_version, hidl_interface_name);
187             }
188             FuzzCommand::GetAaid { aaid_uid } => {
189                 let _res = get_aaid(aaid_uid);
190             }
191             FuzzCommand::Hal { opt, prompt_text, locale, extra_data } => {
192                 let hal = ApcHal::try_get_service();
193                 if hal.is_some() {
194                     let hal = Arc::new(hal.unwrap());
195                     let apc_compat_options = ui_opts_2_compat(opt);
196                     let prompt_text =
197                         std::str::from_utf8(get_valid_cstring_data(prompt_text.as_bytes()))
198                             .unwrap();
199                     let locale =
200                         std::str::from_utf8(get_valid_cstring_data(locale.as_bytes())).unwrap();
201                     let _res = hal.prompt_user_confirmation(
202                         prompt_text,
203                         extra_data,
204                         locale,
205                         apc_compat_options,
206                         move |_, _, _| {},
207                     );
208                 }
209             }
210             FuzzCommand::Context { context } => {
211                 let _res = Context::new(context);
212             }
213             FuzzCommand::Backend { namespace } => {
214                 let backend = KeystoreKeyBackend::new();
215                 if let Ok(backend) = backend {
216                     let _res = backend.lookup(namespace);
217                 }
218             }
219             FuzzCommand::GetPidCon { pid } => {
220                 let _res = getpidcon(pid);
221             }
222             FuzzCommand::CheckAccess { source, target, tclass, perm } => {
223                 let source = get_valid_cstring_data(source);
224                 let target = get_valid_cstring_data(target);
225                 let _res = check_access(
226                     &CString::new(source).unwrap(),
227                     &CString::new(target).unwrap(),
228                     tclass,
229                     perm,
230                 );
231             }
232             FuzzCommand::SetCon { set_target } => {
233                 let _res = setcon(&CString::new(get_valid_cstring_data(set_target)).unwrap());
234             }
235         }
236     }
237 });
238