diff --git a/native-certgen/src/error.rs b/native-certgen/src/error.rs index 938f8ab..74ab02d 100644 --- a/native-certgen/src/error.rs +++ b/native-certgen/src/error.rs @@ -2,33 +2,41 @@ use std::fmt; #[derive(Debug)] pub enum CertGenError { + Jni(String), + NullParam(&'static str), + UnsupportedAlgorithm(i32), + UnsupportedEcCurve(i32), KeyGenFailed(String), CertBuildFailed(String), - AttestationEncodeFailed(String), KeyboxParseFailed(String), - JniError(String), + AttestationBuildFailed(String), + DerError(der::Error), + RcgenError(rcgen::Error), + EmptyKeyboxChain, + ChallengeTooLong(usize), 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::Jni(msg) => write!(f, "JNI error: {}", msg), + Self::NullParam(name) => write!(f, "null required parameter: {}", name), + Self::UnsupportedAlgorithm(v) => write!(f, "unsupported algorithm: {}", v), + Self::UnsupportedEcCurve(v) => write!(f, "unsupported EC curve: {}", v), 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::AttestationBuildFailed(msg) => write!(f, "attestation build failed: {}", msg), + Self::DerError(e) => write!(f, "DER error: {}", e), + Self::RcgenError(e) => write!(f, "rcgen error: {}", e), + Self::EmptyKeyboxChain => write!(f, "keybox certificate chain is empty"), + Self::ChallengeTooLong(len) => write!(f, "attestation challenge too long: {} bytes (max 128)", len), 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), } } } @@ -37,13 +45,13 @@ impl std::error::Error for CertGenError {} impl From for CertGenError { fn from(e: jni::errors::Error) -> Self { - Self::JniError(e.to_string()) + Self::Jni(e.to_string()) } } impl From for CertGenError { fn from(e: der::Error) -> Self { - Self::SerializationFailed(e.to_string()) + Self::DerError(e) } } @@ -67,13 +75,7 @@ impl From for CertGenError { 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()) + Self::RcgenError(e) } } diff --git a/native-certgen/src/keybox.rs b/native-certgen/src/keybox.rs index befbea4..9f99f23 100644 --- a/native-certgen/src/keybox.rs +++ b/native-certgen/src/keybox.rs @@ -18,8 +18,8 @@ pub fn parse_keybox(cert_chain_bytes: &[u8], private_key_bytes: &[u8]) -> Result let leaf = Certificate::from_der(&certs[0]) .map_err(|e| CertGenError::KeyboxParseFailed(format!("leaf cert parse: {e}")))?; - let issuer_dn_der = leaf.tbs_certificate.issuer.to_der() - .map_err(|e| CertGenError::KeyboxParseFailed(format!("issuer DN encode: {e}")))?; + let issuer_dn_der = leaf.tbs_certificate.subject.to_der() + .map_err(|e| CertGenError::KeyboxParseFailed(format!("subject DN encode: {e}")))?; let not_after = leaf.tbs_certificate.validity.not_after; let leaf_not_after = not_after.to_unix_duration().as_secs() as i64; diff --git a/native-certgen/src/keygen.rs b/native-certgen/src/keygen.rs index e9ce19f..3b0b426 100644 --- a/native-certgen/src/keygen.rs +++ b/native-certgen/src/keygen.rs @@ -1,13 +1,18 @@ 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 { +pub fn generate_key_pair( + algorithm: Algorithm, + key_size: u32, + ec_curve: Option, + rsa_public_exponent: u64, +) -> 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), + Algorithm::Rsa => generate_rsa_key_pair(key_size, rsa_public_exponent), } } @@ -17,25 +22,99 @@ fn generate_ec_key_pair(curve: EcCurve) -> Result { 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)), + _ => return Err(CertGenError::UnsupportedEcCurve(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)?; + let raw_point = key_pair.public_key().as_ref(); + let spki = build_ec_spki(curve, raw_point)?; + Ok(GeneratedKeyPair { private_key_pkcs8: pkcs8_doc.as_ref().to_vec(), - public_key_spki: key_pair.public_key().as_ref().to_vec(), + public_key_spki: spki, }) } -fn generate_rsa_key_pair(key_size: u32) -> Result { +/// Build SubjectPublicKeyInfo DER from a raw EC uncompressed point. +fn build_ec_spki(curve: EcCurve, raw_point: &[u8]) -> Result> { + // SPKI = SEQUENCE { AlgorithmIdentifier, BIT STRING(public key) } + // AlgorithmIdentifier = SEQUENCE { OID(ecPublicKey), OID(curve) } + // + // DER-encode manually — the prefix is fixed per curve, only the point varies. + + // OID 1.2.840.10045.2.1 (id-ecPublicKey) + const EC_PUBLIC_KEY_OID: &[u8] = &[0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01]; + + let curve_oid: &[u8] = match curve { + // OID 1.2.840.10045.3.1.7 (prime256v1 / P-256) + EcCurve::P256 => &[0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07], + // OID 1.3.132.0.34 (secp384r1 / P-384) + EcCurve::P384 => &[0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22], + _ => return Err(CertGenError::UnsupportedEcCurve(curve as i32)), + }; + + let alg_id_seq = der_sequence(EC_PUBLIC_KEY_OID, curve_oid); + + // BIT STRING: 0x03, length, 0x00 (unused bits), raw_point + let bit_string_content_len = 1 + raw_point.len(); // 0x00 byte + point + let mut bit_string = vec![0x03]; + encode_der_length(&mut bit_string, bit_string_content_len); + bit_string.push(0x00); // zero unused bits + bit_string.extend_from_slice(raw_point); + + // Outer SEQUENCE + let inner_len = alg_id_seq.len() + bit_string.len(); + let mut spki = vec![0x30]; + encode_der_length(&mut spki, inner_len); + spki.extend_from_slice(&alg_id_seq); + spki.extend_from_slice(&bit_string); + + Ok(spki) +} + +fn der_sequence(a: &[u8], b: &[u8]) -> Vec { + let content_len = a.len() + b.len(); + let mut seq = vec![0x30]; + encode_der_length(&mut seq, content_len); + seq.extend_from_slice(a); + seq.extend_from_slice(b); + seq +} + +fn encode_der_length(buf: &mut Vec, len: usize) { + if len < 0x80 { + buf.push(len as u8); + } else if len < 0x100 { + buf.push(0x81); + buf.push(len as u8); + } else { + buf.push(0x82); + buf.push((len >> 8) as u8); + buf.push(len as u8); + } +} + +fn generate_rsa_key_pair(key_size: u32, rsa_public_exponent: u64) -> Result { use pkcs8::EncodePrivateKey; use rsa::pkcs8::EncodePublicKey; + if !matches!(key_size, 2048 | 3072 | 4096) { + return Err(CertGenError::InvalidParameter( + format!("RSA key size must be 2048, 3072, or 4096; got {key_size}") + )); + } + + let exp = if rsa_public_exponent == 0 { + rsa::BigUint::from(65537u64) + } else { + rsa::BigUint::from(rsa_public_exponent) + }; + let mut rng = rand::thread_rng(); - let private_key = rsa::RsaPrivateKey::new(&mut rng, key_size as usize) + let private_key = rsa::RsaPrivateKey::new_with_exp(&mut rng, key_size as usize, &exp) .map_err(|e| CertGenError::KeyGenFailed(e.to_string()))?; let pkcs8_der = private_key.to_pkcs8_der() diff --git a/native-certgen/src/lib.rs b/native-certgen/src/lib.rs index c666214..2dfacc9 100644 --- a/native-certgen/src/lib.rs +++ b/native-certgen/src/lib.rs @@ -1,7 +1,4 @@ mod error; mod types; mod keygen; -// mod attestation; // Phase 2 pub mod keybox; -// mod certbuilder; // Phase 3 -// mod logging; // Phase 3 diff --git a/native-certgen/src/types.rs b/native-certgen/src/types.rs index c5e11f7..1e76542 100644 --- a/native-certgen/src/types.rs +++ b/native-certgen/src/types.rs @@ -37,7 +37,7 @@ impl TryFrom for EcCurve { 2 => Ok(Self::P384), 3 => Ok(Self::P521), 4 => Ok(Self::Curve25519), - _ => Err(CertGenError::UnsupportedCurve(value)), + _ => Err(CertGenError::UnsupportedEcCurve(value)), } } } @@ -51,6 +51,7 @@ pub enum KeyPurpose { Verify = 3, WrapKey = 5, AgreeKey = 6, + AttestKey = 7, } #[derive(Debug, Clone, Copy, PartialEq, Eq)]