Skip to main content

apk_info_zip/
entry.rs

1//! Describes a `zip` archive
2
3use std::sync::Arc;
4
5use ahash::AHashMap;
6use flate2::{Decompress, FlushDecompress, Status};
7use log::warn;
8use openssl::hash::MessageDigest;
9use openssl::pkcs7::{Pkcs7, Pkcs7Flags};
10use openssl::stack::Stack;
11use openssl::x509::{X509, X509Ref};
12use winnow::binary::{le_u32, le_u64, length_take};
13use winnow::combinator::repeat;
14use winnow::error::ContextError;
15use winnow::prelude::*;
16use winnow::token::take;
17
18use crate::signature::{CertificateInfo, Signature};
19use crate::structs::{CentralDirectory, EndOfCentralDirectory, LocalFileHeader};
20use crate::{CertificateError, FileCompressionType, ZipError};
21
22/// Represents a parsed ZIP archive.
23#[derive(Debug)]
24pub struct ZipEntry {
25    /// Owned zip data
26    input: Vec<u8>,
27
28    /// EOCD structure
29    eocd: EndOfCentralDirectory,
30
31    /// Central directory structure
32    central_directory: CentralDirectory,
33
34    /// Information about local headers
35    local_headers: AHashMap<Arc<str>, LocalFileHeader>,
36}
37
38/// Implementation of basic methods
39impl ZipEntry {
40    /// Creates a new `ZipEntry` from raw ZIP data.
41    ///
42    /// # Errors
43    ///
44    /// Returns a [ZipError] if:
45    /// - The input does not start with a valid ZIP signature [ZipError::InvalidHeader];
46    /// - The End of Central Directory cannot be found [ZipError::NotFoundEOCD];
47    /// - Parsing of the EOCD or central directory fails [ZipError::ParseError].
48    ///
49    /// # Examples
50    ///
51    /// ```
52    /// # use apk_info_zip::{ZipEntry, ZipError};
53    /// let data = std::fs::read("archive.zip").unwrap();
54    /// let zip = ZipEntry::new(data).expect("failed to parse ZIP archive");
55    /// ```
56    pub fn new(input: Vec<u8>) -> Result<ZipEntry, ZipError> {
57        // perform basic sanity check
58        if !input.starts_with(b"PK\x03\x04") {
59            return Err(ZipError::InvalidHeader);
60        }
61
62        let eocd_offset =
63            EndOfCentralDirectory::find_eocd(&input, 4096).ok_or(ZipError::NotFoundEOCD)?;
64
65        let eocd = EndOfCentralDirectory::parse(&mut &input[eocd_offset..])
66            .map_err(|_| ZipError::ParseError)?;
67
68        let central_directory =
69            CentralDirectory::parse(&input, &eocd).map_err(|_| ZipError::ParseError)?;
70
71        let local_headers = central_directory
72            .entries
73            .iter()
74            .filter_map(|(filename, entry)| {
75                LocalFileHeader::parse(&input, entry.local_header_offset as usize)
76                    .ok()
77                    .map(|header| (Arc::clone(filename), header))
78            })
79            .collect();
80
81        Ok(ZipEntry {
82            input,
83            eocd,
84            central_directory,
85            local_headers,
86        })
87    }
88
89    /// Returns an iterator over the names of all files in the ZIP archive.
90    ///
91    /// # Examples
92    ///
93    /// ```
94    /// # use apk_info_zip::ZipEntry;
95    /// # let zip_data = std::fs::read("archive.zip").unwrap();
96    /// # let zip = ZipEntry::new(zip_data).unwrap();
97    /// for filename in zip.namelist() {
98    ///     println!("{}", filename);
99    /// }
100    /// ```
101    pub fn namelist(&self) -> impl Iterator<Item = &str> + '_ {
102        self.central_directory.entries.keys().map(|x| x.as_ref())
103    }
104
105    /// Reads the contents of a file from the ZIP archive.
106    ///
107    /// This method handles both normally compressed files and tampered files
108    /// where the compression metadata may be inconsistent. It returns the
109    /// uncompressed file contents along with the detected compression type.
110    ///
111    /// # Notes
112    ///
113    /// The method attempts to handle files that have tampered headers:
114    /// - If the compression method indicates compression but the compressed
115    ///   size equals the uncompressed size, the file is treated as
116    ///   [FileCompressionType::StoredTampered].
117    /// - If decompression fails but the data is still present, it falls back
118    ///   to [FileCompressionType::StoredTampered].
119    ///
120    /// # Examples
121    ///
122    /// ```rust
123    /// # use apk_info_zip::{ZipEntry, ZipError, FileCompressionType};
124    /// # let zip_data = std::fs::read("archive.zip").unwrap();
125    /// # let zip = ZipEntry::new(zip_data).unwrap();
126    /// let (data, compression) = zip.read("example.txt").expect("failed to read file");
127    /// match compression {
128    ///     FileCompressionType::Stored | FileCompressionType::Deflated => println!("all fine"),
129    ///     FileCompressionType::StoredTampered | FileCompressionType::DeflatedTampered => println!("tampering detected"),
130    /// }
131    /// ```
132    pub fn read(&self, filename: &str) -> Result<(Vec<u8>, FileCompressionType), ZipError> {
133        let local_header = self
134            .local_headers
135            .get(filename)
136            .ok_or(ZipError::FileNotFound)?;
137
138        let central_directory_entry = self
139            .central_directory
140            .entries
141            .get(filename)
142            .ok_or(ZipError::FileNotFound)?;
143
144        let (compressed_size, uncompressed_size) =
145            if local_header.compressed_size == 0 || local_header.uncompressed_size == 0 {
146                (
147                    central_directory_entry.compressed_size as usize,
148                    central_directory_entry.uncompressed_size as usize,
149                )
150            } else {
151                (
152                    local_header.compressed_size as usize,
153                    local_header.uncompressed_size as usize,
154                )
155            };
156
157        let offset = central_directory_entry.local_header_offset as usize + local_header.size();
158        // helper to safely get a slice from input
159        let get_slice = |start: usize, end: usize| self.input.get(start..end).ok_or(ZipError::EOF);
160
161        match (
162            local_header.compression_method,
163            compressed_size == uncompressed_size,
164        ) {
165            (0, _) => {
166                // stored (no compression)
167                let slice = get_slice(offset, offset + uncompressed_size)?;
168                Ok((slice.to_vec(), FileCompressionType::Stored))
169            }
170            (8, _) => {
171                // deflate default
172                let compressed_data = get_slice(offset, offset + compressed_size)?;
173                let mut uncompressed_data = Vec::with_capacity(uncompressed_size);
174
175                Decompress::new(false)
176                    .decompress_vec(
177                        compressed_data,
178                        &mut uncompressed_data,
179                        FlushDecompress::Finish,
180                    )
181                    .map_err(|_| ZipError::DecompressionError)?;
182
183                Ok((uncompressed_data, FileCompressionType::Deflated))
184            }
185            (_, true) => {
186                // stored tampered
187                let slice = get_slice(offset, offset + uncompressed_size)?;
188                Ok((slice.to_vec(), FileCompressionType::StoredTampered))
189            }
190            (_, false) => {
191                // deflate tampered
192                let compressed_data = get_slice(offset, offset + compressed_size)?;
193                let mut uncompressed_data = Vec::with_capacity(uncompressed_size);
194                let mut decompressor = Decompress::new(false);
195
196                let status = decompressor.decompress_vec(
197                    compressed_data,
198                    &mut uncompressed_data,
199                    FlushDecompress::Finish,
200                );
201
202                // check if decompression was actually successfull
203                let is_valid = decompressor.total_in() == compressed_data.len() as u64;
204                match status {
205                    Ok(Status::Ok) | Ok(Status::StreamEnd) if is_valid => {
206                        Ok((uncompressed_data, FileCompressionType::DeflatedTampered))
207                    }
208                    _ => {
209                        // fallback to stored tampered
210                        let slice = get_slice(offset, offset + uncompressed_size)?;
211                        Ok((slice.to_vec(), FileCompressionType::StoredTampered))
212                    }
213                }
214            }
215        }
216    }
217}
218
219/// Implementation for certificate parsing
220///
221/// Very cool research about signature blocks: <https://goa2023.nullcon.net/doc/goa-2023/Android-SigMorph-Covert-Communication-Exploiting-Android-Signing-Schemes.pdf>
222impl ZipEntry {
223    /// Magic of APK signing block
224    ///
225    /// See: <https://source.android.com/docs/security/features/apksigning/v2#apk-signing-block>
226    pub const APK_SIGNATURE_MAGIC: &[u8] = b"APK Sig Block 42";
227
228    /// Magic of V2 Signature Scheme
229    ///
230    /// See: <https://xrefandroid.com/android-16.0.0_r2/xref/tools/apksig/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeConstants.java#23>
231    pub const SIGNATURE_SCHEME_V2_BLOCK_ID: u32 = 0x7109871a;
232
233    /// Magic of V3 Signature Scheme
234    ///
235    /// See: <https://xrefandroid.com/android-16.0.0_r2/xref/tools/apksig/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeConstants.java#25>
236    pub const SIGNATURE_SCHEME_V3_BLOCK_ID: u32 = 0xf05368c0;
237
238    /// Magic of V3.1 Signature Scheme
239    ///
240    /// See: <https://xrefandroid.com/android-16.0.0_r2/xref/tools/apksig/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeConstants.java#26>
241    pub const SIGNATURE_SCHEME_V31_BLOCK_ID: u32 = 0x1b93ad61;
242
243    /// Magic of V1 source stamp signing
244    ///
245    /// Includes metadata such as timestamp of the build, the version of the build tools, source code's git commit hash, etc
246    ///
247    /// See: <https://xrefandroid.com/android-16.0.0_r2/xref/tools/apksig/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java#23>
248    pub const V1_SOURCE_STAMP_BLOCK_ID: u32 = 0x2b09189e;
249
250    /// Magic of V2 source stamp signing
251    ///
252    /// Includes metadata such as timestamp of the build, the version of the build tools, source code's git commit hash, etc
253    ///
254    /// See: <https://xrefandroid.com/android-16.0.0_r2/xref/tools/apksig/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java#24>
255    pub const V2_SOURCE_STAMP_BLOCK_ID: u32 = 0x6dff800d;
256
257    /// Used to increase the size of the signing block (including the length and magic) to a mulitple 4096
258    ///
259    /// See: <https://xrefandroid.com/android-16.0.0_r2/xref/tools/apksig/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java#100>
260    pub const VERITY_PADDING_BLOCK_ID: u32 = 0x42726577;
261
262    /// Block that contains dependency metadata, which is saved by the Android Gradle plugin to identify any issues related to dependencies
263    ///
264    /// This data is compressed, encrypted by a Google Play signing key, so we can't extract it.
265    ///
266    /// Dependency information for Play Console: <https://developer.android.com/build/dependencies#dependency-info-play>
267    ///
268    /// See: <https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:signflinger/src/com/android/signflinger/SignedApk.java;l=58?q=0x504b4453>
269    pub const DEPENDENCY_INFO_BLOCK_ID: u32 = 0x504b4453;
270
271    /// Used to track channels of distribution for an APK, mostly Chinese APKs have this
272    ///
273    /// Alsow known as `MEITAN_APK_CHANNEL_BLOCK`
274    pub const APK_CHANNEL_BLOCK_ID: u32 = 0x71777777;
275
276    /// Google Play Frosting ID
277    pub const GOOGLE_PLAY_FROSTING_ID: u32 = 0x2146444e;
278
279    /// Zero block ID
280    pub const ZERO_BLOCK_ID: u32 = 0xff3b5998;
281
282    /// The signature of some Chinese packer
283    ///
284    /// See: <https://github.com/mcxiaoke/packer-ng-plugin/blob/ffbe05a2d27406f3aea574d083cded27f0742160/common/src/main/java/com/mcxiaoke/packer/common/PackerCommon.java#L29>
285    pub const PACKER_NG_SIG_V2: u32 = 0x7a786b21;
286
287    /// Some apk protector/parser, idk, seen in the wild
288    ///
289    /// The channel information in the ID-Value pair
290    ///
291    /// See: <https://edgeone.ai/document/58005>
292    pub const VASDOLLY_V2: u32 = 0x881155ff;
293
294    /// Converts an OpenSSL [`X509Ref`] into a [`CertificateInfo`] struct.
295    ///
296    /// Extracts common certificate metadata such as serial number, subject, issuer,
297    /// validity period, signature algorithm, and cryptographic fingerprints.
298    fn get_certificate_info(
299        &self,
300        certificate: &X509Ref,
301    ) -> Result<CertificateInfo, CertificateError> {
302        #[inline]
303        fn digest_hex(cert: &X509Ref, md: MessageDigest) -> Result<String, CertificateError> {
304            Ok(const_hex::encode(
305                cert.digest(md).map_err(CertificateError::StackError)?,
306            ))
307        }
308
309        let serial_number = {
310            let bn = certificate
311                .serial_number()
312                .to_bn()
313                .map_err(CertificateError::StackError)?;
314
315            const_hex::encode(bn.to_vec())
316        };
317
318        let mut subject = String::with_capacity(128); // estimate
319        for entry in certificate.subject_name().entries() {
320            if let Ok(value) = entry.data().as_utf8() {
321                if !subject.is_empty() {
322                    subject.push(' ');
323                }
324                subject.push_str(entry.object().nid().short_name().unwrap_or_default());
325                subject.push('=');
326                subject.push_str(value.as_ref());
327            }
328        }
329
330        let mut issuer = String::with_capacity(128);
331
332        for entry in certificate.issuer_name().entries() {
333            if let Ok(value) = entry.data().as_utf8() {
334                if !issuer.is_empty() {
335                    issuer.push(' ');
336                }
337                let name = entry.object().nid().short_name().unwrap_or_default();
338                issuer.push_str(name);
339                issuer.push('=');
340                issuer.push_str(value.as_ref());
341            }
342        }
343
344        let valid_from = certificate.not_before().to_string();
345        let valid_until = certificate.not_after().to_string();
346
347        let signature_type = certificate
348            .signature_algorithm()
349            .object()
350            .nid()
351            .long_name()
352            .map_err(CertificateError::StackError)?
353            .to_string();
354
355        let md5_fingerprint = digest_hex(certificate, MessageDigest::md5())?;
356        let sha1_fingerprint = digest_hex(certificate, MessageDigest::sha1())?;
357        let sha256_fingerprint = digest_hex(certificate, MessageDigest::sha256())?;
358
359        Ok(CertificateInfo {
360            serial_number,
361            subject,
362            issuer,
363            valid_from,
364            valid_until,
365            signature_type,
366            md5_fingerprint,
367            sha1_fingerprint,
368            sha256_fingerprint,
369        })
370    }
371
372    /// Extracts information from a v1 (APK-style) signature in the ZIP archive.
373    ///
374    /// This method searches for signature files in the `META-INF/` directory
375    /// with extensions `.DSA`, `.EC`, or `.RSA`, reads the PKCS#7 data,
376    /// and returns the associated certificates.
377    ///
378    /// # Example
379    ///
380    /// ```
381    /// # use apk_info_zip::{ZipEntry, Signature};
382    /// # let archive = ZipEntry::new(zip_data).unwrap();
383    /// match archive.get_signature_v1() {
384    ///     Ok(Signature::V1(certs)) => println!("Found {} certificates", certs.len()),
385    ///     Ok(Signature::Unknown) => println!("No v1 signature found"),
386    ///     Err(err) => eprintln!("Error parsing signature: {:?}", err),
387    /// }
388    /// ```
389    pub fn get_signature_v1(&self) -> Result<Signature, CertificateError> {
390        let signature_file = match self.namelist().find(|name| {
391            name.starts_with("META-INF/")
392                && (name.ends_with(".DSA") || name.ends_with(".EC") || name.ends_with(".RSA"))
393        }) {
394            Some(v) => v,
395            // just apk without signatures
396            None => return Ok(Signature::Unknown),
397        };
398
399        let (data, _) = self
400            .read(signature_file)
401            .map_err(CertificateError::ZipError)?;
402
403        let info = Pkcs7::from_der(&data).map_err(CertificateError::StackError)?;
404        let certs = Stack::new().map_err(CertificateError::StackError)?;
405
406        let certificates = info
407            .signers(&certs, Pkcs7Flags::STREAM)
408            .map_err(|_| CertificateError::SignerError)?
409            .iter()
410            .map(|signer| self.get_certificate_info(signer))
411            .collect::<Result<Vec<CertificateInfo>, CertificateError>>()?;
412
413        Ok(Signature::V1(certificates))
414    }
415
416    /// Parses the APK Signature Block and extracts useful information.
417    ///
418    /// This method checks for the presence of an APK Signature Scheme block
419    /// at the end of the ZIP archive and attempts to parse all contained
420    /// signatures (v2, v3, etc.).
421    ///
422    /// <div class="warning">
423    ///
424    /// This method handles only v2+ signature blocks.
425    ///
426    /// v1 signatures are handled separately - [ZipEntry::get_signature_v1].
427    ///
428    /// </div>
429    pub fn get_signatures_other(&self) -> Result<Vec<Signature>, CertificateError> {
430        let offset = self.eocd.central_dir_offset as usize;
431        let mut slice = match self.input.get(offset.saturating_sub(24)..offset) {
432            Some(v) => v,
433            None => return Ok(Vec::new()),
434        };
435
436        let size_of_block = le_u64::<&[u8], ContextError>
437            .parse_next(&mut slice)
438            .map_err(|_| CertificateError::ParseError)?;
439
440        let magic = take::<usize, &[u8], ContextError>(16usize)
441            .parse_next(&mut slice)
442            .map_err(|_| CertificateError::ParseError)?;
443
444        // if the magic does not match, then assume that there is no v2+ block with signatures
445        if magic != Self::APK_SIGNATURE_MAGIC {
446            return Ok(Vec::new());
447        }
448
449        // size of block (full) - 8 bytes (size of block - start) - 24 (end signature)
450        slice = match self
451            .input
452            .get(offset.saturating_sub((size_of_block + 8) as usize)..offset.saturating_sub(24))
453        {
454            Some(v) => v,
455            None => return Ok(Vec::new()),
456        };
457
458        let size_of_block_start = le_u64::<&[u8], ContextError>
459            .parse_next(&mut slice)
460            .map_err(|_| CertificateError::ParseError)?;
461
462        if size_of_block != size_of_block_start {
463            return Err(CertificateError::InvalidFormat(
464                size_of_block_start,
465                size_of_block,
466            ));
467        }
468
469        let signatures: Vec<Signature> =
470            repeat::<&[u8], Signature, Vec<Signature>, ContextError, _>(
471                0..,
472                self.parse_apk_signatures(),
473            )
474            .parse_next(&mut slice)
475            .map_err(|_| CertificateError::ParseError)?
476            .into_iter()
477            .filter(|signature| signature != &Signature::Unknown)
478            .collect();
479
480        Ok(signatures)
481    }
482
483    #[allow(unused)]
484    fn parse_digest<'a>() -> impl Parser<&'a [u8], (u32, &'a [u8]), ContextError> {
485        move |input: &mut &'a [u8]| {
486            // digest_block_length, signature_algorith_id, digest_length, digest
487            let (_, signature_algorithm_id, digest) =
488                (le_u32, le_u32, length_take(le_u32)).parse_next(input)?;
489
490            Ok((signature_algorithm_id, digest))
491        }
492    }
493
494    fn parse_certificate<'a>() -> impl Parser<&'a [u8], X509, ContextError> {
495        move |input: &mut &'a [u8]| {
496            let certificate = length_take(le_u32).parse_next(input)?;
497
498            Ok(X509::from_der(certificate).expect("why openssl can't decode this certificate?"))
499        }
500    }
501
502    #[allow(unused)]
503    fn parse_attribute_v2<'a>() -> impl Parser<&'a [u8], (u32, &'a [u8]), ContextError> {
504        move |input: &mut &'a [u8]| {
505            let (attribute_length, id) = (le_u32, le_u32).parse_next(input)?;
506            let value = take(attribute_length.saturating_sub(4)).parse_next(input)?;
507
508            Ok((id, value))
509        }
510    }
511
512    #[allow(unused)]
513    fn parse_attribute_v3<'a>() -> impl Parser<&'a [u8], (u32, &'a [u8]), ContextError> {
514        move |input: &mut &'a [u8]| {
515            let (attribute_length, id) = (le_u32, le_u32).parse_next(input)?;
516            let value = take(attribute_length.saturating_sub(4)).parse_next(input)?;
517            let _const_id = le_u32.parse_next(input)?;
518            // also should be somekind of Proof-of-rotation struct, but skip for now
519
520            Ok((id, value))
521        }
522    }
523
524    #[allow(unused)]
525    fn parse_signature<'a>() -> impl Parser<&'a [u8], (u32, &'a [u8]), ContextError> {
526        move |input: &mut &'a [u8]| {
527            // signature_block_length, signature_algorithm_id, signature_length, signature
528            let (_, signature_algorithm_id, signature) =
529                (le_u32, le_u32, length_take(le_u32)).parse_next(input)?;
530
531            Ok((signature_algorithm_id, signature))
532        }
533    }
534
535    fn parse_signer_v2<'a>() -> impl Parser<&'a [u8], Vec<X509>, ContextError> {
536        move |input: &mut &'a [u8]| {
537            // 1 - parse signer
538            let mut signer_data = length_take(le_u32).parse_next(input)?;
539
540            // 1.1 - parse signed data
541            let mut signed_data = length_take(le_u32).parse_next(&mut signer_data)?;
542
543            // 1.1.1 - parse digests
544            let mut _digests_data = length_take(le_u32).parse_next(&mut signed_data)?;
545            // uncomment this block if actually need parse digests
546            // let digests: Vec<(u32, &[u8])> =
547            //     repeat(0.., Self::parse_digest()).parse_next(&mut digests_data)?;
548
549            // 1.1.2 - parse certificates
550            let mut certificates_data = length_take(le_u32).parse_next(&mut signed_data)?;
551            let certificates: Vec<X509> =
552                repeat(0.., Self::parse_certificate()).parse_next(&mut certificates_data)?;
553
554            // 1.1.3 - parse attributes
555            let mut _attributes_data = length_take(le_u32).parse_next(&mut signed_data)?;
556            // uncomment this block if actually need parse attributes
557            // let attributes: Vec<(u32, &[u8])> =
558            //     repeat(0.., Self::parse_attribute_v2()).parse_next(&mut attributes_data)?;
559
560            // 1.2 - parse signatures
561            let mut _signatures_data = length_take(le_u32).parse_next(&mut signer_data)?;
562            // uncomment this block if actually need parse signatures
563            // let signatures: Vec<(u32, &[u8])> =
564            //     repeat(0.., Self::parse_signature()).parse_next(&mut signatures_data)?;
565
566            // 1.3 - parse public key
567            let _public_key = length_take(le_u32).parse_next(&mut signer_data)?;
568
569            Ok(certificates)
570        }
571    }
572
573    fn parse_signer_v3<'a>() -> impl Parser<&'a [u8], Vec<X509>, ContextError> {
574        move |input: &mut &'a [u8]| {
575            // 1 - parse signer
576            let mut signer_data = length_take(le_u32).parse_next(input)?;
577
578            // 1.1 - parse signed data
579            let mut signed_data = length_take(le_u32).parse_next(&mut signer_data)?;
580
581            // 1.1.1 - parse digests
582            let mut _digests_data = length_take(le_u32).parse_next(&mut signed_data)?;
583            // uncomment this block if actually need parse digests
584            // let digets: Vec<(u32, &[u8])> =
585            //     repeat(0.., Self::parse_digest()).parse_next(&mut digests_data)?;
586
587            // 1.1.2 - parse certificates
588            let mut certificates_data = length_take(le_u32).parse_next(&mut signed_data)?;
589            let certificates: Vec<X509> =
590                repeat(0.., Self::parse_certificate()).parse_next(&mut certificates_data)?;
591
592            // 1.1.3 - parse sdk's
593            let (_min_sdk, _max_sdk) = (le_u32, le_u32).parse_next(&mut signed_data)?;
594
595            // 1.1.4 - parse attributes
596            let mut _attributes_data = length_take(le_u32).parse_next(&mut signed_data)?;
597            // uncomment this block if actually need parse attributes
598            // let attributes: Vec<(u32, &[u8])> =
599            //     repeat(0.., Self::parse_attribute_v3()).parse_next(&mut attributes_data)?;
600
601            // 1.2 - parse duplicates sdk
602            let (_duplicate_min_sdk, _duplicate_max_sdk) =
603                (le_u32, le_u32).parse_next(&mut signer_data)?;
604
605            // 1.3 - parse signatures
606            let mut _signatures_data = length_take(le_u32).parse_next(&mut signer_data)?;
607            // uncomment this block if actually need parse signatures
608            // let signatures: Vec<(u32, &[u8])> =
609            //     repeat(0.., Self::parse_signature()).parse_next(&mut signatures_data)?;
610
611            // 1.4 - parse public key
612            let _public_key = length_take(le_u32).parse_next(&mut signer_data)?;
613
614            Ok(certificates)
615        }
616    }
617
618    fn parse_apk_signatures<'a>(&self) -> impl Parser<&'a [u8], Signature, ContextError> {
619        move |input: &mut &'a [u8]| {
620            let (size, id) = (le_u64, le_u32).parse_next(input)?;
621
622            match id {
623                Self::SIGNATURE_SCHEME_V2_BLOCK_ID => {
624                    let mut signers_data = length_take(le_u32).parse_next(input)?;
625
626                    let certificates =
627                        repeat::<_, Vec<X509>, Vec<Vec<X509>>, _, _>(1.., Self::parse_signer_v2())
628                            .parse_next(&mut signers_data)?
629                            .into_iter()
630                            .flatten()
631                            .filter_map(|cert| self.get_certificate_info(&cert).ok())
632                            .collect();
633
634                    Ok(Signature::V2(certificates))
635                }
636                Self::SIGNATURE_SCHEME_V3_BLOCK_ID => {
637                    let mut signers_data = length_take(le_u32).parse_next(input)?;
638
639                    let certificates =
640                        repeat::<_, Vec<X509>, Vec<Vec<X509>>, _, _>(1.., Self::parse_signer_v3())
641                            .parse_next(&mut signers_data)?
642                            .into_iter()
643                            .flatten()
644                            .filter_map(|cert| self.get_certificate_info(&cert).ok())
645                            .collect();
646
647                    Ok(Signature::V3(certificates))
648                }
649                Self::SIGNATURE_SCHEME_V31_BLOCK_ID => {
650                    let mut signers_data = length_take(le_u32).parse_next(input)?;
651
652                    let certificates =
653                        repeat::<_, Vec<X509>, Vec<Vec<X509>>, _, _>(1.., Self::parse_signer_v3())
654                            .parse_next(&mut signers_data)?
655                            .into_iter()
656                            .flatten()
657                            .filter_map(|cert| self.get_certificate_info(&cert).ok())
658                            .collect();
659
660                    Ok(Signature::V31(certificates))
661                }
662                Self::APK_CHANNEL_BLOCK_ID => {
663                    let data = take(size.saturating_sub(4) as usize).parse_next(input)?;
664
665                    Ok(Signature::ApkChannelBlock(
666                        String::from_utf8_lossy(data).trim().to_string(),
667                    ))
668                }
669                Self::V1_SOURCE_STAMP_BLOCK_ID => {
670                    // https://cs.android.com/android/platform/superproject/main/+/main:tools/apksig/src/main/java/com/android/apksig/internal/apk/stamp/V1SourceStampSigner.java;l=86;bpv=0;bpt=1
671                    let _stamp_block_prefix = le_u32.parse_next(input)?;
672
673                    let certificate = Self::parse_certificate().parse_next(input)?;
674
675                    // i don't think that it is usefull information
676                    let _signed_data = length_take(le_u32).parse_next(input)?;
677
678                    // TODO: proper error message
679                    let certificate = self
680                        .get_certificate_info(&certificate)
681                        .map_err(|_| ContextError::new())?;
682
683                    Ok(Signature::StampBlockV1(certificate))
684                }
685                Self::V2_SOURCE_STAMP_BLOCK_ID => {
686                    // https://cs.android.com/android/platform/superproject/main/+/main:tools/apksig/src/main/java/com/android/apksig/internal/apk/stamp/V2SourceStampSigner.java;l=124;drc=61197364367c9e404c7da6900658f1b16c42d0da;bpv=0;bpt=1
687
688                    let _stamp_block_prefix = le_u32.parse_next(input)?;
689                    let certificate = Self::parse_certificate().parse_next(input)?;
690
691                    // i don't think that it is usefull information
692                    let _signed_digests_data = length_take(le_u32).parse_next(input)?;
693
694                    // i don't think that it is usefull information
695                    let _encoded_stamp_attributes = length_take(le_u32).parse_next(input)?;
696
697                    // i don't think that it is usefull information
698                    let _signed_attributes = length_take(le_u32).parse_next(input)?;
699
700                    // TODO: proper error message
701                    let certificate = self
702                        .get_certificate_info(&certificate)
703                        .map_err(|_| ContextError::new())?;
704
705                    Ok(Signature::StampBlockV2(certificate))
706                }
707                Self::PACKER_NG_SIG_V2 => {
708                    let data = take(size.saturating_sub(4) as usize).parse_next(input)?;
709
710                    Ok(Signature::PackerNextGenV2(data.to_vec()))
711                }
712                Self::GOOGLE_PLAY_FROSTING_ID => {
713                    let _ = take(size.saturating_sub(4) as usize).parse_next(input)?;
714                    Ok(Signature::GooglePlayFrosting)
715                }
716                Self::VASDOLLY_V2 => {
717                    let data = take(size.saturating_sub(4) as usize).parse_next(input)?;
718                    Ok(Signature::VasDollyV2(
719                        String::from_utf8_lossy(data).trim().to_owned(),
720                    ))
721                }
722                Self::VERITY_PADDING_BLOCK_ID
723                | Self::DEPENDENCY_INFO_BLOCK_ID
724                | Self::ZERO_BLOCK_ID => {
725                    // not interesting blocks
726                    let _ = take(size.saturating_sub(4) as usize).parse_next(input)?;
727                    Ok(Signature::Unknown)
728                }
729                _ => {
730                    // highlight new interesting blocks
731                    warn!(
732                        "got unknown id block - 0x{:08x} (size=0x{:08x}), please open issue on github, let's try to figure out",
733                        id, size
734                    );
735
736                    let _ = take(size.saturating_sub(4) as usize).parse_next(input)?;
737
738                    Ok(Signature::Unknown)
739                }
740            }
741        }
742    }
743}