blob: 6f4603d75263796c088efc1e168ce500c298e11b [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
Jiyong Parkbde94ab2021-08-11 18:32:01 +090025/// `V4Signature` provides access to the various fields in an idsig file.
Jiyong Park86c9b082021-06-04 19:03:48 +090026#[derive(Debug)]
27pub struct V4Signature {
Jiyong Parkbde94ab2021-08-11 18:32:01 +090028 /// Version of the header. Should be 2.
Jiyong Park86c9b082021-06-04 19:03:48 +090029 pub version: Version,
Jiyong Parkbde94ab2021-08-11 18:32:01 +090030 /// Provides access to the information about how the APK is hashed.
Jiyong Park86c9b082021-06-04 19:03:48 +090031 pub hashing_info: HashingInfo,
Jiyong Parkbde94ab2021-08-11 18:32:01 +090032 /// Provides access to the information that can be used to verify this file
Jiyong Park86c9b082021-06-04 19:03:48 +090033 pub signing_info: SigningInfo,
Jiyong Parkbde94ab2021-08-11 18:32:01 +090034 /// Total size of the merkle tree
Jiyong Park86c9b082021-06-04 19:03:48 +090035 pub merkle_tree_size: u32,
Jiyong Parkbde94ab2021-08-11 18:32:01 +090036 /// Offset of the merkle tree in the idsig file
Jiyong Park86c9b082021-06-04 19:03:48 +090037 pub merkle_tree_offset: u64,
38}
39
Jiyong Parkbde94ab2021-08-11 18:32:01 +090040/// `HashingInfo` provides information about how the APK is hashed.
Jiyong Park86c9b082021-06-04 19:03:48 +090041#[derive(Debug)]
42pub struct HashingInfo {
Jiyong Parkbde94ab2021-08-11 18:32:01 +090043 /// Hash algorithm used when creating the merkle tree for the APK.
Jiyong Park86c9b082021-06-04 19:03:48 +090044 pub hash_algorithm: HashAlgorithm,
Jiyong Parkbde94ab2021-08-11 18:32:01 +090045 /// The log size of a block used when creating the merkle tree. 12 if 4k block was used.
Jiyong Park86c9b082021-06-04 19:03:48 +090046 pub log2_blocksize: u8,
Jiyong Parkbde94ab2021-08-11 18:32:01 +090047 /// The salt used when creating the merkle tree. 32 bytes max.
Jiyong Park86c9b082021-06-04 19:03:48 +090048 pub salt: Box<[u8]>,
Jiyong Parkbde94ab2021-08-11 18:32:01 +090049 /// The root hash of the merkle tree created.
Jiyong Park86c9b082021-06-04 19:03:48 +090050 pub raw_root_hash: Box<[u8]>,
51}
52
Jiyong Parkbde94ab2021-08-11 18:32:01 +090053/// `SigningInfo` provides information that can be used to verify the idsig file.
Jiyong Park86c9b082021-06-04 19:03:48 +090054#[derive(Debug)]
55pub struct SigningInfo {
Jiyong Parkbde94ab2021-08-11 18:32:01 +090056 /// Digest of the APK that this idsig file is for.
Jiyong Park86c9b082021-06-04 19:03:48 +090057 pub apk_digest: Box<[u8]>,
Jiyong Parkbde94ab2021-08-11 18:32:01 +090058 /// Certificate of the signer that signed this idsig file. ASN.1 DER form.
Jiyong Park86c9b082021-06-04 19:03:48 +090059 pub x509_certificate: Box<[u8]>,
Jiyong Parkbde94ab2021-08-11 18:32:01 +090060 /// A free-form binary data
Jiyong Park86c9b082021-06-04 19:03:48 +090061 pub additional_data: Box<[u8]>,
Jiyong Parkbde94ab2021-08-11 18:32:01 +090062 /// Public key of the signer in ASN.1 DER form. This must match the `x509_certificate` field.
Jiyong Park86c9b082021-06-04 19:03:48 +090063 pub public_key: Box<[u8]>,
Jiyong Parkbde94ab2021-08-11 18:32:01 +090064 /// Signature algorithm used to sign this file.
Jiyong Park86c9b082021-06-04 19:03:48 +090065 pub signature_algorithm_id: SignatureAlgorithmId,
Jiyong Parkbde94ab2021-08-11 18:32:01 +090066 /// The signature of this file.
Jiyong Park86c9b082021-06-04 19:03:48 +090067 pub signature: Box<[u8]>,
68}
69
Jiyong Parkbde94ab2021-08-11 18:32:01 +090070/// Version of the idsig file format
Jiyong Park86c9b082021-06-04 19:03:48 +090071#[derive(Debug, PartialEq, FromPrimitive)]
72#[repr(u32)]
73pub enum Version {
Jiyong Parkbde94ab2021-08-11 18:32:01 +090074 /// Version 2, the only supported version.
Jiyong Park86c9b082021-06-04 19:03:48 +090075 V2 = 2,
76}
77
78impl Version {
79 fn from(val: u32) -> Result<Version> {
Jiyong Park99a35b82021-06-07 10:13:44 +090080 Self::from_u32(val).ok_or_else(|| anyhow!("{} is an unsupported version", val))
Jiyong Park86c9b082021-06-04 19:03:48 +090081 }
82}
83
Jiyong Parkbde94ab2021-08-11 18:32:01 +090084/// Hash algorithm that can be used for idsig file.
Jiyong Park86c9b082021-06-04 19:03:48 +090085#[derive(Debug, PartialEq, FromPrimitive)]
86#[repr(u32)]
87pub enum HashAlgorithm {
Jiyong Parkbde94ab2021-08-11 18:32:01 +090088 /// SHA2-256
Jiyong Park86c9b082021-06-04 19:03:48 +090089 SHA256 = 1,
90}
91
92impl HashAlgorithm {
93 fn from(val: u32) -> Result<HashAlgorithm> {
Jiyong Park99a35b82021-06-07 10:13:44 +090094 Self::from_u32(val).ok_or_else(|| anyhow!("{} is an unsupported hash algorithm", val))
Jiyong Park86c9b082021-06-04 19:03:48 +090095 }
96}
97
Jiyong Parkbde94ab2021-08-11 18:32:01 +090098/// Signature algorithm that can be used for idsig file
Jiyong Park86c9b082021-06-04 19:03:48 +090099#[derive(Debug, PartialEq, FromPrimitive)]
100#[allow(non_camel_case_types)]
101#[repr(u32)]
102pub enum SignatureAlgorithmId {
Jiyong Parkbde94ab2021-08-11 18:32:01 +0900103 /// RSASSA-PSS with SHA2-256 digest, SHA2-256 MGF1, 32 bytes of salt, trailer: 0xbc
Jiyong Park86c9b082021-06-04 19:03:48 +0900104 RSASSA_PSS_SHA2_256 = 0x0101,
Jiyong Parkbde94ab2021-08-11 18:32:01 +0900105 /// RSASSA-PSS with SHA2-512 digest, SHA2-512 MGF1, 64 bytes of salt, trailer: 0xbc
Jiyong Park86c9b082021-06-04 19:03:48 +0900106 RSASSA_PSS_SHA2_512 = 0x0102,
Jiyong Parkbde94ab2021-08-11 18:32:01 +0900107 /// RSASSA-PKCS1-v1_5 with SHA2-256 digest.
Jiyong Park86c9b082021-06-04 19:03:48 +0900108 RSASSA_PKCS1_SHA2_256 = 0x0103,
Jiyong Parkbde94ab2021-08-11 18:32:01 +0900109 /// RSASSA-PKCS1-v1_5 with SHA2-512 digest.
Jiyong Park86c9b082021-06-04 19:03:48 +0900110 RSASSA_PKCS1_SHA2_512 = 0x0104,
Jiyong Parkbde94ab2021-08-11 18:32:01 +0900111 /// ECDSA with SHA2-256 digest.
Jiyong Park86c9b082021-06-04 19:03:48 +0900112 ECDSA_SHA2_256 = 0x0201,
Jiyong Parkbde94ab2021-08-11 18:32:01 +0900113 /// ECDSA with SHA2-512 digest.
Jiyong Park86c9b082021-06-04 19:03:48 +0900114 ECDSA_SHA2_512 = 0x0202,
Jiyong Parkbde94ab2021-08-11 18:32:01 +0900115 /// DSA with SHA2-256 digest
Jiyong Park86c9b082021-06-04 19:03:48 +0900116 DSA_SHA2_256 = 0x0301,
117}
118
119impl SignatureAlgorithmId {
120 fn from(val: u32) -> Result<SignatureAlgorithmId> {
121 Self::from_u32(val)
122 .with_context(|| format!("{:#06x} is an unsupported signature algorithm", val))
123 }
124}
125
126impl V4Signature {
127 /// Reads a stream from `r` and then parses it into a `V4Signature` struct.
128 pub fn from<T: Read + Seek>(mut r: T) -> Result<V4Signature> {
129 Ok(V4Signature {
130 version: Version::from(read_le_u32(&mut r)?)?,
131 hashing_info: HashingInfo::from(&mut r)?,
132 signing_info: SigningInfo::from(&mut r)?,
133 merkle_tree_size: read_le_u32(&mut r)?,
134 merkle_tree_offset: r.stream_position()?,
135 })
136 }
137}
138
139impl HashingInfo {
140 fn from(mut r: &mut dyn Read) -> Result<HashingInfo> {
141 read_le_u32(&mut r)?;
142 Ok(HashingInfo {
143 hash_algorithm: HashAlgorithm::from(read_le_u32(&mut r)?)?,
144 log2_blocksize: read_u8(&mut r)?,
145 salt: read_sized_array(&mut r)?,
146 raw_root_hash: read_sized_array(&mut r)?,
147 })
148 }
149}
150
151impl SigningInfo {
152 fn from(mut r: &mut dyn Read) -> Result<SigningInfo> {
153 read_le_u32(&mut r)?;
154 Ok(SigningInfo {
155 apk_digest: read_sized_array(&mut r)?,
156 x509_certificate: read_sized_array(&mut r)?,
157 additional_data: read_sized_array(&mut r)?,
158 public_key: read_sized_array(&mut r)?,
159 signature_algorithm_id: SignatureAlgorithmId::from(read_le_u32(&mut r)?)?,
160 signature: read_sized_array(&mut r)?,
161 })
162 }
163}
164
165fn read_u8(r: &mut dyn Read) -> Result<u8> {
166 let mut byte = [0; 1];
167 r.read_exact(&mut byte)?;
168 Ok(byte[0])
169}
170
171fn read_le_u32(r: &mut dyn Read) -> Result<u32> {
172 let mut bytes = [0; 4];
173 r.read_exact(&mut bytes)?;
174 Ok(u32::from_le_bytes(bytes))
175}
176
177fn read_sized_array(r: &mut dyn Read) -> Result<Box<[u8]>> {
178 let size = read_le_u32(r)?;
179 let mut data = vec![0; size as usize];
180 r.read_exact(&mut data)?;
181 Ok(data.into_boxed_slice())
182}
183
184#[cfg(test)]
185mod tests {
Jiyong Parkbde94ab2021-08-11 18:32:01 +0900186 use crate::apksigv4::*;
Jiyong Park86c9b082021-06-04 19:03:48 +0900187 use std::io::Cursor;
188
Jiyong Parkbde94ab2021-08-11 18:32:01 +0900189 fn hexstring_from(s: &[u8]) -> String {
190 s.iter().map(|byte| format!("{:02x}", byte)).reduce(|i, j| i + &j).unwrap_or_default()
191 }
192
Jiyong Park86c9b082021-06-04 19:03:48 +0900193 #[test]
194 fn parse_idsig_file() {
195 let idsig = Cursor::new(include_bytes!("../testdata/test.apk.idsig"));
196 let parsed = V4Signature::from(idsig).unwrap();
197
198 assert_eq!(Version::V2, parsed.version);
199
200 let hi = parsed.hashing_info;
201 assert_eq!(HashAlgorithm::SHA256, hi.hash_algorithm);
202 assert_eq!(12, hi.log2_blocksize);
203 assert_eq!("", hexstring_from(hi.salt.as_ref()));
204 assert_eq!(
205 "ce1194fdb3cb2537daf0ac8cdf4926754adcbce5abeece7945fe25d204a0df6a",
206 hexstring_from(hi.raw_root_hash.as_ref())
207 );
208
209 let si = parsed.signing_info;
210 assert_eq!(
211 "b5225523a813fb84ed599dd649698c080bcfed4fb19ddb00283a662a2683bc15",
212 hexstring_from(si.apk_digest.as_ref())
213 );
214 assert_eq!("", hexstring_from(si.additional_data.as_ref()));
215 assert_eq!(
216 "303d021c77304d0f4732a90372bbfce095223e4ba82427ceb381f69bc6762d78021d008b99924\
217 a8585c38d7f654835eb219ae9e176b44e86dcb23153e3d9d6",
218 hexstring_from(si.signature.as_ref())
219 );
220 assert_eq!(SignatureAlgorithmId::DSA_SHA2_256, si.signature_algorithm_id);
221
222 assert_eq!(36864, parsed.merkle_tree_size);
223 assert_eq!(2251, parsed.merkle_tree_offset);
224 }
225}