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