Skip to main content

apk/sign/
block.rs

1//! The APK signing block and the length-prefixed records inside it.
2//!
3//! ```text
4//! u64  size of the block, excluding this field
5//! repeated: u64 length, u32 ID, value
6//! u64  size of the block (again)
7//! "APK Sig Block 42"
8//! ```
9//!
10//! Signature scheme blocks nest `u32`-length-prefixed records. [`Prefixed`],
11//! [`Seq`], and [`Blob`] express that nesting declaratively for `binrw`,
12//! with every length bounded by its enclosing record.
13
14use std::fmt;
15use std::io::{Cursor, Read, Seek, SeekFrom, Write};
16
17use binrw::io::TakeSeekExt;
18use binrw::{BinRead, BinResult, BinWrite, Endian};
19
20use super::VerifyError;
21use crate::archive::SIGNING_BLOCK_MAGIC;
22
23/// The ID of an APK signing block entry.
24#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
25pub struct BlockId(pub u32);
26
27impl BlockId {
28    /// APK Signature Scheme v2.
29    pub const V2: Self = Self(0x7109_871a);
30    /// APK Signature Scheme v3.
31    pub const V3: Self = Self(0xf053_68c0);
32    /// APK Signature Scheme v3.1 (key rotation targeting newer platforms).
33    pub const V3_1: Self = Self(0x1b93_ad61);
34    /// Padding that aligns the block for fs-verity.
35    pub const VERITY_PADDING: Self = Self(0x4272_6577);
36    /// Google Play dependency metadata.
37    pub const DEPENDENCY_INFO: Self = Self(0x504b_4453);
38    /// APK source stamp (v2).
39    pub const SOURCE_STAMP: Self = Self(0x6dff_800d);
40    /// APK source stamp (v1).
41    pub const SOURCE_STAMP_V1: Self = Self(0x2b09_189e);
42
43    /// Whether this entry holds signatures or padding tied to the current
44    /// contents, which re-signing must replace rather than keep.
45    pub fn is_signature_data(self) -> bool {
46        [Self::V2, Self::V3, Self::V3_1, Self::VERITY_PADDING, Self::SOURCE_STAMP, Self::SOURCE_STAMP_V1]
47            .contains(&self)
48    }
49}
50
51impl fmt::Debug for BlockId {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        write!(f, "BlockId({:#010x})", self.0)
54    }
55}
56
57/// The ID-value pairs of an APK signing block.
58#[derive(Clone, Debug, Default, Eq, PartialEq)]
59pub struct SigningBlock {
60    pub pairs: Vec<(BlockId, Vec<u8>)>,
61}
62
63/// Fixed bytes around the pairs: leading size, trailing size, and magic.
64const FRAME: usize = 8 + 8 + 16;
65
66impl SigningBlock {
67    /// Parses a complete signing block, as located by
68    /// [`ApkLayout::signing_block`](crate::archive::ApkLayout::signing_block).
69    /// Both size fields must equal the block's length after the first.
70    pub fn parse(block: &[u8]) -> Result<Self, VerifyError> {
71        let len = block.len();
72        if len < FRAME || block[len - 16..] != SIGNING_BLOCK_MAGIC {
73            return Err(VerifyError::MalformedBlock);
74        }
75        let size = |at: usize| u64::from_le_bytes(block[at..at + 8].try_into().expect("eight bytes"));
76        if size(0) != (len - 8) as u64 || size(len - 24) != (len - 8) as u64 {
77            return Err(VerifyError::MalformedBlock);
78        }
79        let mut body = &block[8..len - 24];
80        let mut offset = 8u64;
81        let mut pairs = Vec::new();
82        while !body.is_empty() {
83            let invalid = |len| VerifyError::InvalidBlockPair { offset, len };
84            let len_bytes: [u8; 8] = body.get(..8).ok_or(invalid(0))?.try_into().expect("eight bytes");
85            let len = u64::from_le_bytes(len_bytes);
86            let value_end = usize::try_from(len)
87                .ok()
88                .and_then(|len| len.checked_add(8))
89                .filter(|&end| len >= 4 && end <= body.len());
90            let end = value_end.ok_or(invalid(len))?;
91            let id = BlockId(u32::from_le_bytes(body[8..12].try_into().expect("four bytes")));
92            pairs.push((id, body[12..end].to_vec()));
93            body = &body[end..];
94            offset += end as u64;
95        }
96        Ok(Self { pairs })
97    }
98
99    /// The value of the first pair with `id`.
100    pub fn get(&self, id: BlockId) -> Option<&[u8]> {
101        self.pairs.iter().find(|(i, _)| *i == id).map(|(_, v)| v.as_slice())
102    }
103
104    /// Encodes the block with its size fields and magic.
105    pub fn encode(&self) -> Vec<u8> {
106        let pairs_len: usize = self.pairs.iter().map(|(_, v)| 12 + v.len()).sum();
107        let size = (pairs_len + 8 + 16) as u64;
108        let mut out = Vec::with_capacity(pairs_len + FRAME);
109        out.extend_from_slice(&size.to_le_bytes());
110        for (id, value) in &self.pairs {
111            out.extend_from_slice(&(value.len() as u64 + 4).to_le_bytes());
112            out.extend_from_slice(&id.0.to_le_bytes());
113            out.extend_from_slice(value);
114        }
115        out.extend_from_slice(&size.to_le_bytes());
116        out.extend_from_slice(&SIGNING_BLOCK_MAGIC);
117        out
118    }
119}
120
121/// A `u32` little-endian length followed by exactly that many bytes holding a `T`.
122#[derive(Clone, Debug, Default, Eq, PartialEq)]
123pub(crate) struct Prefixed<T>(pub T);
124
125impl<T> BinRead for Prefixed<T>
126where
127    T: BinRead,
128{
129    type Args<'a> = T::Args<'a>;
130
131    fn read_options<R: Read + Seek>(reader: &mut R, endian: Endian, args: Self::Args<'_>) -> BinResult<Self> {
132        let len = u32::read_options(reader, endian, ())?;
133        let start = reader.stream_position()?;
134        let mut bounded = reader.take_seek(u64::from(len));
135        let value = T::read_options(&mut bounded, endian, args)?;
136        if bounded.limit() != 0 {
137            return Err(binrw::Error::AssertFail { pos: start, message: "record has trailing bytes".into() });
138        }
139        Ok(Self(value))
140    }
141}
142
143impl<T> BinWrite for Prefixed<T>
144where
145    T: BinWrite,
146{
147    type Args<'a> = T::Args<'a>;
148
149    fn write_options<W: Write + Seek>(&self, writer: &mut W, endian: Endian, args: Self::Args<'_>) -> BinResult<()> {
150        let mut buffer = Cursor::new(Vec::new());
151        self.0.write_options(&mut buffer, endian, args)?;
152        let bytes = buffer.into_inner();
153        let len = u32::try_from(bytes.len())
154            .map_err(|_| binrw::Error::AssertFail { pos: 0, message: "record exceeds 4 GiB".into() })?;
155        len.write_options(writer, endian, ())?;
156        writer.write_all(&bytes)?;
157        Ok(())
158    }
159}
160
161/// Records repeated until the enclosing [`Prefixed`] region ends.
162#[derive(Clone, Debug, Default, Eq, PartialEq)]
163pub(crate) struct Seq<T>(pub Vec<T>);
164
165impl<T> BinRead for Seq<T>
166where
167    T: BinRead,
168    for<'a> T::Args<'a>: Clone,
169{
170    type Args<'a> = T::Args<'a>;
171
172    fn read_options<R: Read + Seek>(reader: &mut R, endian: Endian, args: Self::Args<'_>) -> BinResult<Self> {
173        let start = reader.stream_position()?;
174        let end = reader.seek(SeekFrom::End(0))?;
175        reader.seek(SeekFrom::Start(start))?;
176        let mut items = Vec::new();
177        while reader.stream_position()? < end {
178            items.push(T::read_options(reader, endian, args.clone())?);
179        }
180        Ok(Self(items))
181    }
182}
183
184impl<T> BinWrite for Seq<T>
185where
186    T: BinWrite,
187    for<'a> T::Args<'a>: Clone,
188{
189    type Args<'a> = T::Args<'a>;
190
191    fn write_options<W: Write + Seek>(&self, writer: &mut W, endian: Endian, args: Self::Args<'_>) -> BinResult<()> {
192        self.0.iter().try_for_each(|item| item.write_options(writer, endian, args.clone()))
193    }
194}
195
196/// The remaining bytes of the enclosing [`Prefixed`] region.
197#[derive(Clone, Default, Eq, PartialEq)]
198pub(crate) struct Blob(pub Vec<u8>);
199
200impl fmt::Debug for Blob {
201    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202        write!(f, "Blob({} bytes)", self.0.len())
203    }
204}
205
206impl BinRead for Blob {
207    type Args<'a> = ();
208
209    fn read_options<R: Read + Seek>(reader: &mut R, _: Endian, (): ()) -> BinResult<Self> {
210        let mut bytes = Vec::new();
211        reader.read_to_end(&mut bytes)?;
212        Ok(Self(bytes))
213    }
214}
215
216impl BinWrite for Blob {
217    type Args<'a> = ();
218
219    fn write_options<W: Write + Seek>(&self, writer: &mut W, _: Endian, (): ()) -> BinResult<()> {
220        writer.write_all(&self.0)?;
221        Ok(())
222    }
223}
224
225/// Reads a `T` that must span all of `bytes`; returns the failing offset otherwise.
226pub(crate) fn read_exact<T>(bytes: &[u8], args: T::Args<'_>) -> Result<T, u64>
227where
228    T: BinRead,
229{
230    let mut cursor = Cursor::new(bytes);
231    let value = T::read_options(&mut cursor, Endian::Little, args).map_err(|error| error_offset(&error))?;
232    if cursor.position() as usize != bytes.len() {
233        return Err(cursor.position());
234    }
235    Ok(value)
236}
237
238/// Reads a `T` from the start of `bytes`, ignoring what follows it; returns
239/// the failing offset otherwise.
240pub(crate) fn read_leading<T>(bytes: &[u8], args: T::Args<'_>) -> Result<T, u64>
241where
242    T: BinRead,
243{
244    T::read_options(&mut Cursor::new(bytes), Endian::Little, args).map_err(|error| error_offset(&error))
245}
246
247/// Encodes a `T` into a new buffer.
248pub(crate) fn write_vec<T>(value: &T, args: T::Args<'_>) -> Vec<u8>
249where
250    T: BinWrite,
251{
252    let mut cursor = Cursor::new(Vec::new());
253    value.write_options(&mut cursor, Endian::Little, args).expect("signature records fit in memory and under 4 GiB");
254    cursor.into_inner()
255}
256
257fn error_offset(error: &binrw::Error) -> u64 {
258    match error {
259        binrw::Error::BadMagic { pos, .. }
260        | binrw::Error::AssertFail { pos, .. }
261        | binrw::Error::Custom { pos, .. }
262        | binrw::Error::NoVariantMatch { pos }
263        | binrw::Error::EnumErrors { pos, .. } => *pos,
264        binrw::Error::Backtrace(backtrace) => error_offset(&backtrace.error),
265        _ => 0,
266    }
267}
268
269#[cfg(test)]
270mod tests {
271    use super::*;
272
273    #[test]
274    fn signing_block_round_trips_and_bounds_pairs() {
275        let block = SigningBlock { pairs: vec![(BlockId::V2, vec![1, 2, 3]), (BlockId::V3, vec![])] };
276        let bytes = block.encode();
277        assert_eq!(SigningBlock::parse(&bytes).unwrap(), block);
278        let mut corrupt = bytes.clone();
279        corrupt[8..16].copy_from_slice(&u64::MAX.to_le_bytes());
280        assert!(matches!(SigningBlock::parse(&corrupt), Err(VerifyError::InvalidBlockPair { offset: 8, .. })));
281        for size_field in [0, bytes.len() - 24] {
282            let mut corrupt = bytes.clone();
283            corrupt[size_field..size_field + 8].copy_from_slice(&u64::MAX.to_le_bytes());
284            assert!(matches!(SigningBlock::parse(&corrupt), Err(VerifyError::MalformedBlock)), "{size_field}");
285        }
286    }
287
288    #[test]
289    fn prefixed_records_must_be_exact() {
290        // A Seq of two prefixed blobs: [len=1][a][len=2][b c]
291        let bytes = [1, 0, 0, 0, b'a', 2, 0, 0, 0, b'b', b'c'];
292        let parsed: Seq<Prefixed<Blob>> = read_exact(&bytes, ()).unwrap();
293        assert_eq!(parsed.0.iter().map(|p| p.0.0.clone()).collect::<Vec<_>>(), [b"a".to_vec(), b"bc".to_vec()]);
294        assert_eq!(write_vec(&parsed, ()), bytes);
295        // A length that runs past the data.
296        assert!(read_exact::<Prefixed<Blob>>(&[9, 0, 0, 0, b'x'], ()).is_err());
297        // A u32 field inside a one-byte region.
298        assert!(read_exact::<Prefixed<u32>>(&[1, 0, 0, 0, 7], ()).is_err());
299        // Trailing bytes inside the region.
300        assert!(read_exact::<Prefixed<u8>>(&[2, 0, 0, 0, 7, 7], ()).is_err());
301    }
302}