1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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)
}
}