1use std::collections::BTreeSet;
4use std::fs::File;
5use std::io::{Cursor, Read, Seek};
6use std::ops::RangeInclusive;
7use std::path::Path;
8
9use super::scheme::{MAX_SDK, SchemeSigner, V3_MIN_SDK};
10use super::{BlockId, Certificate, Scheme, Schemes, SignatureAlgorithm, SigningBlock, VerifyError, jar, scheme};
11use crate::archive::{self, ReadAt, ZipError};
12use crate::digest::{ContentDigest, content_digests};
13use crate::error::{Error, FileOperation};
14
15#[derive(Clone, Debug)]
17pub struct Verification {
18 pub schemes: Schemes,
20 signers: Vec<VerifiedSigner>,
21}
22
23impl Verification {
24 pub fn certificate(&self) -> &Certificate {
26 self.signers[0].certificate()
27 }
28
29 pub fn signers(&self) -> &[VerifiedSigner] {
31 &self.signers
32 }
33}
34
35#[derive(Clone, Debug)]
37pub struct VerifiedSigner {
38 certificates: Vec<Certificate>,
39 pub algorithm: Option<SignatureAlgorithm>,
41 pub sdk_range: Option<RangeInclusive<u32>>,
43}
44
45impl VerifiedSigner {
46 pub fn certificate(&self) -> &Certificate {
48 &self.certificates[0]
49 }
50
51 pub fn certificates(&self) -> &[Certificate] {
53 &self.certificates
54 }
55}
56
57impl From<SchemeSigner> for VerifiedSigner {
58 fn from(signer: SchemeSigner) -> Self {
59 Self {
60 certificates: signer.certificates,
61 algorithm: Some(signer.algorithm),
62 sdk_range: signer.sdk.map(|sdk| sdk.min..=sdk.max),
63 }
64 }
65}
66
67pub fn verify(path: impl AsRef<Path>) -> Result<Verification, Error> {
76 let path = path.as_ref();
77 let file = File::open(path).map_err(Error::file(FileOperation::Open, path))?;
78 verify_source(&file, &file)
79}
80
81pub fn verify_bytes(bytes: &[u8]) -> Result<Verification, Error> {
83 verify_source(bytes, Cursor::new(bytes))
84}
85
86fn verify_source(source: &(impl ReadAt + ?Sized), reader: impl Read + Seek) -> Result<Verification, Error> {
87 let (mut archive, layout) = archive::open(reader)?;
88 let manifest = crate::apk::read_manifest(&mut archive)?;
89 let first_v3_sdk = manifest
90 .as_ref()
91 .map(|manifest| manifest.min_sdk().api_level())
92 .transpose()?
93 .map_or(V3_MIN_SDK, |sdk| sdk.get().max(V3_MIN_SDK));
94
95 let mut digests: Vec<(ContentDigest, Vec<u8>)> = Vec::new();
96 let mut content_digest = |kind: ContentDigest| -> Result<Option<Vec<u8>>, VerifyError> {
97 if let Some((_, digest)) = digests.iter().find(|(k, _)| *k == kind) {
98 return Ok(Some(digest.clone()));
99 }
100 let (_, digest) = content_digests(source, &layout, &[kind])?.remove(0);
101 digests.push((kind, digest.clone()));
102 Ok(Some(digest))
103 };
104 let block = match &layout.signing_block {
105 Some(range) => {
106 let bytes = source.read_vec_at(range.start, (range.end - range.start) as usize).map_err(ZipError::from)?;
107 SigningBlock::parse(&bytes)?
108 }
109 None => SigningBlock::default(),
110 };
111 if block.get(BlockId::V3_1).is_some() {
112 return Err(VerifyError::KeyRotationUnsupported.into());
113 }
114
115 let mut verified: Vec<(Scheme, Vec<VerifiedSigner>)> = Vec::new();
116 let mut declared: Vec<(Scheme, u32)> = Vec::new();
117 for scheme in [Scheme::V3, Scheme::V2] {
118 let Some(value) = block.get(scheme.block_id().expect("block scheme")) else { continue };
119 let signers = scheme::verify(scheme, value, &mut content_digest)?;
120 if scheme == Scheme::V3 {
121 check_v3_coverage(&signers, first_v3_sdk)?;
122 }
123 declared.extend(signers.iter().flat_map(|s| s.protected_schemes.iter().map(move |&p| (scheme, p))));
124 verified.push((scheme, signers.into_iter().map(VerifiedSigner::from).collect()));
125 }
126 if let Some(signature) = jar::verify(&mut archive)? {
127 declared.extend(signature.declared_schemes.iter().map(|&p| (Scheme::V1, p)));
128 let signers = signature
129 .signers
130 .into_iter()
131 .map(|certificates| VerifiedSigner { certificates, algorithm: None, sdk_range: None })
132 .collect();
133 verified.push((Scheme::V1, signers));
134 }
135 let Some((strongest, _)) = verified.first() else { return Err(VerifyError::Unsigned.into()) };
136
137 let schemes = verified.iter().fold(Schemes::empty(), |set, (scheme, _)| set | scheme.flag());
138 for (protected_by, number) in declared {
139 match Scheme::from_number(number) {
140 Some(scheme) if !schemes.contains(scheme.flag()) => {
141 return Err(VerifyError::StrippedScheme { scheme, protected_by }.into());
142 }
143 _ => {}
144 }
145 }
146 let leaves =
147 |signers: &[VerifiedSigner]| signers.iter().map(|s| s.certificates[0].der().to_vec()).collect::<BTreeSet<_>>();
148 let expected = leaves(&verified[0].1);
149 if let Some((other, _)) = verified.iter().skip(1).find(|(_, signers)| leaves(signers) != expected) {
150 return Err(VerifyError::SignerMismatch { first: *strongest, second: *other }.into());
151 }
152 if let Some(manifest) = &manifest {
153 let (min_sdk, target_sdk) = (manifest.min_sdk().api_level()?, manifest.effective_target_sdk()?);
154 schemes.check_coverage(min_sdk, target_sdk).map_err(VerifyError::Coverage)?;
155 }
156 let (_, signers) = verified.swap_remove(0);
157 Ok(Verification { schemes, signers })
158}
159
160fn check_v3_coverage(signers: &[SchemeSigner], first_sdk: u32) -> Result<(), VerifyError> {
162 let gap = |sdk| VerifyError::SdkCoverage { scheme: Scheme::V3, sdk };
163 let mut ranges: Vec<_> = signers.iter().filter_map(|s| s.sdk).filter(|r| r.max >= first_sdk).collect();
164 ranges.sort_by_key(|r| r.min);
165 let mut next = first_sdk;
166 for range in ranges {
167 let start = range.min.max(first_sdk);
168 if start != next {
169 return Err(gap(start.min(next)));
170 }
171 next = range.max + 1;
172 }
173 if next != MAX_SDK + 1 {
174 return Err(gap(next));
175 }
176 Ok(())
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::sign::scheme::SdkRange;
183
184 fn signers(ranges: &[(u32, u32)]) -> Vec<SchemeSigner> {
185 ranges
186 .iter()
187 .map(|&(min, max)| SchemeSigner {
188 certificates: Vec::new(),
189 algorithm: SignatureAlgorithm::EcdsaSha256,
190 sdk: Some(SdkRange { min, max }),
191 protected_schemes: Vec::new(),
192 })
193 .collect()
194 }
195
196 #[test]
197 fn v3_coverage_starts_at_the_apps_minimum_sdk() {
198 let ranges = signers(&[(35, MAX_SDK)]);
199 check_v3_coverage(&ranges, 35).unwrap();
200 assert!(matches!(check_v3_coverage(&ranges, 28), Err(VerifyError::SdkCoverage { sdk: 28, .. })));
201 check_v3_coverage(&signers(&[(28, MAX_SDK)]), 35).unwrap();
202 }
203
204 #[test]
205 fn v3_coverage_rejects_gaps_and_overlaps() {
206 for (ranges, sdk) in
207 [(vec![(28, 39), (41, MAX_SDK)], 40), (vec![(28, 40), (40, MAX_SDK)], 40), (vec![(28, 40)], 41)]
208 {
209 assert!(matches!(
210 check_v3_coverage(&signers(&ranges), 28),
211 Err(VerifyError::SdkCoverage { sdk: found, .. }) if found == sdk
212 ));
213 }
214 }
215}