diff --git a/native-certgen/.cargo/config.toml b/native-certgen/.cargo/config.toml new file mode 100644 index 0000000..073a6f8 --- /dev/null +++ b/native-certgen/.cargo/config.toml @@ -0,0 +1,11 @@ +[target.aarch64-linux-android] +linker = "aarch64-linux-android29-clang" + +[target.armv7-linux-androideabi] +linker = "armv7a-linux-androideabi29-clang" + +[target.i686-linux-android] +linker = "i686-linux-android29-clang" + +[target.x86_64-linux-android] +linker = "x86_64-linux-android29-clang" diff --git a/native-certgen/Cargo.toml b/native-certgen/Cargo.toml new file mode 100644 index 0000000..619e515 --- /dev/null +++ b/native-certgen/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "certgen" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +jni = { version = "0.21.1", default-features = false } +ring = "0.17.14" +rsa = { version = "0.9", features = ["sha2"] } +pkcs8 = { version = "0.10", features = ["alloc"] } +rand = "0.8" +rcgen = { version = "0.13.2", default-features = false, features = ["ring"] } +der = { version = "0.7.10", features = ["alloc", "oid"] } +const-oid = "0.9.6" +x509-cert = { version = "0.2.5", features = ["pem"] } +time = { version = "0.3", features = ["std"] } +anyhow = "1.0" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +libc = "0.2" +zip = { version = "2.2", default-features = false, features = ["deflate"] } +serde_json = "1.0" + +[profile.release] +opt-level = "z" +lto = true +codegen-units = 1 +strip = "symbols" +panic = "abort" diff --git a/native-certgen/rust-toolchain.toml b/native-certgen/rust-toolchain.toml new file mode 100644 index 0000000..6777e1a --- /dev/null +++ b/native-certgen/rust-toolchain.toml @@ -0,0 +1,8 @@ +[toolchain] +channel = "stable" +targets = [ + "aarch64-linux-android", + "armv7-linux-androideabi", + "i686-linux-android", + "x86_64-linux-android", +] diff --git a/native-certgen/src/error.rs b/native-certgen/src/error.rs new file mode 100644 index 0000000..938f8ab --- /dev/null +++ b/native-certgen/src/error.rs @@ -0,0 +1,80 @@ +use std::fmt; + +#[derive(Debug)] +pub enum CertGenError { + KeyGenFailed(String), + CertBuildFailed(String), + AttestationEncodeFailed(String), + KeyboxParseFailed(String), + JniError(String), + InvalidParameter(String), + UnsupportedAlgorithm(i32), + UnsupportedCurve(i32), + SigningFailed(String), + SerializationFailed(String), + InternalError(String), +} + +impl fmt::Display for CertGenError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::KeyGenFailed(msg) => write!(f, "key generation failed: {}", msg), + Self::CertBuildFailed(msg) => write!(f, "certificate build failed: {}", msg), + Self::AttestationEncodeFailed(msg) => write!(f, "attestation encode failed: {}", msg), + Self::KeyboxParseFailed(msg) => write!(f, "keybox parse failed: {}", msg), + Self::JniError(msg) => write!(f, "JNI error: {}", msg), + Self::InvalidParameter(msg) => write!(f, "invalid parameter: {}", msg), + Self::UnsupportedAlgorithm(v) => write!(f, "unsupported algorithm: {}", v), + Self::UnsupportedCurve(v) => write!(f, "unsupported EC curve: {}", v), + Self::SigningFailed(msg) => write!(f, "signing failed: {}", msg), + Self::SerializationFailed(msg) => write!(f, "serialization failed: {}", msg), + Self::InternalError(msg) => write!(f, "internal error: {}", msg), + } + } +} + +impl std::error::Error for CertGenError {} + +impl From for CertGenError { + fn from(e: jni::errors::Error) -> Self { + Self::JniError(e.to_string()) + } +} + +impl From for CertGenError { + fn from(e: der::Error) -> Self { + Self::SerializationFailed(e.to_string()) + } +} + +impl From for CertGenError { + fn from(e: ring::error::Unspecified) -> Self { + Self::KeyGenFailed(e.to_string()) + } +} + +impl From for CertGenError { + fn from(e: ring::error::KeyRejected) -> Self { + Self::KeyGenFailed(e.to_string()) + } +} + +impl From for CertGenError { + fn from(e: rsa::Error) -> Self { + Self::KeyGenFailed(e.to_string()) + } +} + +impl From for CertGenError { + fn from(e: rcgen::Error) -> Self { + Self::CertBuildFailed(e.to_string()) + } +} + +impl From for CertGenError { + fn from(e: anyhow::Error) -> Self { + Self::InternalError(e.to_string()) + } +} + +pub type Result = std::result::Result; diff --git a/native-certgen/src/keygen.rs b/native-certgen/src/keygen.rs new file mode 100644 index 0000000..e9ce19f --- /dev/null +++ b/native-certgen/src/keygen.rs @@ -0,0 +1,52 @@ +use crate::error::{CertGenError, Result}; +use crate::types::{Algorithm, EcCurve, GeneratedKeyPair}; + +pub fn generate_key_pair(algorithm: Algorithm, key_size: u32, ec_curve: Option) -> Result { + match algorithm { + Algorithm::Ec => { + let curve = ec_curve.ok_or_else(|| CertGenError::InvalidParameter("ec_curve required for EC".into()))?; + generate_ec_key_pair(curve) + } + Algorithm::Rsa => generate_rsa_key_pair(key_size), + } +} + +fn generate_ec_key_pair(curve: EcCurve) -> Result { + use ring::signature::KeyPair; + + let alg = match curve { + EcCurve::P256 => &ring::signature::ECDSA_P256_SHA256_ASN1_SIGNING, + EcCurve::P384 => &ring::signature::ECDSA_P384_SHA384_ASN1_SIGNING, + _ => return Err(CertGenError::UnsupportedCurve(curve as i32)), + }; + + let rng = ring::rand::SystemRandom::new(); + let pkcs8_doc = ring::signature::EcdsaKeyPair::generate_pkcs8(alg, &rng)?; + let key_pair = ring::signature::EcdsaKeyPair::from_pkcs8(alg, pkcs8_doc.as_ref(), &rng)?; + + Ok(GeneratedKeyPair { + private_key_pkcs8: pkcs8_doc.as_ref().to_vec(), + public_key_spki: key_pair.public_key().as_ref().to_vec(), + }) +} + +fn generate_rsa_key_pair(key_size: u32) -> Result { + use pkcs8::EncodePrivateKey; + use rsa::pkcs8::EncodePublicKey; + + let mut rng = rand::thread_rng(); + let private_key = rsa::RsaPrivateKey::new(&mut rng, key_size as usize) + .map_err(|e| CertGenError::KeyGenFailed(e.to_string()))?; + + let pkcs8_der = private_key.to_pkcs8_der() + .map_err(|e| CertGenError::SerializationFailed(e.to_string()))?; + + let public_key = private_key.to_public_key(); + let pub_der = public_key.to_public_key_der() + .map_err(|e| CertGenError::SerializationFailed(e.to_string()))?; + + Ok(GeneratedKeyPair { + private_key_pkcs8: pkcs8_der.as_bytes().to_vec(), + public_key_spki: pub_der.as_ref().to_vec(), + }) +} diff --git a/native-certgen/src/lib.rs b/native-certgen/src/lib.rs new file mode 100644 index 0000000..305d7ac --- /dev/null +++ b/native-certgen/src/lib.rs @@ -0,0 +1,7 @@ +mod error; +mod types; +mod keygen; +// mod attestation; // Phase 2 +// mod keybox; // Phase 0-1 Task 2 +// mod certbuilder; // Phase 3 +// mod logging; // Phase 3 diff --git a/native-certgen/src/types.rs b/native-certgen/src/types.rs new file mode 100644 index 0000000..c5e11f7 --- /dev/null +++ b/native-certgen/src/types.rs @@ -0,0 +1,121 @@ +use crate::error::CertGenError; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(i32)] +pub enum Algorithm { + Rsa = 1, + Ec = 3, +} + +impl TryFrom for Algorithm { + type Error = CertGenError; + fn try_from(value: i32) -> Result { + match value { + 1 => Ok(Self::Rsa), + 3 => Ok(Self::Ec), + _ => Err(CertGenError::UnsupportedAlgorithm(value)), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(i32)] +pub enum EcCurve { + P224 = 0, + P256 = 1, + P384 = 2, + P521 = 3, + Curve25519 = 4, +} + +impl TryFrom for EcCurve { + type Error = CertGenError; + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(Self::P224), + 1 => Ok(Self::P256), + 2 => Ok(Self::P384), + 3 => Ok(Self::P521), + 4 => Ok(Self::Curve25519), + _ => Err(CertGenError::UnsupportedCurve(value)), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(i32)] +pub enum KeyPurpose { + Encrypt = 0, + Decrypt = 1, + Sign = 2, + Verify = 3, + WrapKey = 5, + AgreeKey = 6, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(i32)] +pub enum SecurityLevel { + Software = 0, + TrustedEnvironment = 1, + StrongBox = 2, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(i32)] +pub enum VerifiedBootState { + Verified = 0, + SelfSigned = 1, + Unverified = 2, + Failed = 3, +} + +pub struct CertGenParams { + pub algorithm: Algorithm, + pub key_size: u32, + pub ec_curve: Option, + pub rsa_public_exponent: u64, + + pub attestation_challenge: Option>, + pub purposes: Vec, + pub digests: Vec, + + pub cert_serial: Option>, + pub cert_subject: Option>, + pub cert_not_before: i64, + pub cert_not_after: i64, + + pub keybox_private_key: Vec, + pub keybox_cert_chain: Vec, + + pub security_level: i32, + pub attest_version: i32, + pub keymaster_version: i32, + + pub os_version: i32, + pub os_patch_level: i32, + pub vendor_patch_level: i32, + pub boot_patch_level: i32, + + pub boot_key: Vec, + pub boot_hash: Vec, + + pub creation_datetime: i64, + pub attestation_application_id: Vec, + pub module_hash: Option>, + + pub id_brand: Option>, + pub id_device: Option>, + pub id_product: Option>, + pub id_serial: Option>, + pub id_imei: Option>, + pub id_meid: Option>, + pub id_manufacturer: Option>, + pub id_model: Option>, + pub id_second_imei: Option>, +} + +pub struct GeneratedKeyPair { + pub private_key_pkcs8: Vec, + pub public_key_spki: Vec, +}