blob: 52e1da4ea58c29b0c99a9460f391967badffd8af [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 Stokes25c86212023-03-09 17:22:19 +000020use log::info;
Alice Wang67d3c002022-09-16 10:08:25 +000021use std::{fs, matches, path::Path};
Jooyung Hand8397852021-08-10 16:29:36 +090022
Seungjae Yoo91e250a2022-06-07 02:21:56 +000023const KEY_NAMES_DSA: &[&str] = &["1024", "2048", "3072"];
24const KEY_NAMES_ECDSA: &[&str] = &["p256", "p384", "p521"];
25const KEY_NAMES_RSA: &[&str] = &["1024", "2048", "3072", "4096", "8192", "16384"];
Jooyung Hancee6de62021-08-11 15:52:07 +090026
Alan Stokes25f69362023-03-06 16:51:54 +000027const SDK_INT: u32 = 31;
28
Alan Stokes25c86212023-03-09 17:22:19 +000029/// Make sure any logging from the code under test ends up in logcat.
30fn setup() {
31 android_logger::init_once(
32 android_logger::Config::default()
33 .with_tag("apkverify_test")
34 .with_min_level(log::Level::Info),
35 );
36 info!("Test starting");
37}
38
Jooyung Hancee6de62021-08-11 15:52:07 +090039#[test]
40fn test_verify_truncated_cd() {
Alan Stokes25c86212023-03-09 17:22:19 +000041 setup();
Jooyung Hancee6de62021-08-11 15:52:07 +090042 use zip::result::ZipError;
Alan Stokes25f69362023-03-06 16:51:54 +000043 let res = verify("tests/data/v2-only-truncated-cd.apk", SDK_INT);
Alice Wang92889352022-09-16 10:42:52 +000044 // TODO(b/190343842): consider making a helper for err assertion
Jooyung Hancee6de62021-08-11 15:52:07 +090045 assert!(matches!(
Andrew Walbran117cd5e2021-08-13 11:42:13 +000046 res.unwrap_err().root_cause().downcast_ref::<ZipError>().unwrap(),
Jooyung Hancee6de62021-08-11 15:52:07 +090047 ZipError::InvalidArchive(_),
48 ));
49}
Seungjae Yoo91e250a2022-06-07 02:21:56 +000050
51#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +000052fn apex_signed_with_v3_rsa_pkcs1_sha512_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +000053 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +000054 validate_apk("tests/data/test.apex", SignatureAlgorithmID::RsaPkcs1V15WithSha512);
Seungjae Yoo91e250a2022-06-07 02:21:56 +000055}
56
Seungjae Yoo91e250a2022-06-07 02:21:56 +000057#[test]
Alice Wang50701022022-09-21 08:51:38 +000058fn apks_signed_with_v3_dsa_sha256_are_not_supported() {
Alan Stokes25c86212023-03-09 17:22:19 +000059 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +000060 for key_name in KEY_NAMES_DSA.iter() {
Alan Stokes25f69362023-03-06 16:51:54 +000061 let res = verify(format!("tests/data/v3-only-with-dsa-sha256-{}.apk", key_name), SDK_INT);
Alice Wang50701022022-09-21 08:51:38 +000062 assert!(res.is_err(), "DSA algorithm is not supported for verification. See b/197052981.");
Alan Stokesa6876992023-01-20 12:26:25 +000063 assert_contains(&res.unwrap_err().to_string(), "No supported APK signatures found");
Alice Wang676bb4a2022-09-19 14:21:39 +000064 }
65}
66
67#[test]
68fn apks_signed_with_v3_ecdsa_sha256_are_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +000069 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +000070 for key_name in KEY_NAMES_ECDSA.iter() {
Alice Wang676bb4a2022-09-19 14:21:39 +000071 validate_apk(
72 format!("tests/data/v3-only-with-ecdsa-sha256-{}.apk", key_name),
73 SignatureAlgorithmID::EcdsaWithSha256,
74 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +000075 }
76}
77
Seungjae Yoo91e250a2022-06-07 02:21:56 +000078#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +000079fn apks_signed_with_v3_ecdsa_sha512_are_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +000080 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +000081 for key_name in KEY_NAMES_ECDSA.iter() {
Alice Wang676bb4a2022-09-19 14:21:39 +000082 validate_apk(
83 format!("tests/data/v3-only-with-ecdsa-sha512-{}.apk", key_name),
84 SignatureAlgorithmID::EcdsaWithSha512,
85 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +000086 }
87}
88
89#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +000090fn apks_signed_with_v3_rsa_pkcs1_sha256_are_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +000091 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +000092 for key_name in KEY_NAMES_RSA.iter() {
Alice Wang676bb4a2022-09-19 14:21:39 +000093 validate_apk(
94 format!("tests/data/v3-only-with-rsa-pkcs1-sha256-{}.apk", key_name),
95 SignatureAlgorithmID::RsaPkcs1V15WithSha256,
96 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +000097 }
98}
99
100#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000101fn apks_signed_with_v3_rsa_pkcs1_sha512_are_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000102 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000103 for key_name in KEY_NAMES_RSA.iter() {
Alice Wang676bb4a2022-09-19 14:21:39 +0000104 validate_apk(
105 format!("tests/data/v3-only-with-rsa-pkcs1-sha512-{}.apk", key_name),
106 SignatureAlgorithmID::RsaPkcs1V15WithSha512,
107 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000108 }
109}
110
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000111#[test]
Alan Stokes25c86212023-03-09 17:22:19 +0000112fn test_verify_v3_sig_min_max_sdk() {
113 setup();
114 // The Signer for this APK has min_sdk=24, max_sdk=32.
115 let path = "tests/data/v31-rsa-2048_2-tgt-33-1-tgt-28.apk";
116
117 let res = verify(path, 23);
118 assert!(res.is_err());
119 assert_contains(&res.unwrap_err().to_string(), "0 signers found");
120
121 let res = verify(path, 24);
122 assert!(res.is_ok());
123
124 let res = verify(path, 32);
125 assert!(res.is_ok());
126
127 let res = verify(path, 33);
128 assert!(res.is_err());
129 assert_contains(&res.unwrap_err().to_string(), "0 signers found");
130}
131
132#[test]
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000133fn test_verify_v3_sig_does_not_verify() {
Alan Stokes25c86212023-03-09 17:22:19 +0000134 setup();
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000135 let path_list = [
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000136 "tests/data/v3-only-with-ecdsa-sha512-p521-sig-does-not-verify.apk",
137 "tests/data/v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk",
138 ];
139 for path in path_list.iter() {
Alan Stokes25f69362023-03-06 16:51:54 +0000140 let res = verify(path, SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000141 assert!(res.is_err());
Alice Wang50701022022-09-21 08:51:38 +0000142 assert_contains(&res.unwrap_err().to_string(), "Signature is invalid");
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000143 }
144}
145
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000146#[test]
147fn test_verify_v3_digest_mismatch() {
Alan Stokes25c86212023-03-09 17:22:19 +0000148 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000149 let res = verify("tests/data/v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk", SDK_INT);
Alice Wang50701022022-09-21 08:51:38 +0000150 assert!(res.is_err());
151 assert_contains(&res.unwrap_err().to_string(), "Digest mismatch");
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000152}
153
154#[test]
155fn test_verify_v3_wrong_apk_sig_block_magic() {
Alan Stokes25c86212023-03-09 17:22:19 +0000156 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000157 let res =
158 verify("tests/data/v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000159 assert!(res.is_err());
160 assert_contains(&res.unwrap_err().to_string(), "No APK Signing Block");
161}
162
163#[test]
164fn test_verify_v3_apk_sig_block_size_mismatch() {
Alan Stokes25c86212023-03-09 17:22:19 +0000165 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000166 let res = verify(
167 "tests/data/v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk",
168 SDK_INT,
169 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000170 assert!(res.is_err());
171 assert_contains(
172 &res.unwrap_err().to_string(),
173 "APK Signing Block sizes in header and footer do not match",
174 );
175}
176
177#[test]
178fn test_verify_v3_cert_and_public_key_mismatch() {
Alan Stokes25c86212023-03-09 17:22:19 +0000179 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000180 let res = verify("tests/data/v3-only-cert-and-public-key-mismatch.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000181 assert!(res.is_err());
182 assert_contains(&res.unwrap_err().to_string(), "Public key mismatch");
183}
184
185#[test]
186fn test_verify_v3_empty() {
Alan Stokes25c86212023-03-09 17:22:19 +0000187 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000188 let res = verify("tests/data/v3-only-empty.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000189 assert!(res.is_err());
190 assert_contains(&res.unwrap_err().to_string(), "APK too small for APK Signing Block");
191}
192
193#[test]
194fn test_verify_v3_no_certs_in_sig() {
Alan Stokes25c86212023-03-09 17:22:19 +0000195 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000196 let res = verify("tests/data/v3-only-no-certs-in-sig.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000197 assert!(res.is_err());
198 assert_contains(&res.unwrap_err().to_string(), "No certificates listed");
199}
200
201#[test]
202fn test_verify_v3_no_supported_sig_algs() {
Alan Stokes25c86212023-03-09 17:22:19 +0000203 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000204 let res = verify("tests/data/v3-only-no-supported-sig-algs.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000205 assert!(res.is_err());
Alan Stokesa6876992023-01-20 12:26:25 +0000206 assert_contains(&res.unwrap_err().to_string(), "No supported APK signatures found");
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000207}
208
209#[test]
210fn test_verify_v3_signatures_and_digests_block_mismatch() {
Alan Stokes25c86212023-03-09 17:22:19 +0000211 setup();
Alan Stokes25f69362023-03-06 16:51:54 +0000212 let res = verify("tests/data/v3-only-signatures-and-digests-block-mismatch.apk", SDK_INT);
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000213 assert!(res.is_err());
214 assert_contains(
215 &res.unwrap_err().to_string(),
216 "Signature algorithms don't match between digests and signatures records",
217 );
218}
219
220#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000221fn apk_signed_with_v3_unknown_additional_attr_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000222 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +0000223 validate_apk(
224 "tests/data/v3-only-unknown-additional-attr.apk",
225 SignatureAlgorithmID::RsaPkcs1V15WithSha256,
226 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000227}
228
229#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000230fn apk_signed_with_v3_unknown_pair_in_apk_sig_block_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000231 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +0000232 validate_apk(
233 "tests/data/v3-only-unknown-pair-in-apk-sig-block.apk",
234 SignatureAlgorithmID::RsaPkcs1V15WithSha256,
235 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000236}
237
238#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000239fn apk_signed_with_v3_ignorable_unsupported_sig_algs_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000240 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +0000241 validate_apk(
242 "tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk",
243 SignatureAlgorithmID::RsaPkcs1V15WithSha256,
244 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000245}
246
247#[test]
Alice Wang676bb4a2022-09-19 14:21:39 +0000248fn apk_signed_with_v3_stamp_is_valid() {
Alan Stokes25c86212023-03-09 17:22:19 +0000249 setup();
Alice Wang676bb4a2022-09-19 14:21:39 +0000250 validate_apk("tests/data/v3-only-with-stamp.apk", SignatureAlgorithmID::EcdsaWithSha256);
Alice Wang67d3c002022-09-16 10:08:25 +0000251}
252
Alice Wang676bb4a2022-09-19 14:21:39 +0000253fn validate_apk<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID) {
254 validate_apk_public_key(&apk_path);
255 validate_apk_digest(&apk_path, expected_algorithm_id);
256}
257
258/// Validates that the following public keys are equal:
259/// * public key from verification
260/// * public key extracted from apk without verification
261/// * expected public key from the corresponding .der file
Alice Wang67d3c002022-09-16 10:08:25 +0000262fn validate_apk_public_key<P: AsRef<Path>>(apk_path: P) {
Alan Stokes25f69362023-03-06 16:51:54 +0000263 let public_key_from_verification = verify(&apk_path, SDK_INT);
Alice Wang67d3c002022-09-16 10:08:25 +0000264 let public_key_from_verification =
265 public_key_from_verification.expect("Error in verification result");
266
267 let expected_public_key_path = format!("{}.der", apk_path.as_ref().to_str().unwrap());
Alice Wang676bb4a2022-09-19 14:21:39 +0000268 assert_bytes_eq_to_data_in_file(&public_key_from_verification, expected_public_key_path);
Alice Wang67d3c002022-09-16 10:08:25 +0000269
Alan Stokes25f69362023-03-06 16:51:54 +0000270 let public_key_from_apk = get_public_key_der(&apk_path, SDK_INT);
Alice Wang3c016622022-09-19 09:08:27 +0000271 let public_key_from_apk =
272 public_key_from_apk.expect("Error when extracting public key from apk");
Alice Wang676bb4a2022-09-19 14:21:39 +0000273 assert_eq!(
274 public_key_from_verification, public_key_from_apk,
275 "Public key extracted directly from apk does not match the public key from verification."
276 );
277}
278
279/// Validates that the following apk_digest are equal:
280/// * apk_digest directly extracted from apk without computation
Alice Wang0cafa142022-09-23 15:17:02 +0000281/// * computed apk_digest
Alice Wang676bb4a2022-09-19 14:21:39 +0000282/// * expected apk digest from the corresponding .apk_digest file
283fn validate_apk_digest<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID) {
284 let apk = fs::File::open(&apk_path).expect("Unabled to open apk file");
285
Alan Stokes25f69362023-03-06 16:51:54 +0000286 let (verified_algorithm_id, verified_digest) =
287 get_apk_digest(&apk, SDK_INT, /*verify=*/ true)
288 .expect("Error when extracting apk digest with verification.");
Alice Wang676bb4a2022-09-19 14:21:39 +0000289
Alice Wang0cafa142022-09-23 15:17:02 +0000290 assert_eq!(expected_algorithm_id, verified_algorithm_id);
Alice Wang676bb4a2022-09-19 14:21:39 +0000291 let expected_digest_path = format!("{}.apk_digest", apk_path.as_ref().to_str().unwrap());
Alice Wang0cafa142022-09-23 15:17:02 +0000292 assert_bytes_eq_to_data_in_file(&verified_digest, expected_digest_path);
293
Alan Stokes25f69362023-03-06 16:51:54 +0000294 let (unverified_algorithm_id, unverified_digest) =
295 get_apk_digest(&apk, SDK_INT, /*verify=*/ false)
296 .expect("Error when extracting apk digest without verification.");
Alice Wang0cafa142022-09-23 15:17:02 +0000297 assert_eq!(expected_algorithm_id, unverified_algorithm_id);
298 assert_eq!(verified_digest, unverified_digest);
Alice Wang676bb4a2022-09-19 14:21:39 +0000299}
300
301fn assert_bytes_eq_to_data_in_file<P: AsRef<Path> + std::fmt::Display>(
302 bytes_data: &[u8],
303 expected_data_path: P,
304) {
305 assert!(
306 fs::metadata(&expected_data_path).is_ok(),
307 "File does not exist. You can re-create it with:\n$ echo -en {} > {}\n",
308 bytes_data.iter().map(|b| format!("\\\\x{:02x}", b)).collect::<String>(),
309 expected_data_path
310 );
311 let expected_data = fs::read(&expected_data_path).unwrap();
312 assert_eq!(
313 expected_data, bytes_data,
314 "Actual data does not match the data from: {}",
315 expected_data_path
316 );
Seungjae Yoo91e250a2022-06-07 02:21:56 +0000317}