blob: 9e6c41595e012d35ebf1d95c112e29f36350743a [file] [log] [blame]
Alice Wang5d0f89a2022-09-15 15:06:10 +00001/*
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
Alice Wang1ffff622022-09-16 13:45:47 +000019use anyhow::{ensure, Result};
Alice Wang0a293bb2022-09-19 08:41:40 +000020use num_derive::{FromPrimitive, ToPrimitive};
Alice Wang5d0f89a2022-09-15 15:06:10 +000021use openssl::hash::MessageDigest;
Alice Wanga66b5c02022-09-16 07:25:17 +000022use openssl::pkey::{self, PKey};
23use openssl::rsa::Padding;
24use openssl::sign::Verifier;
Alice Wang5d0f89a2022-09-15 15:06:10 +000025use std::cmp::Ordering;
26
27/// [Signature Algorithm IDs]: https://source.android.com/docs/security/apksigning/v2#signature-algorithm-ids
Alice Wang0a293bb2022-09-19 08:41:40 +000028/// [SignatureAlgorithm.java]: (tools/apksig/src/main/java/com/android/apksig/internal/apk/SignatureAlgorithm.java)
Alice Wang5d0f89a2022-09-15 15:06:10 +000029///
30/// Some of the algorithms are not implemented. See b/197052981.
Alice Wang0a293bb2022-09-19 08:41:40 +000031#[derive(Clone, Debug, Eq, FromPrimitive, ToPrimitive)]
Alice Wang5d0f89a2022-09-15 15:06:10 +000032#[repr(u32)]
33pub enum SignatureAlgorithmID {
Alice Wang0a293bb2022-09-19 08:41:40 +000034 /// RSASSA-PSS with SHA2-256 digest, SHA2-256 MGF1, 32 bytes of salt, trailer: 0xbc, content
35 /// digested using SHA2-256 in 1 MB chunks.
Alice Wang5d0f89a2022-09-15 15:06:10 +000036 RsaPssWithSha256 = 0x0101,
Alice Wang0a293bb2022-09-19 08:41:40 +000037
38 /// RSASSA-PSS with SHA2-512 digest, SHA2-512 MGF1, 64 bytes of salt, trailer: 0xbc, content
39 /// digested using SHA2-512 in 1 MB chunks.
Alice Wang5d0f89a2022-09-15 15:06:10 +000040 RsaPssWithSha512 = 0x0102,
Alice Wang0a293bb2022-09-19 08:41:40 +000041
42 /// RSASSA-PKCS1-v1_5 with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks.
Alice Wang5d0f89a2022-09-15 15:06:10 +000043 RsaPkcs1V15WithSha256 = 0x0103,
Alice Wang0a293bb2022-09-19 08:41:40 +000044
45 /// RSASSA-PKCS1-v1_5 with SHA2-512 digest, content digested using SHA2-512 in 1 MB chunks.
Alice Wang5d0f89a2022-09-15 15:06:10 +000046 RsaPkcs1V15WithSha512 = 0x0104,
Alice Wang0a293bb2022-09-19 08:41:40 +000047
48 /// ECDSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks.
Alice Wang5d0f89a2022-09-15 15:06:10 +000049 EcdsaWithSha256 = 0x0201,
Alice Wang0a293bb2022-09-19 08:41:40 +000050
51 /// ECDSA with SHA2-512 digest, content digested using SHA2-512 in 1 MB chunks.
Alice Wang5d0f89a2022-09-15 15:06:10 +000052 EcdsaWithSha512 = 0x0202,
Alice Wang0a293bb2022-09-19 08:41:40 +000053
54 /// DSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks.
55 /// Signing is done deterministically according to RFC 6979.
Alice Wang5d0f89a2022-09-15 15:06:10 +000056 DsaWithSha256 = 0x0301,
Alice Wang0a293bb2022-09-19 08:41:40 +000057
58 /// RSASSA-PKCS1-v1_5 with SHA2-256 digest, content digested using SHA2-256 in 4 KB
59 /// chunks, in the same way fsverity operates. This digest and the content length
60 /// (before digestion, 8 bytes in little endian) construct the final digest.
Alice Wang5d0f89a2022-09-15 15:06:10 +000061 VerityRsaPkcs1V15WithSha256 = 0x0421,
Alice Wang0a293bb2022-09-19 08:41:40 +000062
63 /// ECDSA with SHA2-256 digest, content digested using SHA2-256 in 4 KB chunks, in the
64 /// same way fsverity operates. This digest and the content length (before digestion,
65 /// 8 bytes in little endian) construct the final digest.
Alice Wang5d0f89a2022-09-15 15:06:10 +000066 VerityEcdsaWithSha256 = 0x0423,
Alice Wang0a293bb2022-09-19 08:41:40 +000067
68 /// DSA with SHA2-256 digest, content digested using SHA2-256 in 4 KB chunks, in the
69 /// same way fsverity operates. This digest and the content length (before digestion,
70 /// 8 bytes in little endian) construct the final digest.
Alice Wang5d0f89a2022-09-15 15:06:10 +000071 VerityDsaWithSha256 = 0x0425,
72}
73
Alice Wang0a293bb2022-09-19 08:41:40 +000074impl Default for SignatureAlgorithmID {
75 fn default() -> Self {
76 SignatureAlgorithmID::DsaWithSha256
77 }
78}
79
Alice Wang5d0f89a2022-09-15 15:06:10 +000080impl Ord for SignatureAlgorithmID {
81 /// Ranks the signature algorithm according to the corresponding content
82 /// digest algorithm's rank.
83 fn cmp(&self, other: &Self) -> Ordering {
84 self.to_content_digest_algorithm().cmp(&other.to_content_digest_algorithm())
85 }
86}
87
88impl PartialOrd for SignatureAlgorithmID {
89 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
90 Some(self.cmp(other))
91 }
92}
93
94impl PartialEq for SignatureAlgorithmID {
95 fn eq(&self, other: &Self) -> bool {
96 self.cmp(other) == Ordering::Equal
97 }
98}
99
100impl SignatureAlgorithmID {
Alice Wanga66b5c02022-09-16 07:25:17 +0000101 pub(crate) fn new_verifier<'a>(
102 &self,
103 public_key: &'a PKey<pkey::Public>,
104 ) -> Result<Verifier<'a>> {
105 ensure!(
106 !matches!(
107 self,
Alice Wang455b02b2022-09-16 13:32:13 +0000108 SignatureAlgorithmID::DsaWithSha256 | SignatureAlgorithmID::VerityDsaWithSha256
Alice Wanga66b5c02022-09-16 07:25:17 +0000109 ),
110 "TODO(b/197052981): Algorithm '{:#?}' is not implemented.",
111 self
112 );
113 ensure!(public_key.id() == self.pkey_id(), "Public key has the wrong ID");
114 let mut verifier = Verifier::new(self.new_message_digest(), public_key)?;
115 if public_key.id() == pkey::Id::RSA {
116 verifier.set_rsa_padding(self.rsa_padding())?;
117 }
118 Ok(verifier)
119 }
120
121 /// Returns the message digest corresponding to the signature algorithm
122 /// according to the spec [Signature Algorithm IDs].
Alice Wang1ffff622022-09-16 13:45:47 +0000123 pub(crate) fn new_message_digest(&self) -> MessageDigest {
Alice Wanga66b5c02022-09-16 07:25:17 +0000124 match self {
125 SignatureAlgorithmID::RsaPssWithSha256
126 | SignatureAlgorithmID::RsaPkcs1V15WithSha256
127 | SignatureAlgorithmID::EcdsaWithSha256
128 | SignatureAlgorithmID::DsaWithSha256
129 | SignatureAlgorithmID::VerityRsaPkcs1V15WithSha256
130 | SignatureAlgorithmID::VerityEcdsaWithSha256
131 | SignatureAlgorithmID::VerityDsaWithSha256 => MessageDigest::sha256(),
132 SignatureAlgorithmID::RsaPssWithSha512
133 | SignatureAlgorithmID::RsaPkcs1V15WithSha512
134 | SignatureAlgorithmID::EcdsaWithSha512 => MessageDigest::sha512(),
135 }
136 }
137
138 fn pkey_id(&self) -> pkey::Id {
139 match self {
140 SignatureAlgorithmID::RsaPssWithSha256
141 | SignatureAlgorithmID::RsaPssWithSha512
142 | SignatureAlgorithmID::RsaPkcs1V15WithSha256
143 | SignatureAlgorithmID::RsaPkcs1V15WithSha512
144 | SignatureAlgorithmID::VerityRsaPkcs1V15WithSha256 => pkey::Id::RSA,
145 SignatureAlgorithmID::EcdsaWithSha256
146 | SignatureAlgorithmID::EcdsaWithSha512
147 | SignatureAlgorithmID::VerityEcdsaWithSha256 => pkey::Id::EC,
148 SignatureAlgorithmID::DsaWithSha256 | SignatureAlgorithmID::VerityDsaWithSha256 => {
149 pkey::Id::DSA
150 }
151 }
152 }
153
154 fn rsa_padding(&self) -> Padding {
155 match self {
156 SignatureAlgorithmID::RsaPssWithSha256 | SignatureAlgorithmID::RsaPssWithSha512 => {
157 Padding::PKCS1_PSS
158 }
159 SignatureAlgorithmID::RsaPkcs1V15WithSha256
160 | SignatureAlgorithmID::VerityRsaPkcs1V15WithSha256
161 | SignatureAlgorithmID::RsaPkcs1V15WithSha512 => Padding::PKCS1,
162 SignatureAlgorithmID::EcdsaWithSha256
163 | SignatureAlgorithmID::EcdsaWithSha512
164 | SignatureAlgorithmID::VerityEcdsaWithSha256
165 | SignatureAlgorithmID::DsaWithSha256
166 | SignatureAlgorithmID::VerityDsaWithSha256 => Padding::NONE,
167 }
168 }
Alice Wang1ffff622022-09-16 13:45:47 +0000169
170 fn to_content_digest_algorithm(&self) -> ContentDigestAlgorithm {
171 match self {
172 SignatureAlgorithmID::RsaPssWithSha256
173 | SignatureAlgorithmID::RsaPkcs1V15WithSha256
174 | SignatureAlgorithmID::EcdsaWithSha256
175 | SignatureAlgorithmID::DsaWithSha256 => ContentDigestAlgorithm::ChunkedSha256,
176 SignatureAlgorithmID::RsaPssWithSha512
177 | SignatureAlgorithmID::RsaPkcs1V15WithSha512
178 | SignatureAlgorithmID::EcdsaWithSha512 => ContentDigestAlgorithm::ChunkedSha512,
179 SignatureAlgorithmID::VerityRsaPkcs1V15WithSha256
180 | SignatureAlgorithmID::VerityEcdsaWithSha256
181 | SignatureAlgorithmID::VerityDsaWithSha256 => {
182 ContentDigestAlgorithm::VerityChunkedSha256
183 }
184 }
185 }
Alice Wang5d0f89a2022-09-15 15:06:10 +0000186}
187
188/// The rank of the content digest algorithm in this enum is used to help pick
189/// v4 apk digest.
190/// According to APK Signature Scheme v4, [apk digest] is the first available
191/// content digest of the highest rank (rank N).
192///
193/// This rank was also used for step 3a of the v3 signature verification.
194///
195/// [apk digest]: https://source.android.com/docs/security/features/apksigning/v4#apk-digest
196/// [v3 verification]: https://source.android.com/docs/security/apksigning/v3#v3-verification
197#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
Alice Wang1ffff622022-09-16 13:45:47 +0000198enum ContentDigestAlgorithm {
Alice Wang5d0f89a2022-09-15 15:06:10 +0000199 ChunkedSha256 = 1,
200 VerityChunkedSha256,
201 ChunkedSha512,
202}