blob: 9de794a1d7d1742332ffb85496b118796dfc2585 [file] [log] [blame]
Jooyung Han12a0b702021-08-05 23:20:31 +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
17//! Utilities for Signature Verification
18
19use anyhow::{anyhow, bail, Result};
20use byteorder::{LittleEndian, ReadBytesExt};
21use bytes::{Buf, Bytes};
Jooyung Han5d94bfc2021-08-06 14:07:49 +090022use std::io::{Read, Seek, SeekFrom};
23
24use crate::ziputil::zip_sections;
Jooyung Han12a0b702021-08-05 23:20:31 +090025
26const APK_SIG_BLOCK_MIN_SIZE: u32 = 32;
27const APK_SIG_BLOCK_MAGIC: u128 = 0x3234206b636f6c4220676953204b5041;
28
29// TODO(jooyung): introduce type
Jooyung Han5b4c70e2021-08-09 16:36:13 +090030pub const SIGNATURE_RSA_PSS_WITH_SHA256: u32 = 0x0101;
31pub const SIGNATURE_RSA_PSS_WITH_SHA512: u32 = 0x0102;
32pub const SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: u32 = 0x0103;
33pub const SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: u32 = 0x0104;
34pub const SIGNATURE_ECDSA_WITH_SHA256: u32 = 0x0201;
35pub const SIGNATURE_ECDSA_WITH_SHA512: u32 = 0x0202;
36pub const SIGNATURE_DSA_WITH_SHA256: u32 = 0x0301;
37pub const SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: u32 = 0x0421;
38pub const SIGNATURE_VERITY_ECDSA_WITH_SHA256: u32 = 0x0423;
39pub const SIGNATURE_VERITY_DSA_WITH_SHA256: u32 = 0x0425;
Jooyung Han12a0b702021-08-05 23:20:31 +090040
41// TODO(jooyung): introduce type
42const CONTENT_DIGEST_CHUNKED_SHA256: u32 = 1;
43const CONTENT_DIGEST_CHUNKED_SHA512: u32 = 2;
44const CONTENT_DIGEST_VERITY_CHUNKED_SHA256: u32 = 3;
45#[allow(unused)]
46const CONTENT_DIGEST_SHA256: u32 = 4;
47
48pub struct SignatureInfo {
Jooyung Han12a0b702021-08-05 23:20:31 +090049 pub signature_block: Bytes,
Jooyung Han12a0b702021-08-05 23:20:31 +090050}
51
52/// Returns the APK Signature Scheme block contained in the provided file for the given ID
53/// and the additional information relevant for verifying the block against the file.
Jooyung Han5d94bfc2021-08-06 14:07:49 +090054pub fn find_signature<F: Read + Seek>(f: F, block_id: u32) -> Result<SignatureInfo> {
55 let (mut f, sections) = zip_sections(f)?;
56
57 let (signing_block, _signing_block_offset) =
58 find_signing_block(&mut f, sections.central_directory_offset)?;
Jooyung Han12a0b702021-08-05 23:20:31 +090059
60 // TODO(jooyung): propagate NotFound error so that verification can fallback to V2
61 let signature_scheme_block = find_signature_scheme_block(signing_block, block_id)?;
Jooyung Han5d94bfc2021-08-06 14:07:49 +090062 Ok(SignatureInfo { signature_block: signature_scheme_block })
Jooyung Han12a0b702021-08-05 23:20:31 +090063}
64
Jooyung Han5d94bfc2021-08-06 14:07:49 +090065fn find_signing_block<T: Read + Seek>(
Jooyung Han12a0b702021-08-05 23:20:31 +090066 reader: &mut T,
67 central_directory_offset: u32,
68) -> Result<(Bytes, u32)> {
69 // FORMAT:
70 // OFFSET DATA TYPE DESCRIPTION
71 // * @+0 bytes uint64: size in bytes (excluding this field)
72 // * @+8 bytes payload
73 // * @-24 bytes uint64: size in bytes (same as the one above)
74 // * @-16 bytes uint128: magic
75 if central_directory_offset < APK_SIG_BLOCK_MIN_SIZE {
76 bail!(
77 "APK too small for APK Signing Block. ZIP Central Directory offset: {}",
78 central_directory_offset
79 );
80 }
Jooyung Han5d94bfc2021-08-06 14:07:49 +090081 reader.seek(SeekFrom::Start((central_directory_offset - 24) as u64))?;
Jooyung Han12a0b702021-08-05 23:20:31 +090082 let size_in_footer = reader.read_u64::<LittleEndian>()? as u32;
83 if reader.read_u128::<LittleEndian>()? != APK_SIG_BLOCK_MAGIC {
84 bail!("No APK Signing Block before ZIP Central Directory")
85 }
86 let total_size = size_in_footer + 8;
87 let signing_block_offset = central_directory_offset
88 .checked_sub(total_size)
89 .ok_or_else(|| anyhow!("APK Signing Block size out of range: {}", size_in_footer))?;
Jooyung Han5d94bfc2021-08-06 14:07:49 +090090 reader.seek(SeekFrom::Start(signing_block_offset as u64))?;
Jooyung Han12a0b702021-08-05 23:20:31 +090091 let size_in_header = reader.read_u64::<LittleEndian>()? as u32;
92 if size_in_header != size_in_footer {
93 bail!(
94 "APK Signing Block sizes in header and footer do not match: {} vs {}",
95 size_in_header,
96 size_in_footer
97 );
98 }
Jooyung Han5d94bfc2021-08-06 14:07:49 +090099 reader.seek(SeekFrom::Start(signing_block_offset as u64))?;
Jooyung Han12a0b702021-08-05 23:20:31 +0900100 let mut buf = vec![0u8; total_size as usize];
101 reader.read_exact(&mut buf)?;
102 Ok((Bytes::from(buf), signing_block_offset))
103}
104
105fn find_signature_scheme_block(buf: Bytes, block_id: u32) -> Result<Bytes> {
106 // FORMAT:
107 // OFFSET DATA TYPE DESCRIPTION
108 // * @+0 bytes uint64: size in bytes (excluding this field)
109 // * @+8 bytes pairs
110 // * @-24 bytes uint64: size in bytes (same as the one above)
111 // * @-16 bytes uint128: magic
112 let mut pairs = buf.slice(8..(buf.len() - 24));
113 let mut entry_count = 0;
114 while pairs.has_remaining() {
115 entry_count += 1;
116 if pairs.remaining() < 8 {
117 bail!("Insufficient data to read size of APK Signing Block entry #{}", entry_count);
118 }
119 let length = pairs.get_u64_le();
120 let mut pair = pairs.split_to(length as usize);
121 let id = pair.get_u32_le();
122 if id == block_id {
123 return Ok(pair);
124 }
125 }
126 // TODO(jooyung): return NotFound error
127 bail!("No APK Signature Scheme block in APK Signing Block with ID: {}", block_id)
128}
129
130pub fn is_supported_signature_algorithm(algorithm_id: u32) -> bool {
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900131 matches!(
132 algorithm_id,
Jooyung Han12a0b702021-08-05 23:20:31 +0900133 SIGNATURE_RSA_PSS_WITH_SHA256
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900134 | SIGNATURE_RSA_PSS_WITH_SHA512
135 | SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256
136 | SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512
137 | SIGNATURE_ECDSA_WITH_SHA256
138 | SIGNATURE_ECDSA_WITH_SHA512
139 | SIGNATURE_DSA_WITH_SHA256
140 | SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256
141 | SIGNATURE_VERITY_ECDSA_WITH_SHA256
142 | SIGNATURE_VERITY_DSA_WITH_SHA256
143 )
Jooyung Han12a0b702021-08-05 23:20:31 +0900144}
145
146fn to_content_digest_algorithm(algorithm_id: u32) -> Result<u32> {
147 match algorithm_id {
148 SIGNATURE_RSA_PSS_WITH_SHA256
149 | SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256
150 | SIGNATURE_ECDSA_WITH_SHA256
151 | SIGNATURE_DSA_WITH_SHA256 => Ok(CONTENT_DIGEST_CHUNKED_SHA256),
152 SIGNATURE_RSA_PSS_WITH_SHA512
153 | SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512
154 | SIGNATURE_ECDSA_WITH_SHA512 => Ok(CONTENT_DIGEST_CHUNKED_SHA512),
155 SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256
156 | SIGNATURE_VERITY_ECDSA_WITH_SHA256
157 | SIGNATURE_VERITY_DSA_WITH_SHA256 => Ok(CONTENT_DIGEST_VERITY_CHUNKED_SHA256),
158 _ => bail!("Unknown signature algorithm: {}", algorithm_id),
159 }
160}
161
162pub fn rank_signature_algorithm(algo: u32) -> Result<u32> {
163 rank_content_digest_algorithm(to_content_digest_algorithm(algo)?)
164}
165
166fn rank_content_digest_algorithm(id: u32) -> Result<u32> {
167 match id {
168 CONTENT_DIGEST_CHUNKED_SHA256 => Ok(0),
169 CONTENT_DIGEST_VERITY_CHUNKED_SHA256 => Ok(1),
170 CONTENT_DIGEST_CHUNKED_SHA512 => Ok(2),
171 _ => bail!("Unknown digest algorithm: {}", id),
172 }
173}