luct-core 0.2.0

Core types and parsers for certificate transparency
Documentation
use crate::{
    CertificateChain, CtLog, Version,
    signature::{Signature, SignatureValidationError},
    store::Hashable,
    tree::HashOutput,
    utils::codec::{CodecError, Decode, Encode},
    v1::{
        LogEntry, LogId, SignatureType,
        extension::{CtExtensions, LeafIndex},
    },
};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::io::{Cursor, ErrorKind, IoSlice, Read, Write};

impl CtLog {
    pub fn validate_sct_v1(
        &self,
        cert: &CertificateChain,
        sct: &SignedCertificateTimestamp,
        as_precert: bool,
    ) -> Result<(), SignatureValidationError> {
        let timestamp = CertificateTimeStamp {
            sct_version: Version::V1,
            timestamp: sct.timestamp,
            entry: cert
                .as_log_entry_v1(as_precert)
                .map_err(SignatureValidationError::CodecError)?,
            extensions: sct.extensions.clone(),
        };

        sct.signature.validate(&timestamp, &self.config.key)
    }
}

/// See RFC 6962 3.2
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct SctList(Vec<SignedCertificateTimestamp>);

impl SctList {
    #[allow(dead_code)]
    pub fn new(scts: Vec<SignedCertificateTimestamp>) -> Self {
        Self(scts)
    }

    pub fn into_inner(self) -> Vec<SignedCertificateTimestamp> {
        self.0
    }
}

impl Encode for SctList {
    fn encode(&self, mut writer: impl Write) -> Result<(), CodecError> {
        let mut bytes = 0;
        let mut encoded_scts = vec![];
        for sct in &self.0 {
            let mut buf = Cursor::new(vec![0, 0]);
            buf.set_position(2);

            sct.encode(&mut buf)?;
            let mut buf = buf.into_inner();

            // Encode the length of the field
            let len = ((buf.len() - 2) as u16).to_be_bytes();
            buf[0] = len[0];
            buf[1] = len[1];

            // Add to byte counter for field size
            bytes += buf.len();
            encoded_scts.push(buf);
        }
        let mut slices = encoded_scts
            .iter()
            .map(|buf| IoSlice::new(buf))
            .collect::<Vec<_>>();

        let bytes: u16 = bytes.try_into().map_err(|_| CodecError::UnexpectedSize {
            read: bytes,
            expected: u16::MAX as usize,
        })?;

        bytes.encode(&mut writer)?;

        let mut slices: &mut [IoSlice] = &mut slices;
        while !slices.is_empty() {
            match writer.write_vectored(slices) {
                Ok(0) => {
                    return Err(CodecError::IoError(std::io::ErrorKind::WriteZero));
                }
                Ok(n) => IoSlice::advance_slices(&mut slices, n),
                Err(e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => return Err(e.into()),
            }
        }

        Ok(())
    }
}

impl Decode for SctList {
    fn decode(mut reader: impl Read) -> Result<Self, CodecError> {
        let length = u16::decode(&mut reader)?.into();
        let mut scts = vec![];

        let mut reader = reader.take(length);
        loop {
            match u16::decode(&mut reader) {
                Ok(_) => (),
                Err(CodecError::IoError(ErrorKind::UnexpectedEof)) => break,
                Err(err) => return Err(err),
            }

            let sct = SignedCertificateTimestamp::decode(&mut reader)?;
            scts.push(sct)
        }

        Ok(Self(scts))
    }
}

/// A signed certificate timestamp of version 1.
///
/// See RFC 6962 3.2
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SignedCertificateTimestamp {
    pub(crate) sct_version: Version,
    pub(crate) id: LogId,
    pub(crate) timestamp: u64,
    pub(crate) extensions: CtExtensions,
    pub(crate) signature: Signature<CertificateTimeStamp>,
}

impl SignedCertificateTimestamp {
    pub fn log_id(&self) -> crate::LogId {
        crate::LogId::V1(self.id.clone())
    }

    pub fn timestamp(&self) -> u64 {
        self.timestamp
    }

    pub fn leaf_index(&self) -> Option<LeafIndex> {
        self.extensions.leaf_index()
    }
}

impl Encode for SignedCertificateTimestamp {
    fn encode(&self, mut writer: impl Write) -> Result<(), CodecError> {
        self.sct_version.encode(&mut writer)?;
        self.id.encode(&mut writer)?;
        self.timestamp.encode(&mut writer)?;
        self.extensions.encode(&mut writer)?;
        self.signature.encode(&mut writer)?;
        Ok(())
    }
}

impl Decode for SignedCertificateTimestamp {
    fn decode(mut reader: impl Read) -> Result<Self, CodecError> {
        Ok(Self {
            sct_version: Version::decode(&mut reader)?,
            id: LogId::decode(&mut reader)?,
            timestamp: u64::decode(&mut reader)?,
            extensions: CtExtensions::decode(&mut reader)?,
            signature: Signature::decode(&mut reader)?,
        })
    }
}

impl Hashable for SignedCertificateTimestamp {
    fn hash(&self) -> HashOutput {
        let mut bytes = Cursor::new(vec![]);
        self.encode(&mut bytes).unwrap();
        Sha256::digest(bytes.into_inner()).into()
    }
}

/// See RFC 6962 3.2
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct CertificateTimeStamp {
    sct_version: Version,
    // SignatureType signature_type = certificate_timestamp;
    timestamp: u64,
    entry: LogEntry,
    extensions: CtExtensions,
}

impl Encode for CertificateTimeStamp {
    fn encode(&self, mut writer: impl Write) -> Result<(), CodecError> {
        self.sct_version.encode(&mut writer)?;
        SignatureType::CertificateTimeStamp.encode(&mut writer)?;
        self.timestamp.encode(&mut writer)?;
        self.entry.encode(&mut writer)?;
        self.extensions.encode(&mut writer)?;
        Ok(())
    }
}

impl Decode for CertificateTimeStamp {
    fn decode(mut reader: impl Read) -> Result<Self, CodecError> {
        let sct_version = Version::decode(&mut reader)?;
        let signature_type = SignatureType::decode(&mut reader)?;
        match signature_type {
            SignatureType::TreeHash => return Err(CodecError::UnexpectedVariant),
            SignatureType::CertificateTimeStamp => (),
        }
        let timestamp = u64::decode(&mut reader)?;
        let entry = LogEntry::decode(&mut reader)?;
        let extensions = CtExtensions::decode(&mut reader)?;

        Ok(Self {
            sct_version,
            timestamp,
            entry,
            extensions,
        })
    }
}