• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 //! Algorithms used for APK Signature Scheme.
18 
19 use anyhow::{ensure, Context, Result};
20 use byteorder::{LittleEndian, ReadBytesExt};
21 use bytes::{Buf, Bytes};
22 use num_derive::{FromPrimitive, ToPrimitive};
23 use num_traits::{FromPrimitive, ToPrimitive};
24 use openssl::hash::MessageDigest;
25 use openssl::pkey::{self, PKey};
26 use openssl::rsa::Padding;
27 use openssl::sign::Verifier;
28 use serde::{Deserialize, Serialize};
29 use std::io::Read;
30 
31 use crate::bytes_ext::ReadFromBytes;
32 
33 /// [Signature Algorithm IDs]: https://source.android.com/docs/security/apksigning/v2#signature-algorithm-ids
34 /// [SignatureAlgorithm.java]: (tools/apksig/src/main/java/com/android/apksig/internal/apk/SignatureAlgorithm.java)
35 ///
36 /// Some of the algorithms are not implemented. See b/197052981.
37 #[derive(
38     Serialize, Deserialize, Clone, Copy, Debug, Default, Eq, PartialEq, FromPrimitive, ToPrimitive,
39 )]
40 #[repr(u32)]
41 pub enum SignatureAlgorithmID {
42     /// RSASSA-PSS with SHA2-256 digest, SHA2-256 MGF1, 32 bytes of salt, trailer: 0xbc, content
43     /// digested using SHA2-256 in 1 MB chunks.
44     #[default]
45     RsaPssWithSha256 = 0x0101,
46 
47     /// RSASSA-PSS with SHA2-512 digest, SHA2-512 MGF1, 64 bytes of salt, trailer: 0xbc, content
48     /// digested using SHA2-512 in 1 MB chunks.
49     RsaPssWithSha512 = 0x0102,
50 
51     /// RSASSA-PKCS1-v1_5 with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks.
52     RsaPkcs1V15WithSha256 = 0x0103,
53 
54     /// RSASSA-PKCS1-v1_5 with SHA2-512 digest, content digested using SHA2-512 in 1 MB chunks.
55     RsaPkcs1V15WithSha512 = 0x0104,
56 
57     /// ECDSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks.
58     EcdsaWithSha256 = 0x0201,
59 
60     /// ECDSA with SHA2-512 digest, content digested using SHA2-512 in 1 MB chunks.
61     EcdsaWithSha512 = 0x0202,
62 
63     /// DSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks.
64     /// Signing is done deterministically according to RFC 6979.
65     DsaWithSha256 = 0x0301,
66 
67     /// RSASSA-PKCS1-v1_5 with SHA2-256 digest, content digested using SHA2-256 in 4 KB
68     /// chunks, in the same way fsverity operates. This digest and the content length
69     /// (before digestion, 8 bytes in little endian) construct the final digest.
70     VerityRsaPkcs1V15WithSha256 = 0x0421,
71 
72     /// ECDSA with SHA2-256 digest, content digested using SHA2-256 in 4 KB chunks, in the
73     /// same way fsverity operates. This digest and the content length (before digestion,
74     /// 8 bytes in little endian) construct the final digest.
75     VerityEcdsaWithSha256 = 0x0423,
76 
77     /// DSA with SHA2-256 digest, content digested using SHA2-256 in 4 KB chunks, in the
78     /// same way fsverity operates. This digest and the content length (before digestion,
79     /// 8 bytes in little endian) construct the final digest.
80     VerityDsaWithSha256 = 0x0425,
81 }
82 
83 impl ReadFromBytes for Option<SignatureAlgorithmID> {
read_from_bytes(buf: &mut Bytes) -> Result<Self>84     fn read_from_bytes(buf: &mut Bytes) -> Result<Self> {
85         Ok(SignatureAlgorithmID::from_u32(buf.get_u32_le()))
86     }
87 }
88 
89 impl SignatureAlgorithmID {
90     /// Converts the signature algorithm ID to the corresponding u32.
to_u32(&self) -> u3291     pub fn to_u32(&self) -> u32 {
92         ToPrimitive::to_u32(self).expect("Unsupported algorithm for to_u32.")
93     }
94 
new_verifier<'a>( &self, public_key: &'a PKey<pkey::Public>, ) -> Result<Verifier<'a>>95     pub(crate) fn new_verifier<'a>(
96         &self,
97         public_key: &'a PKey<pkey::Public>,
98     ) -> Result<Verifier<'a>> {
99         ensure!(
100             !matches!(
101                 self,
102                 SignatureAlgorithmID::DsaWithSha256 | SignatureAlgorithmID::VerityDsaWithSha256
103             ),
104             "Algorithm '{:?}' is not supported in openssl to build this verifier (b/197052981).",
105             self
106         );
107         ensure!(public_key.id() == self.pkey_id(), "Public key has the wrong ID");
108         let mut verifier = Verifier::new(self.new_message_digest(), public_key)?;
109         if public_key.id() == pkey::Id::RSA {
110             verifier.set_rsa_padding(self.rsa_padding())?;
111         }
112         Ok(verifier)
113     }
114 
115     /// Returns the message digest corresponding to the signature algorithm
116     /// according to the spec [Signature Algorithm IDs].
new_message_digest(&self) -> MessageDigest117     pub(crate) fn new_message_digest(&self) -> MessageDigest {
118         match self {
119             SignatureAlgorithmID::RsaPssWithSha256
120             | SignatureAlgorithmID::RsaPkcs1V15WithSha256
121             | SignatureAlgorithmID::EcdsaWithSha256
122             | SignatureAlgorithmID::DsaWithSha256
123             | SignatureAlgorithmID::VerityRsaPkcs1V15WithSha256
124             | SignatureAlgorithmID::VerityEcdsaWithSha256
125             | SignatureAlgorithmID::VerityDsaWithSha256 => MessageDigest::sha256(),
126             SignatureAlgorithmID::RsaPssWithSha512
127             | SignatureAlgorithmID::RsaPkcs1V15WithSha512
128             | SignatureAlgorithmID::EcdsaWithSha512 => MessageDigest::sha512(),
129         }
130     }
131 
132     /// DSA is not directly supported in openssl today. See b/197052981.
is_supported(&self) -> bool133     pub(crate) fn is_supported(&self) -> bool {
134         !matches!(
135             self,
136             SignatureAlgorithmID::DsaWithSha256 | SignatureAlgorithmID::VerityDsaWithSha256,
137         )
138     }
139 
pkey_id(&self) -> pkey::Id140     fn pkey_id(&self) -> pkey::Id {
141         match self {
142             SignatureAlgorithmID::RsaPssWithSha256
143             | SignatureAlgorithmID::RsaPssWithSha512
144             | SignatureAlgorithmID::RsaPkcs1V15WithSha256
145             | SignatureAlgorithmID::RsaPkcs1V15WithSha512
146             | SignatureAlgorithmID::VerityRsaPkcs1V15WithSha256 => pkey::Id::RSA,
147             SignatureAlgorithmID::EcdsaWithSha256
148             | SignatureAlgorithmID::EcdsaWithSha512
149             | SignatureAlgorithmID::VerityEcdsaWithSha256 => pkey::Id::EC,
150             SignatureAlgorithmID::DsaWithSha256 | SignatureAlgorithmID::VerityDsaWithSha256 => {
151                 pkey::Id::DSA
152             }
153         }
154     }
155 
rsa_padding(&self) -> Padding156     fn rsa_padding(&self) -> Padding {
157         match self {
158             SignatureAlgorithmID::RsaPssWithSha256 | SignatureAlgorithmID::RsaPssWithSha512 => {
159                 Padding::PKCS1_PSS
160             }
161             SignatureAlgorithmID::RsaPkcs1V15WithSha256
162             | SignatureAlgorithmID::VerityRsaPkcs1V15WithSha256
163             | SignatureAlgorithmID::RsaPkcs1V15WithSha512 => Padding::PKCS1,
164             SignatureAlgorithmID::EcdsaWithSha256
165             | SignatureAlgorithmID::EcdsaWithSha512
166             | SignatureAlgorithmID::VerityEcdsaWithSha256
167             | SignatureAlgorithmID::DsaWithSha256
168             | SignatureAlgorithmID::VerityDsaWithSha256 => Padding::NONE,
169         }
170     }
171 
content_digest_algorithm(&self) -> ContentDigestAlgorithm172     pub(crate) fn content_digest_algorithm(&self) -> ContentDigestAlgorithm {
173         match self {
174             SignatureAlgorithmID::RsaPssWithSha256
175             | SignatureAlgorithmID::RsaPkcs1V15WithSha256
176             | SignatureAlgorithmID::EcdsaWithSha256
177             | SignatureAlgorithmID::DsaWithSha256 => ContentDigestAlgorithm::ChunkedSha256,
178             SignatureAlgorithmID::RsaPssWithSha512
179             | SignatureAlgorithmID::RsaPkcs1V15WithSha512
180             | SignatureAlgorithmID::EcdsaWithSha512 => ContentDigestAlgorithm::ChunkedSha512,
181             SignatureAlgorithmID::VerityRsaPkcs1V15WithSha256
182             | SignatureAlgorithmID::VerityEcdsaWithSha256
183             | SignatureAlgorithmID::VerityDsaWithSha256 => {
184                 ContentDigestAlgorithm::VerityChunkedSha256
185             }
186         }
187     }
188 }
189 
190 /// The rank of the content digest algorithm in this enum is used to help pick
191 /// v4 apk digest.
192 /// According to APK Signature Scheme v4, [apk digest] is the first available
193 /// content digest of the highest rank (rank N).
194 ///
195 /// This rank was also used for step 3a of the v3 signature verification.
196 ///
197 /// [apk digest]: https://source.android.com/docs/security/features/apksigning/v4#apk-digest
198 /// [v3 verification]: https://source.android.com/docs/security/apksigning/v3#v3-verification
199 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
200 pub(crate) enum ContentDigestAlgorithm {
201     ChunkedSha256 = 1,
202     VerityChunkedSha256,
203     ChunkedSha512,
204 }
205 
206 /// Hash algorithms.
207 #[derive(Clone, Copy, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive, Default)]
208 #[repr(u32)]
209 pub enum HashAlgorithm {
210     #[default]
211     /// SHA-256
212     SHA256 = 1,
213 }
214 
215 impl HashAlgorithm {
from_read<R: Read>(read: &mut R) -> Result<Self>216     pub(crate) fn from_read<R: Read>(read: &mut R) -> Result<Self> {
217         let val = read.read_u32::<LittleEndian>()?;
218         Self::from_u32(val).context(format!("Unsupported hash algorithm: {}", val))
219     }
220 }
221