rust-apk 1.0.0

Build, align, sign, verify, and inspect Android APKs in pure Rust
Documentation
use der::oid::ObjectIdentifier;

use super::{CoverageError, JarError, KeyKind, Scheme, SignatureAlgorithm, SignatureError};
use crate::archive::ZipError;
use crate::types::ApiLevel;

/// Why a key, certificate, or keystore could not be used.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum KeyError {
    #[error("malformed PEM: {0}")]
    Pem(#[from] pem_rfc7468::Error),
    #[error("no private key found")]
    MissingPrivateKey,
    #[error("no certificate found")]
    MissingCertificate,
    #[error("the private key is encrypted; supply its password")]
    PasswordRequired,
    #[error("encrypted private keys require the `keystore` feature")]
    EncryptionDisabled,
    #[error("the password is incorrect")]
    WrongPassword,
    #[error("PKCS#8 private key: {0}")]
    Pkcs8(#[from] pkcs8::Error),
    #[error("malformed PKCS#1 RSA private key: {0}")]
    Pkcs1(#[from] pkcs1::Error),
    #[error("malformed or unsupported EC private key (P-256 and P-384 are supported)")]
    InvalidEcKey,
    #[error("malformed public key: {0}")]
    PublicKey(#[from] spki::Error),
    #[error("invalid RSA key: {0}")]
    Rsa(#[from] rsa::Error),
    #[error("{bits}-bit RSA keys are too weak to sign with; use at least {min} bits")]
    RsaKeyTooSmall { bits: u32, min: u32 },
    #[error("unsupported key algorithm {oid}")]
    UnsupportedAlgorithm { oid: ObjectIdentifier },
    #[error("unsupported elliptic curve {oid}")]
    UnsupportedCurve { oid: ObjectIdentifier },
    #[error("malformed X.509 certificate: {0}")]
    Certificate(#[source] der::Error),
    #[error("the private key does not match the certificate")]
    CertificateMismatch,
    #[error("{algorithm} cannot be used with a {key} key")]
    AlgorithmMismatch { algorithm: SignatureAlgorithm, key: KeyKind },
    #[error("signing failed: {0}")]
    Signing(#[source] signature::Error),
    #[error("encoding a signature block failed: {0}")]
    Encoding(#[source] der::Error),
    #[cfg(feature = "keystore")]
    #[error("JKS keystore: {0}")]
    Jks(#[from] jks::KeyStoreError),
    #[cfg(feature = "keystore")]
    #[error("PKCS#12 keystore: {0}")]
    Pkcs12(#[from] p12_keystore::error::Error),
    #[error("the keystore parser rejected malformed input")]
    MalformedKeystore,
    #[error("the keystore has no entry {alias:?}")]
    UnknownAlias { alias: String },
    #[error("keystore entry {alias:?} is not a private key")]
    NotAPrivateKey { alias: String },
    #[error("the keystore has {count} private keys; name one")]
    AmbiguousAlias { count: usize },
}
/// Why an APK could not be signed.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SignError {
    #[error(transparent)]
    Coverage(#[from] CoverageError),
    #[error("APK Signature Scheme v3 supports one signer; {count} were given")]
    MultipleV3Signers { count: usize },
    #[error("Android {min_sdk} cannot verify JAR signatures made with EC keys (API 18+ required)")]
    EcdsaJarRequiresApi18 { min_sdk: ApiLevel },
    #[error("the APK has no AndroidManifest.xml; set the minimum SDK explicitly")]
    UnknownMinSdk,
    #[error(transparent)]
    Key(#[from] KeyError),
    #[error(transparent)]
    Zip(#[from] ZipError),
    #[error("signed APK failed its self-check: {0}")]
    SelfCheck(#[source] Box<VerifyError>),
}

/// Why an APK's signatures did not verify.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum VerifyError {
    #[error("the APK is not signed")]
    Unsigned,
    #[error(transparent)]
    Zip(#[from] ZipError),
    #[error("the APK signing block's size fields do not match its length")]
    MalformedBlock,
    #[error("APK signing block entry at offset {offset:#x} has invalid length {len}")]
    InvalidBlockPair { offset: u64, len: u64 },
    #[error("malformed {scheme} block near offset {offset:#x}")]
    MalformedScheme { scheme: Scheme, offset: u64 },
    #[error("{scheme} block has no signers")]
    NoSigners { scheme: Scheme },
    #[error("{scheme} signer {signer} has no signatures")]
    NoSignatures { scheme: Scheme, signer: usize },
    #[error("{scheme} signer {signer} uses no supported algorithm (IDs {ids:x?})")]
    NoSupportedAlgorithm { scheme: Scheme, signer: usize, ids: Vec<u32> },
    #[error("{scheme} signer {signer}: {algorithm} signature: {source}")]
    BadSignature { scheme: Scheme, signer: usize, algorithm: SignatureAlgorithm, source: SignatureError },
    #[error("{scheme} signer {signer}: digests and signatures name different algorithms")]
    AlgorithmListMismatch { scheme: Scheme, signer: usize },
    #[error("{scheme} signer {signer} has no certificates")]
    NoCertificates { scheme: Scheme, signer: usize },
    #[error("{scheme} signer {signer}: public key does not match the certificate")]
    PublicKeyMismatch { scheme: Scheme, signer: usize },
    #[error("{scheme} signer {signer}: SDK range differs between signed and unsigned data")]
    SdkRangeMismatch { scheme: Scheme, signer: usize },
    #[error("{scheme} signer {signer}: SDK range {min}..={max} is empty")]
    InvalidSdkRange { scheme: Scheme, signer: usize, min: u32, max: u32 },
    #[error("{scheme} signers do not cover API level {sdk} exactly once")]
    SdkCoverage { scheme: Scheme, sdk: u32 },
    #[error("{scheme} signer {signer}: no digest for {algorithm}")]
    MissingDigest { scheme: Scheme, signer: usize, algorithm: SignatureAlgorithm },
    #[error("{scheme} signer {signer}: the APK's contents do not match the signed digest")]
    DigestMismatch { scheme: Scheme, signer: usize },
    #[error("{protected_by} declares a {scheme} signature that is missing (stripping attack?)")]
    StrippedScheme { scheme: Scheme, protected_by: Scheme },
    #[error("{first} and {second} were signed by different signers")]
    SignerMismatch { first: Scheme, second: Scheme },
    #[error("the APK uses signing key rotation, which this crate cannot verify")]
    KeyRotationUnsupported,
    #[error("the APK's signatures do not cover the releases it supports: {0}")]
    Coverage(#[from] CoverageError),
    #[error(transparent)]
    Key(#[from] KeyError),
    #[error(transparent)]
    Jar(#[from] JarError),
}