feat(native-certgen): scaffold Rust crate with foundation types and keygen
Cargo.toml with 16 dependencies per build spec, error types with From impls for all upstream error types, CertGenParams mapping the full JNI config contract, EC/RSA key generation via ring and rsa crates. Compiles clean for aarch64-linux-android via cargo-ndk.
This commit is contained in:
@@ -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<jni::errors::Error> for CertGenError {
|
||||
fn from(e: jni::errors::Error) -> Self {
|
||||
Self::JniError(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<der::Error> for CertGenError {
|
||||
fn from(e: der::Error) -> Self {
|
||||
Self::SerializationFailed(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ring::error::Unspecified> for CertGenError {
|
||||
fn from(e: ring::error::Unspecified) -> Self {
|
||||
Self::KeyGenFailed(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ring::error::KeyRejected> for CertGenError {
|
||||
fn from(e: ring::error::KeyRejected) -> Self {
|
||||
Self::KeyGenFailed(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rsa::Error> for CertGenError {
|
||||
fn from(e: rsa::Error) -> Self {
|
||||
Self::KeyGenFailed(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rcgen::Error> for CertGenError {
|
||||
fn from(e: rcgen::Error) -> Self {
|
||||
Self::CertBuildFailed(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<anyhow::Error> for CertGenError {
|
||||
fn from(e: anyhow::Error) -> Self {
|
||||
Self::InternalError(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, CertGenError>;
|
||||
@@ -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<EcCurve>) -> Result<GeneratedKeyPair> {
|
||||
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<GeneratedKeyPair> {
|
||||
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<GeneratedKeyPair> {
|
||||
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(),
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
@@ -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<i32> for Algorithm {
|
||||
type Error = CertGenError;
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
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<i32> for EcCurve {
|
||||
type Error = CertGenError;
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
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<EcCurve>,
|
||||
pub rsa_public_exponent: u64,
|
||||
|
||||
pub attestation_challenge: Option<Vec<u8>>,
|
||||
pub purposes: Vec<i32>,
|
||||
pub digests: Vec<i32>,
|
||||
|
||||
pub cert_serial: Option<Vec<u8>>,
|
||||
pub cert_subject: Option<Vec<u8>>,
|
||||
pub cert_not_before: i64,
|
||||
pub cert_not_after: i64,
|
||||
|
||||
pub keybox_private_key: Vec<u8>,
|
||||
pub keybox_cert_chain: Vec<u8>,
|
||||
|
||||
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<u8>,
|
||||
pub boot_hash: Vec<u8>,
|
||||
|
||||
pub creation_datetime: i64,
|
||||
pub attestation_application_id: Vec<u8>,
|
||||
pub module_hash: Option<Vec<u8>>,
|
||||
|
||||
pub id_brand: Option<Vec<u8>>,
|
||||
pub id_device: Option<Vec<u8>>,
|
||||
pub id_product: Option<Vec<u8>>,
|
||||
pub id_serial: Option<Vec<u8>>,
|
||||
pub id_imei: Option<Vec<u8>>,
|
||||
pub id_meid: Option<Vec<u8>>,
|
||||
pub id_manufacturer: Option<Vec<u8>>,
|
||||
pub id_model: Option<Vec<u8>>,
|
||||
pub id_second_imei: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
pub struct GeneratedKeyPair {
|
||||
pub private_key_pkcs8: Vec<u8>,
|
||||
pub public_key_spki: Vec<u8>,
|
||||
}
|
||||
Reference in New Issue
Block a user