Skip to main content

apk/sign/
key.rs

1//! Signing keys and signature algorithms.
2//!
3//! [`PrivateKey`] and [`PublicKey`] are opaque so that the cryptography
4//! crates behind them stay out of this crate's public API.
5
6use std::fmt;
7
8use der::Decode;
9use der::oid::ObjectIdentifier;
10use rsa::pkcs1::DecodeRsaPrivateKey;
11use rsa::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePublicKey};
12use rsa::traits::PublicKeyParts;
13use rsa::{RsaPrivateKey, RsaPublicKey, pkcs1v15, pss};
14use sha1::Sha1;
15use sha2::{Digest, Sha256, Sha384, Sha512};
16use signature::hazmat::{PrehashSigner, PrehashVerifier};
17use signature::{SignatureEncoding, Signer, Verifier};
18use zeroize::Zeroizing;
19
20use super::{DigestAlgorithm, KeyError};
21use crate::digest::ContentDigest;
22
23const RSA_ENCRYPTION: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1");
24const EC_PUBLIC_KEY: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
25const SECP256R1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
26const SECP384R1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.132.0.34");
27
28/// The smallest RSA key this crate signs with.
29pub const MIN_RSA_SIGNING_BITS: u32 = 2048;
30
31/// The family and size of a key.
32#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
33pub enum KeyKind {
34    Rsa { bits: u32 },
35    EcP256,
36    EcP384,
37}
38
39impl fmt::Display for KeyKind {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        match self {
42            Self::Rsa { bits } => write!(f, "{bits}-bit RSA"),
43            Self::EcP256 => f.write_str("P-256 EC"),
44            Self::EcP384 => f.write_str("P-384 EC"),
45        }
46    }
47}
48
49/// A signature algorithm of APK Signature Scheme v2 and v3.
50#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
51#[non_exhaustive]
52pub enum SignatureAlgorithm {
53    RsaPssSha256,
54    RsaPssSha512,
55    RsaPkcs1Sha256,
56    RsaPkcs1Sha512,
57    EcdsaSha256,
58    EcdsaSha512,
59    DsaSha256,
60    VerityRsaPkcs1Sha256,
61    VerityEcdsaSha256,
62    VerityDsaSha256,
63}
64
65impl SignatureAlgorithm {
66    const ALL: [(Self, u32); 10] = [
67        (Self::RsaPssSha256, 0x0101),
68        (Self::RsaPssSha512, 0x0102),
69        (Self::RsaPkcs1Sha256, 0x0103),
70        (Self::RsaPkcs1Sha512, 0x0104),
71        (Self::EcdsaSha256, 0x0201),
72        (Self::EcdsaSha512, 0x0202),
73        (Self::DsaSha256, 0x0301),
74        (Self::VerityRsaPkcs1Sha256, 0x0421),
75        (Self::VerityEcdsaSha256, 0x0423),
76        (Self::VerityDsaSha256, 0x0425),
77    ];
78
79    /// The ID stored in signature records.
80    pub fn id(self) -> u32 {
81        Self::ALL.iter().find(|(a, _)| *a == self).map(|(_, id)| *id).expect("every algorithm has an ID")
82    }
83
84    pub fn from_id(id: u32) -> Option<Self> {
85        Self::ALL.iter().find(|(_, i)| *i == id).map(|(a, _)| *a)
86    }
87
88    /// The content digest this algorithm signs, if this crate can compute it
89    /// (verity digests are not supported).
90    pub(crate) fn content_digest(self) -> Option<ContentDigest> {
91        match self {
92            Self::RsaPssSha256 | Self::RsaPkcs1Sha256 | Self::EcdsaSha256 | Self::DsaSha256 => {
93                Some(ContentDigest::ChunkedSha256)
94            }
95            Self::RsaPssSha512 | Self::RsaPkcs1Sha512 | Self::EcdsaSha512 => Some(ContentDigest::ChunkedSha512),
96            Self::VerityRsaPkcs1Sha256 | Self::VerityEcdsaSha256 | Self::VerityDsaSha256 => None,
97        }
98    }
99
100    /// How Android ranks this algorithm when a signer offers several: by the
101    /// strength of its content digest.
102    pub(crate) fn strength(self) -> u8 {
103        match self {
104            Self::RsaPssSha512 | Self::RsaPkcs1Sha512 | Self::EcdsaSha512 => 3,
105            Self::VerityRsaPkcs1Sha256 | Self::VerityEcdsaSha256 | Self::VerityDsaSha256 => 2,
106            Self::RsaPssSha256 | Self::RsaPkcs1Sha256 | Self::EcdsaSha256 | Self::DsaSha256 => 1,
107        }
108    }
109
110    /// Whether this crate can verify signatures made with this algorithm.
111    pub fn is_verifiable(self) -> bool {
112        !matches!(self, Self::DsaSha256 | Self::VerityRsaPkcs1Sha256 | Self::VerityEcdsaSha256 | Self::VerityDsaSha256)
113    }
114}
115
116impl fmt::Display for SignatureAlgorithm {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        write!(f, "{self:?} ({:#06x})", self.id())
119    }
120}
121
122#[derive(Clone)]
123enum Private {
124    Rsa(RsaPrivateKey),
125    P256(p256::ecdsa::SigningKey),
126    P384(p384::ecdsa::SigningKey),
127}
128
129/// A private key that can sign APKs: RSA, or ECDSA on P-256 or P-384.
130#[derive(Clone)]
131pub struct PrivateKey(Private);
132
133impl fmt::Debug for PrivateKey {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        write!(f, "PrivateKey({})", self.kind())
136    }
137}
138
139impl PrivateKey {
140    /// Parses a PKCS#8 `PrivateKeyInfo` (PEM label `PRIVATE KEY`).
141    pub fn from_pkcs8_der(der: &[u8]) -> Result<Self, KeyError> {
142        let info = pkcs8::PrivateKeyInfoRef::from_der(der).map_err(pkcs8::Error::from)?;
143        let algorithm = info.algorithm.oid;
144        let private = match algorithm {
145            RSA_ENCRYPTION => Private::Rsa(RsaPrivateKey::from_pkcs8_der(der)?),
146            EC_PUBLIC_KEY => match info.algorithm.parameters_oid().map_err(pkcs8::Error::from)? {
147                SECP256R1 => Private::P256(p256::ecdsa::SigningKey::from_pkcs8_der(der)?),
148                SECP384R1 => Private::P384(p384::ecdsa::SigningKey::from_pkcs8_der(der)?),
149                curve => return Err(KeyError::UnsupportedCurve { oid: curve }),
150            },
151            oid => return Err(KeyError::UnsupportedAlgorithm { oid }),
152        };
153        Self::checked(private)
154    }
155
156    /// Parses a PKCS#1 `RSAPrivateKey` (PEM label `RSA PRIVATE KEY`).
157    pub fn from_pkcs1_der(der: &[u8]) -> Result<Self, KeyError> {
158        Self::checked(Private::Rsa(RsaPrivateKey::from_pkcs1_der(der)?))
159    }
160
161    /// Parses a SEC1 `ECPrivateKey` (PEM label `EC PRIVATE KEY`) on P-256 or P-384.
162    pub fn from_sec1_der(der: &[u8]) -> Result<Self, KeyError> {
163        if let Ok(key) = p256::SecretKey::from_sec1_der(der) {
164            return Ok(Self(Private::P256(key.into())));
165        }
166        match p384::SecretKey::from_sec1_der(der) {
167            Ok(key) => Ok(Self(Private::P384(key.into()))),
168            Err(_) => Err(KeyError::InvalidEcKey),
169        }
170    }
171
172    fn checked(private: Private) -> Result<Self, KeyError> {
173        if let Private::Rsa(key) = &private {
174            key.validate()?;
175            let bits = key.n().bits_vartime();
176            if bits < MIN_RSA_SIGNING_BITS {
177                return Err(KeyError::RsaKeyTooSmall { bits, min: MIN_RSA_SIGNING_BITS });
178            }
179        }
180        Ok(Self(private))
181    }
182
183    pub fn kind(&self) -> KeyKind {
184        self.public_key().kind()
185    }
186
187    /// Encodes this key as an unencrypted PKCS#8 `PrivateKeyInfo`, for
188    /// exporting it to other tools. Handle the result as secret material.
189    pub fn to_pkcs8_der(&self) -> Result<Zeroizing<Vec<u8>>, KeyError> {
190        use pkcs8::EncodePrivateKey;
191        let document = match &self.0 {
192            Private::Rsa(key) => key.to_pkcs8_der()?,
193            Private::P256(key) => key.to_pkcs8_der()?,
194            Private::P384(key) => key.to_pkcs8_der()?,
195        };
196        Ok(Zeroizing::new(document.as_bytes().to_vec()))
197    }
198
199    pub fn public_key(&self) -> PublicKey {
200        PublicKey(match &self.0 {
201            Private::Rsa(key) => Public::Rsa(key.to_public_key()),
202            Private::P256(key) => Public::P256(*key.verifying_key()),
203            Private::P384(key) => Public::P384(*key.verifying_key()),
204        })
205    }
206
207    /// The v2 algorithm apksigner chooses for this key: SHA-512 variants for
208    /// RSA keys over 3072 bits and EC keys over 256 bits.
209    pub fn signature_algorithm(&self) -> SignatureAlgorithm {
210        match self.kind() {
211            KeyKind::Rsa { bits } if bits <= 3072 => SignatureAlgorithm::RsaPkcs1Sha256,
212            KeyKind::Rsa { .. } => SignatureAlgorithm::RsaPkcs1Sha512,
213            KeyKind::EcP256 => SignatureAlgorithm::EcdsaSha256,
214            KeyKind::EcP384 => SignatureAlgorithm::EcdsaSha512,
215        }
216    }
217
218    /// Signs `message` with a v2/v3 `algorithm`.
219    pub(crate) fn sign(&self, algorithm: SignatureAlgorithm, message: &[u8]) -> Result<Vec<u8>, KeyError> {
220        let mismatch = || KeyError::AlgorithmMismatch { algorithm, key: self.kind() };
221        match (&self.0, algorithm) {
222            (Private::Rsa(key), SignatureAlgorithm::RsaPkcs1Sha256) => rsa_pkcs1::<Sha256>(key, message),
223            (Private::Rsa(key), SignatureAlgorithm::RsaPkcs1Sha512) => rsa_pkcs1::<Sha512>(key, message),
224            (Private::P256(key), SignatureAlgorithm::EcdsaSha256) => sign_p256::<Sha256>(key, message),
225            (Private::P256(key), SignatureAlgorithm::EcdsaSha512) => sign_p256::<Sha512>(key, message),
226            (Private::P384(key), SignatureAlgorithm::EcdsaSha256) => sign_p384::<Sha256>(key, message),
227            (Private::P384(key), SignatureAlgorithm::EcdsaSha512) => sign_p384::<Sha512>(key, message),
228            _ => Err(mismatch()),
229        }
230    }
231
232    /// Signs a JAR signature file with PKCS#1 v1.5 RSA or ECDSA over `digest`.
233    pub(crate) fn sign_jar(&self, digest: DigestAlgorithm, message: &[u8]) -> Result<Vec<u8>, KeyError> {
234        match digest {
235            DigestAlgorithm::Sha1 => self.sign_jar_with::<Sha1>(message),
236            DigestAlgorithm::Sha256 => self.sign_jar_with::<Sha256>(message),
237            DigestAlgorithm::Sha384 => self.sign_jar_with::<Sha384>(message),
238            DigestAlgorithm::Sha512 => self.sign_jar_with::<Sha512>(message),
239        }
240    }
241
242    fn sign_jar_with<D: Digest + der::oid::AssociatedOid>(&self, message: &[u8]) -> Result<Vec<u8>, KeyError> {
243        match &self.0 {
244            Private::Rsa(key) => rsa_pkcs1::<D>(key, message),
245            Private::P256(key) => sign_p256::<D>(key, message),
246            Private::P384(key) => sign_p384::<D>(key, message),
247        }
248    }
249}
250
251fn rsa_pkcs1<D>(key: &RsaPrivateKey, message: &[u8]) -> Result<Vec<u8>, KeyError>
252where
253    D: Digest + der::oid::AssociatedOid,
254{
255    let signer = pkcs1v15::SigningKey::<D>::new(key.clone());
256    Ok(signer.try_sign(message).map_err(KeyError::Signing)?.to_vec())
257}
258
259/// ECDSA over a prehash, which lets any digest pair with any curve (Android
260/// pairs SHA-512 with P-256 and P-384 alike). Signatures are DER-encoded and
261/// deterministic (RFC 6979).
262macro_rules! ecdsa_curve {
263    ($curve:ident, $sign:ident, $verify:ident) => {
264        fn $sign<D: Digest>(key: &$curve::ecdsa::SigningKey, message: &[u8]) -> Result<Vec<u8>, KeyError> {
265            let signature: $curve::ecdsa::Signature =
266                key.sign_prehash(&D::digest(message)).map_err(KeyError::Signing)?;
267            Ok(signature.to_der().to_vec())
268        }
269
270        fn $verify<D: Digest>(
271            key: &$curve::ecdsa::VerifyingKey,
272            message: &[u8],
273            signature: &[u8],
274        ) -> Result<(), SignatureError> {
275            let signature = $curve::ecdsa::Signature::from_der(signature).map_err(|_| SignatureError::Malformed)?;
276            key.verify_prehash(&D::digest(message), &signature).map_err(|_| SignatureError::Invalid)
277        }
278    };
279}
280
281ecdsa_curve!(p256, sign_p256, verify_p256);
282ecdsa_curve!(p384, sign_p384, verify_p384);
283
284#[derive(Clone, Debug, Eq, PartialEq)]
285enum Public {
286    Rsa(RsaPublicKey),
287    P256(p256::ecdsa::VerifyingKey),
288    P384(p384::ecdsa::VerifyingKey),
289}
290
291/// A public key: RSA, or ECDSA on P-256 or P-384.
292#[derive(Clone, Debug, Eq, PartialEq)]
293pub struct PublicKey(Public);
294
295/// A signature that did not verify.
296#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
297#[non_exhaustive]
298pub enum SignatureError {
299    #[error("{algorithm} cannot be verified with a {key} key")]
300    AlgorithmMismatch { algorithm: SignatureAlgorithm, key: KeyKind },
301    #[error("{algorithm} is not supported for verification")]
302    Unsupported { algorithm: SignatureAlgorithm },
303    #[error("signature is malformed")]
304    Malformed,
305    #[error("signature does not match")]
306    Invalid,
307}
308
309impl PublicKey {
310    /// Parses a DER `SubjectPublicKeyInfo`.
311    pub fn from_spki_der(der: &[u8]) -> Result<Self, KeyError> {
312        let info = spki::SubjectPublicKeyInfoRef::from_der(der).map_err(spki::Error::from)?;
313        Ok(Self(match info.algorithm.oid {
314            RSA_ENCRYPTION => Public::Rsa(RsaPublicKey::from_public_key_der(der)?),
315            EC_PUBLIC_KEY => match info.algorithm.parameters_oid()? {
316                SECP256R1 => Public::P256(p256::ecdsa::VerifyingKey::from_public_key_der(der)?),
317                SECP384R1 => Public::P384(p384::ecdsa::VerifyingKey::from_public_key_der(der)?),
318                curve => return Err(KeyError::UnsupportedCurve { oid: curve }),
319            },
320            oid => return Err(KeyError::UnsupportedAlgorithm { oid }),
321        }))
322    }
323
324    /// Encodes this key as a DER `SubjectPublicKeyInfo`.
325    pub fn to_spki_der(&self) -> Result<Vec<u8>, KeyError> {
326        let document = match &self.0 {
327            Public::Rsa(key) => key.to_public_key_der()?,
328            Public::P256(key) => key.to_public_key_der()?,
329            Public::P384(key) => key.to_public_key_der()?,
330        };
331        Ok(document.into_vec())
332    }
333
334    pub fn kind(&self) -> KeyKind {
335        match &self.0 {
336            Public::Rsa(key) => KeyKind::Rsa { bits: key.n().bits_vartime() },
337            Public::P256(_) => KeyKind::EcP256,
338            Public::P384(_) => KeyKind::EcP384,
339        }
340    }
341
342    /// Verifies a v2/v3 signature over `message`.
343    pub fn verify(
344        &self,
345        algorithm: SignatureAlgorithm,
346        message: &[u8],
347        signature: &[u8],
348    ) -> Result<(), SignatureError> {
349        use SignatureAlgorithm as A;
350        let mismatch = SignatureError::AlgorithmMismatch { algorithm, key: self.kind() };
351        match (&self.0, algorithm) {
352            (Public::Rsa(key), A::RsaPkcs1Sha256) => verify_rsa_pkcs1::<Sha256>(key, message, signature),
353            (Public::Rsa(key), A::RsaPkcs1Sha512) => verify_rsa_pkcs1::<Sha512>(key, message, signature),
354            (Public::Rsa(key), A::RsaPssSha256) => verify_rsa_pss::<Sha256>(key, message, signature),
355            (Public::Rsa(key), A::RsaPssSha512) => verify_rsa_pss::<Sha512>(key, message, signature),
356            (Public::P256(key), A::EcdsaSha256) => verify_p256::<Sha256>(key, message, signature),
357            (Public::P256(key), A::EcdsaSha512) => verify_p256::<Sha512>(key, message, signature),
358            (Public::P384(key), A::EcdsaSha256) => verify_p384::<Sha256>(key, message, signature),
359            (Public::P384(key), A::EcdsaSha512) => verify_p384::<Sha512>(key, message, signature),
360            (_, algorithm) if !algorithm.is_verifiable() => Err(SignatureError::Unsupported { algorithm }),
361            _ => Err(mismatch),
362        }
363    }
364
365    /// Verifies a JAR signature file signature made with `digest`.
366    pub(crate) fn verify_jar(
367        &self,
368        digest: DigestAlgorithm,
369        message: &[u8],
370        signature: &[u8],
371    ) -> Result<(), SignatureError> {
372        match digest {
373            DigestAlgorithm::Sha1 => self.verify_jar_with::<Sha1>(message, signature),
374            DigestAlgorithm::Sha256 => self.verify_jar_with::<Sha256>(message, signature),
375            DigestAlgorithm::Sha384 => self.verify_jar_with::<Sha384>(message, signature),
376            DigestAlgorithm::Sha512 => self.verify_jar_with::<Sha512>(message, signature),
377        }
378    }
379
380    fn verify_jar_with<D: Digest + der::oid::AssociatedOid>(
381        &self,
382        message: &[u8],
383        signature: &[u8],
384    ) -> Result<(), SignatureError> {
385        match &self.0 {
386            Public::Rsa(key) => verify_rsa_pkcs1::<D>(key, message, signature),
387            Public::P256(key) => verify_p256::<D>(key, message, signature),
388            Public::P384(key) => verify_p384::<D>(key, message, signature),
389        }
390    }
391}
392
393fn verify_rsa_pkcs1<D>(key: &RsaPublicKey, message: &[u8], signature: &[u8]) -> Result<(), SignatureError>
394where
395    D: Digest + der::oid::AssociatedOid,
396{
397    let signature = pkcs1v15::Signature::try_from(signature).map_err(|_| SignatureError::Malformed)?;
398    pkcs1v15::VerifyingKey::<D>::new(key.clone()).verify(message, &signature).map_err(|_| SignatureError::Invalid)
399}
400
401fn verify_rsa_pss<D>(key: &RsaPublicKey, message: &[u8], signature: &[u8]) -> Result<(), SignatureError>
402where
403    D: Digest + sha2::digest::FixedOutputReset,
404{
405    let signature = pss::Signature::try_from(signature).map_err(|_| SignatureError::Malformed)?;
406    pss::VerifyingKey::<D>::new(key.clone()).verify(message, &signature).map_err(|_| SignatureError::Invalid)
407}