blob: ee9c869ecbce4c1f48b947e870dd45d24c986457 [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 Wanga66b5c02022-09-16 07:25:17 +000019use anyhow::{bail, ensure, Result};
Alice Wang5d0f89a2022-09-15 15:06:10 +000020use num_derive::FromPrimitive;
21use 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
28///
29/// Some of the algorithms are not implemented. See b/197052981.
30#[derive(Clone, Debug, Eq, FromPrimitive)]
31#[repr(u32)]
32pub enum SignatureAlgorithmID {
33 RsaPssWithSha256 = 0x0101,
34 RsaPssWithSha512 = 0x0102,
35 RsaPkcs1V15WithSha256 = 0x0103,
36 RsaPkcs1V15WithSha512 = 0x0104,
37 EcdsaWithSha256 = 0x0201,
38 EcdsaWithSha512 = 0x0202,
39 DsaWithSha256 = 0x0301,
40 VerityRsaPkcs1V15WithSha256 = 0x0421,
41 VerityEcdsaWithSha256 = 0x0423,
42 VerityDsaWithSha256 = 0x0425,
43}
44
45impl Ord for SignatureAlgorithmID {
46 /// Ranks the signature algorithm according to the corresponding content
47 /// digest algorithm's rank.
48 fn cmp(&self, other: &Self) -> Ordering {
49 self.to_content_digest_algorithm().cmp(&other.to_content_digest_algorithm())
50 }
51}
52
53impl PartialOrd for SignatureAlgorithmID {
54 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
55 Some(self.cmp(other))
56 }
57}
58
59impl PartialEq for SignatureAlgorithmID {
60 fn eq(&self, other: &Self) -> bool {
61 self.cmp(other) == Ordering::Equal
62 }
63}
64
65impl SignatureAlgorithmID {
66 pub(crate) fn to_content_digest_algorithm(&self) -> ContentDigestAlgorithm {
67 match self {
68 SignatureAlgorithmID::RsaPssWithSha256
69 | SignatureAlgorithmID::RsaPkcs1V15WithSha256
70 | SignatureAlgorithmID::EcdsaWithSha256
71 | SignatureAlgorithmID::DsaWithSha256 => ContentDigestAlgorithm::ChunkedSha256,
72 SignatureAlgorithmID::RsaPssWithSha512
73 | SignatureAlgorithmID::RsaPkcs1V15WithSha512
74 | SignatureAlgorithmID::EcdsaWithSha512 => ContentDigestAlgorithm::ChunkedSha512,
75 SignatureAlgorithmID::VerityRsaPkcs1V15WithSha256
76 | SignatureAlgorithmID::VerityEcdsaWithSha256
77 | SignatureAlgorithmID::VerityDsaWithSha256 => {
78 ContentDigestAlgorithm::VerityChunkedSha256
79 }
80 }
81 }
Alice Wanga66b5c02022-09-16 07:25:17 +000082
83 pub(crate) fn new_verifier<'a>(
84 &self,
85 public_key: &'a PKey<pkey::Public>,
86 ) -> Result<Verifier<'a>> {
87 ensure!(
88 !matches!(
89 self,
90 SignatureAlgorithmID::EcdsaWithSha512
91 | SignatureAlgorithmID::DsaWithSha256
92 | SignatureAlgorithmID::VerityDsaWithSha256
93 ),
94 "TODO(b/197052981): Algorithm '{:#?}' is not implemented.",
95 self
96 );
97 ensure!(public_key.id() == self.pkey_id(), "Public key has the wrong ID");
98 let mut verifier = Verifier::new(self.new_message_digest(), public_key)?;
99 if public_key.id() == pkey::Id::RSA {
100 verifier.set_rsa_padding(self.rsa_padding())?;
101 }
102 Ok(verifier)
103 }
104
105 /// Returns the message digest corresponding to the signature algorithm
106 /// according to the spec [Signature Algorithm IDs].
107 fn new_message_digest(&self) -> MessageDigest {
108 match self {
109 SignatureAlgorithmID::RsaPssWithSha256
110 | SignatureAlgorithmID::RsaPkcs1V15WithSha256
111 | SignatureAlgorithmID::EcdsaWithSha256
112 | SignatureAlgorithmID::DsaWithSha256
113 | SignatureAlgorithmID::VerityRsaPkcs1V15WithSha256
114 | SignatureAlgorithmID::VerityEcdsaWithSha256
115 | SignatureAlgorithmID::VerityDsaWithSha256 => MessageDigest::sha256(),
116 SignatureAlgorithmID::RsaPssWithSha512
117 | SignatureAlgorithmID::RsaPkcs1V15WithSha512
118 | SignatureAlgorithmID::EcdsaWithSha512 => MessageDigest::sha512(),
119 }
120 }
121
122 fn pkey_id(&self) -> pkey::Id {
123 match self {
124 SignatureAlgorithmID::RsaPssWithSha256
125 | SignatureAlgorithmID::RsaPssWithSha512
126 | SignatureAlgorithmID::RsaPkcs1V15WithSha256
127 | SignatureAlgorithmID::RsaPkcs1V15WithSha512
128 | SignatureAlgorithmID::VerityRsaPkcs1V15WithSha256 => pkey::Id::RSA,
129 SignatureAlgorithmID::EcdsaWithSha256
130 | SignatureAlgorithmID::EcdsaWithSha512
131 | SignatureAlgorithmID::VerityEcdsaWithSha256 => pkey::Id::EC,
132 SignatureAlgorithmID::DsaWithSha256 | SignatureAlgorithmID::VerityDsaWithSha256 => {
133 pkey::Id::DSA
134 }
135 }
136 }
137
138 fn rsa_padding(&self) -> Padding {
139 match self {
140 SignatureAlgorithmID::RsaPssWithSha256 | SignatureAlgorithmID::RsaPssWithSha512 => {
141 Padding::PKCS1_PSS
142 }
143 SignatureAlgorithmID::RsaPkcs1V15WithSha256
144 | SignatureAlgorithmID::VerityRsaPkcs1V15WithSha256
145 | SignatureAlgorithmID::RsaPkcs1V15WithSha512 => Padding::PKCS1,
146 SignatureAlgorithmID::EcdsaWithSha256
147 | SignatureAlgorithmID::EcdsaWithSha512
148 | SignatureAlgorithmID::VerityEcdsaWithSha256
149 | SignatureAlgorithmID::DsaWithSha256
150 | SignatureAlgorithmID::VerityDsaWithSha256 => Padding::NONE,
151 }
152 }
Alice Wang5d0f89a2022-09-15 15:06:10 +0000153}
154
155/// The rank of the content digest algorithm in this enum is used to help pick
156/// v4 apk digest.
157/// According to APK Signature Scheme v4, [apk digest] is the first available
158/// content digest of the highest rank (rank N).
159///
160/// This rank was also used for step 3a of the v3 signature verification.
161///
162/// [apk digest]: https://source.android.com/docs/security/features/apksigning/v4#apk-digest
163/// [v3 verification]: https://source.android.com/docs/security/apksigning/v3#v3-verification
164#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
165pub(crate) enum ContentDigestAlgorithm {
166 ChunkedSha256 = 1,
167 VerityChunkedSha256,
168 ChunkedSha512,
169}
170
171impl ContentDigestAlgorithm {
172 pub(crate) fn new_message_digest(&self) -> Result<MessageDigest> {
173 match self {
174 ContentDigestAlgorithm::ChunkedSha256 => Ok(MessageDigest::sha256()),
175 ContentDigestAlgorithm::ChunkedSha512 => Ok(MessageDigest::sha512()),
176 ContentDigestAlgorithm::VerityChunkedSha256 => {
177 bail!("TODO(b/197052981): CONTENT_DIGEST_VERITY_CHUNKED_SHA256 is not implemented")
178 }
179 }
180 }
181}