use std::convert::TryInto;
use openssl::pkey::PKey;
use openssl::sha::sha256;
use openssl::x509::X509Ref;
use crate::Error;
use crate::internal::leaf_hash_constructors;
use crate::internal::openssl_ffi::{
SCTVersion, SignatureAlgorithm, sct_list_from_x509, x509_clone, x509_remove_sct_list,
x509_to_tbs,
};
use crate::internal::verify_dss_raw;
fn to_unknown_err(openssl_err: openssl::error::ErrorStack) -> Error {
Error::Unknown(format!("{}", openssl_err))
}
#[derive(Debug, Clone)]
pub struct SignedCertificateTimestamp {
pub log_id: [u8; 32],
pub timestamp: u64,
pub extensions_data: Vec<u8>,
pub entry: SctEntry,
pub signature_algorithm: SignatureAlgorithm,
pub raw_signature: Vec<u8>,
}
#[derive(Debug, Clone)]
pub enum SctEntry {
X509(Vec<u8>),
PreCert {
tbs: Vec<u8>,
issuer_key_hash: [u8; 32],
},
}
impl SignedCertificateTimestamp {
pub fn from_cert_sct_extension(
cert: &X509Ref,
issuer: &X509Ref,
) -> Result<Vec<SignedCertificateTimestamp>, Error> {
let sctlist = sct_list_from_x509(cert)?;
if sctlist.is_none() {
return Ok(Vec::new());
}
let sctlist = sctlist.unwrap();
let tbs = {
let mut cert_clone = x509_clone(cert).map_err(to_unknown_err)?;
x509_remove_sct_list(&mut cert_clone).map_err(to_unknown_err)?;
x509_to_tbs(&cert_clone).map_err(to_unknown_err)?
};
let issuer_key_hash = {
let k = issuer
.public_key()
.map_err(|e| {
Error::BadCertificate(format!("Can't parse public key from issuer: {}", e))
})?
.public_key_to_der()
.map_err(to_unknown_err)?;
sha256(&k)
};
let mut scts = Vec::with_capacity(sctlist.len());
for raw_sct in sctlist.into_iter() {
if raw_sct.version() != Some(SCTVersion::V1) {
return Err(Error::BadCertificate("Invalid SCT version.".to_owned()));
}
scts.push(SignedCertificateTimestamp {
log_id: raw_sct.log_id().try_into().map_err(|_| {
Error::BadCertificate("Expected log_id to have len 32".to_owned())
})?,
timestamp: raw_sct.timestamp(),
extensions_data: raw_sct.extensions().to_vec(),
entry: SctEntry::PreCert {
tbs: tbs.clone(),
issuer_key_hash,
},
signature_algorithm: raw_sct
.signature_algorithm()
.ok_or_else(|| Error::BadSct("Unknown signature algorithm.".to_owned()))?,
raw_signature: raw_sct.raw_signature().to_vec(),
});
}
Ok(scts)
}
pub fn derive_leaf_hash(&self) -> [u8; 32] {
match &self.entry {
SctEntry::PreCert {
tbs,
issuer_key_hash,
} => leaf_hash_constructors::with_precert(
&tbs[..],
&issuer_key_hash[..],
self.timestamp,
&self.extensions_data,
),
SctEntry::X509(x509) => {
leaf_hash_constructors::with_x509(x509, self.timestamp, &self.extensions_data)
}
}
}
pub fn verify(&self, log_public_key: &PKey<openssl::pkey::Public>) -> Result<(), Error> {
let mut signed_data: Vec<u8> = Vec::new();
signed_data.push(0u8);
signed_data.push(0u8);
signed_data.extend_from_slice(&self.timestamp.to_be_bytes());
signed_data.extend_from_slice(
&match &self.entry {
SctEntry::X509(_) => 0u16,
SctEntry::PreCert {
tbs: _,
issuer_key_hash: _,
} => 1u16,
}
.to_be_bytes(),
);
match &self.entry {
SctEntry::X509(cert) => {
let len = cert.len();
if len > 1 << 24 {
return Err(Error::BadSct("Certificate too long.".to_owned()));
}
signed_data.extend_from_slice(&u32::to_be_bytes(len as u32)[1..4]);
signed_data.extend_from_slice(cert);
}
SctEntry::PreCert {
tbs,
issuer_key_hash,
} => {
signed_data.extend_from_slice(issuer_key_hash);
let len = tbs.len();
if len > 1 << 24 {
return Err(Error::BadSct("TBS certificate too long.".to_owned()));
}
signed_data.extend_from_slice(&u32::to_be_bytes(len as u32)[1..4]);
signed_data.extend_from_slice(tbs);
}
}
let ext_len = self.extensions_data.len();
if ext_len > 1 << 16 {
return Err(Error::BadSct("extension data too long.".to_owned()));
}
signed_data.extend_from_slice(&u16::to_be_bytes(ext_len as u16));
signed_data.extend_from_slice(&self.extensions_data);
verify_dss_raw(
self.signature_algorithm,
log_public_key,
&self.raw_signature,
&signed_data,
)
}
}