From 6b96e1db515bf8fc1a2b1d60a2974daa125bc1ac Mon Sep 17 00:00:00 2001 From: Maple Date: Mon, 27 Apr 2026 19:08:46 +0200 Subject: [PATCH] add more specific error handeling to certs --- src/account/account.rs | 68 ++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/src/account/account.rs b/src/account/account.rs index 8e81069..7620367 100644 --- a/src/account/account.rs +++ b/src/account/account.rs @@ -15,6 +15,7 @@ use bytemuck::bytes_of; use chrono::{NaiveDate, NaiveDateTime, Utc}; use openssl::bn::BigNum; use openssl::ecdsa::EcdsaSig; +use openssl::error::ErrorStack; use rand::Rng; use rocket::http::Status; use rocket::request::{FromRequest, Outcome}; @@ -262,7 +263,7 @@ pub async fn link_certificate_to_pid( sqlx::query( "INSERT INTO certificate_pids (cert_hash, pid) VALUES ($1, $2) - ON CONFLICT DO NOTHING" + ON CONFLICT DO NOTHING", ) .bind(&hash[..]) .bind(pid) @@ -330,14 +331,17 @@ impl<'r> FromRequest<'r> for DeviceCert { }; let cert = match Certificate::new(cert_header) { - Some(c) => c, - None => return Outcome::Error((Status::BadGateway, INVALID_TOKEN_ERRORS)), + Ok(c) => c, + Err(e) => { + println!("error occurred whilest getting cert data: {}", e); + return Outcome::Error((Status::BadGateway, INVALID_TOKEN_ERRORS)); + } }; let hash = cert.hash(); let existing = sqlx::query_as::<_, CertificateRecord>( - "SELECT hash, banned FROM certificates WHERE hash = $1" + "SELECT hash, banned FROM certificates WHERE hash = $1", ) .bind(&hash[..]) .fetch_optional(pool) @@ -359,7 +363,7 @@ impl<'r> FromRequest<'r> for DeviceCert { } else { sqlx::query( "INSERT INTO certificates (hash, banned) - VALUES ($1, false)" + VALUES ($1, false)", ) .bind(&hash[..]) .execute(pool) @@ -396,19 +400,36 @@ MFIwEAYHKoZIzj0CAQYFK4EEABsDPgAEAP1WBBgs8XUJIQDDCK5IOZEbb5+h1TqV rwgzSUcrAAFxMWm1kf/TDL9z2nZkuo0N+VtNEQREZDXA7aQv -----END PUBLIC KEY-----"#; +#[derive(thiserror::Error, Debug)] +enum CertError { + #[error("unable to decode base64: {0}")] + Base64(#[from] base64::DecodeError), + #[error("unable to decode outer cert: {0}")] + OuterBinError(binrw::Error), + #[error("unable to decode inner cert: {0}")] + BinError(binrw::Error), + #[error("unable to create openssl ecdsa sig: {0}")] + EcdsaSig(ErrorStack), + #[error("error whilest validating signature: {0}")] + CryptoVerifError(ErrorStack), + #[error("certificate is not valid")] + ValidationError, +} + impl Certificate { - fn new(data: &str) -> Option { - let data = BASE64_STANDARD.decode(data).unwrap(); + fn new(data: &str) -> Result { + let data = BASE64_STANDARD.decode(data)?; - let cert = OuterCertificate::read(&mut Cursor::new(&data)).ok()?; + let cert = + OuterCertificate::read(&mut Cursor::new(&data)).map_err(CertError::OuterBinError)?; - println!("key"); + let key = openssl::ec::EcKey::public_key_from_pem(PUB_PEM).expect("invalid pem file"); - let key = openssl::ec::EcKey::public_key_from_pem(PUB_PEM).ok()?; + let sig_components = read_p1363(&cert.signature) + .expect("unable to read signature despite fixed size signature"); - let sig_components = read_p1363(&cert.signature)?; - - let sig = EcdsaSig::from_private_components(sig_components.0, sig_components.1).unwrap(); + let sig = EcdsaSig::from_private_components(sig_components.0, sig_components.1) + .map_err(CertError::EcdsaSig)?; let mut hasher = openssl::sha::Sha256::new(); @@ -416,11 +437,14 @@ impl Certificate { let hash = hasher.finish(); - if !sig.verify(&hash[..], &key).ok()? { - return None; + if !sig + .verify(&hash[..], &key) + .map_err(CertError::CryptoVerifError)? + { + return Err(CertError::ValidationError); } - Certificate::read(&mut Cursor::new(cert.data)).ok() + Certificate::read(&mut Cursor::new(cert.data)).map_err(CertError::BinError) } pub fn hash(&self) -> [u8; 32] { @@ -446,3 +470,15 @@ fn read_p1363(data: &[u8]) -> Option<(BigNum, BigNum)> { BigNum::from_slice(&data[half_len..]).ok()?, )) } + +#[cfg(test)] +mod test { + use crate::account::account::Certificate; + + #[test] + fn test() { + const CERT: &str = "AAEABQAsTGvAP1XNwh6JO+z47AkwVvqPYHRDO9X11BdhYwCIEipUJne32JSWMAYsFe1LqWT+0AkSnaDCkMWK1QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDMtTVMwMDAwMDAxMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAk5HNDQ0Y2FiZmMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1/THZAcTyFksveCuIl5fOM7Fb6ebi6mUqcVJlQQDqvsgMAW12aEaECJWV+Y6wXOL+E7yP5WJ/9VAd4jlgrvZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + Certificate::new(CERT).unwrap(); + } +}