~whynothugo/vdirsyncer-rs

d2b2e72c4457f079f922618a7f52dc0a7384c063 — Hugo Osvaldo Barrera 10 months ago e7e9f82 fingerprint-as-arrays
Use arrays for in-memory fingerprints

No point in paying the overhead for using Vec. The main issue here is
formatting: the error message for FingerprintError is still unreadable.
1 files changed, 29 insertions(+), 11 deletions(-)

M vdirsyncer/src/tls.rs
M vdirsyncer/src/tls.rs => vdirsyncer/src/tls.rs +29 -11
@@ 3,7 3,7 @@
// SPDX-License-Identifier: EUPL-1.2

//! Helpers used for advanced TLS configuration.
use std::{fs::File, io::BufReader, num::ParseIntError, path::Path, sync::Arc};
use std::{fs::File, io::BufReader, path::Path, sync::Arc};

use anyhow::{bail, Context};
use rustls::{


@@ 14,16 14,22 @@ use sha2::{Digest, Sha256};

/// Verifies that the fingerprint of a certificate matches.
pub(crate) struct FingerprintVerifier {
    fingerprint: Vec<u8>,
    fingerprint: [u8; 32],
}

impl FingerprintVerifier {
    // Create a new verifier from a hexadecimal fingerprint representation.
    /// Create a new verifier from a hexadecimal fingerprint representation.
    ///
    /// Fingerprints must not include any colons or separators; only hexadecimal characters.
    pub(crate) fn new(hex_fingerprint: &str) -> anyhow::Result<Self> {
        let fingerprint = (0..hex_fingerprint.len())
            .step_by(2)
            .map(|i| u8::from_str_radix(&hex_fingerprint[i..=i + 1], 16))
            .collect::<Result<Vec<u8>, ParseIntError>>()?;
        if hex_fingerprint.len() != 64 {
            bail!("fingerprint must be 64 bytes long");
        }

        let mut fingerprint = [0u8; 32];
        for i in 0..32 {
            fingerprint[i] = u8::from_str_radix(&hex_fingerprint[i * 2..=(i * 2) + 1], 16)?;
        }

        Ok(FingerprintVerifier { fingerprint })
    }


@@ 39,24 45,36 @@ impl ServerCertVerifier for FingerprintVerifier {
        _ocsp_response: &[u8],
        _now: std::time::SystemTime,
    ) -> Result<rustls::client::ServerCertVerified, rustls::Error> {
        let fingerprint = Sha256::digest(&end_entity.0).to_vec();
        let mut fingerprint = [0u8; 32];
        let mut hasher = Sha256::new();
        hasher.update(&end_entity.0);
        fingerprint.copy_from_slice(&hasher.finalize());

        if self.fingerprint == fingerprint {
            Ok(ServerCertVerified::assertion())
        } else {
            Err(rustls::Error::InvalidCertificate(CertificateError::Other(
                Arc::from(FingerprintError),
                Arc::from(FingerprintError {
                    expected: self.fingerprint.clone(),
                    received: fingerprint,
                }),
            )))
        }
    }
}

#[derive(Debug)]
struct FingerprintError;
struct FingerprintError {
    expected: [u8; 32],
    received: [u8; 32],
}

impl std::fmt::Display for FingerprintError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("certificate fingerprint does not match expectation")
        f.write_fmt(format_args!(
            "received certificate fingerprint ({:?}) does not match expectation ({:?})",
            self.received, self.expected,
        ))
    }
}