blob: 96fad5f804b001008c046c4c42ddf1e1958e5345 [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
Alan Stokes1508df22023-12-04 11:31:21 +000017use anyhow::Result;
Alice Wang676bb4a2022-09-19 14:21:39 +000018use apkverify::{
Alan Stokes1508df22023-12-04 11:31:21 +000019 extract_signed_data, get_apk_digest, testing::assert_contains, verify, SignatureAlgorithmID,
Alice Wang676bb4a2022-09-19 14:21:39 +000020};
Alan Stokes068f6d42023-10-09 10:13:03 +010021use apkzip::zip_sections;
22use byteorder::{LittleEndian, ReadBytesExt};
Alan Stokes25c86212023-03-09 17:22:19 +000023use log::info;
Alan Stokes1508df22023-12-04 11:31:21 +000024use openssl::x509::X509;
Chariseece832152023-11-01 18:01:09 +000025use std::fmt::Write;
Alan Stokes068f6d42023-10-09 10:13:03 +010026use std::io::{Seek, SeekFrom};
Alice Wang67d3c002022-09-16 10:08:25 +000027use std::{fs, matches, path::Path};
Jooyung Hand8397852021-08-10 16:29:36 +090028
Seungjae Yoo91e250a2022-06-07 02:21:56 +000029const KEY_NAMES_DSA: &[&str] = &["1024", "2048", "3072"];
30const KEY_NAMES_ECDSA: &[&str] = &["p256", "p384", "p521"];
31const KEY_NAMES_RSA: &[&str] = &["1024", "2048", "3072", "4096", "8192", "16384"];
Jooyung Hancee6de62021-08-11 15:52:07 +090032
Alan Stokes25f69362023-03-06 16:51:54 +000033const SDK_INT: u32 = 31;
34
Alan Stokes25c86212023-03-09 17:22:19 +000035/// Make sure any logging from the code under test ends up in logcat.
36fn setup() {
37 android_logger::init_once(
38 android_logger::Config::default()
39 .with_tag("apkverify_test")
Jeff Vander Stoepd9dda0c2024-02-07 14:27:06 +010040 .with_max_level(log::LevelFilter::Info),
Alan Stokes25c86212023-03-09 17:22:19 +000041 );
42 info!("Test starting");
43}
44
Jooyung Hancee6de62021-08-11 15:52:07 +090045#[test]
Alan Stokes068f6d42023-10-09 10:13:03 +010046fn 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]
Jooyung Hancee6de62021-08-11 15:52:07 +090068fn test_verify_truncated_cd() {
Alan Stokes25c86212023-03-09 17:22:19 +000069 setup();
Jooyung Hancee6de62021-08-11 15:52:07 +090070 use zip::result::ZipError;
Alan Stokes25f69362023-03-06 16:51:54 +000071 let res = verify("tests/data/v2-only-truncated-cd.apk", SDK_INT);
Alice Wang92889352022-09-16 10:42:52 +000072 // TODO(b/190343842): consider making a helper for err assertion
Jooyung Hancee6de62021-08-11 15:52:07 +090073 assert!(matches!(
Andrew Walbran117cd5e2021-08-13 11:42:13 +000074 res.unwrap_err().root_cause().downcast_ref::<ZipError>().unwrap(),
Jooyung Hancee6de62021-08-11 15:52:07 +090075 ZipError::InvalidArchive(_),
76 ));
77}
Seungjae Yoo91e250a2022-06-07 02:21:56 +000078
79#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +000080fn apex_signed_with_v3_rsa_pkcs1_sha512_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +000081 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +000082 validate_apk("tests/data/test.apex", SignatureAlgorithmID::RsaPkcs1V15WithSha512);
Seungjae Yoo91e250a2022-06-07 02:21:56 +000083}
84
Seungjae Yoo91e250a2022-06-07 02:21:56 +000085#[test]
Alice Wang50701022022-09-21 08:51:38 +000086fn apks_signed_with_v3_dsa_sha256_are_not_supported() {
Alan Stokes25c86212023-03-09 17:22:19 +000087 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +000088 for key_name in KEY_NAMES_DSA.iter() {
Alan Stokes25f69362023-03-06 16:51:54 +000089 let res = verify(format!("tests/data/v3-only-with-dsa-sha256-{}.apk", key_name), SDK_INT);
Alice Wang50701022022-09-21 08:51:38 +000090 assert!(res.is_err(), "DSA algorithm is not supported for verification. See b/197052981.");
Alan Stokesa6876992023-01-20 12:26:25 +000091 assert_contains(&res.unwrap_err().to_string(), "No supported APK signatures found");
Alice Wang676bb4a2022-09-19 14:21:39 +000092 }
93}
94
95#[test]
96fn apks_signed_with_v3_ecdsa_sha256_are_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +000097 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +000098 for key_name in KEY_NAMES_ECDSA.iter() {
Alice Wang676bb4a2022-09-19 14:21:39 +000099 validate_apk(
100 format!("tests/data/v3-only-with-ecdsa-sha256-{}.apk", key_name),
101 SignatureAlgorithmID::EcdsaWithSha256,
102 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000103 }
104}
105
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000106#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000107fn apks_signed_with_v3_ecdsa_sha512_are_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000108 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000109 for key_name in KEY_NAMES_ECDSA.iter() {
Alice Wang676bb4a2022-09-19 14:21:39 +0000110 validate_apk(
111 format!("tests/data/v3-only-with-ecdsa-sha512-{}.apk", key_name),
112 SignatureAlgorithmID::EcdsaWithSha512,
113 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000114 }
115}
116
117#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000118fn apks_signed_with_v3_rsa_pkcs1_sha256_are_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000119 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000120 for key_name in KEY_NAMES_RSA.iter() {
Alice Wang676bb4a2022-09-19 14:21:39 +0000121 validate_apk(
122 format!("tests/data/v3-only-with-rsa-pkcs1-sha256-{}.apk", key_name),
123 SignatureAlgorithmID::RsaPkcs1V15WithSha256,
124 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000125 }
126}
127
128#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000129fn apks_signed_with_v3_rsa_pkcs1_sha512_are_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000130 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000131 for key_name in KEY_NAMES_RSA.iter() {
Alice Wang676bb4a2022-09-19 14:21:39 +0000132 validate_apk(
133 format!("tests/data/v3-only-with-rsa-pkcs1-sha512-{}.apk", key_name),
134 SignatureAlgorithmID::RsaPkcs1V15WithSha512,
135 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000136 }
137}
138
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000139#[test]
Alan Stokes25c86212023-03-09 17:22:19 +0000140fn 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]
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000161fn test_verify_v3_sig_does_not_verify() {
Alan Stokes25c86212023-03-09 17:22:19 +0000162 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000163 let path_list = [
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000164 "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() {
Alan Stokes25f69362023-03-06 16:51:54 +0000168 let res = verify(path, SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000169 assert!(res.is_err());
Alice Wang50701022022-09-21 08:51:38 +0000170 assert_contains(&res.unwrap_err().to_string(), "Signature is invalid");
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000171 }
172}
173
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000174#[test]
175fn test_verify_v3_digest_mismatch() {
Alan Stokes25c86212023-03-09 17:22:19 +0000176 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000177 let res = verify("tests/data/v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk", SDK_INT);
Alice Wang50701022022-09-21 08:51:38 +0000178 assert!(res.is_err());
179 assert_contains(&res.unwrap_err().to_string(), "Digest mismatch");
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000180}
181
182#[test]
183fn test_verify_v3_wrong_apk_sig_block_magic() {
Alan Stokes25c86212023-03-09 17:22:19 +0000184 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000185 let res =
186 verify("tests/data/v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000187 assert!(res.is_err());
188 assert_contains(&res.unwrap_err().to_string(), "No APK Signing Block");
189}
190
191#[test]
192fn test_verify_v3_apk_sig_block_size_mismatch() {
Alan Stokes25c86212023-03-09 17:22:19 +0000193 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000194 let res = verify(
195 "tests/data/v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk",
196 SDK_INT,
197 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000198 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]
206fn test_verify_v3_cert_and_public_key_mismatch() {
Alan Stokes25c86212023-03-09 17:22:19 +0000207 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000208 let res = verify("tests/data/v3-only-cert-and-public-key-mismatch.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000209 assert!(res.is_err());
210 assert_contains(&res.unwrap_err().to_string(), "Public key mismatch");
211}
212
213#[test]
214fn test_verify_v3_empty() {
Alan Stokes25c86212023-03-09 17:22:19 +0000215 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000216 let res = verify("tests/data/v3-only-empty.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000217 assert!(res.is_err());
218 assert_contains(&res.unwrap_err().to_string(), "APK too small for APK Signing Block");
219}
220
221#[test]
222fn test_verify_v3_no_certs_in_sig() {
Alan Stokes25c86212023-03-09 17:22:19 +0000223 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000224 let res = verify("tests/data/v3-only-no-certs-in-sig.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000225 assert!(res.is_err());
226 assert_contains(&res.unwrap_err().to_string(), "No certificates listed");
227}
228
229#[test]
230fn test_verify_v3_no_supported_sig_algs() {
Alan Stokes25c86212023-03-09 17:22:19 +0000231 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000232 let res = verify("tests/data/v3-only-no-supported-sig-algs.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000233 assert!(res.is_err());
Alan Stokesa6876992023-01-20 12:26:25 +0000234 assert_contains(&res.unwrap_err().to_string(), "No supported APK signatures found");
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000235}
236
237#[test]
238fn test_verify_v3_signatures_and_digests_block_mismatch() {
Alan Stokes25c86212023-03-09 17:22:19 +0000239 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000240 let res = verify("tests/data/v3-only-signatures-and-digests-block-mismatch.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000241 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]
Alice Wang676bb4a2022-09-19 14:21:39 +0000249fn apk_signed_with_v3_unknown_additional_attr_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000250 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +0000251 validate_apk(
252 "tests/data/v3-only-unknown-additional-attr.apk",
253 SignatureAlgorithmID::RsaPkcs1V15WithSha256,
254 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000255}
256
257#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000258fn apk_signed_with_v3_unknown_pair_in_apk_sig_block_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000259 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +0000260 validate_apk(
261 "tests/data/v3-only-unknown-pair-in-apk-sig-block.apk",
262 SignatureAlgorithmID::RsaPkcs1V15WithSha256,
263 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000264}
265
266#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000267fn apk_signed_with_v3_ignorable_unsupported_sig_algs_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000268 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +0000269 validate_apk(
270 "tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk",
271 SignatureAlgorithmID::RsaPkcs1V15WithSha256,
272 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000273}
274
275#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000276fn apk_signed_with_v3_stamp_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000277 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +0000278 validate_apk("tests/data/v3-only-with-stamp.apk", SignatureAlgorithmID::EcdsaWithSha256);
Alice Wang67d3c002022-09-16 10:08:25 +0000279}
280
Alice Wang676bb4a2022-09-19 14:21:39 +0000281fn 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
Alice Wang67d3c002022-09-16 10:08:25 +0000290fn validate_apk_public_key<P: AsRef<Path>>(apk_path: P) {
Alan Stokes1508df22023-12-04 11:31:21 +0000291 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();
Alice Wang67d3c002022-09-16 10:08:25 +0000295
296 let expected_public_key_path = format!("{}.der", apk_path.as_ref().to_str().unwrap());
Alice Wang676bb4a2022-09-19 14:21:39 +0000297 assert_bytes_eq_to_data_in_file(&public_key_from_verification, expected_public_key_path);
Alice Wang67d3c002022-09-16 10:08:25 +0000298
Alan Stokes1508df22023-12-04 11:31:21 +0000299 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.
Alice Wang676bb4a2022-09-19 14:21:39 +0000304 assert_eq!(
Alan Stokes1508df22023-12-04 11:31:21 +0000305 cert_from_verification, cert_from_apk,
306 "Certificate extracted directly from apk does not match the certificate from verification."
Alice Wang676bb4a2022-09-19 14:21:39 +0000307 );
308}
309
Alan Stokes1508df22023-12-04 11:31:21 +0000310fn 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
Alice Wang676bb4a2022-09-19 14:21:39 +0000315/// Validates that the following apk_digest are equal:
316/// * apk_digest directly extracted from apk without computation
Alice Wang0cafa142022-09-23 15:17:02 +0000317/// * computed apk_digest
Alice Wang676bb4a2022-09-19 14:21:39 +0000318/// * expected apk digest from the corresponding .apk_digest file
319fn 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
Alan Stokes25f69362023-03-06 16:51:54 +0000322 let (verified_algorithm_id, verified_digest) =
Alan Stokes068f6d42023-10-09 10:13:03 +0100323 get_apk_digest(&apk, SDK_INT, /* verify= */ true)
Alan Stokes25f69362023-03-06 16:51:54 +0000324 .expect("Error when extracting apk digest with verification.");
Alice Wang676bb4a2022-09-19 14:21:39 +0000325
Alice Wang0cafa142022-09-23 15:17:02 +0000326 assert_eq!(expected_algorithm_id, verified_algorithm_id);
Alice Wang676bb4a2022-09-19 14:21:39 +0000327 let expected_digest_path = format!("{}.apk_digest", apk_path.as_ref().to_str().unwrap());
Alice Wang0cafa142022-09-23 15:17:02 +0000328 assert_bytes_eq_to_data_in_file(&verified_digest, expected_digest_path);
329
Alan Stokes25f69362023-03-06 16:51:54 +0000330 let (unverified_algorithm_id, unverified_digest) =
Alan Stokes068f6d42023-10-09 10:13:03 +0100331 get_apk_digest(&apk, SDK_INT, /* verify= */ false)
Alan Stokes25f69362023-03-06 16:51:54 +0000332 .expect("Error when extracting apk digest without verification.");
Alice Wang0cafa142022-09-23 15:17:02 +0000333 assert_eq!(expected_algorithm_id, unverified_algorithm_id);
334 assert_eq!(verified_digest, unverified_digest);
Alice Wang676bb4a2022-09-19 14:21:39 +0000335}
336
337fn 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",
Chariseece832152023-11-01 18:01:09 +0000344 bytes_data.iter().fold(String::new(), |mut output, b| {
345 let _ = write!(output, "\\\\x{:02x}", b);
346 output
347 }),
Alice Wang676bb4a2022-09-19 14:21:39 +0000348 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 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000356}