1 use super::{gen_keypair, KeyPair, KeyType, SubjectAttribute};
2 use crate::b64_encode;
3 use crate::crypto::HashFunction;
4 use crate::error::Error;
5 use openssl::asn1::Asn1Time;
6 use openssl::bn::{BigNum, MsbOption};
7 use openssl::hash::MessageDigest;
8 use openssl::stack::Stack;
9 use openssl::x509::extension::{BasicConstraints, SubjectAlternativeName};
10 use openssl::x509::{X509Builder, X509Extension, X509NameBuilder, X509Req, X509ReqBuilder, X509};
11 use std::collections::{HashMap, HashSet};
12 use std::net::IpAddr;
13 use std::time::{Duration, SystemTime, UNIX_EPOCH};
14 
get_digest(digest: HashFunction, key_pair: &KeyPair) -> MessageDigest15 fn get_digest(digest: HashFunction, key_pair: &KeyPair) -> MessageDigest {
16     #[cfg(not(any(ed25519, ed448)))]
17     let digest = digest.native_digest();
18     let _ = key_pair;
19     #[cfg(any(ed25519, ed448))]
20     let digest = match key_pair.key_type {
21         #[cfg(ed25519)]
22         KeyType::Ed25519 => MessageDigest::null(),
23         #[cfg(ed448)]
24         KeyType::Ed448 => MessageDigest::null(),
25         _ => digest.native_digest(),
26     };
27     digest
28 }
29 
30 pub struct Csr {
31     inner_csr: X509Req,
32 }
33 
34 impl Csr {
new( key_pair: &KeyPair, digest: HashFunction, domains: &[String], ips: &[String], subject_attributes: &HashMap<SubjectAttribute, String>, ) -> Result<Self, Error>35     pub fn new(
36         key_pair: &KeyPair,
37         digest: HashFunction,
38         domains: &[String],
39         ips: &[String],
40         subject_attributes: &HashMap<SubjectAttribute, String>,
41     ) -> Result<Self, Error> {
42         let mut builder = X509ReqBuilder::new()?;
43         builder.set_pubkey(&key_pair.inner_key)?;
44         if !subject_attributes.is_empty() {
45             let mut snb = X509NameBuilder::new()?;
46             for (sattr, val) in subject_attributes.iter() {
47                 snb.append_entry_by_nid(sattr.get_nid(), &val)?;
48             }
49             let name = snb.build();
50             builder.set_subject_name(&name)?;
51         }
52         let ctx = builder.x509v3_context(None);
53         let mut san = SubjectAlternativeName::new();
54         for dns in domains.iter() {
55             san.dns(&dns);
56         }
57         for ip in ips.iter() {
58             san.ip(&ip);
59         }
60         let san = san.build(&ctx)?;
61         let mut ext_stack = Stack::new()?;
62         ext_stack.push(san)?;
63         builder.add_extensions(&ext_stack)?;
64         let digest = get_digest(digest, key_pair);
65         builder.sign(&key_pair.inner_key, digest)?;
66         Ok(Csr {
67             inner_csr: builder.build(),
68         })
69     }
70 
to_der_base64(&self) -> Result<String, Error>71     pub fn to_der_base64(&self) -> Result<String, Error> {
72         let csr = self.inner_csr.to_der()?;
73         let csr = b64_encode(&csr);
74         Ok(csr)
75     }
76 
to_pem(&self) -> Result<String, Error>77     pub fn to_pem(&self) -> Result<String, Error> {
78         let csr = self.inner_csr.to_pem()?;
79         Ok(String::from_utf8(csr)?)
80     }
81 }
82 
83 pub struct X509Certificate {
84     pub inner_cert: X509,
85 }
86 
87 impl X509Certificate {
from_pem(pem_data: &[u8]) -> Result<Self, Error>88     pub fn from_pem(pem_data: &[u8]) -> Result<Self, Error> {
89         Ok(X509Certificate {
90             inner_cert: X509::from_pem(pem_data)?,
91         })
92     }
93 
from_pem_native(pem_data: &[u8]) -> Result<native_tls::Certificate, Error>94     pub fn from_pem_native(pem_data: &[u8]) -> Result<native_tls::Certificate, Error> {
95         Ok(native_tls::Certificate::from_pem(pem_data)?)
96     }
97 
from_acme_ext( domain: &str, acme_ext: &str, key_type: KeyType, digest: HashFunction, ) -> Result<(KeyPair, Self), Error>98     pub fn from_acme_ext(
99         domain: &str,
100         acme_ext: &str,
101         key_type: KeyType,
102         digest: HashFunction,
103     ) -> Result<(KeyPair, Self), Error> {
104         let key_pair = gen_keypair(key_type)?;
105         let digest = get_digest(digest, &key_pair);
106         let inner_cert = gen_certificate(domain, &key_pair, &digest, acme_ext)?;
107         let cert = X509Certificate { inner_cert };
108         Ok((key_pair, cert))
109     }
110 
expires_in(&self) -> Result<Duration, Error>111     pub fn expires_in(&self) -> Result<Duration, Error> {
112         let timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as libc::time_t;
113         let now = Asn1Time::from_unix(timestamp)?;
114         let not_after = self.inner_cert.not_after();
115         let diff = now.diff(not_after)?;
116         let nb_secs = diff.days * 24 * 60 * 60 + diff.secs;
117         let nb_secs = if nb_secs > 0 { nb_secs as u64 } else { 0 };
118         Ok(Duration::from_secs(nb_secs))
119     }
120 
subject_alt_names(&self) -> HashSet<String>121     pub fn subject_alt_names(&self) -> HashSet<String> {
122         match self.inner_cert.subject_alt_names() {
123             Some(s) => s
124                 .iter()
125                 .filter(|v| v.dnsname().is_some() || v.ipaddress().is_some())
126                 .map(|v| match v.dnsname() {
127                     Some(d) => d.to_string(),
128                     None => match v.ipaddress() {
129                         Some(i) => match i.len() {
130                             4 => {
131                                 let ipv4: [u8; 4] = [i[0], i[1], i[2], i[3]];
132                                 IpAddr::from(ipv4).to_string()
133                             }
134                             16 => {
135                                 let ipv6: [u8; 16] = [
136                                     i[0], i[1], i[2], i[3], i[4], i[5], i[6], i[7], i[8], i[9],
137                                     i[10], i[11], i[12], i[13], i[14], i[15],
138                                 ];
139                                 IpAddr::from(ipv6).to_string()
140                             }
141                             _ => String::new(),
142                         },
143                         None => String::new(),
144                     },
145                 })
146                 .collect(),
147             None => HashSet::new(),
148         }
149     }
150 }
151 
gen_certificate( domain: &str, key_pair: &KeyPair, digest: &MessageDigest, acme_ext: &str, ) -> Result<X509, Error>152 fn gen_certificate(
153     domain: &str,
154     key_pair: &KeyPair,
155     digest: &MessageDigest,
156     acme_ext: &str,
157 ) -> Result<X509, Error> {
158     let mut x509_name = X509NameBuilder::new()?;
159     x509_name.append_entry_by_text("O", super::APP_ORG)?;
160     let ca_name = format!("{} TLS-ALPN-01 Authority", super::APP_NAME);
161     x509_name.append_entry_by_text("CN", &ca_name)?;
162     let x509_name = x509_name.build();
163 
164     let mut builder = X509Builder::new()?;
165     builder.set_version(super::X509_VERSION)?;
166     let serial_number = {
167         let mut serial = BigNum::new()?;
168         serial.rand(super::CRT_SERIAL_NB_BITS - 1, MsbOption::MAYBE_ZERO, false)?;
169         serial.to_asn1_integer()?
170     };
171     builder.set_serial_number(&serial_number)?;
172     builder.set_subject_name(&x509_name)?;
173     builder.set_issuer_name(&x509_name)?;
174     builder.set_pubkey(&key_pair.inner_key)?;
175     let not_before = Asn1Time::days_from_now(0)?;
176     builder.set_not_before(&not_before)?;
177     let not_after = Asn1Time::days_from_now(super::CRT_NB_DAYS_VALIDITY)?;
178     builder.set_not_after(&not_after)?;
179 
180     builder.append_extension(BasicConstraints::new().build()?)?;
181     let ctx = builder.x509v3_context(None, None);
182     let san_ext = SubjectAlternativeName::new().dns(domain).build(&ctx)?;
183     builder.append_extension(san_ext)?;
184 
185     if !acme_ext.is_empty() {
186         let ctx = builder.x509v3_context(None, None);
187         let mut v: Vec<&str> = acme_ext.split('=').collect();
188         let value = v.pop().ok_or_else(|| Error::from(super::INVALID_EXT_MSG))?;
189         let acme_ext_name = v.pop().ok_or_else(|| Error::from(super::INVALID_EXT_MSG))?;
190         if !v.is_empty() {
191             return Err(Error::from(super::INVALID_EXT_MSG));
192         }
193         let acme_ext = X509Extension::new(None, Some(&ctx), &acme_ext_name, &value)
194             .map_err(|_| Error::from(super::INVALID_EXT_MSG))?;
195         builder
196             .append_extension(acme_ext)
197             .map_err(|_| Error::from(super::INVALID_EXT_MSG))?;
198     }
199 
200     builder.sign(&key_pair.inner_key, *digest)?;
201     let cert = builder.build();
202     Ok(cert)
203 }
204