mod block;
mod certificate;
mod error;
mod identity;
mod jar;
mod key;
#[cfg(feature = "keystore")]
mod keystore;
mod scheme;
mod signer;
mod verify;
pub use crate::digest::DigestAlgorithm;
pub use certificate::Certificate;
pub use error::{KeyError, SignError, VerifyError};
pub use identity::SigningIdentity;
pub use jar::JarError;
pub use key::{KeyKind, MIN_RSA_SIGNING_BITS, PrivateKey, PublicKey, SignatureAlgorithm, SignatureError};
#[cfg(feature = "keystore")]
pub use keystore::Keystore;
pub use signer::Signer;
pub use verify::{Verification, VerifiedSigner, verify, verify_bytes};
use crate::types::ApiLevel;
use bitflags::bitflags;
pub use block::{BlockId, SigningBlock};
use std::fmt;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Scheme {
V1,
V2,
V3,
}
impl Scheme {
const ALL: [Self; 3] = [Self::V1, Self::V2, Self::V3];
pub const fn number(self) -> u32 {
match self {
Self::V1 => 1,
Self::V2 => 2,
Self::V3 => 3,
}
}
pub const fn min_sdk(self) -> ApiLevel {
match self {
Self::V1 => ApiLevel::BASE,
Self::V2 => ApiLevel::N,
Self::V3 => ApiLevel::P,
}
}
fn from_number(number: u32) -> Option<Self> {
Self::ALL.into_iter().find(|s| s.number() == number)
}
fn block_id(self) -> Option<BlockId> {
match self {
Self::V1 => None,
Self::V2 => Some(BlockId::V2),
Self::V3 => Some(BlockId::V3),
}
}
fn flag(self) -> Schemes {
match self {
Self::V1 => Schemes::V1,
Self::V2 => Schemes::V2,
Self::V3 => Schemes::V3,
}
}
}
impl fmt::Display for Scheme {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::V1 => "JAR signature (v1)",
Self::V2 => "APK Signature Scheme v2",
Self::V3 => "APK Signature Scheme v3",
})
}
}
bitflags! {
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct Schemes: u8 {
const V1 = 1 << 0;
const V2 = 1 << 1;
const V3 = 1 << 2;
}
}
impl Schemes {
pub fn for_min_sdk(min_sdk: ApiLevel) -> Self {
if min_sdk < ApiLevel::N { Self::V1 | Self::V2 | Self::V3 } else { Self::V2 | Self::V3 }
}
pub fn iter_schemes(self) -> impl Iterator<Item = Scheme> {
Scheme::ALL.into_iter().filter(move |s| self.contains(s.flag()))
}
pub fn check_coverage(self, min_sdk: ApiLevel, target_sdk: ApiLevel) -> Result<(), CoverageError> {
let covered_from = self.iter_schemes().map(Scheme::min_sdk).min().ok_or(CoverageError::NoSchemes)?;
if covered_from > min_sdk {
let needed = if min_sdk < ApiLevel::N { Scheme::V1 } else { Scheme::V2 };
return Err(CoverageError::UnverifiableOnMinSdk { schemes: self, min_sdk, needed });
}
if target_sdk >= ApiLevel::R && !self.intersects(Self::V2 | Self::V3) {
return Err(CoverageError::TargetRequiresV2 { target_sdk });
}
Ok(())
}
}
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum CoverageError {
#[error("no signature schemes were selected")]
NoSchemes,
#[error("{schemes:?} cannot be verified by Android {min_sdk}; add {needed}")]
UnverifiableOnMinSdk { schemes: Schemes, min_sdk: ApiLevel, needed: Scheme },
#[error("targetSdkVersion {target_sdk} requires APK Signature Scheme v2 or v3")]
TargetRequiresV2 { target_sdk: ApiLevel },
}