1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 use anyhow::Result;
18 use apkverify::{
19     extract_signed_data, get_apk_digest, testing::assert_contains, verify, SignatureAlgorithmID,
20 };
21 use apkzip::zip_sections;
22 use byteorder::{LittleEndian, ReadBytesExt};
23 use log::info;
24 use openssl::x509::X509;
25 use std::fmt::Write;
26 use std::io::{Seek, SeekFrom};
27 use std::{fs, matches, path::Path};
28 
29 const KEY_NAMES_DSA: &[&str] = &["1024", "2048", "3072"];
30 const KEY_NAMES_ECDSA: &[&str] = &["p256", "p384", "p521"];
31 const KEY_NAMES_RSA: &[&str] = &["1024", "2048", "3072", "4096", "8192", "16384"];
32 
33 const SDK_INT: u32 = 31;
34 
35 /// Make sure any logging from the code under test ends up in logcat.
setup()36 fn setup() {
37     android_logger::init_once(
38         android_logger::Config::default()
39             .with_tag("apkverify_test")
40             .with_max_level(log::LevelFilter::Info),
41     );
42     info!("Test starting");
43 }
44 
45 #[test]
test_zip_sections_with_apk()46 fn test_zip_sections_with_apk() {
47     let mut reader = fs::File::open("tests/data/v3-only-with-stamp.apk").unwrap();
48     let sections = zip_sections(&mut reader).unwrap();
49 
50     // Checks Central directory.
51     assert_eq!(
52         sections.central_directory_offset + sections.central_directory_size,
53         sections.eocd_offset
54     );
55 
56     // Checks EOCD.
57     const EOCD_SIGNATURE: u32 = 0x06054b50;
58 
59     reader.seek(SeekFrom::Start(sections.eocd_offset as u64)).unwrap();
60     assert_eq!(reader.read_u32::<LittleEndian>().unwrap(), EOCD_SIGNATURE);
61     assert_eq!(
62         reader.metadata().unwrap().len(),
63         (sections.eocd_offset + sections.eocd_size) as u64
64     );
65 }
66 
67 #[test]
test_verify_truncated_cd()68 fn test_verify_truncated_cd() {
69     setup();
70     use zip::result::ZipError;
71     let res = verify("tests/data/v2-only-truncated-cd.apk", SDK_INT);
72     // TODO(b/190343842): consider making a helper for err assertion
73     assert!(matches!(
74         res.unwrap_err().root_cause().downcast_ref::<ZipError>().unwrap(),
75         ZipError::InvalidArchive(_),
76     ));
77 }
78 
79 #[test]
apex_signed_with_v3_rsa_pkcs1_sha512_is_valid()80 fn apex_signed_with_v3_rsa_pkcs1_sha512_is_valid() {
81     setup();
82     validate_apk("tests/data/test.apex", SignatureAlgorithmID::RsaPkcs1V15WithSha512);
83 }
84 
85 #[test]
apks_signed_with_v3_dsa_sha256_are_not_supported()86 fn apks_signed_with_v3_dsa_sha256_are_not_supported() {
87     setup();
88     for key_name in KEY_NAMES_DSA.iter() {
89         let res = verify(format!("tests/data/v3-only-with-dsa-sha256-{}.apk", key_name), SDK_INT);
90         assert!(res.is_err(), "DSA algorithm is not supported for verification. See b/197052981.");
91         assert_contains(&res.unwrap_err().to_string(), "No supported APK signatures found");
92     }
93 }
94 
95 #[test]
apks_signed_with_v3_ecdsa_sha256_are_valid()96 fn apks_signed_with_v3_ecdsa_sha256_are_valid() {
97     setup();
98     for key_name in KEY_NAMES_ECDSA.iter() {
99         validate_apk(
100             format!("tests/data/v3-only-with-ecdsa-sha256-{}.apk", key_name),
101             SignatureAlgorithmID::EcdsaWithSha256,
102         );
103     }
104 }
105 
106 #[test]
apks_signed_with_v3_ecdsa_sha512_are_valid()107 fn apks_signed_with_v3_ecdsa_sha512_are_valid() {
108     setup();
109     for key_name in KEY_NAMES_ECDSA.iter() {
110         validate_apk(
111             format!("tests/data/v3-only-with-ecdsa-sha512-{}.apk", key_name),
112             SignatureAlgorithmID::EcdsaWithSha512,
113         );
114     }
115 }
116 
117 #[test]
apks_signed_with_v3_rsa_pkcs1_sha256_are_valid()118 fn apks_signed_with_v3_rsa_pkcs1_sha256_are_valid() {
119     setup();
120     for key_name in KEY_NAMES_RSA.iter() {
121         validate_apk(
122             format!("tests/data/v3-only-with-rsa-pkcs1-sha256-{}.apk", key_name),
123             SignatureAlgorithmID::RsaPkcs1V15WithSha256,
124         );
125     }
126 }
127 
128 #[test]
apks_signed_with_v3_rsa_pkcs1_sha512_are_valid()129 fn apks_signed_with_v3_rsa_pkcs1_sha512_are_valid() {
130     setup();
131     for key_name in KEY_NAMES_RSA.iter() {
132         validate_apk(
133             format!("tests/data/v3-only-with-rsa-pkcs1-sha512-{}.apk", key_name),
134             SignatureAlgorithmID::RsaPkcs1V15WithSha512,
135         );
136     }
137 }
138 
139 #[test]
test_verify_v3_sig_min_max_sdk()140 fn test_verify_v3_sig_min_max_sdk() {
141     setup();
142     // The Signer for this APK has min_sdk=24, max_sdk=32.
143     let path = "tests/data/v31-rsa-2048_2-tgt-33-1-tgt-28.apk";
144 
145     let res = verify(path, 23);
146     assert!(res.is_err());
147     assert_contains(&res.unwrap_err().to_string(), "0 signers found");
148 
149     let res = verify(path, 24);
150     assert!(res.is_ok());
151 
152     let res = verify(path, 32);
153     assert!(res.is_ok());
154 
155     let res = verify(path, 33);
156     assert!(res.is_err());
157     assert_contains(&res.unwrap_err().to_string(), "0 signers found");
158 }
159 
160 #[test]
test_verify_v3_sig_does_not_verify()161 fn test_verify_v3_sig_does_not_verify() {
162     setup();
163     let path_list = [
164         "tests/data/v3-only-with-ecdsa-sha512-p521-sig-does-not-verify.apk",
165         "tests/data/v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk",
166     ];
167     for path in path_list.iter() {
168         let res = verify(path, SDK_INT);
169         assert!(res.is_err());
170         assert_contains(&res.unwrap_err().to_string(), "Signature is invalid");
171     }
172 }
173 
174 #[test]
test_verify_v3_digest_mismatch()175 fn test_verify_v3_digest_mismatch() {
176     setup();
177     let res = verify("tests/data/v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk", SDK_INT);
178     assert!(res.is_err());
179     assert_contains(&res.unwrap_err().to_string(), "Digest mismatch");
180 }
181 
182 #[test]
test_verify_v3_wrong_apk_sig_block_magic()183 fn test_verify_v3_wrong_apk_sig_block_magic() {
184     setup();
185     let res =
186         verify("tests/data/v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk", SDK_INT);
187     assert!(res.is_err());
188     assert_contains(&res.unwrap_err().to_string(), "No APK Signing Block");
189 }
190 
191 #[test]
test_verify_v3_apk_sig_block_size_mismatch()192 fn test_verify_v3_apk_sig_block_size_mismatch() {
193     setup();
194     let res = verify(
195         "tests/data/v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk",
196         SDK_INT,
197     );
198     assert!(res.is_err());
199     assert_contains(
200         &res.unwrap_err().to_string(),
201         "APK Signing Block sizes in header and footer do not match",
202     );
203 }
204 
205 #[test]
test_verify_v3_cert_and_public_key_mismatch()206 fn test_verify_v3_cert_and_public_key_mismatch() {
207     setup();
208     let res = verify("tests/data/v3-only-cert-and-public-key-mismatch.apk", SDK_INT);
209     assert!(res.is_err());
210     assert_contains(&res.unwrap_err().to_string(), "Public key mismatch");
211 }
212 
213 #[test]
test_verify_v3_empty()214 fn test_verify_v3_empty() {
215     setup();
216     let res = verify("tests/data/v3-only-empty.apk", SDK_INT);
217     assert!(res.is_err());
218     assert_contains(&res.unwrap_err().to_string(), "APK too small for APK Signing Block");
219 }
220 
221 #[test]
test_verify_v3_no_certs_in_sig()222 fn test_verify_v3_no_certs_in_sig() {
223     setup();
224     let res = verify("tests/data/v3-only-no-certs-in-sig.apk", SDK_INT);
225     assert!(res.is_err());
226     assert_contains(&res.unwrap_err().to_string(), "No certificates listed");
227 }
228 
229 #[test]
test_verify_v3_no_supported_sig_algs()230 fn test_verify_v3_no_supported_sig_algs() {
231     setup();
232     let res = verify("tests/data/v3-only-no-supported-sig-algs.apk", SDK_INT);
233     assert!(res.is_err());
234     assert_contains(&res.unwrap_err().to_string(), "No supported APK signatures found");
235 }
236 
237 #[test]
test_verify_v3_signatures_and_digests_block_mismatch()238 fn test_verify_v3_signatures_and_digests_block_mismatch() {
239     setup();
240     let res = verify("tests/data/v3-only-signatures-and-digests-block-mismatch.apk", SDK_INT);
241     assert!(res.is_err());
242     assert_contains(
243         &res.unwrap_err().to_string(),
244         "Signature algorithms don't match between digests and signatures records",
245     );
246 }
247 
248 #[test]
apk_signed_with_v3_unknown_additional_attr_is_valid()249 fn apk_signed_with_v3_unknown_additional_attr_is_valid() {
250     setup();
251     validate_apk(
252         "tests/data/v3-only-unknown-additional-attr.apk",
253         SignatureAlgorithmID::RsaPkcs1V15WithSha256,
254     );
255 }
256 
257 #[test]
apk_signed_with_v3_unknown_pair_in_apk_sig_block_is_valid()258 fn apk_signed_with_v3_unknown_pair_in_apk_sig_block_is_valid() {
259     setup();
260     validate_apk(
261         "tests/data/v3-only-unknown-pair-in-apk-sig-block.apk",
262         SignatureAlgorithmID::RsaPkcs1V15WithSha256,
263     );
264 }
265 
266 #[test]
apk_signed_with_v3_ignorable_unsupported_sig_algs_is_valid()267 fn apk_signed_with_v3_ignorable_unsupported_sig_algs_is_valid() {
268     setup();
269     validate_apk(
270         "tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk",
271         SignatureAlgorithmID::RsaPkcs1V15WithSha256,
272     );
273 }
274 
275 #[test]
apk_signed_with_v3_stamp_is_valid()276 fn apk_signed_with_v3_stamp_is_valid() {
277     setup();
278     validate_apk("tests/data/v3-only-with-stamp.apk", SignatureAlgorithmID::EcdsaWithSha256);
279 }
280 
validate_apk<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID)281 fn validate_apk<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID) {
282     validate_apk_public_key(&apk_path);
283     validate_apk_digest(&apk_path, expected_algorithm_id);
284 }
285 
286 /// Validates that the following public keys are equal:
287 /// * public key from verification
288 /// * public key extracted from apk without verification
289 /// * expected public key from the corresponding .der file
validate_apk_public_key<P: AsRef<Path>>(apk_path: P)290 fn validate_apk_public_key<P: AsRef<Path>>(apk_path: P) {
291     let signed_data_from_verification =
292         verify(&apk_path, SDK_INT).expect("Error in verification result");
293     let cert_from_verification = signed_data_from_verification.first_certificate_der().unwrap();
294     let public_key_from_verification = public_key_der_from_cert(cert_from_verification).unwrap();
295 
296     let expected_public_key_path = format!("{}.der", apk_path.as_ref().to_str().unwrap());
297     assert_bytes_eq_to_data_in_file(&public_key_from_verification, expected_public_key_path);
298 
299     let signed_data_from_apk = extract_signed_data(&apk_path, SDK_INT)
300         .expect("Error when extracting signed data from apk");
301     let cert_from_apk = signed_data_from_apk.first_certificate_der().unwrap();
302     // If the two certficiates are byte for byte identical (which they should be), then so are
303     // the public keys embedded in them.
304     assert_eq!(
305         cert_from_verification, cert_from_apk,
306         "Certificate extracted directly from apk does not match the certificate from verification."
307     );
308 }
309 
public_key_der_from_cert(cert_der: &[u8]) -> Result<Vec<u8>>310 fn public_key_der_from_cert(cert_der: &[u8]) -> Result<Vec<u8>> {
311     let cert = X509::from_der(cert_der)?;
312     Ok(cert.public_key()?.public_key_to_der()?)
313 }
314 
315 /// Validates that the following apk_digest are equal:
316 /// * apk_digest directly extracted from apk without computation
317 /// * computed apk_digest
318 /// * expected apk digest from the corresponding .apk_digest file
validate_apk_digest<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID)319 fn validate_apk_digest<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID) {
320     let apk = fs::File::open(&apk_path).expect("Unabled to open apk file");
321 
322     let (verified_algorithm_id, verified_digest) =
323         get_apk_digest(&apk, SDK_INT, /* verify= */ true)
324             .expect("Error when extracting apk digest with verification.");
325 
326     assert_eq!(expected_algorithm_id, verified_algorithm_id);
327     let expected_digest_path = format!("{}.apk_digest", apk_path.as_ref().to_str().unwrap());
328     assert_bytes_eq_to_data_in_file(&verified_digest, expected_digest_path);
329 
330     let (unverified_algorithm_id, unverified_digest) =
331         get_apk_digest(&apk, SDK_INT, /* verify= */ false)
332             .expect("Error when extracting apk digest without verification.");
333     assert_eq!(expected_algorithm_id, unverified_algorithm_id);
334     assert_eq!(verified_digest, unverified_digest);
335 }
336 
assert_bytes_eq_to_data_in_file<P: AsRef<Path> + std::fmt::Display>( bytes_data: &[u8], expected_data_path: P, )337 fn assert_bytes_eq_to_data_in_file<P: AsRef<Path> + std::fmt::Display>(
338     bytes_data: &[u8],
339     expected_data_path: P,
340 ) {
341     assert!(
342         fs::metadata(&expected_data_path).is_ok(),
343         "File does not exist. You can re-create it with:\n$ echo -en {} > {}\n",
344         bytes_data.iter().fold(String::new(), |mut output, b| {
345             let _ = write!(output, "\\\\x{:02x}", b);
346             output
347         }),
348         expected_data_path
349     );
350     let expected_data = fs::read(&expected_data_path).unwrap();
351     assert_eq!(
352         expected_data, bytes_data,
353         "Actual data does not match the data from: {}",
354         expected_data_path
355     );
356 }
357