blob: f1ee0a4f2b1307e113a87c5e316a7846bc2cfac4 [file] [log] [blame]
Jiyong Park86c9b082021-06-04 19:03:48 +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
17use anyhow::{anyhow, Context, Result};
18use num_derive::FromPrimitive;
19use num_traits::FromPrimitive;
20use std::io::{Read, Seek};
21
22// `apksigv4` module provides routines to decode the idsig file as defined in [APK signature
23// scheme v4] (https://source.android.com/security/apksigning/v4).
24
25#[derive(Debug)]
26pub struct V4Signature {
27 pub version: Version,
28 pub hashing_info: HashingInfo,
29 pub signing_info: SigningInfo,
30 pub merkle_tree_size: u32,
31 pub merkle_tree_offset: u64,
32}
33
34#[derive(Debug)]
35pub struct HashingInfo {
36 pub hash_algorithm: HashAlgorithm,
37 pub log2_blocksize: u8,
38 pub salt: Box<[u8]>,
39 pub raw_root_hash: Box<[u8]>,
40}
41
42#[derive(Debug)]
43pub struct SigningInfo {
44 pub apk_digest: Box<[u8]>,
45 pub x509_certificate: Box<[u8]>,
46 pub additional_data: Box<[u8]>,
47 pub public_key: Box<[u8]>,
48 pub signature_algorithm_id: SignatureAlgorithmId,
49 pub signature: Box<[u8]>,
50}
51
52#[derive(Debug, PartialEq, FromPrimitive)]
53#[repr(u32)]
54pub enum Version {
55 V2 = 2,
56}
57
58impl Version {
59 fn from(val: u32) -> Result<Version> {
60 Self::from_u32(val).ok_or(anyhow!("{} is an unsupported version", val))
61 }
62}
63
64#[derive(Debug, PartialEq, FromPrimitive)]
65#[repr(u32)]
66pub enum HashAlgorithm {
67 SHA256 = 1,
68}
69
70impl HashAlgorithm {
71 fn from(val: u32) -> Result<HashAlgorithm> {
72 Self::from_u32(val).ok_or(anyhow!("{} is an unsupported hash algorithm", val))
73 }
74}
75
76#[derive(Debug, PartialEq, FromPrimitive)]
77#[allow(non_camel_case_types)]
78#[repr(u32)]
79pub enum SignatureAlgorithmId {
80 RSASSA_PSS_SHA2_256 = 0x0101,
81 RSASSA_PSS_SHA2_512 = 0x0102,
82 RSASSA_PKCS1_SHA2_256 = 0x0103,
83 RSASSA_PKCS1_SHA2_512 = 0x0104,
84 ECDSA_SHA2_256 = 0x0201,
85 ECDSA_SHA2_512 = 0x0202,
86 DSA_SHA2_256 = 0x0301,
87}
88
89impl SignatureAlgorithmId {
90 fn from(val: u32) -> Result<SignatureAlgorithmId> {
91 Self::from_u32(val)
92 .with_context(|| format!("{:#06x} is an unsupported signature algorithm", val))
93 }
94}
95
96impl V4Signature {
97 /// Reads a stream from `r` and then parses it into a `V4Signature` struct.
98 pub fn from<T: Read + Seek>(mut r: T) -> Result<V4Signature> {
99 Ok(V4Signature {
100 version: Version::from(read_le_u32(&mut r)?)?,
101 hashing_info: HashingInfo::from(&mut r)?,
102 signing_info: SigningInfo::from(&mut r)?,
103 merkle_tree_size: read_le_u32(&mut r)?,
104 merkle_tree_offset: r.stream_position()?,
105 })
106 }
107}
108
109impl HashingInfo {
110 fn from(mut r: &mut dyn Read) -> Result<HashingInfo> {
111 read_le_u32(&mut r)?;
112 Ok(HashingInfo {
113 hash_algorithm: HashAlgorithm::from(read_le_u32(&mut r)?)?,
114 log2_blocksize: read_u8(&mut r)?,
115 salt: read_sized_array(&mut r)?,
116 raw_root_hash: read_sized_array(&mut r)?,
117 })
118 }
119}
120
121impl SigningInfo {
122 fn from(mut r: &mut dyn Read) -> Result<SigningInfo> {
123 read_le_u32(&mut r)?;
124 Ok(SigningInfo {
125 apk_digest: read_sized_array(&mut r)?,
126 x509_certificate: read_sized_array(&mut r)?,
127 additional_data: read_sized_array(&mut r)?,
128 public_key: read_sized_array(&mut r)?,
129 signature_algorithm_id: SignatureAlgorithmId::from(read_le_u32(&mut r)?)?,
130 signature: read_sized_array(&mut r)?,
131 })
132 }
133}
134
135fn read_u8(r: &mut dyn Read) -> Result<u8> {
136 let mut byte = [0; 1];
137 r.read_exact(&mut byte)?;
138 Ok(byte[0])
139}
140
141fn read_le_u32(r: &mut dyn Read) -> Result<u32> {
142 let mut bytes = [0; 4];
143 r.read_exact(&mut bytes)?;
144 Ok(u32::from_le_bytes(bytes))
145}
146
147fn read_sized_array(r: &mut dyn Read) -> Result<Box<[u8]>> {
148 let size = read_le_u32(r)?;
149 let mut data = vec![0; size as usize];
150 r.read_exact(&mut data)?;
151 Ok(data.into_boxed_slice())
152}
153
154#[cfg(test)]
155mod tests {
156 use crate::*;
157 use std::io::Cursor;
158
159 fn hexstring_from(s: &[u8]) -> String {
160 s.iter().map(|byte| format!("{:02x}", byte)).reduce(|i, j| i + &j).unwrap_or(String::new())
161 }
162
163 #[test]
164 fn parse_idsig_file() {
165 let idsig = Cursor::new(include_bytes!("../testdata/test.apk.idsig"));
166 let parsed = V4Signature::from(idsig).unwrap();
167
168 assert_eq!(Version::V2, parsed.version);
169
170 let hi = parsed.hashing_info;
171 assert_eq!(HashAlgorithm::SHA256, hi.hash_algorithm);
172 assert_eq!(12, hi.log2_blocksize);
173 assert_eq!("", hexstring_from(hi.salt.as_ref()));
174 assert_eq!(
175 "ce1194fdb3cb2537daf0ac8cdf4926754adcbce5abeece7945fe25d204a0df6a",
176 hexstring_from(hi.raw_root_hash.as_ref())
177 );
178
179 let si = parsed.signing_info;
180 assert_eq!(
181 "b5225523a813fb84ed599dd649698c080bcfed4fb19ddb00283a662a2683bc15",
182 hexstring_from(si.apk_digest.as_ref())
183 );
184 assert_eq!("", hexstring_from(si.additional_data.as_ref()));
185 assert_eq!(
186 "303d021c77304d0f4732a90372bbfce095223e4ba82427ceb381f69bc6762d78021d008b99924\
187 a8585c38d7f654835eb219ae9e176b44e86dcb23153e3d9d6",
188 hexstring_from(si.signature.as_ref())
189 );
190 assert_eq!(SignatureAlgorithmId::DSA_SHA2_256, si.signature_algorithm_id);
191
192 assert_eq!(36864, parsed.merkle_tree_size);
193 assert_eq!(2251, parsed.merkle_tree_offset);
194 }
195}