• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 apkverify::{
18     get_apk_digest, get_public_key_der, testing::assert_contains, verify, SignatureAlgorithmID,
19 };
20 use log::info;
21 use std::{fs, matches, path::Path};
22 
23 const KEY_NAMES_DSA: &[&str] = &["1024", "2048", "3072"];
24 const KEY_NAMES_ECDSA: &[&str] = &["p256", "p384", "p521"];
25 const KEY_NAMES_RSA: &[&str] = &["1024", "2048", "3072", "4096", "8192", "16384"];
26 
27 const SDK_INT: u32 = 31;
28 
29 /// Make sure any logging from the code under test ends up in logcat.
setup()30 fn 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 
39 #[test]
test_verify_truncated_cd()40 fn test_verify_truncated_cd() {
41     setup();
42     use zip::result::ZipError;
43     let res = verify("tests/data/v2-only-truncated-cd.apk", SDK_INT);
44     // TODO(b/190343842): consider making a helper for err assertion
45     assert!(matches!(
46         res.unwrap_err().root_cause().downcast_ref::<ZipError>().unwrap(),
47         ZipError::InvalidArchive(_),
48     ));
49 }
50 
51 #[test]
apex_signed_with_v3_rsa_pkcs1_sha512_is_valid()52 fn apex_signed_with_v3_rsa_pkcs1_sha512_is_valid() {
53     setup();
54     validate_apk("tests/data/test.apex", SignatureAlgorithmID::RsaPkcs1V15WithSha512);
55 }
56 
57 #[test]
apks_signed_with_v3_dsa_sha256_are_not_supported()58 fn apks_signed_with_v3_dsa_sha256_are_not_supported() {
59     setup();
60     for key_name in KEY_NAMES_DSA.iter() {
61         let res = verify(format!("tests/data/v3-only-with-dsa-sha256-{}.apk", key_name), SDK_INT);
62         assert!(res.is_err(), "DSA algorithm is not supported for verification. See b/197052981.");
63         assert_contains(&res.unwrap_err().to_string(), "No supported APK signatures found");
64     }
65 }
66 
67 #[test]
apks_signed_with_v3_ecdsa_sha256_are_valid()68 fn apks_signed_with_v3_ecdsa_sha256_are_valid() {
69     setup();
70     for key_name in KEY_NAMES_ECDSA.iter() {
71         validate_apk(
72             format!("tests/data/v3-only-with-ecdsa-sha256-{}.apk", key_name),
73             SignatureAlgorithmID::EcdsaWithSha256,
74         );
75     }
76 }
77 
78 #[test]
apks_signed_with_v3_ecdsa_sha512_are_valid()79 fn apks_signed_with_v3_ecdsa_sha512_are_valid() {
80     setup();
81     for key_name in KEY_NAMES_ECDSA.iter() {
82         validate_apk(
83             format!("tests/data/v3-only-with-ecdsa-sha512-{}.apk", key_name),
84             SignatureAlgorithmID::EcdsaWithSha512,
85         );
86     }
87 }
88 
89 #[test]
apks_signed_with_v3_rsa_pkcs1_sha256_are_valid()90 fn apks_signed_with_v3_rsa_pkcs1_sha256_are_valid() {
91     setup();
92     for key_name in KEY_NAMES_RSA.iter() {
93         validate_apk(
94             format!("tests/data/v3-only-with-rsa-pkcs1-sha256-{}.apk", key_name),
95             SignatureAlgorithmID::RsaPkcs1V15WithSha256,
96         );
97     }
98 }
99 
100 #[test]
apks_signed_with_v3_rsa_pkcs1_sha512_are_valid()101 fn apks_signed_with_v3_rsa_pkcs1_sha512_are_valid() {
102     setup();
103     for key_name in KEY_NAMES_RSA.iter() {
104         validate_apk(
105             format!("tests/data/v3-only-with-rsa-pkcs1-sha512-{}.apk", key_name),
106             SignatureAlgorithmID::RsaPkcs1V15WithSha512,
107         );
108     }
109 }
110 
111 #[test]
test_verify_v3_sig_min_max_sdk()112 fn 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]
test_verify_v3_sig_does_not_verify()133 fn test_verify_v3_sig_does_not_verify() {
134     setup();
135     let path_list = [
136         "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() {
140         let res = verify(path, SDK_INT);
141         assert!(res.is_err());
142         assert_contains(&res.unwrap_err().to_string(), "Signature is invalid");
143     }
144 }
145 
146 #[test]
test_verify_v3_digest_mismatch()147 fn test_verify_v3_digest_mismatch() {
148     setup();
149     let res = verify("tests/data/v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk", SDK_INT);
150     assert!(res.is_err());
151     assert_contains(&res.unwrap_err().to_string(), "Digest mismatch");
152 }
153 
154 #[test]
test_verify_v3_wrong_apk_sig_block_magic()155 fn test_verify_v3_wrong_apk_sig_block_magic() {
156     setup();
157     let res =
158         verify("tests/data/v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk", SDK_INT);
159     assert!(res.is_err());
160     assert_contains(&res.unwrap_err().to_string(), "No APK Signing Block");
161 }
162 
163 #[test]
test_verify_v3_apk_sig_block_size_mismatch()164 fn test_verify_v3_apk_sig_block_size_mismatch() {
165     setup();
166     let res = verify(
167         "tests/data/v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk",
168         SDK_INT,
169     );
170     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]
test_verify_v3_cert_and_public_key_mismatch()178 fn test_verify_v3_cert_and_public_key_mismatch() {
179     setup();
180     let res = verify("tests/data/v3-only-cert-and-public-key-mismatch.apk", SDK_INT);
181     assert!(res.is_err());
182     assert_contains(&res.unwrap_err().to_string(), "Public key mismatch");
183 }
184 
185 #[test]
test_verify_v3_empty()186 fn test_verify_v3_empty() {
187     setup();
188     let res = verify("tests/data/v3-only-empty.apk", SDK_INT);
189     assert!(res.is_err());
190     assert_contains(&res.unwrap_err().to_string(), "APK too small for APK Signing Block");
191 }
192 
193 #[test]
test_verify_v3_no_certs_in_sig()194 fn test_verify_v3_no_certs_in_sig() {
195     setup();
196     let res = verify("tests/data/v3-only-no-certs-in-sig.apk", SDK_INT);
197     assert!(res.is_err());
198     assert_contains(&res.unwrap_err().to_string(), "No certificates listed");
199 }
200 
201 #[test]
test_verify_v3_no_supported_sig_algs()202 fn test_verify_v3_no_supported_sig_algs() {
203     setup();
204     let res = verify("tests/data/v3-only-no-supported-sig-algs.apk", SDK_INT);
205     assert!(res.is_err());
206     assert_contains(&res.unwrap_err().to_string(), "No supported APK signatures found");
207 }
208 
209 #[test]
test_verify_v3_signatures_and_digests_block_mismatch()210 fn test_verify_v3_signatures_and_digests_block_mismatch() {
211     setup();
212     let res = verify("tests/data/v3-only-signatures-and-digests-block-mismatch.apk", SDK_INT);
213     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]
apk_signed_with_v3_unknown_additional_attr_is_valid()221 fn apk_signed_with_v3_unknown_additional_attr_is_valid() {
222     setup();
223     validate_apk(
224         "tests/data/v3-only-unknown-additional-attr.apk",
225         SignatureAlgorithmID::RsaPkcs1V15WithSha256,
226     );
227 }
228 
229 #[test]
apk_signed_with_v3_unknown_pair_in_apk_sig_block_is_valid()230 fn apk_signed_with_v3_unknown_pair_in_apk_sig_block_is_valid() {
231     setup();
232     validate_apk(
233         "tests/data/v3-only-unknown-pair-in-apk-sig-block.apk",
234         SignatureAlgorithmID::RsaPkcs1V15WithSha256,
235     );
236 }
237 
238 #[test]
apk_signed_with_v3_ignorable_unsupported_sig_algs_is_valid()239 fn apk_signed_with_v3_ignorable_unsupported_sig_algs_is_valid() {
240     setup();
241     validate_apk(
242         "tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk",
243         SignatureAlgorithmID::RsaPkcs1V15WithSha256,
244     );
245 }
246 
247 #[test]
apk_signed_with_v3_stamp_is_valid()248 fn apk_signed_with_v3_stamp_is_valid() {
249     setup();
250     validate_apk("tests/data/v3-only-with-stamp.apk", SignatureAlgorithmID::EcdsaWithSha256);
251 }
252 
validate_apk<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID)253 fn 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
validate_apk_public_key<P: AsRef<Path>>(apk_path: P)262 fn validate_apk_public_key<P: AsRef<Path>>(apk_path: P) {
263     let public_key_from_verification = verify(&apk_path, SDK_INT);
264     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());
268     assert_bytes_eq_to_data_in_file(&public_key_from_verification, expected_public_key_path);
269 
270     let public_key_from_apk = get_public_key_der(&apk_path, SDK_INT);
271     let public_key_from_apk =
272         public_key_from_apk.expect("Error when extracting public key from apk");
273     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
281 /// * computed apk_digest
282 /// * expected apk digest from the corresponding .apk_digest file
validate_apk_digest<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID)283 fn 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 
286     let (verified_algorithm_id, verified_digest) =
287         get_apk_digest(&apk, SDK_INT, /*verify=*/ true)
288             .expect("Error when extracting apk digest with verification.");
289 
290     assert_eq!(expected_algorithm_id, verified_algorithm_id);
291     let expected_digest_path = format!("{}.apk_digest", apk_path.as_ref().to_str().unwrap());
292     assert_bytes_eq_to_data_in_file(&verified_digest, expected_digest_path);
293 
294     let (unverified_algorithm_id, unverified_digest) =
295         get_apk_digest(&apk, SDK_INT, /*verify=*/ false)
296             .expect("Error when extracting apk digest without verification.");
297     assert_eq!(expected_algorithm_id, unverified_algorithm_id);
298     assert_eq!(verified_digest, unverified_digest);
299 }
300 
assert_bytes_eq_to_data_in_file<P: AsRef<Path> + std::fmt::Display>( bytes_data: &[u8], expected_data_path: P, )301 fn 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     );
317 }
318