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::{sct_list_from_x509, SCTVersion, SignatureAlgorithm, 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: issuer_key_hash.clone() },
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)
}
}