blob: 0d8e020a294e36a8dba12ba7ad89cc48e5b7c49c [file] [log] [blame]
Jooyung Han12a0b702021-08-05 23:20:31 +09001/*
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
Alice Wang676bb4a2022-09-19 14:21:39 +000017use apkverify::{
Alice Wang0cafa142022-09-23 15:17:02 +000018 get_apk_digest, get_public_key_der, testing::assert_contains, verify, SignatureAlgorithmID,
Alice Wang676bb4a2022-09-19 14:21:39 +000019};
Alan Stokes068f6d42023-10-09 10:13:03 +010020use apkzip::zip_sections;
21use byteorder::{LittleEndian, ReadBytesExt};
Alan Stokes25c86212023-03-09 17:22:19 +000022use log::info;
Chariseece832152023-11-01 18:01:09 +000023use std::fmt::Write;
Alan Stokes068f6d42023-10-09 10:13:03 +010024use std::io::{Seek, SeekFrom};
Alice Wang67d3c002022-09-16 10:08:25 +000025use std::{fs, matches, path::Path};
Jooyung Hand8397852021-08-10 16:29:36 +090026
Seungjae Yoo91e250a2022-06-07 02:21:56 +000027const KEY_NAMES_DSA: &[&str] = &["1024", "2048", "3072"];
28const KEY_NAMES_ECDSA: &[&str] = &["p256", "p384", "p521"];
29const KEY_NAMES_RSA: &[&str] = &["1024", "2048", "3072", "4096", "8192", "16384"];
Jooyung Hancee6de62021-08-11 15:52:07 +090030
Alan Stokes25f69362023-03-06 16:51:54 +000031const SDK_INT: u32 = 31;
32
Alan Stokes25c86212023-03-09 17:22:19 +000033/// Make sure any logging from the code under test ends up in logcat.
34fn setup() {
35 android_logger::init_once(
36 android_logger::Config::default()
37 .with_tag("apkverify_test")
38 .with_min_level(log::Level::Info),
39 );
40 info!("Test starting");
41}
42
Jooyung Hancee6de62021-08-11 15:52:07 +090043#[test]
Alan Stokes068f6d42023-10-09 10:13:03 +010044fn test_zip_sections_with_apk() {
45 let mut reader = fs::File::open("tests/data/v3-only-with-stamp.apk").unwrap();
46 let sections = zip_sections(&mut reader).unwrap();
47
48 // Checks Central directory.
49 assert_eq!(
50 sections.central_directory_offset + sections.central_directory_size,
51 sections.eocd_offset
52 );
53
54 // Checks EOCD.
55 const EOCD_SIGNATURE: u32 = 0x06054b50;
56
57 reader.seek(SeekFrom::Start(sections.eocd_offset as u64)).unwrap();
58 assert_eq!(reader.read_u32::<LittleEndian>().unwrap(), EOCD_SIGNATURE);
59 assert_eq!(
60 reader.metadata().unwrap().len(),
61 (sections.eocd_offset + sections.eocd_size) as u64
62 );
63}
64
65#[test]
Jooyung Hancee6de62021-08-11 15:52:07 +090066fn test_verify_truncated_cd() {
Alan Stokes25c86212023-03-09 17:22:19 +000067 setup();
Jooyung Hancee6de62021-08-11 15:52:07 +090068 use zip::result::ZipError;
Alan Stokes25f69362023-03-06 16:51:54 +000069 let res = verify("tests/data/v2-only-truncated-cd.apk", SDK_INT);
Alice Wang92889352022-09-16 10:42:52 +000070 // TODO(b/190343842): consider making a helper for err assertion
Jooyung Hancee6de62021-08-11 15:52:07 +090071 assert!(matches!(
Andrew Walbran117cd5e2021-08-13 11:42:13 +000072 res.unwrap_err().root_cause().downcast_ref::<ZipError>().unwrap(),
Jooyung Hancee6de62021-08-11 15:52:07 +090073 ZipError::InvalidArchive(_),
74 ));
75}
Seungjae Yoo91e250a2022-06-07 02:21:56 +000076
77#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +000078fn apex_signed_with_v3_rsa_pkcs1_sha512_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +000079 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +000080 validate_apk("tests/data/test.apex", SignatureAlgorithmID::RsaPkcs1V15WithSha512);
Seungjae Yoo91e250a2022-06-07 02:21:56 +000081}
82
Seungjae Yoo91e250a2022-06-07 02:21:56 +000083#[test]
Alice Wang50701022022-09-21 08:51:38 +000084fn apks_signed_with_v3_dsa_sha256_are_not_supported() {
Alan Stokes25c86212023-03-09 17:22:19 +000085 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +000086 for key_name in KEY_NAMES_DSA.iter() {
Alan Stokes25f69362023-03-06 16:51:54 +000087 let res = verify(format!("tests/data/v3-only-with-dsa-sha256-{}.apk", key_name), SDK_INT);
Alice Wang50701022022-09-21 08:51:38 +000088 assert!(res.is_err(), "DSA algorithm is not supported for verification. See b/197052981.");
Alan Stokesa6876992023-01-20 12:26:25 +000089 assert_contains(&res.unwrap_err().to_string(), "No supported APK signatures found");
Alice Wang676bb4a2022-09-19 14:21:39 +000090 }
91}
92
93#[test]
94fn apks_signed_with_v3_ecdsa_sha256_are_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +000095 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +000096 for key_name in KEY_NAMES_ECDSA.iter() {
Alice Wang676bb4a2022-09-19 14:21:39 +000097 validate_apk(
98 format!("tests/data/v3-only-with-ecdsa-sha256-{}.apk", key_name),
99 SignatureAlgorithmID::EcdsaWithSha256,
100 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000101 }
102}
103
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000104#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000105fn apks_signed_with_v3_ecdsa_sha512_are_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000106 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000107 for key_name in KEY_NAMES_ECDSA.iter() {
Alice Wang676bb4a2022-09-19 14:21:39 +0000108 validate_apk(
109 format!("tests/data/v3-only-with-ecdsa-sha512-{}.apk", key_name),
110 SignatureAlgorithmID::EcdsaWithSha512,
111 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000112 }
113}
114
115#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000116fn apks_signed_with_v3_rsa_pkcs1_sha256_are_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000117 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000118 for key_name in KEY_NAMES_RSA.iter() {
Alice Wang676bb4a2022-09-19 14:21:39 +0000119 validate_apk(
120 format!("tests/data/v3-only-with-rsa-pkcs1-sha256-{}.apk", key_name),
121 SignatureAlgorithmID::RsaPkcs1V15WithSha256,
122 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000123 }
124}
125
126#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000127fn apks_signed_with_v3_rsa_pkcs1_sha512_are_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000128 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000129 for key_name in KEY_NAMES_RSA.iter() {
Alice Wang676bb4a2022-09-19 14:21:39 +0000130 validate_apk(
131 format!("tests/data/v3-only-with-rsa-pkcs1-sha512-{}.apk", key_name),
132 SignatureAlgorithmID::RsaPkcs1V15WithSha512,
133 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000134 }
135}
136
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000137#[test]
Alan Stokes25c86212023-03-09 17:22:19 +0000138fn test_verify_v3_sig_min_max_sdk() {
139 setup();
140 // The Signer for this APK has min_sdk=24, max_sdk=32.
141 let path = "tests/data/v31-rsa-2048_2-tgt-33-1-tgt-28.apk";
142
143 let res = verify(path, 23);
144 assert!(res.is_err());
145 assert_contains(&res.unwrap_err().to_string(), "0 signers found");
146
147 let res = verify(path, 24);
148 assert!(res.is_ok());
149
150 let res = verify(path, 32);
151 assert!(res.is_ok());
152
153 let res = verify(path, 33);
154 assert!(res.is_err());
155 assert_contains(&res.unwrap_err().to_string(), "0 signers found");
156}
157
158#[test]
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000159fn test_verify_v3_sig_does_not_verify() {
Alan Stokes25c86212023-03-09 17:22:19 +0000160 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000161 let path_list = [
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000162 "tests/data/v3-only-with-ecdsa-sha512-p521-sig-does-not-verify.apk",
163 "tests/data/v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk",
164 ];
165 for path in path_list.iter() {
Alan Stokes25f69362023-03-06 16:51:54 +0000166 let res = verify(path, SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000167 assert!(res.is_err());
Alice Wang50701022022-09-21 08:51:38 +0000168 assert_contains(&res.unwrap_err().to_string(), "Signature is invalid");
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000169 }
170}
171
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000172#[test]
173fn test_verify_v3_digest_mismatch() {
Alan Stokes25c86212023-03-09 17:22:19 +0000174 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000175 let res = verify("tests/data/v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk", SDK_INT);
Alice Wang50701022022-09-21 08:51:38 +0000176 assert!(res.is_err());
177 assert_contains(&res.unwrap_err().to_string(), "Digest mismatch");
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000178}
179
180#[test]
181fn test_verify_v3_wrong_apk_sig_block_magic() {
Alan Stokes25c86212023-03-09 17:22:19 +0000182 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000183 let res =
184 verify("tests/data/v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000185 assert!(res.is_err());
186 assert_contains(&res.unwrap_err().to_string(), "No APK Signing Block");
187}
188
189#[test]
190fn test_verify_v3_apk_sig_block_size_mismatch() {
Alan Stokes25c86212023-03-09 17:22:19 +0000191 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000192 let res = verify(
193 "tests/data/v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk",
194 SDK_INT,
195 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000196 assert!(res.is_err());
197 assert_contains(
198 &res.unwrap_err().to_string(),
199 "APK Signing Block sizes in header and footer do not match",
200 );
201}
202
203#[test]
204fn test_verify_v3_cert_and_public_key_mismatch() {
Alan Stokes25c86212023-03-09 17:22:19 +0000205 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000206 let res = verify("tests/data/v3-only-cert-and-public-key-mismatch.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000207 assert!(res.is_err());
208 assert_contains(&res.unwrap_err().to_string(), "Public key mismatch");
209}
210
211#[test]
212fn test_verify_v3_empty() {
Alan Stokes25c86212023-03-09 17:22:19 +0000213 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000214 let res = verify("tests/data/v3-only-empty.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000215 assert!(res.is_err());
216 assert_contains(&res.unwrap_err().to_string(), "APK too small for APK Signing Block");
217}
218
219#[test]
220fn test_verify_v3_no_certs_in_sig() {
Alan Stokes25c86212023-03-09 17:22:19 +0000221 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000222 let res = verify("tests/data/v3-only-no-certs-in-sig.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000223 assert!(res.is_err());
224 assert_contains(&res.unwrap_err().to_string(), "No certificates listed");
225}
226
227#[test]
228fn test_verify_v3_no_supported_sig_algs() {
Alan Stokes25c86212023-03-09 17:22:19 +0000229 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000230 let res = verify("tests/data/v3-only-no-supported-sig-algs.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000231 assert!(res.is_err());
Alan Stokesa6876992023-01-20 12:26:25 +0000232 assert_contains(&res.unwrap_err().to_string(), "No supported APK signatures found");
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000233}
234
235#[test]
236fn test_verify_v3_signatures_and_digests_block_mismatch() {
Alan Stokes25c86212023-03-09 17:22:19 +0000237 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000238 let res = verify("tests/data/v3-only-signatures-and-digests-block-mismatch.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000239 assert!(res.is_err());
240 assert_contains(
241 &res.unwrap_err().to_string(),
242 "Signature algorithms don't match between digests and signatures records",
243 );
244}
245
246#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000247fn apk_signed_with_v3_unknown_additional_attr_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000248 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +0000249 validate_apk(
250 "tests/data/v3-only-unknown-additional-attr.apk",
251 SignatureAlgorithmID::RsaPkcs1V15WithSha256,
252 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000253}
254
255#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000256fn apk_signed_with_v3_unknown_pair_in_apk_sig_block_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000257 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +0000258 validate_apk(
259 "tests/data/v3-only-unknown-pair-in-apk-sig-block.apk",
260 SignatureAlgorithmID::RsaPkcs1V15WithSha256,
261 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000262}
263
264#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000265fn apk_signed_with_v3_ignorable_unsupported_sig_algs_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000266 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +0000267 validate_apk(
268 "tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk",
269 SignatureAlgorithmID::RsaPkcs1V15WithSha256,
270 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000271}
272
273#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000274fn apk_signed_with_v3_stamp_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000275 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +0000276 validate_apk("tests/data/v3-only-with-stamp.apk", SignatureAlgorithmID::EcdsaWithSha256);
Alice Wang67d3c002022-09-16 10:08:25 +0000277}
278
Alice Wang676bb4a2022-09-19 14:21:39 +0000279fn validate_apk<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID) {
280 validate_apk_public_key(&apk_path);
281 validate_apk_digest(&apk_path, expected_algorithm_id);
282}
283
284/// Validates that the following public keys are equal:
285/// * public key from verification
286/// * public key extracted from apk without verification
287/// * expected public key from the corresponding .der file
Alice Wang67d3c002022-09-16 10:08:25 +0000288fn validate_apk_public_key<P: AsRef<Path>>(apk_path: P) {
Alan Stokes25f69362023-03-06 16:51:54 +0000289 let public_key_from_verification = verify(&apk_path, SDK_INT);
Alice Wang67d3c002022-09-16 10:08:25 +0000290 let public_key_from_verification =
291 public_key_from_verification.expect("Error in verification result");
292
293 let expected_public_key_path = format!("{}.der", apk_path.as_ref().to_str().unwrap());
Alice Wang676bb4a2022-09-19 14:21:39 +0000294 assert_bytes_eq_to_data_in_file(&public_key_from_verification, expected_public_key_path);
Alice Wang67d3c002022-09-16 10:08:25 +0000295
Alan Stokes25f69362023-03-06 16:51:54 +0000296 let public_key_from_apk = get_public_key_der(&apk_path, SDK_INT);
Alice Wang3c016622022-09-19 09:08:27 +0000297 let public_key_from_apk =
298 public_key_from_apk.expect("Error when extracting public key from apk");
Alice Wang676bb4a2022-09-19 14:21:39 +0000299 assert_eq!(
300 public_key_from_verification, public_key_from_apk,
301 "Public key extracted directly from apk does not match the public key from verification."
302 );
303}
304
305/// Validates that the following apk_digest are equal:
306/// * apk_digest directly extracted from apk without computation
Alice Wang0cafa142022-09-23 15:17:02 +0000307/// * computed apk_digest
Alice Wang676bb4a2022-09-19 14:21:39 +0000308/// * expected apk digest from the corresponding .apk_digest file
309fn validate_apk_digest<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID) {
310 let apk = fs::File::open(&apk_path).expect("Unabled to open apk file");
311
Alan Stokes25f69362023-03-06 16:51:54 +0000312 let (verified_algorithm_id, verified_digest) =
Alan Stokes068f6d42023-10-09 10:13:03 +0100313 get_apk_digest(&apk, SDK_INT, /* verify= */ true)
Alan Stokes25f69362023-03-06 16:51:54 +0000314 .expect("Error when extracting apk digest with verification.");
Alice Wang676bb4a2022-09-19 14:21:39 +0000315
Alice Wang0cafa142022-09-23 15:17:02 +0000316 assert_eq!(expected_algorithm_id, verified_algorithm_id);
Alice Wang676bb4a2022-09-19 14:21:39 +0000317 let expected_digest_path = format!("{}.apk_digest", apk_path.as_ref().to_str().unwrap());
Alice Wang0cafa142022-09-23 15:17:02 +0000318 assert_bytes_eq_to_data_in_file(&verified_digest, expected_digest_path);
319
Alan Stokes25f69362023-03-06 16:51:54 +0000320 let (unverified_algorithm_id, unverified_digest) =
Alan Stokes068f6d42023-10-09 10:13:03 +0100321 get_apk_digest(&apk, SDK_INT, /* verify= */ false)
Alan Stokes25f69362023-03-06 16:51:54 +0000322 .expect("Error when extracting apk digest without verification.");
Alice Wang0cafa142022-09-23 15:17:02 +0000323 assert_eq!(expected_algorithm_id, unverified_algorithm_id);
324 assert_eq!(verified_digest, unverified_digest);
Alice Wang676bb4a2022-09-19 14:21:39 +0000325}
326
327fn assert_bytes_eq_to_data_in_file<P: AsRef<Path> + std::fmt::Display>(
328 bytes_data: &[u8],
329 expected_data_path: P,
330) {
331 assert!(
332 fs::metadata(&expected_data_path).is_ok(),
333 "File does not exist. You can re-create it with:\n$ echo -en {} > {}\n",
Chariseece832152023-11-01 18:01:09 +0000334 bytes_data.iter().fold(String::new(), |mut output, b| {
335 let _ = write!(output, "\\\\x{:02x}", b);
336 output
337 }),
Alice Wang676bb4a2022-09-19 14:21:39 +0000338 expected_data_path
339 );
340 let expected_data = fs::read(&expected_data_path).unwrap();
341 assert_eq!(
342 expected_data, bytes_data,
343 "Actual data does not match the data from: {}",
344 expected_data_path
345 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000346}