From 1afc6dc27ecece5715eaf5301cc6e129e5f932bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodolphe=20Br=C3=A9ard?= Date: Sat, 28 Jan 2023 17:37:34 +0100 Subject: [PATCH] Indent with tabs instead of spaces https://adamtuttle.codes/blog/2021/tabs-vs-spaces-its-an-accessibility-issue/ --- acme_common/build.rs | 30 +- acme_common/src/crypto.rs | 78 +- .../src/crypto/jws_signature_algorithm.rs | 122 +- acme_common/src/crypto/key_type.rs | 168 +-- acme_common/src/crypto/openssl_certificate.rs | 340 ++--- acme_common/src/crypto/openssl_hash.rs | 44 +- acme_common/src/crypto/openssl_keys.rs | 566 ++++---- .../src/crypto/openssl_subject_attribute.rs | 38 +- acme_common/src/crypto/openssl_version.rs | 38 +- acme_common/src/error.rs | 130 +- acme_common/src/lib.rs | 88 +- acme_common/src/logs.rs | 112 +- acme_common/src/tests/certificate.rs | 106 +- acme_common/src/tests/crypto_keys.rs | 492 +++---- acme_common/src/tests/hash.rs | 622 ++++----- acme_common/src/tests/idna.rs | 42 +- .../src/tests/jws_signature_algorithm.rs | 42 +- acmed/build.rs | 146 +- acmed/src/account.rs | 518 +++---- acmed/src/account/contact.rs | 130 +- acmed/src/account/storage.rs | 280 ++-- acmed/src/acme_proto.rs | 384 +++--- acmed/src/acme_proto/account.rs | 246 ++-- acmed/src/acme_proto/certificate.rs | 24 +- acmed/src/acme_proto/http.rs | 178 +-- acmed/src/acme_proto/structs.rs | 20 +- acmed/src/acme_proto/structs/account.rs | 272 ++-- acmed/src/acme_proto/structs/authorization.rs | 440 +++--- acmed/src/acme_proto/structs/directory.rs | 178 +-- acmed/src/acme_proto/structs/error.rs | 210 +-- acmed/src/acme_proto/structs/order.rs | 160 +-- acmed/src/certificate.rs | 342 ++--- acmed/src/config.rs | 1216 ++++++++--------- acmed/src/duration.rs | 58 +- acmed/src/endpoint.rs | 198 +-- acmed/src/hooks.rs | 294 ++-- acmed/src/http.rs | 378 ++--- acmed/src/identifier.rs | 220 +-- acmed/src/jws.rs | 310 ++--- acmed/src/logs.rs | 8 +- acmed/src/main.rs | 218 +-- acmed/src/main_event_loop.rs | 358 ++--- acmed/src/storage.rs | 418 +++--- acmed/src/template.rs | 102 +- rustfmt.toml | 1 + tacd/build.rs | 44 +- tacd/src/main.rs | 148 +- tacd/src/openssl_server.rs | 66 +- 48 files changed, 5312 insertions(+), 5311 deletions(-) diff --git a/acme_common/build.rs b/acme_common/build.rs index d159ea9..d9d9b5e 100644 --- a/acme_common/build.rs +++ b/acme_common/build.rs @@ -1,23 +1,23 @@ use std::env; macro_rules! set_rustc_env_var { - ($name: expr, $value: expr) => {{ - println!("cargo:rustc-env={}={}", $name, $value); - }}; + ($name: expr, $value: expr) => {{ + println!("cargo:rustc-env={}={}", $name, $value); + }}; } #[allow(clippy::unusual_byte_groupings)] fn main() { - if let Ok(v) = env::var("DEP_OPENSSL_VERSION_NUMBER") { - let version = u64::from_str_radix(&v, 16).unwrap(); - // OpenSSL 1.1.1 - if version >= 0x1_01_01_00_0 { - println!("cargo:rustc-cfg=ed25519"); - println!("cargo:rustc-cfg=ed448"); - } - set_rustc_env_var!("ACMED_TLS_LIB_NAME", "OpenSSL"); - } - if env::var("DEP_OPENSSL_LIBRESSL_VERSION_NUMBER").is_ok() { - set_rustc_env_var!("ACMED_TLS_LIB_NAME", "LibreSSL"); - } + if let Ok(v) = env::var("DEP_OPENSSL_VERSION_NUMBER") { + let version = u64::from_str_radix(&v, 16).unwrap(); + // OpenSSL 1.1.1 + if version >= 0x1_01_01_00_0 { + println!("cargo:rustc-cfg=ed25519"); + println!("cargo:rustc-cfg=ed448"); + } + set_rustc_env_var!("ACMED_TLS_LIB_NAME", "OpenSSL"); + } + if env::var("DEP_OPENSSL_LIBRESSL_VERSION_NUMBER").is_ok() { + set_rustc_env_var!("ACMED_TLS_LIB_NAME", "LibreSSL"); + } } diff --git a/acme_common/src/crypto.rs b/acme_common/src/crypto.rs index a2d0592..dfe6be0 100644 --- a/acme_common/src/crypto.rs +++ b/acme_common/src/crypto.rs @@ -24,59 +24,59 @@ pub const CRT_NB_DAYS_VALIDITY: u32 = 7; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum BaseSubjectAttribute { - CountryName, - GenerationQualifier, - GivenName, - Initials, - LocalityName, - Name, - OrganizationName, - OrganizationalUnitName, - Pkcs9EmailAddress, - PostalAddress, - PostalCode, - StateOrProvinceName, - Street, - Surname, - Title, + CountryName, + GenerationQualifier, + GivenName, + Initials, + LocalityName, + Name, + OrganizationName, + OrganizationalUnitName, + Pkcs9EmailAddress, + PostalAddress, + PostalCode, + StateOrProvinceName, + Street, + Surname, + Title, } #[derive(Clone, Copy, Debug, PartialEq)] pub enum BaseHashFunction { - Sha256, - Sha384, - Sha512, + Sha256, + Sha384, + Sha512, } impl BaseHashFunction { - pub fn list_possible_values() -> Vec<&'static str> { - vec!["sha256", "sha384", "sha512"] - } + pub fn list_possible_values() -> Vec<&'static str> { + vec!["sha256", "sha384", "sha512"] + } } impl FromStr for BaseHashFunction { - type Err = Error; + type Err = Error; - fn from_str(s: &str) -> Result { - let s = s.to_lowercase().replace(['-', '_'], ""); - match s.as_str() { - "sha256" => Ok(BaseHashFunction::Sha256), - "sha384" => Ok(BaseHashFunction::Sha384), - "sha512" => Ok(BaseHashFunction::Sha512), - _ => Err(format!("{}: unknown hash function.", s).into()), - } - } + fn from_str(s: &str) -> Result { + let s = s.to_lowercase().replace(['-', '_'], ""); + match s.as_str() { + "sha256" => Ok(BaseHashFunction::Sha256), + "sha384" => Ok(BaseHashFunction::Sha384), + "sha512" => Ok(BaseHashFunction::Sha512), + _ => Err(format!("{}: unknown hash function.", s).into()), + } + } } impl fmt::Display for BaseHashFunction { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match self { - BaseHashFunction::Sha256 => "sha256", - BaseHashFunction::Sha384 => "sha384", - BaseHashFunction::Sha512 => "sha512", - }; - write!(f, "{}", s) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + BaseHashFunction::Sha256 => "sha256", + BaseHashFunction::Sha384 => "sha384", + BaseHashFunction::Sha512 => "sha512", + }; + write!(f, "{}", s) + } } pub use jws_signature_algorithm::JwsSignatureAlgorithm; diff --git a/acme_common/src/crypto/jws_signature_algorithm.rs b/acme_common/src/crypto/jws_signature_algorithm.rs index 8b2403c..26bcf44 100644 --- a/acme_common/src/crypto/jws_signature_algorithm.rs +++ b/acme_common/src/crypto/jws_signature_algorithm.rs @@ -4,78 +4,78 @@ use std::str::FromStr; #[derive(Clone, Copy, Debug, PartialEq)] pub enum JwsSignatureAlgorithm { - Hs256, - Hs384, - Hs512, - Rs256, - Es256, - Es384, - Es512, - #[cfg(ed25519)] - Ed25519, - #[cfg(ed448)] - Ed448, + Hs256, + Hs384, + Hs512, + Rs256, + Es256, + Es384, + Es512, + #[cfg(ed25519)] + Ed25519, + #[cfg(ed448)] + Ed448, } impl FromStr for JwsSignatureAlgorithm { - type Err = Error; + type Err = Error; - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "hs256" => Ok(JwsSignatureAlgorithm::Hs256), - "hs384" => Ok(JwsSignatureAlgorithm::Hs384), - "hs512" => Ok(JwsSignatureAlgorithm::Hs512), - "rs256" => Ok(JwsSignatureAlgorithm::Rs256), - "es256" => Ok(JwsSignatureAlgorithm::Es256), - "es384" => Ok(JwsSignatureAlgorithm::Es384), - "es512" => Ok(JwsSignatureAlgorithm::Es512), - #[cfg(ed25519)] - "ed25519" => Ok(JwsSignatureAlgorithm::Ed25519), - #[cfg(ed448)] - "ed448" => Ok(JwsSignatureAlgorithm::Ed448), - _ => Err(format!("{}: unknown algorithm.", s).into()), - } - } + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "hs256" => Ok(JwsSignatureAlgorithm::Hs256), + "hs384" => Ok(JwsSignatureAlgorithm::Hs384), + "hs512" => Ok(JwsSignatureAlgorithm::Hs512), + "rs256" => Ok(JwsSignatureAlgorithm::Rs256), + "es256" => Ok(JwsSignatureAlgorithm::Es256), + "es384" => Ok(JwsSignatureAlgorithm::Es384), + "es512" => Ok(JwsSignatureAlgorithm::Es512), + #[cfg(ed25519)] + "ed25519" => Ok(JwsSignatureAlgorithm::Ed25519), + #[cfg(ed448)] + "ed448" => Ok(JwsSignatureAlgorithm::Ed448), + _ => Err(format!("{}: unknown algorithm.", s).into()), + } + } } impl fmt::Display for JwsSignatureAlgorithm { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match self { - JwsSignatureAlgorithm::Hs256 => "HS256", - JwsSignatureAlgorithm::Hs384 => "HS384", - JwsSignatureAlgorithm::Hs512 => "HS512", - JwsSignatureAlgorithm::Rs256 => "RS256", - JwsSignatureAlgorithm::Es256 => "ES256", - JwsSignatureAlgorithm::Es384 => "ES384", - JwsSignatureAlgorithm::Es512 => "ES512", - #[cfg(ed25519)] - JwsSignatureAlgorithm::Ed25519 => "Ed25519", - #[cfg(ed448)] - JwsSignatureAlgorithm::Ed448 => "Ed448", - }; - write!(f, "{}", s) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + JwsSignatureAlgorithm::Hs256 => "HS256", + JwsSignatureAlgorithm::Hs384 => "HS384", + JwsSignatureAlgorithm::Hs512 => "HS512", + JwsSignatureAlgorithm::Rs256 => "RS256", + JwsSignatureAlgorithm::Es256 => "ES256", + JwsSignatureAlgorithm::Es384 => "ES384", + JwsSignatureAlgorithm::Es512 => "ES512", + #[cfg(ed25519)] + JwsSignatureAlgorithm::Ed25519 => "Ed25519", + #[cfg(ed448)] + JwsSignatureAlgorithm::Ed448 => "Ed448", + }; + write!(f, "{}", s) + } } #[cfg(test)] mod tests { - use super::JwsSignatureAlgorithm; - use std::str::FromStr; + use super::JwsSignatureAlgorithm; + use std::str::FromStr; - #[test] - fn test_es256_from_str() { - let variants = ["ES256", "Es256", "es256"]; - for v in variants.iter() { - let a = JwsSignatureAlgorithm::from_str(v); - assert!(a.is_ok()); - let a = a.unwrap(); - assert_eq!(a, JwsSignatureAlgorithm::Es256); - } - } + #[test] + fn test_es256_from_str() { + let variants = ["ES256", "Es256", "es256"]; + for v in variants.iter() { + let a = JwsSignatureAlgorithm::from_str(v); + assert!(a.is_ok()); + let a = a.unwrap(); + assert_eq!(a, JwsSignatureAlgorithm::Es256); + } + } - #[test] - fn test_es256_to_str() { - let a = JwsSignatureAlgorithm::Es256; - assert_eq!(a.to_string().as_str(), "ES256"); - } + #[test] + fn test_es256_to_str() { + let a = JwsSignatureAlgorithm::Es256; + assert_eq!(a.to_string().as_str(), "ES256"); + } } diff --git a/acme_common/src/crypto/key_type.rs b/acme_common/src/crypto/key_type.rs index 36ae6d8..513afc2 100644 --- a/acme_common/src/crypto/key_type.rs +++ b/acme_common/src/crypto/key_type.rs @@ -5,100 +5,100 @@ use std::str::FromStr; #[derive(Clone, Copy, Debug, PartialEq)] pub enum KeyType { - Rsa2048, - Rsa4096, - EcdsaP256, - EcdsaP384, - EcdsaP521, - #[cfg(ed25519)] - Ed25519, - #[cfg(ed448)] - Ed448, + Rsa2048, + Rsa4096, + EcdsaP256, + EcdsaP384, + EcdsaP521, + #[cfg(ed25519)] + Ed25519, + #[cfg(ed448)] + Ed448, } impl KeyType { - pub fn get_default_signature_alg(&self) -> JwsSignatureAlgorithm { - match self { - KeyType::Rsa2048 | KeyType::Rsa4096 => JwsSignatureAlgorithm::Rs256, - KeyType::EcdsaP256 => JwsSignatureAlgorithm::Es256, - KeyType::EcdsaP384 => JwsSignatureAlgorithm::Es384, - KeyType::EcdsaP521 => JwsSignatureAlgorithm::Es512, - #[cfg(ed25519)] - KeyType::Ed25519 => JwsSignatureAlgorithm::Ed25519, - #[cfg(ed448)] - KeyType::Ed448 => JwsSignatureAlgorithm::Ed448, - } - } + pub fn get_default_signature_alg(&self) -> JwsSignatureAlgorithm { + match self { + KeyType::Rsa2048 | KeyType::Rsa4096 => JwsSignatureAlgorithm::Rs256, + KeyType::EcdsaP256 => JwsSignatureAlgorithm::Es256, + KeyType::EcdsaP384 => JwsSignatureAlgorithm::Es384, + KeyType::EcdsaP521 => JwsSignatureAlgorithm::Es512, + #[cfg(ed25519)] + KeyType::Ed25519 => JwsSignatureAlgorithm::Ed25519, + #[cfg(ed448)] + KeyType::Ed448 => JwsSignatureAlgorithm::Ed448, + } + } - pub fn check_alg_compatibility(&self, alg: &JwsSignatureAlgorithm) -> Result<(), Error> { - let ok = match self { - KeyType::Rsa2048 | KeyType::Rsa4096 => *alg == JwsSignatureAlgorithm::Rs256, - KeyType::EcdsaP256 | KeyType::EcdsaP384 | KeyType::EcdsaP521 => { - *alg == self.get_default_signature_alg() - } - #[cfg(ed25519)] - KeyType::Ed25519 => *alg == self.get_default_signature_alg(), - #[cfg(ed448)] - KeyType::Ed448 => *alg == self.get_default_signature_alg(), - }; - if ok { - Ok(()) - } else { - let err_msg = format!( - "incompatible signature algorithm: {} cannot be used with an {} key", - alg, self - ); - Err(err_msg.into()) - } - } + pub fn check_alg_compatibility(&self, alg: &JwsSignatureAlgorithm) -> Result<(), Error> { + let ok = match self { + KeyType::Rsa2048 | KeyType::Rsa4096 => *alg == JwsSignatureAlgorithm::Rs256, + KeyType::EcdsaP256 | KeyType::EcdsaP384 | KeyType::EcdsaP521 => { + *alg == self.get_default_signature_alg() + } + #[cfg(ed25519)] + KeyType::Ed25519 => *alg == self.get_default_signature_alg(), + #[cfg(ed448)] + KeyType::Ed448 => *alg == self.get_default_signature_alg(), + }; + if ok { + Ok(()) + } else { + let err_msg = format!( + "incompatible signature algorithm: {} cannot be used with an {} key", + alg, self + ); + Err(err_msg.into()) + } + } - pub fn list_possible_values() -> Vec<&'static str> { - vec![ - "rsa2048", - "rsa4096", - "ecdsa-p256", - "ecdsa-p384", - "ecdsa-p521", - #[cfg(ed25519)] - "ed25519", - #[cfg(ed448)] - "ed448", - ] - } + pub fn list_possible_values() -> Vec<&'static str> { + vec![ + "rsa2048", + "rsa4096", + "ecdsa-p256", + "ecdsa-p384", + "ecdsa-p521", + #[cfg(ed25519)] + "ed25519", + #[cfg(ed448)] + "ed448", + ] + } } impl FromStr for KeyType { - type Err = Error; + type Err = Error; - fn from_str(s: &str) -> Result { - match s.to_lowercase().replace('-', "_").as_str() { - "rsa2048" => Ok(KeyType::Rsa2048), - "rsa4096" => Ok(KeyType::Rsa4096), - "ecdsa_p256" => Ok(KeyType::EcdsaP256), - "ecdsa_p384" => Ok(KeyType::EcdsaP384), - "ecdsa_p521" => Ok(KeyType::EcdsaP521), - #[cfg(ed25519)] - "ed25519" => Ok(KeyType::Ed25519), - #[cfg(ed448)] - "ed448" => Ok(KeyType::Ed448), - _ => Err(format!("{}: unknown algorithm", s).into()), - } - } + fn from_str(s: &str) -> Result { + match s.to_lowercase().replace('-', "_").as_str() { + "rsa2048" => Ok(KeyType::Rsa2048), + "rsa4096" => Ok(KeyType::Rsa4096), + "ecdsa_p256" => Ok(KeyType::EcdsaP256), + "ecdsa_p384" => Ok(KeyType::EcdsaP384), + "ecdsa_p521" => Ok(KeyType::EcdsaP521), + #[cfg(ed25519)] + "ed25519" => Ok(KeyType::Ed25519), + #[cfg(ed448)] + "ed448" => Ok(KeyType::Ed448), + _ => Err(format!("{}: unknown algorithm", s).into()), + } + } } impl fmt::Display for KeyType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match self { - KeyType::Rsa2048 => "rsa2048", - KeyType::Rsa4096 => "rsa4096", - KeyType::EcdsaP256 => "ecdsa-p256", - KeyType::EcdsaP384 => "ecdsa-p384", - KeyType::EcdsaP521 => "ecdsa-p521", - #[cfg(ed25519)] - KeyType::Ed25519 => "ed25519", - #[cfg(ed448)] - KeyType::Ed448 => "ed448", - }; - write!(f, "{}", s) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + KeyType::Rsa2048 => "rsa2048", + KeyType::Rsa4096 => "rsa4096", + KeyType::EcdsaP256 => "ecdsa-p256", + KeyType::EcdsaP384 => "ecdsa-p384", + KeyType::EcdsaP521 => "ecdsa-p521", + #[cfg(ed25519)] + KeyType::Ed25519 => "ed25519", + #[cfg(ed448)] + KeyType::Ed448 => "ed448", + }; + write!(f, "{}", s) + } } diff --git a/acme_common/src/crypto/openssl_certificate.rs b/acme_common/src/crypto/openssl_certificate.rs index 287dc04..10d40d5 100644 --- a/acme_common/src/crypto/openssl_certificate.rs +++ b/acme_common/src/crypto/openssl_certificate.rs @@ -13,190 +13,190 @@ use std::net::IpAddr; use std::time::Duration; fn get_digest(digest: HashFunction, key_pair: &KeyPair) -> MessageDigest { - #[cfg(not(any(ed25519, ed448)))] - let digest = digest.native_digest(); - let _ = key_pair; - #[cfg(any(ed25519, ed448))] - let digest = match key_pair.key_type { - #[cfg(ed25519)] - KeyType::Ed25519 => MessageDigest::null(), - #[cfg(ed448)] - KeyType::Ed448 => MessageDigest::null(), - _ => digest.native_digest(), - }; - digest + #[cfg(not(any(ed25519, ed448)))] + let digest = digest.native_digest(); + let _ = key_pair; + #[cfg(any(ed25519, ed448))] + let digest = match key_pair.key_type { + #[cfg(ed25519)] + KeyType::Ed25519 => MessageDigest::null(), + #[cfg(ed448)] + KeyType::Ed448 => MessageDigest::null(), + _ => digest.native_digest(), + }; + digest } pub struct Csr { - inner_csr: X509Req, + inner_csr: X509Req, } impl Csr { - pub fn new( - key_pair: &KeyPair, - digest: HashFunction, - domains: &[String], - ips: &[String], - subject_attributes: &HashMap, - ) -> Result { - let mut builder = X509ReqBuilder::new()?; - builder.set_pubkey(&key_pair.inner_key)?; - if !subject_attributes.is_empty() { - let mut snb = X509NameBuilder::new()?; - for (sattr, val) in subject_attributes.iter() { - snb.append_entry_by_nid(sattr.get_nid(), val)?; - } - let name = snb.build(); - builder.set_subject_name(&name)?; - } - let ctx = builder.x509v3_context(None); - let mut san = SubjectAlternativeName::new(); - for dns in domains.iter() { - san.dns(dns); - } - for ip in ips.iter() { - san.ip(ip); - } - let san = san.build(&ctx)?; - let mut ext_stack = Stack::new()?; - ext_stack.push(san)?; - builder.add_extensions(&ext_stack)?; - let digest = get_digest(digest, key_pair); - builder.sign(&key_pair.inner_key, digest)?; - Ok(Csr { - inner_csr: builder.build(), - }) - } - - pub fn to_der_base64(&self) -> Result { - let csr = self.inner_csr.to_der()?; - let csr = b64_encode(&csr); - Ok(csr) - } - - pub fn to_pem(&self) -> Result { - let csr = self.inner_csr.to_pem()?; - Ok(String::from_utf8(csr)?) - } + pub fn new( + key_pair: &KeyPair, + digest: HashFunction, + domains: &[String], + ips: &[String], + subject_attributes: &HashMap, + ) -> Result { + let mut builder = X509ReqBuilder::new()?; + builder.set_pubkey(&key_pair.inner_key)?; + if !subject_attributes.is_empty() { + let mut snb = X509NameBuilder::new()?; + for (sattr, val) in subject_attributes.iter() { + snb.append_entry_by_nid(sattr.get_nid(), val)?; + } + let name = snb.build(); + builder.set_subject_name(&name)?; + } + let ctx = builder.x509v3_context(None); + let mut san = SubjectAlternativeName::new(); + for dns in domains.iter() { + san.dns(dns); + } + for ip in ips.iter() { + san.ip(ip); + } + let san = san.build(&ctx)?; + let mut ext_stack = Stack::new()?; + ext_stack.push(san)?; + builder.add_extensions(&ext_stack)?; + let digest = get_digest(digest, key_pair); + builder.sign(&key_pair.inner_key, digest)?; + Ok(Csr { + inner_csr: builder.build(), + }) + } + + pub fn to_der_base64(&self) -> Result { + let csr = self.inner_csr.to_der()?; + let csr = b64_encode(&csr); + Ok(csr) + } + + pub fn to_pem(&self) -> Result { + let csr = self.inner_csr.to_pem()?; + Ok(String::from_utf8(csr)?) + } } pub struct X509Certificate { - pub inner_cert: X509, + pub inner_cert: X509, } impl X509Certificate { - pub fn from_pem(pem_data: &[u8]) -> Result { - Ok(X509Certificate { - inner_cert: X509::from_pem(pem_data)?, - }) - } - - pub fn from_pem_native(pem_data: &[u8]) -> Result { - Ok(native_tls::Certificate::from_pem(pem_data)?) - } - - pub fn from_acme_ext( - domain: &str, - acme_ext: &str, - key_type: KeyType, - digest: HashFunction, - ) -> Result<(KeyPair, Self), Error> { - let key_pair = gen_keypair(key_type)?; - let digest = get_digest(digest, &key_pair); - let inner_cert = gen_certificate(domain, &key_pair, &digest, acme_ext)?; - let cert = X509Certificate { inner_cert }; - Ok((key_pair, cert)) - } - - pub fn expires_in(&self) -> Result { - let now = Asn1Time::days_from_now(0)?; - let not_after = self.inner_cert.not_after(); - let diff = now.diff(not_after)?; - let nb_secs = diff.days * 24 * 60 * 60 + diff.secs; - let nb_secs = if nb_secs > 0 { nb_secs as u64 } else { 0 }; - Ok(Duration::from_secs(nb_secs)) - } - - pub fn subject_alt_names(&self) -> HashSet { - match self.inner_cert.subject_alt_names() { - Some(s) => s - .iter() - .filter(|v| v.dnsname().is_some() || v.ipaddress().is_some()) - .map(|v| match v.dnsname() { - Some(d) => d.to_string(), - None => match v.ipaddress() { - Some(i) => match i.len() { - 4 => { - let ipv4: [u8; 4] = [i[0], i[1], i[2], i[3]]; - IpAddr::from(ipv4).to_string() - } - 16 => { - let ipv6: [u8; 16] = [ - i[0], i[1], i[2], i[3], i[4], i[5], i[6], i[7], i[8], i[9], - i[10], i[11], i[12], i[13], i[14], i[15], - ]; - IpAddr::from(ipv6).to_string() - } - _ => String::new(), - }, - None => String::new(), - }, - }) - .collect(), - None => HashSet::new(), - } - } + pub fn from_pem(pem_data: &[u8]) -> Result { + Ok(X509Certificate { + inner_cert: X509::from_pem(pem_data)?, + }) + } + + pub fn from_pem_native(pem_data: &[u8]) -> Result { + Ok(native_tls::Certificate::from_pem(pem_data)?) + } + + pub fn from_acme_ext( + domain: &str, + acme_ext: &str, + key_type: KeyType, + digest: HashFunction, + ) -> Result<(KeyPair, Self), Error> { + let key_pair = gen_keypair(key_type)?; + let digest = get_digest(digest, &key_pair); + let inner_cert = gen_certificate(domain, &key_pair, &digest, acme_ext)?; + let cert = X509Certificate { inner_cert }; + Ok((key_pair, cert)) + } + + pub fn expires_in(&self) -> Result { + let now = Asn1Time::days_from_now(0)?; + let not_after = self.inner_cert.not_after(); + let diff = now.diff(not_after)?; + let nb_secs = diff.days * 24 * 60 * 60 + diff.secs; + let nb_secs = if nb_secs > 0 { nb_secs as u64 } else { 0 }; + Ok(Duration::from_secs(nb_secs)) + } + + pub fn subject_alt_names(&self) -> HashSet { + match self.inner_cert.subject_alt_names() { + Some(s) => s + .iter() + .filter(|v| v.dnsname().is_some() || v.ipaddress().is_some()) + .map(|v| match v.dnsname() { + Some(d) => d.to_string(), + None => match v.ipaddress() { + Some(i) => match i.len() { + 4 => { + let ipv4: [u8; 4] = [i[0], i[1], i[2], i[3]]; + IpAddr::from(ipv4).to_string() + } + 16 => { + let ipv6: [u8; 16] = [ + i[0], i[1], i[2], i[3], i[4], i[5], i[6], i[7], i[8], i[9], + i[10], i[11], i[12], i[13], i[14], i[15], + ]; + IpAddr::from(ipv6).to_string() + } + _ => String::new(), + }, + None => String::new(), + }, + }) + .collect(), + None => HashSet::new(), + } + } } fn gen_certificate( - domain: &str, - key_pair: &KeyPair, - digest: &MessageDigest, - acme_ext: &str, + domain: &str, + key_pair: &KeyPair, + digest: &MessageDigest, + acme_ext: &str, ) -> Result { - let mut x509_name = X509NameBuilder::new()?; - x509_name.append_entry_by_text("O", super::APP_ORG)?; - let ca_name = format!("{} TLS-ALPN-01 Authority", super::APP_NAME); - x509_name.append_entry_by_text("CN", &ca_name)?; - let x509_name = x509_name.build(); - - let mut builder = X509Builder::new()?; - builder.set_version(super::X509_VERSION)?; - let serial_number = { - let mut serial = BigNum::new()?; - serial.rand(super::CRT_SERIAL_NB_BITS - 1, MsbOption::MAYBE_ZERO, false)?; - serial.to_asn1_integer()? - }; - builder.set_serial_number(&serial_number)?; - builder.set_subject_name(&x509_name)?; - builder.set_issuer_name(&x509_name)?; - builder.set_pubkey(&key_pair.inner_key)?; - let not_before = Asn1Time::days_from_now(0)?; - builder.set_not_before(¬_before)?; - let not_after = Asn1Time::days_from_now(super::CRT_NB_DAYS_VALIDITY)?; - builder.set_not_after(¬_after)?; - - builder.append_extension(BasicConstraints::new().build()?)?; - let ctx = builder.x509v3_context(None, None); - let san_ext = SubjectAlternativeName::new().dns(domain).build(&ctx)?; - builder.append_extension(san_ext)?; - - if !acme_ext.is_empty() { - let ctx = builder.x509v3_context(None, None); - let mut v: Vec<&str> = acme_ext.split('=').collect(); - let value = v.pop().ok_or_else(|| Error::from(super::INVALID_EXT_MSG))?; - let acme_ext_name = v.pop().ok_or_else(|| Error::from(super::INVALID_EXT_MSG))?; - if !v.is_empty() { - return Err(Error::from(super::INVALID_EXT_MSG)); - } - let acme_ext = X509Extension::new(None, Some(&ctx), acme_ext_name, value) - .map_err(|_| Error::from(super::INVALID_EXT_MSG))?; - builder - .append_extension(acme_ext) - .map_err(|_| Error::from(super::INVALID_EXT_MSG))?; - } - - builder.sign(&key_pair.inner_key, *digest)?; - let cert = builder.build(); - Ok(cert) + let mut x509_name = X509NameBuilder::new()?; + x509_name.append_entry_by_text("O", super::APP_ORG)?; + let ca_name = format!("{} TLS-ALPN-01 Authority", super::APP_NAME); + x509_name.append_entry_by_text("CN", &ca_name)?; + let x509_name = x509_name.build(); + + let mut builder = X509Builder::new()?; + builder.set_version(super::X509_VERSION)?; + let serial_number = { + let mut serial = BigNum::new()?; + serial.rand(super::CRT_SERIAL_NB_BITS - 1, MsbOption::MAYBE_ZERO, false)?; + serial.to_asn1_integer()? + }; + builder.set_serial_number(&serial_number)?; + builder.set_subject_name(&x509_name)?; + builder.set_issuer_name(&x509_name)?; + builder.set_pubkey(&key_pair.inner_key)?; + let not_before = Asn1Time::days_from_now(0)?; + builder.set_not_before(¬_before)?; + let not_after = Asn1Time::days_from_now(super::CRT_NB_DAYS_VALIDITY)?; + builder.set_not_after(¬_after)?; + + builder.append_extension(BasicConstraints::new().build()?)?; + let ctx = builder.x509v3_context(None, None); + let san_ext = SubjectAlternativeName::new().dns(domain).build(&ctx)?; + builder.append_extension(san_ext)?; + + if !acme_ext.is_empty() { + let ctx = builder.x509v3_context(None, None); + let mut v: Vec<&str> = acme_ext.split('=').collect(); + let value = v.pop().ok_or_else(|| Error::from(super::INVALID_EXT_MSG))?; + let acme_ext_name = v.pop().ok_or_else(|| Error::from(super::INVALID_EXT_MSG))?; + if !v.is_empty() { + return Err(Error::from(super::INVALID_EXT_MSG)); + } + let acme_ext = X509Extension::new(None, Some(&ctx), acme_ext_name, value) + .map_err(|_| Error::from(super::INVALID_EXT_MSG))?; + builder + .append_extension(acme_ext) + .map_err(|_| Error::from(super::INVALID_EXT_MSG))?; + } + + builder.sign(&key_pair.inner_key, *digest)?; + let cert = builder.build(); + Ok(cert) } diff --git a/acme_common/src/crypto/openssl_hash.rs b/acme_common/src/crypto/openssl_hash.rs index 707aef3..18fdcd6 100644 --- a/acme_common/src/crypto/openssl_hash.rs +++ b/acme_common/src/crypto/openssl_hash.rs @@ -7,28 +7,28 @@ use openssl::sign::Signer; pub type HashFunction = super::BaseHashFunction; impl HashFunction { - pub fn hash(&self, data: &[u8]) -> Vec { - match self { - HashFunction::Sha256 => sha256(data).to_vec(), - HashFunction::Sha384 => sha384(data).to_vec(), - HashFunction::Sha512 => sha512(data).to_vec(), - } - } + pub fn hash(&self, data: &[u8]) -> Vec { + match self { + HashFunction::Sha256 => sha256(data).to_vec(), + HashFunction::Sha384 => sha384(data).to_vec(), + HashFunction::Sha512 => sha512(data).to_vec(), + } + } - pub fn hmac(&self, key: &[u8], data: &[u8]) -> Result, Error> { - let key = PKey::hmac(key)?; - let h_func = self.native_digest(); - let mut signer = Signer::new(h_func, &key)?; - signer.update(data)?; - let res = signer.sign_to_vec()?; - Ok(res) - } + pub fn hmac(&self, key: &[u8], data: &[u8]) -> Result, Error> { + let key = PKey::hmac(key)?; + let h_func = self.native_digest(); + let mut signer = Signer::new(h_func, &key)?; + signer.update(data)?; + let res = signer.sign_to_vec()?; + Ok(res) + } - pub(crate) fn native_digest(&self) -> MessageDigest { - match self { - HashFunction::Sha256 => MessageDigest::sha256(), - HashFunction::Sha384 => MessageDigest::sha384(), - HashFunction::Sha512 => MessageDigest::sha512(), - } - } + pub(crate) fn native_digest(&self) -> MessageDigest { + match self { + HashFunction::Sha256 => MessageDigest::sha256(), + HashFunction::Sha384 => MessageDigest::sha384(), + HashFunction::Sha512 => MessageDigest::sha512(), + } + } } diff --git a/acme_common/src/crypto/openssl_keys.rs b/acme_common/src/crypto/openssl_keys.rs index 3fa4b12..96ebfb0 100644 --- a/acme_common/src/crypto/openssl_keys.rs +++ b/acme_common/src/crypto/openssl_keys.rs @@ -13,333 +13,333 @@ use serde_json::json; use serde_json::value::Value; macro_rules! get_key_type { - ($key: expr) => { - match $key.id() { - Id::RSA => match $key.rsa()?.size() { - 256 => KeyType::Rsa2048, - 512 => KeyType::Rsa4096, - s => { - return Err(format!("{}: unsupported RSA key size", s * 8).into()); - } - }, - Id::EC => match $key.ec_key()?.group().curve_name() { - Some(Nid::X9_62_PRIME256V1) => KeyType::EcdsaP256, - Some(Nid::SECP384R1) => KeyType::EcdsaP384, - Some(Nid::SECP521R1) => KeyType::EcdsaP521, - Some(nid) => { - return Err(format!("{:?}: unsupported EC key", nid).into()); - } - None => { - return Err("unsupported EC key".into()); - } - }, - #[cfg(ed25519)] - Id::ED25519 => KeyType::Ed25519, - #[cfg(ed448)] - Id::ED448 => KeyType::Ed448, - _ => { - return Err("unsupported key type".into()); - } - } - }; + ($key: expr) => { + match $key.id() { + Id::RSA => match $key.rsa()?.size() { + 256 => KeyType::Rsa2048, + 512 => KeyType::Rsa4096, + s => { + return Err(format!("{}: unsupported RSA key size", s * 8).into()); + } + }, + Id::EC => match $key.ec_key()?.group().curve_name() { + Some(Nid::X9_62_PRIME256V1) => KeyType::EcdsaP256, + Some(Nid::SECP384R1) => KeyType::EcdsaP384, + Some(Nid::SECP521R1) => KeyType::EcdsaP521, + Some(nid) => { + return Err(format!("{:?}: unsupported EC key", nid).into()); + } + None => { + return Err("unsupported EC key".into()); + } + }, + #[cfg(ed25519)] + Id::ED25519 => KeyType::Ed25519, + #[cfg(ed448)] + Id::ED448 => KeyType::Ed448, + _ => { + return Err("unsupported key type".into()); + } + } + }; } macro_rules! get_ecdsa_sig_part { - ($part: expr, $size: ident) => {{ - let mut p = $part.to_vec(); - let length = p.len(); - if length != $size { - let mut s: Vec = Vec::with_capacity($size); - s.resize_with($size - length, || 0); - s.append(&mut p); - s - } else { - p - } - }}; + ($part: expr, $size: ident) => {{ + let mut p = $part.to_vec(); + let length = p.len(); + if length != $size { + let mut s: Vec = Vec::with_capacity($size); + s.resize_with($size - length, || 0); + s.append(&mut p); + s + } else { + p + } + }}; } #[derive(Clone, Debug)] pub struct KeyPair { - pub key_type: KeyType, - pub inner_key: PKey, + pub key_type: KeyType, + pub inner_key: PKey, } impl KeyPair { - pub fn from_der(der_data: &[u8]) -> Result { - let inner_key = PKey::private_key_from_der(der_data)?; - let key_type = get_key_type!(inner_key); - Ok(KeyPair { - key_type, - inner_key, - }) - } + pub fn from_der(der_data: &[u8]) -> Result { + let inner_key = PKey::private_key_from_der(der_data)?; + let key_type = get_key_type!(inner_key); + Ok(KeyPair { + key_type, + inner_key, + }) + } - pub fn from_pem(pem_data: &[u8]) -> Result { - let inner_key = PKey::private_key_from_pem(pem_data)?; - let key_type = get_key_type!(inner_key); - Ok(KeyPair { - key_type, - inner_key, - }) - } + pub fn from_pem(pem_data: &[u8]) -> Result { + let inner_key = PKey::private_key_from_pem(pem_data)?; + let key_type = get_key_type!(inner_key); + Ok(KeyPair { + key_type, + inner_key, + }) + } - pub fn private_key_to_der(&self) -> Result, Error> { - self.inner_key.private_key_to_der().map_err(Error::from) - } + pub fn private_key_to_der(&self) -> Result, Error> { + self.inner_key.private_key_to_der().map_err(Error::from) + } - pub fn private_key_to_pem(&self) -> Result, Error> { - self.inner_key - .private_key_to_pem_pkcs8() - .map_err(Error::from) - } + pub fn private_key_to_pem(&self) -> Result, Error> { + self.inner_key + .private_key_to_pem_pkcs8() + .map_err(Error::from) + } - pub fn public_key_to_pem(&self) -> Result, Error> { - self.inner_key.public_key_to_pem().map_err(Error::from) - } + pub fn public_key_to_pem(&self) -> Result, Error> { + self.inner_key.public_key_to_pem().map_err(Error::from) + } - pub fn sign(&self, alg: &JwsSignatureAlgorithm, data: &[u8]) -> Result, Error> { - self.key_type.check_alg_compatibility(alg)?; - match alg { - JwsSignatureAlgorithm::Hs256 - | JwsSignatureAlgorithm::Hs384 - | JwsSignatureAlgorithm::Hs512 => Err(format!( - "{} key pair cannot be used for the {} signature algorithm", - self.key_type, alg - ) - .into()), - JwsSignatureAlgorithm::Rs256 => self.sign_rsa(&MessageDigest::sha256(), data), - JwsSignatureAlgorithm::Es256 => self.sign_ecdsa(&HashFunction::Sha256, data), - JwsSignatureAlgorithm::Es384 => self.sign_ecdsa(&HashFunction::Sha384, data), - JwsSignatureAlgorithm::Es512 => self.sign_ecdsa(&HashFunction::Sha512, data), - #[cfg(ed25519)] - JwsSignatureAlgorithm::Ed25519 => self.sign_eddsa(data), - #[cfg(ed448)] - JwsSignatureAlgorithm::Ed448 => self.sign_eddsa(data), - } - } + pub fn sign(&self, alg: &JwsSignatureAlgorithm, data: &[u8]) -> Result, Error> { + self.key_type.check_alg_compatibility(alg)?; + match alg { + JwsSignatureAlgorithm::Hs256 + | JwsSignatureAlgorithm::Hs384 + | JwsSignatureAlgorithm::Hs512 => Err(format!( + "{} key pair cannot be used for the {} signature algorithm", + self.key_type, alg + ) + .into()), + JwsSignatureAlgorithm::Rs256 => self.sign_rsa(&MessageDigest::sha256(), data), + JwsSignatureAlgorithm::Es256 => self.sign_ecdsa(&HashFunction::Sha256, data), + JwsSignatureAlgorithm::Es384 => self.sign_ecdsa(&HashFunction::Sha384, data), + JwsSignatureAlgorithm::Es512 => self.sign_ecdsa(&HashFunction::Sha512, data), + #[cfg(ed25519)] + JwsSignatureAlgorithm::Ed25519 => self.sign_eddsa(data), + #[cfg(ed448)] + JwsSignatureAlgorithm::Ed448 => self.sign_eddsa(data), + } + } - fn sign_rsa(&self, hash_func: &MessageDigest, data: &[u8]) -> Result, Error> { - let mut signer = Signer::new(*hash_func, &self.inner_key)?; - signer.update(data)?; - let signature = signer.sign_to_vec()?; - Ok(signature) - } + fn sign_rsa(&self, hash_func: &MessageDigest, data: &[u8]) -> Result, Error> { + let mut signer = Signer::new(*hash_func, &self.inner_key)?; + signer.update(data)?; + let signature = signer.sign_to_vec()?; + Ok(signature) + } - fn sign_ecdsa(&self, hash_func: &HashFunction, data: &[u8]) -> Result, Error> { - let fingerprint = hash_func.hash(data); - let signature = EcdsaSig::sign(&fingerprint, self.inner_key.ec_key()?.as_ref())?; - let sig_size = match self.key_type { - KeyType::EcdsaP256 => 32, - KeyType::EcdsaP384 => 48, - KeyType::EcdsaP521 => 66, - _ => { - return Err("not an ecdsa key".into()); - } - }; - let r = get_ecdsa_sig_part!(signature.r(), sig_size); - let mut s = get_ecdsa_sig_part!(signature.s(), sig_size); - let mut signature = r; - signature.append(&mut s); - Ok(signature) - } + fn sign_ecdsa(&self, hash_func: &HashFunction, data: &[u8]) -> Result, Error> { + let fingerprint = hash_func.hash(data); + let signature = EcdsaSig::sign(&fingerprint, self.inner_key.ec_key()?.as_ref())?; + let sig_size = match self.key_type { + KeyType::EcdsaP256 => 32, + KeyType::EcdsaP384 => 48, + KeyType::EcdsaP521 => 66, + _ => { + return Err("not an ecdsa key".into()); + } + }; + let r = get_ecdsa_sig_part!(signature.r(), sig_size); + let mut s = get_ecdsa_sig_part!(signature.s(), sig_size); + let mut signature = r; + signature.append(&mut s); + Ok(signature) + } - #[cfg(any(ed25519, ed448))] - fn sign_eddsa(&self, data: &[u8]) -> Result, Error> { - let mut signer = Signer::new_without_digest(&self.inner_key)?; - let signature = signer.sign_oneshot_to_vec(data)?; - Ok(signature) - } + #[cfg(any(ed25519, ed448))] + fn sign_eddsa(&self, data: &[u8]) -> Result, Error> { + let mut signer = Signer::new_without_digest(&self.inner_key)?; + let signature = signer.sign_oneshot_to_vec(data)?; + Ok(signature) + } - pub fn jwk_public_key(&self) -> Result { - self.get_jwk_public_key(false) - } + pub fn jwk_public_key(&self) -> Result { + self.get_jwk_public_key(false) + } - pub fn jwk_public_key_thumbprint(&self) -> Result { - self.get_jwk_public_key(true) - } + pub fn jwk_public_key_thumbprint(&self) -> Result { + self.get_jwk_public_key(true) + } - fn get_jwk_public_key(&self, thumbprint: bool) -> Result { - match self.key_type { - KeyType::Rsa2048 | KeyType::Rsa4096 => self.get_rsa_jwk(thumbprint), - KeyType::EcdsaP256 | KeyType::EcdsaP384 | KeyType::EcdsaP521 => { - self.get_ecdsa_jwk(thumbprint) - } - #[cfg(ed25519)] - KeyType::Ed25519 => self.get_eddsa_jwk(thumbprint), - #[cfg(ed448)] - KeyType::Ed448 => self.get_eddsa_jwk(thumbprint), - } - } + fn get_jwk_public_key(&self, thumbprint: bool) -> Result { + match self.key_type { + KeyType::Rsa2048 | KeyType::Rsa4096 => self.get_rsa_jwk(thumbprint), + KeyType::EcdsaP256 | KeyType::EcdsaP384 | KeyType::EcdsaP521 => { + self.get_ecdsa_jwk(thumbprint) + } + #[cfg(ed25519)] + KeyType::Ed25519 => self.get_eddsa_jwk(thumbprint), + #[cfg(ed448)] + KeyType::Ed448 => self.get_eddsa_jwk(thumbprint), + } + } - fn get_rsa_jwk(&self, thumbprint: bool) -> Result { - let rsa = self.inner_key.rsa().unwrap(); - let e = rsa.e(); - let n = rsa.n(); - let e = b64_encode(&e.to_vec()); - let n = b64_encode(&n.to_vec()); - let jwk = if thumbprint { - json!({ - "kty": "RSA", - "e": e, - "n": n, - }) - } else { - json!({ - "alg": "RS256", - "kty": "RSA", - "use": "sig", - "e": e, - "n": n, - }) - }; - Ok(jwk) - } + fn get_rsa_jwk(&self, thumbprint: bool) -> Result { + let rsa = self.inner_key.rsa().unwrap(); + let e = rsa.e(); + let n = rsa.n(); + let e = b64_encode(&e.to_vec()); + let n = b64_encode(&n.to_vec()); + let jwk = if thumbprint { + json!({ + "kty": "RSA", + "e": e, + "n": n, + }) + } else { + json!({ + "alg": "RS256", + "kty": "RSA", + "use": "sig", + "e": e, + "n": n, + }) + }; + Ok(jwk) + } - fn get_ecdsa_jwk(&self, thumbprint: bool) -> Result { - let (crv, alg, size, curve) = match self.key_type { - KeyType::EcdsaP256 => ("P-256", "ES256", 32, Nid::X9_62_PRIME256V1), - KeyType::EcdsaP384 => ("P-384", "ES384", 48, Nid::SECP384R1), - KeyType::EcdsaP521 => ("P-521", "ES512", 66, Nid::SECP521R1), - _ => { - return Err("not an ECDSA elliptic curve".into()); - } - }; - let group = EcGroup::from_curve_name(curve).unwrap(); - let mut ctx = BigNumContext::new().unwrap(); - let mut x = BigNum::new().unwrap(); - let mut y = BigNum::new().unwrap(); - self.inner_key - .ec_key() - .unwrap() - .public_key() - .affine_coordinates_gfp(&group, &mut x, &mut y, &mut ctx)?; - let x = b64_encode(&x.to_vec_padded(size)?); - let y = b64_encode(&y.to_vec_padded(size)?); - let jwk = if thumbprint { - json!({ - "crv": crv, - "kty": "EC", - "x": x, - "y": y, - }) - } else { - json!({ - "alg": alg, - "crv": crv, - "kty": "EC", - "use": "sig", - "x": x, - "y": y, - }) - }; - Ok(jwk) - } + fn get_ecdsa_jwk(&self, thumbprint: bool) -> Result { + let (crv, alg, size, curve) = match self.key_type { + KeyType::EcdsaP256 => ("P-256", "ES256", 32, Nid::X9_62_PRIME256V1), + KeyType::EcdsaP384 => ("P-384", "ES384", 48, Nid::SECP384R1), + KeyType::EcdsaP521 => ("P-521", "ES512", 66, Nid::SECP521R1), + _ => { + return Err("not an ECDSA elliptic curve".into()); + } + }; + let group = EcGroup::from_curve_name(curve).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let mut x = BigNum::new().unwrap(); + let mut y = BigNum::new().unwrap(); + self.inner_key + .ec_key() + .unwrap() + .public_key() + .affine_coordinates_gfp(&group, &mut x, &mut y, &mut ctx)?; + let x = b64_encode(&x.to_vec_padded(size)?); + let y = b64_encode(&y.to_vec_padded(size)?); + let jwk = if thumbprint { + json!({ + "crv": crv, + "kty": "EC", + "x": x, + "y": y, + }) + } else { + json!({ + "alg": alg, + "crv": crv, + "kty": "EC", + "use": "sig", + "x": x, + "y": y, + }) + }; + Ok(jwk) + } - #[cfg(any(ed25519, ed448))] - fn get_eddsa_jwk(&self, thumbprint: bool) -> Result { - let crv = match self.key_type { - #[cfg(ed25519)] - KeyType::Ed25519 => "Ed25519", - #[cfg(ed448)] - KeyType::Ed448 => "Ed448", - _ => { - return Err("not an EdDSA elliptic curve".into()); - } - }; + #[cfg(any(ed25519, ed448))] + fn get_eddsa_jwk(&self, thumbprint: bool) -> Result { + let crv = match self.key_type { + #[cfg(ed25519)] + KeyType::Ed25519 => "Ed25519", + #[cfg(ed448)] + KeyType::Ed448 => "Ed448", + _ => { + return Err("not an EdDSA elliptic curve".into()); + } + }; - /* - * /!\ WARNING: HAZARDOUS AND UGLY CODE /!\ - * - * I couldn't find a way to get the value of `x` using the OpenSSL - * interface, therefore I had to hack my way arround. - * - * The idea behind this hack is to export the public key in PEM, then - * get the PEM base64 part, convert it to base64url without padding - * and finally truncate the first part so only the value of `x` - * remains. - */ + /* + * /!\ WARNING: HAZARDOUS AND UGLY CODE /!\ + * + * I couldn't find a way to get the value of `x` using the OpenSSL + * interface, therefore I had to hack my way arround. + * + * The idea behind this hack is to export the public key in PEM, then + * get the PEM base64 part, convert it to base64url without padding + * and finally truncate the first part so only the value of `x` + * remains. + */ - // -----BEGIN UGLY----- - let mut x = String::new(); - let public_pem = self.public_key_to_pem()?; - let public_pem = String::from_utf8(public_pem)?; - for pem_line in public_pem.lines() { - if !pem_line.is_empty() && !pem_line.starts_with("-----") { - x += &pem_line - .trim() - .trim_end_matches('=') - .replace('/', "_") - .replace('+', "-"); - } - } - x.replace_range(..16, ""); - // -----END UGLY----- + // -----BEGIN UGLY----- + let mut x = String::new(); + let public_pem = self.public_key_to_pem()?; + let public_pem = String::from_utf8(public_pem)?; + for pem_line in public_pem.lines() { + if !pem_line.is_empty() && !pem_line.starts_with("-----") { + x += &pem_line + .trim() + .trim_end_matches('=') + .replace('/', "_") + .replace('+', "-"); + } + } + x.replace_range(..16, ""); + // -----END UGLY----- - let jwk = if thumbprint { - json!({ - "crv": crv, - "kty": "OKP", - "x": &x, - }) - } else { - json!({ - "alg": "EdDSA", - "crv": crv, - "kty": "OKP", - "use": "sig", - "x": &x, - }) - }; - Ok(jwk) - } + let jwk = if thumbprint { + json!({ + "crv": crv, + "kty": "OKP", + "x": &x, + }) + } else { + json!({ + "alg": "EdDSA", + "crv": crv, + "kty": "OKP", + "use": "sig", + "x": &x, + }) + }; + Ok(jwk) + } } fn gen_rsa_pair(nb_bits: u32) -> Result, Error> { - let priv_key = Rsa::generate(nb_bits)?; - let pk = PKey::from_rsa(priv_key).map_err(|_| Error::from(""))?; - Ok(pk) + let priv_key = Rsa::generate(nb_bits)?; + let pk = PKey::from_rsa(priv_key).map_err(|_| Error::from(""))?; + Ok(pk) } fn gen_ec_pair(nid: Nid) -> Result, Error> { - let mut group = EcGroup::from_curve_name(nid)?; + let mut group = EcGroup::from_curve_name(nid)?; - // Use NAMED_CURVE format; OpenSSL 1.0.1 and 1.0.2 default to EXPLICIT_CURVE which won't work (see #9) - group.set_asn1_flag(Asn1Flag::NAMED_CURVE); + // Use NAMED_CURVE format; OpenSSL 1.0.1 and 1.0.2 default to EXPLICIT_CURVE which won't work (see #9) + group.set_asn1_flag(Asn1Flag::NAMED_CURVE); - let ec_priv_key = EcKey::generate(&group).map_err(|_| Error::from(""))?; - let pk = PKey::from_ec_key(ec_priv_key).map_err(|_| Error::from(""))?; - Ok(pk) + let ec_priv_key = EcKey::generate(&group).map_err(|_| Error::from(""))?; + let pk = PKey::from_ec_key(ec_priv_key).map_err(|_| Error::from(""))?; + Ok(pk) } #[cfg(ed25519)] fn gen_ed25519_pair() -> Result, Error> { - let pk = PKey::generate_ed25519().map_err(|_| Error::from(""))?; - Ok(pk) + let pk = PKey::generate_ed25519().map_err(|_| Error::from(""))?; + Ok(pk) } #[cfg(ed448)] fn gen_ed448_pair() -> Result, Error> { - let pk = PKey::generate_ed448().map_err(|_| Error::from(""))?; - Ok(pk) + let pk = PKey::generate_ed448().map_err(|_| Error::from(""))?; + Ok(pk) } pub fn gen_keypair(key_type: KeyType) -> Result { - let priv_key = match key_type { - KeyType::Rsa2048 => gen_rsa_pair(2048), - KeyType::Rsa4096 => gen_rsa_pair(4096), - KeyType::EcdsaP256 => gen_ec_pair(Nid::X9_62_PRIME256V1), - KeyType::EcdsaP384 => gen_ec_pair(Nid::SECP384R1), - KeyType::EcdsaP521 => gen_ec_pair(Nid::SECP521R1), - #[cfg(ed25519)] - KeyType::Ed25519 => gen_ed25519_pair(), - #[cfg(ed448)] - KeyType::Ed448 => gen_ed448_pair(), - } - .map_err(|_| Error::from(format!("unable to generate a {} key pair", key_type)))?; - let key_pair = KeyPair { - key_type, - inner_key: priv_key, - }; - Ok(key_pair) + let priv_key = match key_type { + KeyType::Rsa2048 => gen_rsa_pair(2048), + KeyType::Rsa4096 => gen_rsa_pair(4096), + KeyType::EcdsaP256 => gen_ec_pair(Nid::X9_62_PRIME256V1), + KeyType::EcdsaP384 => gen_ec_pair(Nid::SECP384R1), + KeyType::EcdsaP521 => gen_ec_pair(Nid::SECP521R1), + #[cfg(ed25519)] + KeyType::Ed25519 => gen_ed25519_pair(), + #[cfg(ed448)] + KeyType::Ed448 => gen_ed448_pair(), + } + .map_err(|_| Error::from(format!("unable to generate a {} key pair", key_type)))?; + let key_pair = KeyPair { + key_type, + inner_key: priv_key, + }; + Ok(key_pair) } diff --git a/acme_common/src/crypto/openssl_subject_attribute.rs b/acme_common/src/crypto/openssl_subject_attribute.rs index e12167f..35477a6 100644 --- a/acme_common/src/crypto/openssl_subject_attribute.rs +++ b/acme_common/src/crypto/openssl_subject_attribute.rs @@ -3,23 +3,23 @@ use openssl::nid::Nid; pub type SubjectAttribute = super::BaseSubjectAttribute; impl SubjectAttribute { - pub fn get_nid(&self) -> Nid { - match self { - SubjectAttribute::CountryName => Nid::COUNTRYNAME, - SubjectAttribute::GenerationQualifier => Nid::GENERATIONQUALIFIER, - SubjectAttribute::GivenName => Nid::GIVENNAME, - SubjectAttribute::Initials => Nid::INITIALS, - SubjectAttribute::LocalityName => Nid::LOCALITYNAME, - SubjectAttribute::Name => Nid::NAME, - SubjectAttribute::OrganizationName => Nid::ORGANIZATIONNAME, - SubjectAttribute::OrganizationalUnitName => Nid::ORGANIZATIONALUNITNAME, - SubjectAttribute::Pkcs9EmailAddress => Nid::PKCS9_EMAILADDRESS, - SubjectAttribute::PostalAddress => Nid::POSTALADDRESS, - SubjectAttribute::PostalCode => Nid::POSTALCODE, - SubjectAttribute::StateOrProvinceName => Nid::STATEORPROVINCENAME, - SubjectAttribute::Street => Nid::STREETADDRESS, - SubjectAttribute::Surname => Nid::SURNAME, - SubjectAttribute::Title => Nid::TITLE, - } - } + pub fn get_nid(&self) -> Nid { + match self { + SubjectAttribute::CountryName => Nid::COUNTRYNAME, + SubjectAttribute::GenerationQualifier => Nid::GENERATIONQUALIFIER, + SubjectAttribute::GivenName => Nid::GIVENNAME, + SubjectAttribute::Initials => Nid::INITIALS, + SubjectAttribute::LocalityName => Nid::LOCALITYNAME, + SubjectAttribute::Name => Nid::NAME, + SubjectAttribute::OrganizationName => Nid::ORGANIZATIONNAME, + SubjectAttribute::OrganizationalUnitName => Nid::ORGANIZATIONALUNITNAME, + SubjectAttribute::Pkcs9EmailAddress => Nid::PKCS9_EMAILADDRESS, + SubjectAttribute::PostalAddress => Nid::POSTALADDRESS, + SubjectAttribute::PostalCode => Nid::POSTALCODE, + SubjectAttribute::StateOrProvinceName => Nid::STATEORPROVINCENAME, + SubjectAttribute::Street => Nid::STREETADDRESS, + SubjectAttribute::Surname => Nid::SURNAME, + SubjectAttribute::Title => Nid::TITLE, + } + } } diff --git a/acme_common/src/crypto/openssl_version.rs b/acme_common/src/crypto/openssl_version.rs index 6eb11d4..40de2e6 100644 --- a/acme_common/src/crypto/openssl_version.rs +++ b/acme_common/src/crypto/openssl_version.rs @@ -1,27 +1,27 @@ pub fn get_lib_name() -> String { - env!("ACMED_TLS_LIB_NAME").to_string() + env!("ACMED_TLS_LIB_NAME").to_string() } pub fn get_lib_version() -> String { - let v = openssl::version::number() as u64; - let mut version = vec![]; - for i in 0..3 { - let n = get_openssl_version_unit(v, i); - version.push(format!("{}", n)); - } - let version = version.join("."); - let p = get_openssl_version_unit(v, 3); - if p != 0 { - let p = p + 0x60; - let p = std::char::from_u32(p as u32).unwrap(); - format!("{}{}", version, p) - } else { - version - } + let v = openssl::version::number() as u64; + let mut version = vec![]; + for i in 0..3 { + let n = get_openssl_version_unit(v, i); + version.push(format!("{}", n)); + } + let version = version.join("."); + let p = get_openssl_version_unit(v, 3); + if p != 0 { + let p = p + 0x60; + let p = std::char::from_u32(p as u32).unwrap(); + format!("{}{}", version, p) + } else { + version + } } fn get_openssl_version_unit(n: u64, pos: u32) -> u64 { - let p = 0x000f_f000_0000 >> (8 * pos); - let n = n & p; - n >> (8 * (3 - pos) + 4) + let p = 0x000f_f000_0000 >> (8 * pos); + let n = n & p; + n >> (8 * (3 - pos) + 4) } diff --git a/acme_common/src/error.rs b/acme_common/src/error.rs index fe3f8f3..f64997d 100644 --- a/acme_common/src/error.rs +++ b/acme_common/src/error.rs @@ -2,132 +2,132 @@ use std::fmt; #[derive(Clone, Debug)] pub struct Error { - pub message: String, + pub message: String, } impl Error { - pub fn prefix(&self, prefix: &str) -> Self { - Error { - message: format!("{}: {}", prefix, &self.message), - } - } + pub fn prefix(&self, prefix: &str) -> Self { + Error { + message: format!("{}: {}", prefix, &self.message), + } + } } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.message) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.message) + } } impl From<&str> for Error { - fn from(error: &str) -> Self { - Error { - message: error.to_string(), - } - } + fn from(error: &str) -> Self { + Error { + message: error.to_string(), + } + } } impl From for Error { - fn from(error: String) -> Self { - error.as_str().into() - } + fn from(error: String) -> Self { + error.as_str().into() + } } impl From<&String> for Error { - fn from(error: &String) -> Self { - error.as_str().into() - } + fn from(error: &String) -> Self { + error.as_str().into() + } } impl From for Error { - fn from(error: std::io::Error) -> Self { - format!("IO error: {}", error).into() - } + fn from(error: std::io::Error) -> Self { + format!("IO error: {}", error).into() + } } impl From for Error { - fn from(error: std::net::AddrParseError) -> Self { - format!("{}", error).into() - } + fn from(error: std::net::AddrParseError) -> Self { + format!("{}", error).into() + } } impl From for Error { - fn from(error: std::string::FromUtf8Error) -> Self { - format!("UTF-8 error: {}", error).into() - } + fn from(error: std::string::FromUtf8Error) -> Self { + format!("UTF-8 error: {}", error).into() + } } impl From for Error { - fn from(error: std::sync::mpsc::RecvError) -> Self { - format!("MSPC receiver error: {}", error).into() - } + fn from(error: std::sync::mpsc::RecvError) -> Self { + format!("MSPC receiver error: {}", error).into() + } } impl From for Error { - fn from(error: std::time::SystemTimeError) -> Self { - format!("SystemTimeError difference: {:?}", error.duration()).into() - } + fn from(error: std::time::SystemTimeError) -> Self { + format!("SystemTimeError difference: {:?}", error.duration()).into() + } } impl From for Error { - fn from(error: base64::DecodeError) -> Self { - format!("base 64 decode error: {}", error).into() - } + fn from(error: base64::DecodeError) -> Self { + format!("base 64 decode error: {}", error).into() + } } impl From for Error { - fn from(error: syslog::Error) -> Self { - format!("syslog error: {}", error).into() - } + fn from(error: syslog::Error) -> Self { + format!("syslog error: {}", error).into() + } } impl From for Error { - fn from(error: toml::de::Error) -> Self { - format!("IO error: {}", error).into() - } + fn from(error: toml::de::Error) -> Self { + format!("IO error: {}", error).into() + } } impl From for Error { - fn from(error: serde_json::error::Error) -> Self { - format!("IO error: {}", error).into() - } + fn from(error: serde_json::error::Error) -> Self { + format!("IO error: {}", error).into() + } } impl From for Error { - fn from(error: attohttpc::Error) -> Self { - format!("HTTP error: {}", error).into() - } + fn from(error: attohttpc::Error) -> Self { + format!("HTTP error: {}", error).into() + } } impl From for Error { - fn from(error: glob::PatternError) -> Self { - format!("pattern error: {}", error).into() - } + fn from(error: glob::PatternError) -> Self { + format!("pattern error: {}", error).into() + } } impl From for Error { - fn from(error: tinytemplate::error::Error) -> Self { - format!("template error: {}", error).into() - } + fn from(error: tinytemplate::error::Error) -> Self { + format!("template error: {}", error).into() + } } #[cfg(feature = "crypto_openssl")] impl From for Error { - fn from(error: native_tls::Error) -> Self { - format!("{}", error).into() - } + fn from(error: native_tls::Error) -> Self { + format!("{}", error).into() + } } #[cfg(feature = "crypto_openssl")] impl From for Error { - fn from(error: openssl::error::ErrorStack) -> Self { - format!("{}", error).into() - } + fn from(error: openssl::error::ErrorStack) -> Self { + format!("{}", error).into() + } } #[cfg(unix)] impl From for Error { - fn from(error: nix::Error) -> Self { - format!("{}", error).into() - } + fn from(error: nix::Error) -> Self { + format!("{}", error).into() + } } diff --git a/acme_common/src/lib.rs b/acme_common/src/lib.rs index c10c0e9..6d6e287 100644 --- a/acme_common/src/lib.rs +++ b/acme_common/src/lib.rs @@ -11,66 +11,66 @@ pub mod logs; mod tests; macro_rules! exit_match { - ($e: expr) => { - match $e { - Ok(_) => {} - Err(e) => { - log::error!("error: {}", e); - std::process::exit(3); - } - } - }; + ($e: expr) => { + match $e { + Ok(_) => {} + Err(e) => { + log::error!("error: {}", e); + std::process::exit(3); + } + } + }; } pub fn to_idna(domain_name: &str) -> Result { - let mut idna_parts = vec![]; - let parts: Vec<&str> = domain_name.split('.').collect(); - for name in parts.iter() { - let raw_name = name.to_lowercase(); - let idna_name = if name.is_ascii() { - raw_name - } else { - let idna_name = punycode::encode(&raw_name) - .map_err(|_| error::Error::from("IDNA encoding failed."))?; - format!("xn--{}", idna_name) - }; - idna_parts.push(idna_name); - } - Ok(idna_parts.join(".")) + let mut idna_parts = vec![]; + let parts: Vec<&str> = domain_name.split('.').collect(); + for name in parts.iter() { + let raw_name = name.to_lowercase(); + let idna_name = if name.is_ascii() { + raw_name + } else { + let idna_name = punycode::encode(&raw_name) + .map_err(|_| error::Error::from("IDNA encoding failed."))?; + format!("xn--{}", idna_name) + }; + idna_parts.push(idna_name); + } + Ok(idna_parts.join(".")) } pub fn b64_encode>(input: &T) -> String { - base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(input) + base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(input) } pub fn b64_decode>(input: &T) -> Result, error::Error> { - let res = base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(input)?; - Ok(res) + let res = base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(input)?; + Ok(res) } pub fn init_server(foreground: bool, pid_file: Option<&str>) { - if !foreground { - let mut daemonize = Daemonize::new(); - if let Some(f) = pid_file { - daemonize = daemonize.pid_file(f); - } - exit_match!(daemonize.start()); - } else if let Some(f) = pid_file { - exit_match!(write_pid_file(f).map_err(|e| e.prefix(f))); - } + if !foreground { + let mut daemonize = Daemonize::new(); + if let Some(f) = pid_file { + daemonize = daemonize.pid_file(f); + } + exit_match!(daemonize.start()); + } else if let Some(f) = pid_file { + exit_match!(write_pid_file(f).map_err(|e| e.prefix(f))); + } } fn write_pid_file(pid_file: &str) -> Result<(), error::Error> { - let data = format!("{}\n", process::id()).into_bytes(); - let mut file = File::create(pid_file)?; - file.write_all(&data)?; - file.sync_all()?; - Ok(()) + let data = format!("{}\n", process::id()).into_bytes(); + let mut file = File::create(pid_file)?; + file.write_all(&data)?; + file.sync_all()?; + Ok(()) } pub fn clean_pid_file(pid_file: Option<&str>) -> Result<(), error::Error> { - if let Some(f) = pid_file { - fs::remove_file(f)?; - } - Ok(()) + if let Some(f) = pid_file { + fs::remove_file(f)?; + } + Ok(()) } diff --git a/acme_common/src/logs.rs b/acme_common/src/logs.rs index 7fb2b96..89ec246 100644 --- a/acme_common/src/logs.rs +++ b/acme_common/src/logs.rs @@ -8,79 +8,79 @@ pub const DEFAULT_LOG_LEVEL: LevelFilter = LevelFilter::Warn; #[derive(Debug, PartialEq, Eq)] pub enum LogSystem { - SysLog, - StdErr, + SysLog, + StdErr, } fn get_loglevel(log_level: Option<&str>) -> Result { - let level = match log_level { - Some(v) => match v { - "error" => LevelFilter::Error, - "warn" => LevelFilter::Warn, - "info" => LevelFilter::Info, - "debug" => LevelFilter::Debug, - "trace" => LevelFilter::Trace, - _ => { - return Err(format!("{}: invalid log level", v).into()); - } - }, - None => DEFAULT_LOG_LEVEL, - }; - Ok(level) + let level = match log_level { + Some(v) => match v { + "error" => LevelFilter::Error, + "warn" => LevelFilter::Warn, + "info" => LevelFilter::Info, + "debug" => LevelFilter::Debug, + "trace" => LevelFilter::Trace, + _ => { + return Err(format!("{}: invalid log level", v).into()); + } + }, + None => DEFAULT_LOG_LEVEL, + }; + Ok(level) } fn set_log_syslog(log_level: LevelFilter) -> Result<(), Error> { - syslog::init( - Facility::LOG_DAEMON, - log_level, - Some(env!("CARGO_PKG_NAME")), - )?; - Ok(()) + syslog::init( + Facility::LOG_DAEMON, + log_level, + Some(env!("CARGO_PKG_NAME")), + )?; + Ok(()) } fn set_log_stderr(log_level: LevelFilter) -> Result<(), Error> { - let mut builder = Builder::from_env("ACMED_LOG_LEVEL"); - builder.filter_level(log_level); - builder.init(); - Ok(()) + let mut builder = Builder::from_env("ACMED_LOG_LEVEL"); + builder.filter_level(log_level); + builder.init(); + Ok(()) } pub fn set_log_system( - log_level: Option<&str>, - has_syslog: bool, - has_stderr: bool, + log_level: Option<&str>, + has_syslog: bool, + has_stderr: bool, ) -> Result<(LogSystem, LevelFilter), Error> { - let log_level = get_loglevel(log_level)?; - let logtype = if has_syslog { - LogSystem::SysLog - } else if has_stderr { - LogSystem::StdErr - } else { - DEFAULT_LOG_SYSTEM - }; - match logtype { - LogSystem::SysLog => set_log_syslog(log_level)?, - LogSystem::StdErr => set_log_stderr(log_level)?, - }; - Ok((logtype, log_level)) + let log_level = get_loglevel(log_level)?; + let logtype = if has_syslog { + LogSystem::SysLog + } else if has_stderr { + LogSystem::StdErr + } else { + DEFAULT_LOG_SYSTEM + }; + match logtype { + LogSystem::SysLog => set_log_syslog(log_level)?, + LogSystem::StdErr => set_log_stderr(log_level)?, + }; + Ok((logtype, log_level)) } #[cfg(test)] mod tests { - use super::{set_log_system, DEFAULT_LOG_LEVEL, DEFAULT_LOG_SYSTEM}; + use super::{set_log_system, DEFAULT_LOG_LEVEL, DEFAULT_LOG_SYSTEM}; - #[test] - fn test_invalid_level() { - let ret = set_log_system(Some("invalid"), false, false); - assert!(ret.is_err()); - } + #[test] + fn test_invalid_level() { + let ret = set_log_system(Some("invalid"), false, false); + assert!(ret.is_err()); + } - #[test] - fn test_default_values() { - let ret = set_log_system(None, false, false); - assert!(ret.is_ok()); - let (logtype, log_level) = ret.unwrap(); - assert_eq!(logtype, DEFAULT_LOG_SYSTEM); - assert_eq!(log_level, DEFAULT_LOG_LEVEL); - } + #[test] + fn test_default_values() { + let ret = set_log_system(None, false, false); + assert!(ret.is_ok()); + let (logtype, log_level) = ret.unwrap(); + assert_eq!(logtype, DEFAULT_LOG_SYSTEM); + assert_eq!(log_level, DEFAULT_LOG_LEVEL); + } } diff --git a/acme_common/src/tests/certificate.rs b/acme_common/src/tests/certificate.rs index 2feb950..ad5d094 100644 --- a/acme_common/src/tests/certificate.rs +++ b/acme_common/src/tests/certificate.rs @@ -83,99 +83,99 @@ ehm+yKg= #[test] fn test_san_domains() { - let san = vec!["local.what.tf", "1.local.what.tf", "2.local.what.tf"]; - let san = HashSet::from_iter(san.iter().map(|v| v.to_string())); - let crt = X509Certificate::from_pem(CERTIFICATE_P256_DOMAINS_PEM.as_bytes()).unwrap(); - assert_eq!(crt.subject_alt_names(), san); + let san = vec!["local.what.tf", "1.local.what.tf", "2.local.what.tf"]; + let san = HashSet::from_iter(san.iter().map(|v| v.to_string())); + let crt = X509Certificate::from_pem(CERTIFICATE_P256_DOMAINS_PEM.as_bytes()).unwrap(); + assert_eq!(crt.subject_alt_names(), san); } #[test] fn test_san_ip() { - let san = vec!["127.0.0.1", "::1"]; - let san = HashSet::from_iter(san.iter().map(|v| v.to_string())); - let crt = X509Certificate::from_pem(CERTIFICATE_P256_IP_PEM.as_bytes()).unwrap(); - assert_eq!(crt.subject_alt_names(), san); + let san = vec!["127.0.0.1", "::1"]; + let san = HashSet::from_iter(san.iter().map(|v| v.to_string())); + let crt = X509Certificate::from_pem(CERTIFICATE_P256_IP_PEM.as_bytes()).unwrap(); + assert_eq!(crt.subject_alt_names(), san); } #[test] fn test_san_domains_and_ip() { - let san = vec![ - "127.0.0.1", - "::1", - "local.what.tf", - "1.local.what.tf", - "2.local.what.tf", - ]; - let san = HashSet::from_iter(san.iter().map(|v| v.to_string())); - let crt = X509Certificate::from_pem(CERTIFICATE_P256_DOMAINS_IP_PEM.as_bytes()).unwrap(); - assert_eq!(crt.subject_alt_names(), san); + let san = vec![ + "127.0.0.1", + "::1", + "local.what.tf", + "1.local.what.tf", + "2.local.what.tf", + ]; + let san = HashSet::from_iter(san.iter().map(|v| v.to_string())); + let crt = X509Certificate::from_pem(CERTIFICATE_P256_DOMAINS_IP_PEM.as_bytes()).unwrap(); + assert_eq!(crt.subject_alt_names(), san); } #[test] fn generate_rsa2048_certificate() { - let (kp, _) = - X509Certificate::from_acme_ext("example.org", "", KeyType::Rsa2048, HashFunction::Sha256) - .unwrap(); - assert_eq!(kp.key_type, KeyType::Rsa2048); + let (kp, _) = + X509Certificate::from_acme_ext("example.org", "", KeyType::Rsa2048, HashFunction::Sha256) + .unwrap(); + assert_eq!(kp.key_type, KeyType::Rsa2048); } #[test] fn generate_rsa4096_certificate() { - let (kp, _) = - X509Certificate::from_acme_ext("example.org", "", KeyType::Rsa4096, HashFunction::Sha256) - .unwrap(); - assert_eq!(kp.key_type, KeyType::Rsa4096); + let (kp, _) = + X509Certificate::from_acme_ext("example.org", "", KeyType::Rsa4096, HashFunction::Sha256) + .unwrap(); + assert_eq!(kp.key_type, KeyType::Rsa4096); } #[test] fn generate_ecdsa_p256_certificate() { - let (kp, _) = - X509Certificate::from_acme_ext("example.org", "", KeyType::EcdsaP256, HashFunction::Sha256) - .unwrap(); - assert_eq!(kp.key_type, KeyType::EcdsaP256); + let (kp, _) = + X509Certificate::from_acme_ext("example.org", "", KeyType::EcdsaP256, HashFunction::Sha256) + .unwrap(); + assert_eq!(kp.key_type, KeyType::EcdsaP256); } #[test] fn generate_ecdsa_p384_certificate() { - let (kp, _) = - X509Certificate::from_acme_ext("example.org", "", KeyType::EcdsaP384, HashFunction::Sha256) - .unwrap(); - assert_eq!(kp.key_type, KeyType::EcdsaP384); + let (kp, _) = + X509Certificate::from_acme_ext("example.org", "", KeyType::EcdsaP384, HashFunction::Sha256) + .unwrap(); + assert_eq!(kp.key_type, KeyType::EcdsaP384); } #[cfg(ed25519)] #[test] fn generate_ed25519_certificate() { - let (kp, _) = - X509Certificate::from_acme_ext("example.org", "", KeyType::Ed25519, HashFunction::Sha256) - .unwrap(); - assert_eq!(kp.key_type, KeyType::Ed25519); + let (kp, _) = + X509Certificate::from_acme_ext("example.org", "", KeyType::Ed25519, HashFunction::Sha256) + .unwrap(); + assert_eq!(kp.key_type, KeyType::Ed25519); } #[cfg(ed448)] #[test] fn generate_ed448_certificate() { - let (kp, _) = - X509Certificate::from_acme_ext("example.org", "", KeyType::Ed448, HashFunction::Sha256) - .unwrap(); - assert_eq!(kp.key_type, KeyType::Ed448); + let (kp, _) = + X509Certificate::from_acme_ext("example.org", "", KeyType::Ed448, HashFunction::Sha256) + .unwrap(); + assert_eq!(kp.key_type, KeyType::Ed448); } #[test] fn cert_expiration_date_future() { - let (_, crt) = - X509Certificate::from_acme_ext("example.org", "", KeyType::EcdsaP256, HashFunction::Sha256) - .unwrap(); - let duration = crt.expires_in().unwrap().as_secs(); - let validity_sec = CRT_NB_DAYS_VALIDITY as u64 * 24 * 60 * 60; - let delta = 60; - assert!(duration > validity_sec - delta); - assert!(duration < validity_sec + delta); + let (_, crt) = + X509Certificate::from_acme_ext("example.org", "", KeyType::EcdsaP256, HashFunction::Sha256) + .unwrap(); + let duration = crt.expires_in().unwrap().as_secs(); + let validity_sec = CRT_NB_DAYS_VALIDITY as u64 * 24 * 60 * 60; + let delta = 60; + assert!(duration > validity_sec - delta); + assert!(duration < validity_sec + delta); } #[test] fn cert_expiration_date_past() { - let crt = X509Certificate::from_pem(CERTIFICATE_EXPIRED_PEM.as_bytes()).unwrap(); - let duration = crt.expires_in().unwrap().as_secs(); - assert_eq!(duration, 0); + let crt = X509Certificate::from_pem(CERTIFICATE_EXPIRED_PEM.as_bytes()).unwrap(); + let duration = crt.expires_in().unwrap().as_secs(); + assert_eq!(duration, 0); } diff --git a/acme_common/src/tests/crypto_keys.rs b/acme_common/src/tests/crypto_keys.rs index 8019410..1580eec 100644 --- a/acme_common/src/tests/crypto_keys.rs +++ b/acme_common/src/tests/crypto_keys.rs @@ -104,308 +104,308 @@ uMXco2JR1CSjKWgqgu1CAKadJIYiv2EgIw== #[test] fn test_rsa_2048_jwk() { - let k = KeyPair::from_pem(KEY_RSA_2048_PEM.as_bytes()).unwrap(); - let jwk = k.jwk_public_key().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 5); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("e")); - assert!(jwk.contains_key("n")); - assert!(jwk.contains_key("use")); - assert!(jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "RSA"); - assert_eq!(jwk.get("e").unwrap(), "AQAB"); - assert_eq!(jwk.get("n").unwrap(), "s38GRhfMyjQINvZnWfYnhO1dnJWN6HiPtle3leZFMDhqoYaQ2g8g5o7vpdHShcBfMXg3nhpk0hA9dHt_GbB6iRdHGaig6wd4TngwLJ-2erLR3_0WaM0DubAJmaTe4ND9JYVyZ8gK_li-fF-NZFrrn4j1W71EUL_7St8jdivqwujHWdpS7C3piosAJW8hqz31M7lXOnV61PCb15JMLiKQMhBCezk13QWk-FQBx7ZtmA1iMFvt-Drcqdhb20iWLCMCYwtNLez4ZmofWzI4sqQmQejpJ2Ve1gGeeY2hf68qQEQf8804nksp-EIv1Y4qVhO5zvxo7m8s6ybUJqvqOz5u9Q"); - assert_eq!(jwk.get("use").unwrap(), "sig"); - assert_eq!(jwk.get("alg").unwrap(), "RS256"); + let k = KeyPair::from_pem(KEY_RSA_2048_PEM.as_bytes()).unwrap(); + let jwk = k.jwk_public_key().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 5); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("e")); + assert!(jwk.contains_key("n")); + assert!(jwk.contains_key("use")); + assert!(jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "RSA"); + assert_eq!(jwk.get("e").unwrap(), "AQAB"); + assert_eq!(jwk.get("n").unwrap(), "s38GRhfMyjQINvZnWfYnhO1dnJWN6HiPtle3leZFMDhqoYaQ2g8g5o7vpdHShcBfMXg3nhpk0hA9dHt_GbB6iRdHGaig6wd4TngwLJ-2erLR3_0WaM0DubAJmaTe4ND9JYVyZ8gK_li-fF-NZFrrn4j1W71EUL_7St8jdivqwujHWdpS7C3piosAJW8hqz31M7lXOnV61PCb15JMLiKQMhBCezk13QWk-FQBx7ZtmA1iMFvt-Drcqdhb20iWLCMCYwtNLez4ZmofWzI4sqQmQejpJ2Ve1gGeeY2hf68qQEQf8804nksp-EIv1Y4qVhO5zvxo7m8s6ybUJqvqOz5u9Q"); + assert_eq!(jwk.get("use").unwrap(), "sig"); + assert_eq!(jwk.get("alg").unwrap(), "RS256"); } #[test] fn test_rsa_2048_jwk_thumbprint() { - let k = KeyPair::from_pem(KEY_RSA_2048_PEM.as_bytes()).unwrap(); - let jwk = k.jwk_public_key_thumbprint().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 3); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("e")); - assert!(jwk.contains_key("n")); - assert!(!jwk.contains_key("use")); - assert!(!jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "RSA"); - assert_eq!(jwk.get("e").unwrap(), "AQAB"); - assert_eq!(jwk.get("n").unwrap(), "s38GRhfMyjQINvZnWfYnhO1dnJWN6HiPtle3leZFMDhqoYaQ2g8g5o7vpdHShcBfMXg3nhpk0hA9dHt_GbB6iRdHGaig6wd4TngwLJ-2erLR3_0WaM0DubAJmaTe4ND9JYVyZ8gK_li-fF-NZFrrn4j1W71EUL_7St8jdivqwujHWdpS7C3piosAJW8hqz31M7lXOnV61PCb15JMLiKQMhBCezk13QWk-FQBx7ZtmA1iMFvt-Drcqdhb20iWLCMCYwtNLez4ZmofWzI4sqQmQejpJ2Ve1gGeeY2hf68qQEQf8804nksp-EIv1Y4qVhO5zvxo7m8s6ybUJqvqOz5u9Q"); + let k = KeyPair::from_pem(KEY_RSA_2048_PEM.as_bytes()).unwrap(); + let jwk = k.jwk_public_key_thumbprint().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 3); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("e")); + assert!(jwk.contains_key("n")); + assert!(!jwk.contains_key("use")); + assert!(!jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "RSA"); + assert_eq!(jwk.get("e").unwrap(), "AQAB"); + assert_eq!(jwk.get("n").unwrap(), "s38GRhfMyjQINvZnWfYnhO1dnJWN6HiPtle3leZFMDhqoYaQ2g8g5o7vpdHShcBfMXg3nhpk0hA9dHt_GbB6iRdHGaig6wd4TngwLJ-2erLR3_0WaM0DubAJmaTe4ND9JYVyZ8gK_li-fF-NZFrrn4j1W71EUL_7St8jdivqwujHWdpS7C3piosAJW8hqz31M7lXOnV61PCb15JMLiKQMhBCezk13QWk-FQBx7ZtmA1iMFvt-Drcqdhb20iWLCMCYwtNLez4ZmofWzI4sqQmQejpJ2Ve1gGeeY2hf68qQEQf8804nksp-EIv1Y4qVhO5zvxo7m8s6ybUJqvqOz5u9Q"); } #[test] fn test_rsa_4096_jwk() { - let k = KeyPair::from_pem(KEY_RSA_4096_PEM.as_bytes()).unwrap(); - let jwk = k.jwk_public_key().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 5); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("e")); - assert!(jwk.contains_key("n")); - assert!(jwk.contains_key("use")); - assert!(jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "RSA"); - assert_eq!(jwk.get("e").unwrap(), "AQAB"); - assert_eq!(jwk.get("n").unwrap(), "jm0Jh8Zk-aRqUxME0yLeHhBtdDFq6IvVesr_ffFwGqtlI4tIa6MqYuuOE5qlqoBrxt1fpccqGF82j47JqeoWerkNdZxYh1uKGQa9G2vB4OBOFHxYujT-NNPPlwnVPrp1_pjxK0hiOGzZRliNhO65lMbEueQCaLbQ6wstr4aVsWKiw8O4MG8Cc2ZegkWlYg0ZHOAn-uJlAt17NJfX8x576XRwX2KCssPYeyiwBukRne3ahVVqrc35EmV9JH3CxDaG9MeBLo7FUV5P1GNOIs313cbAgdMYV6Ahr2LvsaLucFvhSwmHh42--meDui-0wdFILjrz7MsITZy4xXjl8_zqMZ2dBaO38wP5o9VCfCXaVlD3IYB9A4-Y9IQ6wEVeodzGloPQyddUZ70kZ7O3E1kYu0tBKhoAL1VcKiUvOj1ABGooqf8QIAWCiAWqDqkZJ5HiR61NC9lJ3MH-aqYXzeHUuOxdlfr3oWPQKlRyYoUW93xD4dXjHQyHsIS4gWYE9ZJ9aIomNHj93UPNfR8ScxQTQBJV9Ttgl_vOUdmlasTmo0OXbTLMexzWCVISvUuoBoTcTY4DXCFeYHz3EE3ijJ9fnDkX3SsvV43x8X58py9rEyAQnRxfeahpWEoxW6TA5qBxg1GMBQu6CY2MupqsmI0XHTXzy8xCt4Pqd_a5zP6Wh_E"); - assert_eq!(jwk.get("use").unwrap(), "sig"); - assert_eq!(jwk.get("alg").unwrap(), "RS256"); + let k = KeyPair::from_pem(KEY_RSA_4096_PEM.as_bytes()).unwrap(); + let jwk = k.jwk_public_key().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 5); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("e")); + assert!(jwk.contains_key("n")); + assert!(jwk.contains_key("use")); + assert!(jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "RSA"); + assert_eq!(jwk.get("e").unwrap(), "AQAB"); + assert_eq!(jwk.get("n").unwrap(), "jm0Jh8Zk-aRqUxME0yLeHhBtdDFq6IvVesr_ffFwGqtlI4tIa6MqYuuOE5qlqoBrxt1fpccqGF82j47JqeoWerkNdZxYh1uKGQa9G2vB4OBOFHxYujT-NNPPlwnVPrp1_pjxK0hiOGzZRliNhO65lMbEueQCaLbQ6wstr4aVsWKiw8O4MG8Cc2ZegkWlYg0ZHOAn-uJlAt17NJfX8x576XRwX2KCssPYeyiwBukRne3ahVVqrc35EmV9JH3CxDaG9MeBLo7FUV5P1GNOIs313cbAgdMYV6Ahr2LvsaLucFvhSwmHh42--meDui-0wdFILjrz7MsITZy4xXjl8_zqMZ2dBaO38wP5o9VCfCXaVlD3IYB9A4-Y9IQ6wEVeodzGloPQyddUZ70kZ7O3E1kYu0tBKhoAL1VcKiUvOj1ABGooqf8QIAWCiAWqDqkZJ5HiR61NC9lJ3MH-aqYXzeHUuOxdlfr3oWPQKlRyYoUW93xD4dXjHQyHsIS4gWYE9ZJ9aIomNHj93UPNfR8ScxQTQBJV9Ttgl_vOUdmlasTmo0OXbTLMexzWCVISvUuoBoTcTY4DXCFeYHz3EE3ijJ9fnDkX3SsvV43x8X58py9rEyAQnRxfeahpWEoxW6TA5qBxg1GMBQu6CY2MupqsmI0XHTXzy8xCt4Pqd_a5zP6Wh_E"); + assert_eq!(jwk.get("use").unwrap(), "sig"); + assert_eq!(jwk.get("alg").unwrap(), "RS256"); } #[test] fn test_rsa_4096_jwk_thumbprint() { - let k = KeyPair::from_pem(KEY_RSA_4096_PEM.as_bytes()).unwrap(); - let jwk = k.jwk_public_key_thumbprint().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 3); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("e")); - assert!(jwk.contains_key("n")); - assert!(!jwk.contains_key("use")); - assert!(!jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "RSA"); - assert_eq!(jwk.get("e").unwrap(), "AQAB"); - assert_eq!(jwk.get("n").unwrap(), "jm0Jh8Zk-aRqUxME0yLeHhBtdDFq6IvVesr_ffFwGqtlI4tIa6MqYuuOE5qlqoBrxt1fpccqGF82j47JqeoWerkNdZxYh1uKGQa9G2vB4OBOFHxYujT-NNPPlwnVPrp1_pjxK0hiOGzZRliNhO65lMbEueQCaLbQ6wstr4aVsWKiw8O4MG8Cc2ZegkWlYg0ZHOAn-uJlAt17NJfX8x576XRwX2KCssPYeyiwBukRne3ahVVqrc35EmV9JH3CxDaG9MeBLo7FUV5P1GNOIs313cbAgdMYV6Ahr2LvsaLucFvhSwmHh42--meDui-0wdFILjrz7MsITZy4xXjl8_zqMZ2dBaO38wP5o9VCfCXaVlD3IYB9A4-Y9IQ6wEVeodzGloPQyddUZ70kZ7O3E1kYu0tBKhoAL1VcKiUvOj1ABGooqf8QIAWCiAWqDqkZJ5HiR61NC9lJ3MH-aqYXzeHUuOxdlfr3oWPQKlRyYoUW93xD4dXjHQyHsIS4gWYE9ZJ9aIomNHj93UPNfR8ScxQTQBJV9Ttgl_vOUdmlasTmo0OXbTLMexzWCVISvUuoBoTcTY4DXCFeYHz3EE3ijJ9fnDkX3SsvV43x8X58py9rEyAQnRxfeahpWEoxW6TA5qBxg1GMBQu6CY2MupqsmI0XHTXzy8xCt4Pqd_a5zP6Wh_E"); + let k = KeyPair::from_pem(KEY_RSA_4096_PEM.as_bytes()).unwrap(); + let jwk = k.jwk_public_key_thumbprint().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 3); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("e")); + assert!(jwk.contains_key("n")); + assert!(!jwk.contains_key("use")); + assert!(!jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "RSA"); + assert_eq!(jwk.get("e").unwrap(), "AQAB"); + assert_eq!(jwk.get("n").unwrap(), "jm0Jh8Zk-aRqUxME0yLeHhBtdDFq6IvVesr_ffFwGqtlI4tIa6MqYuuOE5qlqoBrxt1fpccqGF82j47JqeoWerkNdZxYh1uKGQa9G2vB4OBOFHxYujT-NNPPlwnVPrp1_pjxK0hiOGzZRliNhO65lMbEueQCaLbQ6wstr4aVsWKiw8O4MG8Cc2ZegkWlYg0ZHOAn-uJlAt17NJfX8x576XRwX2KCssPYeyiwBukRne3ahVVqrc35EmV9JH3CxDaG9MeBLo7FUV5P1GNOIs313cbAgdMYV6Ahr2LvsaLucFvhSwmHh42--meDui-0wdFILjrz7MsITZy4xXjl8_zqMZ2dBaO38wP5o9VCfCXaVlD3IYB9A4-Y9IQ6wEVeodzGloPQyddUZ70kZ7O3E1kYu0tBKhoAL1VcKiUvOj1ABGooqf8QIAWCiAWqDqkZJ5HiR61NC9lJ3MH-aqYXzeHUuOxdlfr3oWPQKlRyYoUW93xD4dXjHQyHsIS4gWYE9ZJ9aIomNHj93UPNfR8ScxQTQBJV9Ttgl_vOUdmlasTmo0OXbTLMexzWCVISvUuoBoTcTY4DXCFeYHz3EE3ijJ9fnDkX3SsvV43x8X58py9rEyAQnRxfeahpWEoxW6TA5qBxg1GMBQu6CY2MupqsmI0XHTXzy8xCt4Pqd_a5zP6Wh_E"); } #[test] fn test_ecdsa_p256_jwk() { - let k = KeyPair::from_pem(KEY_ECDSA_P256_PEM.as_bytes()).unwrap(); - let jwk = k.jwk_public_key().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 6); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("crv")); - assert!(jwk.contains_key("x")); - assert!(jwk.contains_key("y")); - assert!(jwk.contains_key("use")); - assert!(jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "EC"); - assert_eq!(jwk.get("crv").unwrap(), "P-256"); - assert_eq!( - jwk.get("x").unwrap(), - "VpJrz2a8rASzmbHStuDxNCjQc8ZiDnrGvVeRayNskrQ" - ); - assert_eq!( - jwk.get("y").unwrap(), - "GrVCHhF5hN68efEgdoYS7acUT88qhMKQbULVcBgPBUg" - ); - assert_eq!(jwk.get("use").unwrap(), "sig"); - assert_eq!(jwk.get("alg").unwrap(), "ES256"); + let k = KeyPair::from_pem(KEY_ECDSA_P256_PEM.as_bytes()).unwrap(); + let jwk = k.jwk_public_key().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 6); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("crv")); + assert!(jwk.contains_key("x")); + assert!(jwk.contains_key("y")); + assert!(jwk.contains_key("use")); + assert!(jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "EC"); + assert_eq!(jwk.get("crv").unwrap(), "P-256"); + assert_eq!( + jwk.get("x").unwrap(), + "VpJrz2a8rASzmbHStuDxNCjQc8ZiDnrGvVeRayNskrQ" + ); + assert_eq!( + jwk.get("y").unwrap(), + "GrVCHhF5hN68efEgdoYS7acUT88qhMKQbULVcBgPBUg" + ); + assert_eq!(jwk.get("use").unwrap(), "sig"); + assert_eq!(jwk.get("alg").unwrap(), "ES256"); } #[test] fn test_ecdsa_p256_jwk_thumbprint() { - let k = KeyPair::from_pem(KEY_ECDSA_P256_PEM.as_bytes()).unwrap(); - let jwk = k.jwk_public_key_thumbprint().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 4); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("crv")); - assert!(jwk.contains_key("x")); - assert!(jwk.contains_key("y")); - assert!(!jwk.contains_key("use")); - assert!(!jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "EC"); - assert_eq!(jwk.get("crv").unwrap(), "P-256"); - assert_eq!( - jwk.get("x").unwrap(), - "VpJrz2a8rASzmbHStuDxNCjQc8ZiDnrGvVeRayNskrQ" - ); - assert_eq!( - jwk.get("y").unwrap(), - "GrVCHhF5hN68efEgdoYS7acUT88qhMKQbULVcBgPBUg" - ); + let k = KeyPair::from_pem(KEY_ECDSA_P256_PEM.as_bytes()).unwrap(); + let jwk = k.jwk_public_key_thumbprint().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 4); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("crv")); + assert!(jwk.contains_key("x")); + assert!(jwk.contains_key("y")); + assert!(!jwk.contains_key("use")); + assert!(!jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "EC"); + assert_eq!(jwk.get("crv").unwrap(), "P-256"); + assert_eq!( + jwk.get("x").unwrap(), + "VpJrz2a8rASzmbHStuDxNCjQc8ZiDnrGvVeRayNskrQ" + ); + assert_eq!( + jwk.get("y").unwrap(), + "GrVCHhF5hN68efEgdoYS7acUT88qhMKQbULVcBgPBUg" + ); } #[test] fn test_ecdsa_p384_jwk() { - let k = KeyPair::from_pem(KEY_ECDSA_P384_PEM.as_bytes()).unwrap(); - let jwk = k.jwk_public_key().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 6); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("crv")); - assert!(jwk.contains_key("x")); - assert!(jwk.contains_key("y")); - assert!(jwk.contains_key("use")); - assert!(jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "EC"); - assert_eq!(jwk.get("crv").unwrap(), "P-384"); - assert_eq!( - jwk.get("x").unwrap(), - "N7TmS8prIp0DAGvwg1saML4UK61oe2PPJTeGLJt0iW-PMNcetFPcMF4WCa0ez80a" - ); - assert_eq!( - jwk.get("y").unwrap(), - "RE5dtMDKV9Y8hsKf3fqLzMx75WORJaGswqC68xkRNjo0HcTar4tCB9VF9eSFfTMU" - ); - assert_eq!(jwk.get("use").unwrap(), "sig"); - assert_eq!(jwk.get("alg").unwrap(), "ES384"); + let k = KeyPair::from_pem(KEY_ECDSA_P384_PEM.as_bytes()).unwrap(); + let jwk = k.jwk_public_key().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 6); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("crv")); + assert!(jwk.contains_key("x")); + assert!(jwk.contains_key("y")); + assert!(jwk.contains_key("use")); + assert!(jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "EC"); + assert_eq!(jwk.get("crv").unwrap(), "P-384"); + assert_eq!( + jwk.get("x").unwrap(), + "N7TmS8prIp0DAGvwg1saML4UK61oe2PPJTeGLJt0iW-PMNcetFPcMF4WCa0ez80a" + ); + assert_eq!( + jwk.get("y").unwrap(), + "RE5dtMDKV9Y8hsKf3fqLzMx75WORJaGswqC68xkRNjo0HcTar4tCB9VF9eSFfTMU" + ); + assert_eq!(jwk.get("use").unwrap(), "sig"); + assert_eq!(jwk.get("alg").unwrap(), "ES384"); } #[test] fn test_ecdsa_p384_jwk_thumbprint() { - let k = KeyPair::from_pem(KEY_ECDSA_P384_PEM.as_bytes()).unwrap(); - let jwk = k.jwk_public_key_thumbprint().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 4); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("crv")); - assert!(jwk.contains_key("x")); - assert!(jwk.contains_key("y")); - assert!(!jwk.contains_key("use")); - assert!(!jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "EC"); - assert_eq!(jwk.get("crv").unwrap(), "P-384"); - assert_eq!( - jwk.get("x").unwrap(), - "N7TmS8prIp0DAGvwg1saML4UK61oe2PPJTeGLJt0iW-PMNcetFPcMF4WCa0ez80a" - ); - assert_eq!( - jwk.get("y").unwrap(), - "RE5dtMDKV9Y8hsKf3fqLzMx75WORJaGswqC68xkRNjo0HcTar4tCB9VF9eSFfTMU" - ); + let k = KeyPair::from_pem(KEY_ECDSA_P384_PEM.as_bytes()).unwrap(); + let jwk = k.jwk_public_key_thumbprint().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 4); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("crv")); + assert!(jwk.contains_key("x")); + assert!(jwk.contains_key("y")); + assert!(!jwk.contains_key("use")); + assert!(!jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "EC"); + assert_eq!(jwk.get("crv").unwrap(), "P-384"); + assert_eq!( + jwk.get("x").unwrap(), + "N7TmS8prIp0DAGvwg1saML4UK61oe2PPJTeGLJt0iW-PMNcetFPcMF4WCa0ez80a" + ); + assert_eq!( + jwk.get("y").unwrap(), + "RE5dtMDKV9Y8hsKf3fqLzMx75WORJaGswqC68xkRNjo0HcTar4tCB9VF9eSFfTMU" + ); } #[cfg(ed25519)] #[test] fn test_ed25519_jwk() { - let k = KeyPair::from_pem(KEY_ECDSA_ED25519_PEM.as_bytes()).unwrap(); - let jwk = k.jwk_public_key().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 5); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("crv")); - assert!(jwk.contains_key("x")); - assert!(jwk.contains_key("use")); - assert!(jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "OKP"); - assert_eq!(jwk.get("crv").unwrap(), "Ed25519"); - assert_eq!( - jwk.get("x").unwrap(), - "DUX9ja8pq2wfkxuIaHzmhkdcVXMav_3rk5Y5ozOcp4o" - ); - assert_eq!(jwk.get("use").unwrap(), "sig"); - assert_eq!(jwk.get("alg").unwrap(), "EdDSA"); + let k = KeyPair::from_pem(KEY_ECDSA_ED25519_PEM.as_bytes()).unwrap(); + let jwk = k.jwk_public_key().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 5); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("crv")); + assert!(jwk.contains_key("x")); + assert!(jwk.contains_key("use")); + assert!(jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "OKP"); + assert_eq!(jwk.get("crv").unwrap(), "Ed25519"); + assert_eq!( + jwk.get("x").unwrap(), + "DUX9ja8pq2wfkxuIaHzmhkdcVXMav_3rk5Y5ozOcp4o" + ); + assert_eq!(jwk.get("use").unwrap(), "sig"); + assert_eq!(jwk.get("alg").unwrap(), "EdDSA"); } #[cfg(ed25519)] #[test] fn test_ed25519_jwk_thumbprint() { - let k = KeyPair::from_pem(KEY_ECDSA_ED25519_PEM.as_bytes()).unwrap(); - let jwk = k.jwk_public_key_thumbprint().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 3); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("crv")); - assert!(jwk.contains_key("x")); - assert!(!jwk.contains_key("use")); - assert!(!jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "OKP"); - assert_eq!(jwk.get("crv").unwrap(), "Ed25519"); - assert_eq!( - jwk.get("x").unwrap(), - "DUX9ja8pq2wfkxuIaHzmhkdcVXMav_3rk5Y5ozOcp4o" - ); + let k = KeyPair::from_pem(KEY_ECDSA_ED25519_PEM.as_bytes()).unwrap(); + let jwk = k.jwk_public_key_thumbprint().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 3); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("crv")); + assert!(jwk.contains_key("x")); + assert!(!jwk.contains_key("use")); + assert!(!jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "OKP"); + assert_eq!(jwk.get("crv").unwrap(), "Ed25519"); + assert_eq!( + jwk.get("x").unwrap(), + "DUX9ja8pq2wfkxuIaHzmhkdcVXMav_3rk5Y5ozOcp4o" + ); } #[cfg(ed25519)] #[test] fn test_ed25519_jwk_bis() { - let k = KeyPair::from_pem(KEY_ECDSA_ED25519_PEM_BIS.as_bytes()).unwrap(); - let jwk = k.jwk_public_key().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 5); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("crv")); - assert!(jwk.contains_key("x")); - assert!(jwk.contains_key("use")); - assert!(jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "OKP"); - assert_eq!(jwk.get("crv").unwrap(), "Ed25519"); - assert_eq!( - jwk.get("x").unwrap(), - "i9K0eV5qOJ_l_TWjWFLm8R-JbyGdlqFFeL_J0eEXFnc" - ); - assert_eq!(jwk.get("use").unwrap(), "sig"); - assert_eq!(jwk.get("alg").unwrap(), "EdDSA"); + let k = KeyPair::from_pem(KEY_ECDSA_ED25519_PEM_BIS.as_bytes()).unwrap(); + let jwk = k.jwk_public_key().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 5); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("crv")); + assert!(jwk.contains_key("x")); + assert!(jwk.contains_key("use")); + assert!(jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "OKP"); + assert_eq!(jwk.get("crv").unwrap(), "Ed25519"); + assert_eq!( + jwk.get("x").unwrap(), + "i9K0eV5qOJ_l_TWjWFLm8R-JbyGdlqFFeL_J0eEXFnc" + ); + assert_eq!(jwk.get("use").unwrap(), "sig"); + assert_eq!(jwk.get("alg").unwrap(), "EdDSA"); } #[cfg(ed25519)] #[test] fn test_ed25519_jwk_thumbprint_bis() { - let k = KeyPair::from_pem(KEY_ECDSA_ED25519_PEM_BIS.as_bytes()).unwrap(); - let jwk = k.jwk_public_key_thumbprint().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 3); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("crv")); - assert!(jwk.contains_key("x")); - assert!(!jwk.contains_key("use")); - assert!(!jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "OKP"); - assert_eq!(jwk.get("crv").unwrap(), "Ed25519"); - assert_eq!( - jwk.get("x").unwrap(), - "i9K0eV5qOJ_l_TWjWFLm8R-JbyGdlqFFeL_J0eEXFnc" - ); + let k = KeyPair::from_pem(KEY_ECDSA_ED25519_PEM_BIS.as_bytes()).unwrap(); + let jwk = k.jwk_public_key_thumbprint().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 3); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("crv")); + assert!(jwk.contains_key("x")); + assert!(!jwk.contains_key("use")); + assert!(!jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "OKP"); + assert_eq!(jwk.get("crv").unwrap(), "Ed25519"); + assert_eq!( + jwk.get("x").unwrap(), + "i9K0eV5qOJ_l_TWjWFLm8R-JbyGdlqFFeL_J0eEXFnc" + ); } #[cfg(ed448)] #[test] fn test_ed448_jwk() { - let k = KeyPair::from_pem(KEY_ECDSA_ED448_PEM.as_bytes()).unwrap(); - let jwk = k.jwk_public_key().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 5); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("crv")); - assert!(jwk.contains_key("x")); - assert!(jwk.contains_key("use")); - assert!(jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "OKP"); - assert_eq!(jwk.get("crv").unwrap(), "Ed448"); - assert_eq!( - jwk.get("x").unwrap(), - "b9GZ8b1hip3UMzkkNBdMF4JWBTZojxsNHK-jQBH94SY3boVs4Oeo291E1dGXz7RUMqIXjkSbU4EA" - ); - assert_eq!(jwk.get("use").unwrap(), "sig"); - assert_eq!(jwk.get("alg").unwrap(), "EdDSA"); + let k = KeyPair::from_pem(KEY_ECDSA_ED448_PEM.as_bytes()).unwrap(); + let jwk = k.jwk_public_key().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 5); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("crv")); + assert!(jwk.contains_key("x")); + assert!(jwk.contains_key("use")); + assert!(jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "OKP"); + assert_eq!(jwk.get("crv").unwrap(), "Ed448"); + assert_eq!( + jwk.get("x").unwrap(), + "b9GZ8b1hip3UMzkkNBdMF4JWBTZojxsNHK-jQBH94SY3boVs4Oeo291E1dGXz7RUMqIXjkSbU4EA" + ); + assert_eq!(jwk.get("use").unwrap(), "sig"); + assert_eq!(jwk.get("alg").unwrap(), "EdDSA"); } #[cfg(ed448)] #[test] fn test_ed448_jwk_thumbprint() { - let k = KeyPair::from_pem(KEY_ECDSA_ED448_PEM.as_bytes()).unwrap(); - let jwk = k.jwk_public_key_thumbprint().unwrap(); - assert!(jwk.is_object()); - let jwk = jwk.as_object().unwrap(); - assert_eq!(jwk.len(), 3); - assert!(jwk.contains_key("kty")); - assert!(jwk.contains_key("crv")); - assert!(jwk.contains_key("x")); - assert!(!jwk.contains_key("use")); - assert!(!jwk.contains_key("alg")); - assert_eq!(jwk.get("kty").unwrap(), "OKP"); - assert_eq!(jwk.get("crv").unwrap(), "Ed448"); - assert_eq!( - jwk.get("x").unwrap(), - "b9GZ8b1hip3UMzkkNBdMF4JWBTZojxsNHK-jQBH94SY3boVs4Oeo291E1dGXz7RUMqIXjkSbU4EA" - ); + let k = KeyPair::from_pem(KEY_ECDSA_ED448_PEM.as_bytes()).unwrap(); + let jwk = k.jwk_public_key_thumbprint().unwrap(); + assert!(jwk.is_object()); + let jwk = jwk.as_object().unwrap(); + assert_eq!(jwk.len(), 3); + assert!(jwk.contains_key("kty")); + assert!(jwk.contains_key("crv")); + assert!(jwk.contains_key("x")); + assert!(!jwk.contains_key("use")); + assert!(!jwk.contains_key("alg")); + assert_eq!(jwk.get("kty").unwrap(), "OKP"); + assert_eq!(jwk.get("crv").unwrap(), "Ed448"); + assert_eq!( + jwk.get("x").unwrap(), + "b9GZ8b1hip3UMzkkNBdMF4JWBTZojxsNHK-jQBH94SY3boVs4Oeo291E1dGXz7RUMqIXjkSbU4EA" + ); } diff --git a/acme_common/src/tests/hash.rs b/acme_common/src/tests/hash.rs index 6cebc12..039bcf4 100644 --- a/acme_common/src/tests/hash.rs +++ b/acme_common/src/tests/hash.rs @@ -2,343 +2,343 @@ use crate::crypto::HashFunction; #[test] fn test_hash_from_str() { - let test_vectors = vec![ - ("sha256", HashFunction::Sha256), - ("Sha256", HashFunction::Sha256), - ("sha-256", HashFunction::Sha256), - ("SHA_256", HashFunction::Sha256), - ("sha384", HashFunction::Sha384), - ("Sha-512", HashFunction::Sha512), - ]; - for (s, ref_h) in test_vectors { - let h: HashFunction = s.parse().unwrap(); - assert_eq!(h, ref_h); - } + let test_vectors = vec![ + ("sha256", HashFunction::Sha256), + ("Sha256", HashFunction::Sha256), + ("sha-256", HashFunction::Sha256), + ("SHA_256", HashFunction::Sha256), + ("sha384", HashFunction::Sha384), + ("Sha-512", HashFunction::Sha512), + ]; + for (s, ref_h) in test_vectors { + let h: HashFunction = s.parse().unwrap(); + assert_eq!(h, ref_h); + } } #[test] fn test_hash_from_invalid_str() { - let test_vectors = vec!["sha42", "sha", "", "plop"]; - for s in test_vectors { - let h = s.parse::(); - assert!(h.is_err()); - } + let test_vectors = vec!["sha42", "sha", "", "plop"]; + for s in test_vectors { + let h = s.parse::(); + assert!(h.is_err()); + } } #[test] fn test_hash_sha256() { - let test_vectors = vec![ - ( - "Hello World!".as_bytes(), - vec![ - 127, 131, 177, 101, 127, 241, 252, 83, 185, 45, 193, 129, 72, 161, 214, 93, 252, - 45, 75, 31, 163, 214, 119, 40, 74, 221, 210, 0, 18, 109, 144, 105, - ], - ), - ( - &[], - vec![ - 227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, - 174, 65, 228, 100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, 85, - ], - ), - ( - &[ - 194, 43, 6, 43, 252, 50, 206, 26, 240, 105, 85, 119, 40, 153, 213, 123, 158, 59, 8, - 45, 114, - ], - vec![ - 65, 72, 199, 76, 128, 174, 196, 223, 91, 235, 87, 119, 200, 212, 133, 13, 219, 223, - 60, 4, 73, 70, 65, 41, 226, 83, 221, 107, 112, 29, 205, 28, - ], - ), - ]; - for (data, expected) in test_vectors { - let h = HashFunction::Sha256; - let res = h.hash(data); - assert_eq!(res, expected); - } + let test_vectors = vec![ + ( + "Hello World!".as_bytes(), + vec![ + 127, 131, 177, 101, 127, 241, 252, 83, 185, 45, 193, 129, 72, 161, 214, 93, 252, + 45, 75, 31, 163, 214, 119, 40, 74, 221, 210, 0, 18, 109, 144, 105, + ], + ), + ( + &[], + vec![ + 227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, + 174, 65, 228, 100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, 85, + ], + ), + ( + &[ + 194, 43, 6, 43, 252, 50, 206, 26, 240, 105, 85, 119, 40, 153, 213, 123, 158, 59, 8, + 45, 114, + ], + vec![ + 65, 72, 199, 76, 128, 174, 196, 223, 91, 235, 87, 119, 200, 212, 133, 13, 219, 223, + 60, 4, 73, 70, 65, 41, 226, 83, 221, 107, 112, 29, 205, 28, + ], + ), + ]; + for (data, expected) in test_vectors { + let h = HashFunction::Sha256; + let res = h.hash(data); + assert_eq!(res, expected); + } } #[test] fn test_hmac_sha256() { - let test_vectors = vec![ - ( - vec![ - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - ], - vec![72, 105, 32, 84, 104, 101, 114, 101], - vec![ - 176, 52, 76, 97, 216, 219, 56, 83, 92, 168, 175, 206, 175, 11, 241, 43, 136, 29, - 194, 0, 201, 131, 61, 167, 38, 233, 55, 108, 46, 50, 207, 247, - ], - ), - ( - vec![74, 101, 102, 101], - vec![ - 119, 104, 97, 116, 32, 100, 111, 32, 121, 97, 32, 119, 97, 110, 116, 32, 102, 111, - 114, 32, 110, 111, 116, 104, 105, 110, 103, 63, - ], - vec![ - 91, 220, 193, 70, 191, 96, 117, 78, 106, 4, 36, 38, 8, 149, 117, 199, 90, 0, 63, 8, - 157, 39, 57, 131, 157, 236, 88, 185, 100, 236, 56, 67, - ], - ), - ( - vec![ - 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, - 170, 170, 170, 170, - ], - vec![ - 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, - 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, - 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, - 221, 221, - ], - vec![ - 119, 62, 169, 30, 54, 128, 14, 70, 133, 77, 184, 235, 208, 145, 129, 167, 41, 89, - 9, 139, 62, 248, 193, 34, 217, 99, 85, 20, 206, 213, 101, 254, - ], - ), - ( - vec![ - 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, - ], - vec![ - 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, - 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, - 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, - 205, 205, - ], - vec![ - 130, 85, 138, 56, 154, 68, 60, 14, 164, 204, 129, 152, 153, 242, 8, 58, 133, 240, - 250, 163, 229, 120, 248, 7, 122, 46, 63, 244, 103, 41, 102, 91, - ], - ), - ]; - for (key, data, expected) in test_vectors { - let h = HashFunction::Sha256; - let res = h.hmac(&key, &data).unwrap(); - assert_eq!(res, expected); - } + let test_vectors = vec![ + ( + vec![ + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + ], + vec![72, 105, 32, 84, 104, 101, 114, 101], + vec![ + 176, 52, 76, 97, 216, 219, 56, 83, 92, 168, 175, 206, 175, 11, 241, 43, 136, 29, + 194, 0, 201, 131, 61, 167, 38, 233, 55, 108, 46, 50, 207, 247, + ], + ), + ( + vec![74, 101, 102, 101], + vec![ + 119, 104, 97, 116, 32, 100, 111, 32, 121, 97, 32, 119, 97, 110, 116, 32, 102, 111, + 114, 32, 110, 111, 116, 104, 105, 110, 103, 63, + ], + vec![ + 91, 220, 193, 70, 191, 96, 117, 78, 106, 4, 36, 38, 8, 149, 117, 199, 90, 0, 63, 8, + 157, 39, 57, 131, 157, 236, 88, 185, 100, 236, 56, 67, + ], + ), + ( + vec![ + 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 170, 170, 170, + ], + vec![ + 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, + 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, + 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, + 221, 221, + ], + vec![ + 119, 62, 169, 30, 54, 128, 14, 70, 133, 77, 184, 235, 208, 145, 129, 167, 41, 89, + 9, 139, 62, 248, 193, 34, 217, 99, 85, 20, 206, 213, 101, 254, + ], + ), + ( + vec![ + 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, + ], + vec![ + 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, + 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, + 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, + 205, 205, + ], + vec![ + 130, 85, 138, 56, 154, 68, 60, 14, 164, 204, 129, 152, 153, 242, 8, 58, 133, 240, + 250, 163, 229, 120, 248, 7, 122, 46, 63, 244, 103, 41, 102, 91, + ], + ), + ]; + for (key, data, expected) in test_vectors { + let h = HashFunction::Sha256; + let res = h.hmac(&key, &data).unwrap(); + assert_eq!(res, expected); + } } #[test] fn test_hash_sha384() { - let test_vectors = vec![ - ( - "Hello World!".as_bytes(), - vec![ - 191, 215, 108, 14, 187, 208, 6, 254, 229, 131, 65, 5, 71, 193, 136, 123, 2, 146, - 190, 118, 213, 130, 217, 108, 36, 45, 42, 121, 39, 35, 227, 253, 111, 208, 97, 249, - 213, 207, 209, 59, 143, 150, 19, 88, 230, 173, 186, 74, - ], - ), - ( - &[], - vec![ - 56, 176, 96, 167, 81, 172, 150, 56, 76, 217, 50, 126, 177, 177, 227, 106, 33, 253, - 183, 17, 20, 190, 7, 67, 76, 12, 199, 191, 99, 246, 225, 218, 39, 78, 222, 191, - 231, 111, 101, 251, 213, 26, 210, 241, 72, 152, 185, 91, - ], - ), - ( - &[ - 194, 43, 6, 43, 252, 50, 206, 26, 240, 105, 85, 119, 40, 153, 213, 123, 158, 59, 8, - 45, 114, - ], - vec![ - 170, 126, 84, 2, 141, 91, 106, 70, 80, 53, 98, 101, 184, 3, 34, 146, 130, 238, 146, - 221, 113, 197, 154, 91, 4, 208, 229, 15, 8, 179, 51, 29, 224, 200, 187, 127, 9, - 243, 29, 171, 189, 124, 60, 39, 3, 74, 171, 156, - ], - ), - ]; - for (data, expected) in test_vectors { - let h = HashFunction::Sha384; - let res = h.hash(data); - assert_eq!(res, expected); - } + let test_vectors = vec![ + ( + "Hello World!".as_bytes(), + vec![ + 191, 215, 108, 14, 187, 208, 6, 254, 229, 131, 65, 5, 71, 193, 136, 123, 2, 146, + 190, 118, 213, 130, 217, 108, 36, 45, 42, 121, 39, 35, 227, 253, 111, 208, 97, 249, + 213, 207, 209, 59, 143, 150, 19, 88, 230, 173, 186, 74, + ], + ), + ( + &[], + vec![ + 56, 176, 96, 167, 81, 172, 150, 56, 76, 217, 50, 126, 177, 177, 227, 106, 33, 253, + 183, 17, 20, 190, 7, 67, 76, 12, 199, 191, 99, 246, 225, 218, 39, 78, 222, 191, + 231, 111, 101, 251, 213, 26, 210, 241, 72, 152, 185, 91, + ], + ), + ( + &[ + 194, 43, 6, 43, 252, 50, 206, 26, 240, 105, 85, 119, 40, 153, 213, 123, 158, 59, 8, + 45, 114, + ], + vec![ + 170, 126, 84, 2, 141, 91, 106, 70, 80, 53, 98, 101, 184, 3, 34, 146, 130, 238, 146, + 221, 113, 197, 154, 91, 4, 208, 229, 15, 8, 179, 51, 29, 224, 200, 187, 127, 9, + 243, 29, 171, 189, 124, 60, 39, 3, 74, 171, 156, + ], + ), + ]; + for (data, expected) in test_vectors { + let h = HashFunction::Sha384; + let res = h.hash(data); + assert_eq!(res, expected); + } } #[test] fn test_hmac_sha384() { - let test_vectors = vec![ - ( - vec![ - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - ], - vec![72, 105, 32, 84, 104, 101, 114, 101], - vec![ - 175, 208, 57, 68, 216, 72, 149, 98, 107, 8, 37, 244, 171, 70, 144, 127, 21, 249, - 218, 219, 228, 16, 30, 198, 130, 170, 3, 76, 124, 235, 197, 156, 250, 234, 158, - 169, 7, 110, 222, 127, 74, 241, 82, 232, 178, 250, 156, 182, - ], - ), - ( - vec![74, 101, 102, 101], - vec![ - 119, 104, 97, 116, 32, 100, 111, 32, 121, 97, 32, 119, 97, 110, 116, 32, 102, 111, - 114, 32, 110, 111, 116, 104, 105, 110, 103, 63, - ], - vec![ - 175, 69, 210, 227, 118, 72, 64, 49, 97, 127, 120, 210, 181, 138, 107, 27, 156, 126, - 244, 100, 245, 160, 27, 71, 228, 46, 195, 115, 99, 34, 68, 94, 142, 34, 64, 202, - 94, 105, 226, 199, 139, 50, 57, 236, 250, 178, 22, 73, - ], - ), - ( - vec![ - 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, - 170, 170, 170, 170, - ], - vec![ - 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, - 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, - 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, - 221, 221, - ], - vec![ - 136, 6, 38, 8, 211, 230, 173, 138, 10, 162, 172, 224, 20, 200, 168, 111, 10, 166, - 53, 217, 71, 172, 159, 235, 232, 62, 244, 229, 89, 102, 20, 75, 42, 90, 179, 157, - 193, 56, 20, 185, 78, 58, 182, 225, 1, 163, 79, 39, - ], - ), - ( - vec![ - 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, - ], - vec![ - 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, - 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, - 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, - 205, 205, - ], - vec![ - 62, 138, 105, 183, 120, 60, 37, 133, 25, 51, 171, 98, 144, 175, 108, 167, 122, 153, - 129, 72, 8, 80, 0, 156, 197, 87, 124, 110, 31, 87, 59, 78, 104, 1, 221, 35, 196, - 167, 214, 121, 204, 248, 163, 134, 198, 116, 207, 251, - ], - ), - ]; - for (key, data, expected) in test_vectors { - let h = HashFunction::Sha384; - let res = h.hmac(&key, &data).unwrap(); - assert_eq!(res, expected); - } + let test_vectors = vec![ + ( + vec![ + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + ], + vec![72, 105, 32, 84, 104, 101, 114, 101], + vec![ + 175, 208, 57, 68, 216, 72, 149, 98, 107, 8, 37, 244, 171, 70, 144, 127, 21, 249, + 218, 219, 228, 16, 30, 198, 130, 170, 3, 76, 124, 235, 197, 156, 250, 234, 158, + 169, 7, 110, 222, 127, 74, 241, 82, 232, 178, 250, 156, 182, + ], + ), + ( + vec![74, 101, 102, 101], + vec![ + 119, 104, 97, 116, 32, 100, 111, 32, 121, 97, 32, 119, 97, 110, 116, 32, 102, 111, + 114, 32, 110, 111, 116, 104, 105, 110, 103, 63, + ], + vec![ + 175, 69, 210, 227, 118, 72, 64, 49, 97, 127, 120, 210, 181, 138, 107, 27, 156, 126, + 244, 100, 245, 160, 27, 71, 228, 46, 195, 115, 99, 34, 68, 94, 142, 34, 64, 202, + 94, 105, 226, 199, 139, 50, 57, 236, 250, 178, 22, 73, + ], + ), + ( + vec![ + 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 170, 170, 170, + ], + vec![ + 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, + 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, + 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, + 221, 221, + ], + vec![ + 136, 6, 38, 8, 211, 230, 173, 138, 10, 162, 172, 224, 20, 200, 168, 111, 10, 166, + 53, 217, 71, 172, 159, 235, 232, 62, 244, 229, 89, 102, 20, 75, 42, 90, 179, 157, + 193, 56, 20, 185, 78, 58, 182, 225, 1, 163, 79, 39, + ], + ), + ( + vec![ + 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, + ], + vec![ + 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, + 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, + 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, + 205, 205, + ], + vec![ + 62, 138, 105, 183, 120, 60, 37, 133, 25, 51, 171, 98, 144, 175, 108, 167, 122, 153, + 129, 72, 8, 80, 0, 156, 197, 87, 124, 110, 31, 87, 59, 78, 104, 1, 221, 35, 196, + 167, 214, 121, 204, 248, 163, 134, 198, 116, 207, 251, + ], + ), + ]; + for (key, data, expected) in test_vectors { + let h = HashFunction::Sha384; + let res = h.hmac(&key, &data).unwrap(); + assert_eq!(res, expected); + } } #[test] fn test_hash_sha512() { - let test_vectors = vec![ - ( - "Hello World!".as_bytes(), - vec![ - 134, 24, 68, 214, 112, 78, 133, 115, 254, 195, 77, 150, 126, 32, 188, 254, 243, - 212, 36, 207, 72, 190, 4, 230, 220, 8, 242, 189, 88, 199, 41, 116, 51, 113, 1, 94, - 173, 137, 28, 195, 207, 28, 157, 52, 180, 146, 100, 181, 16, 117, 27, 31, 249, 229, - 55, 147, 123, 196, 107, 93, 111, 244, 236, 200, - ], - ), - ( - &[], - vec![ - 207, 131, 225, 53, 126, 239, 184, 189, 241, 84, 40, 80, 214, 109, 128, 7, 214, 32, - 228, 5, 11, 87, 21, 220, 131, 244, 169, 33, 211, 108, 233, 206, 71, 208, 209, 60, - 93, 133, 242, 176, 255, 131, 24, 210, 135, 126, 236, 47, 99, 185, 49, 189, 71, 65, - 122, 129, 165, 56, 50, 122, 249, 39, 218, 62, - ], - ), - ( - &[ - 194, 43, 6, 43, 252, 50, 206, 26, 240, 105, 85, 119, 40, 153, 213, 123, 158, 59, 8, - 45, 114, - ], - vec![ - 58, 93, 210, 174, 119, 179, 246, 25, 14, 148, 182, 109, 28, 14, 16, 80, 45, 231, - 104, 169, 130, 43, 39, 221, 12, 112, 85, 159, 123, 6, 227, 35, 61, 24, 158, 190, - 162, 11, 247, 204, 98, 41, 242, 5, 52, 116, 149, 220, 124, 82, 159, 181, 74, 210, - 85, 190, 59, 130, 209, 8, 181, 247, 192, 65, - ], - ), - ]; - for (data, expected) in test_vectors { - let h = HashFunction::Sha512; - let res = h.hash(data); - assert_eq!(res, expected); - } + let test_vectors = vec![ + ( + "Hello World!".as_bytes(), + vec![ + 134, 24, 68, 214, 112, 78, 133, 115, 254, 195, 77, 150, 126, 32, 188, 254, 243, + 212, 36, 207, 72, 190, 4, 230, 220, 8, 242, 189, 88, 199, 41, 116, 51, 113, 1, 94, + 173, 137, 28, 195, 207, 28, 157, 52, 180, 146, 100, 181, 16, 117, 27, 31, 249, 229, + 55, 147, 123, 196, 107, 93, 111, 244, 236, 200, + ], + ), + ( + &[], + vec![ + 207, 131, 225, 53, 126, 239, 184, 189, 241, 84, 40, 80, 214, 109, 128, 7, 214, 32, + 228, 5, 11, 87, 21, 220, 131, 244, 169, 33, 211, 108, 233, 206, 71, 208, 209, 60, + 93, 133, 242, 176, 255, 131, 24, 210, 135, 126, 236, 47, 99, 185, 49, 189, 71, 65, + 122, 129, 165, 56, 50, 122, 249, 39, 218, 62, + ], + ), + ( + &[ + 194, 43, 6, 43, 252, 50, 206, 26, 240, 105, 85, 119, 40, 153, 213, 123, 158, 59, 8, + 45, 114, + ], + vec![ + 58, 93, 210, 174, 119, 179, 246, 25, 14, 148, 182, 109, 28, 14, 16, 80, 45, 231, + 104, 169, 130, 43, 39, 221, 12, 112, 85, 159, 123, 6, 227, 35, 61, 24, 158, 190, + 162, 11, 247, 204, 98, 41, 242, 5, 52, 116, 149, 220, 124, 82, 159, 181, 74, 210, + 85, 190, 59, 130, 209, 8, 181, 247, 192, 65, + ], + ), + ]; + for (data, expected) in test_vectors { + let h = HashFunction::Sha512; + let res = h.hash(data); + assert_eq!(res, expected); + } } #[test] fn test_hmac_sha512() { - let test_vectors = vec![ - ( - vec![ - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - ], - vec![72, 105, 32, 84, 104, 101, 114, 101], - vec![ - 135, 170, 124, 222, 165, 239, 97, 157, 79, 240, 180, 36, 26, 29, 108, 176, 35, 121, - 244, 226, 206, 78, 194, 120, 122, 208, 179, 5, 69, 225, 124, 222, 218, 168, 51, - 183, 214, 184, 167, 2, 3, 139, 39, 78, 174, 163, 244, 228, 190, 157, 145, 78, 235, - 97, 241, 112, 46, 105, 108, 32, 58, 18, 104, 84, - ], - ), - ( - vec![74, 101, 102, 101], - vec![ - 119, 104, 97, 116, 32, 100, 111, 32, 121, 97, 32, 119, 97, 110, 116, 32, 102, 111, - 114, 32, 110, 111, 116, 104, 105, 110, 103, 63, - ], - vec![ - 22, 75, 122, 123, 252, 248, 25, 226, 227, 149, 251, 231, 59, 86, 224, 163, 135, - 189, 100, 34, 46, 131, 31, 214, 16, 39, 12, 215, 234, 37, 5, 84, 151, 88, 191, 117, - 192, 90, 153, 74, 109, 3, 79, 101, 248, 240, 230, 253, 202, 234, 177, 163, 77, 74, - 107, 75, 99, 110, 7, 10, 56, 188, 231, 55, - ], - ), - ( - vec![ - 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, - 170, 170, 170, 170, - ], - vec![ - 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, - 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, - 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, - 221, 221, - ], - vec![ - 250, 115, 176, 8, 157, 86, 162, 132, 239, 176, 240, 117, 108, 137, 11, 233, 177, - 181, 219, 221, 142, 232, 26, 54, 85, 248, 62, 51, 178, 39, 157, 57, 191, 62, 132, - 130, 121, 167, 34, 200, 6, 180, 133, 164, 126, 103, 200, 7, 185, 70, 163, 55, 190, - 232, 148, 38, 116, 39, 136, 89, 225, 50, 146, 251, - ], - ), - ( - vec![ - 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, - ], - vec![ - 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, - 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, - 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, - 205, 205, - ], - vec![ - 176, 186, 70, 86, 55, 69, 140, 105, 144, 229, 168, 197, 246, 29, 74, 247, 229, 118, - 217, 127, 249, 75, 135, 45, 231, 111, 128, 80, 54, 30, 227, 219, 169, 28, 165, 193, - 26, 162, 94, 180, 214, 121, 39, 92, 197, 120, 128, 99, 165, 241, 151, 65, 18, 12, - 79, 45, 226, 173, 235, 235, 16, 162, 152, 221, - ], - ), - ]; - for (key, data, expected) in test_vectors { - let h = HashFunction::Sha512; - let res = h.hmac(&key, &data).unwrap(); - assert_eq!(res, expected); - } + let test_vectors = vec![ + ( + vec![ + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + ], + vec![72, 105, 32, 84, 104, 101, 114, 101], + vec![ + 135, 170, 124, 222, 165, 239, 97, 157, 79, 240, 180, 36, 26, 29, 108, 176, 35, 121, + 244, 226, 206, 78, 194, 120, 122, 208, 179, 5, 69, 225, 124, 222, 218, 168, 51, + 183, 214, 184, 167, 2, 3, 139, 39, 78, 174, 163, 244, 228, 190, 157, 145, 78, 235, + 97, 241, 112, 46, 105, 108, 32, 58, 18, 104, 84, + ], + ), + ( + vec![74, 101, 102, 101], + vec![ + 119, 104, 97, 116, 32, 100, 111, 32, 121, 97, 32, 119, 97, 110, 116, 32, 102, 111, + 114, 32, 110, 111, 116, 104, 105, 110, 103, 63, + ], + vec![ + 22, 75, 122, 123, 252, 248, 25, 226, 227, 149, 251, 231, 59, 86, 224, 163, 135, + 189, 100, 34, 46, 131, 31, 214, 16, 39, 12, 215, 234, 37, 5, 84, 151, 88, 191, 117, + 192, 90, 153, 74, 109, 3, 79, 101, 248, 240, 230, 253, 202, 234, 177, 163, 77, 74, + 107, 75, 99, 110, 7, 10, 56, 188, 231, 55, + ], + ), + ( + vec![ + 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 170, 170, 170, + ], + vec![ + 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, + 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, + 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, + 221, 221, + ], + vec![ + 250, 115, 176, 8, 157, 86, 162, 132, 239, 176, 240, 117, 108, 137, 11, 233, 177, + 181, 219, 221, 142, 232, 26, 54, 85, 248, 62, 51, 178, 39, 157, 57, 191, 62, 132, + 130, 121, 167, 34, 200, 6, 180, 133, 164, 126, 103, 200, 7, 185, 70, 163, 55, 190, + 232, 148, 38, 116, 39, 136, 89, 225, 50, 146, 251, + ], + ), + ( + vec![ + 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, + ], + vec![ + 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, + 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, + 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, + 205, 205, + ], + vec![ + 176, 186, 70, 86, 55, 69, 140, 105, 144, 229, 168, 197, 246, 29, 74, 247, 229, 118, + 217, 127, 249, 75, 135, 45, 231, 111, 128, 80, 54, 30, 227, 219, 169, 28, 165, 193, + 26, 162, 94, 180, 214, 121, 39, 92, 197, 120, 128, 99, 165, 241, 151, 65, 18, 12, + 79, 45, 226, 173, 235, 235, 16, 162, 152, 221, + ], + ), + ]; + for (key, data, expected) in test_vectors { + let h = HashFunction::Sha512; + let res = h.hmac(&key, &data).unwrap(); + assert_eq!(res, expected); + } } diff --git a/acme_common/src/tests/idna.rs b/acme_common/src/tests/idna.rs index 154172d..7ff055d 100644 --- a/acme_common/src/tests/idna.rs +++ b/acme_common/src/tests/idna.rs @@ -2,41 +2,41 @@ use crate::to_idna; #[test] fn test_no_idna() { - let idna_res = to_idna("HeLo.example.com"); - assert!(idna_res.is_ok()); - assert_eq!(idna_res.unwrap(), "helo.example.com"); + let idna_res = to_idna("HeLo.example.com"); + assert!(idna_res.is_ok()); + assert_eq!(idna_res.unwrap(), "helo.example.com"); } #[test] fn test_simple_idna() { - let idna_res = to_idna("Hélo.Example.com"); - assert!(idna_res.is_ok()); - assert_eq!(idna_res.unwrap(), "xn--hlo-bma.example.com"); + let idna_res = to_idna("Hélo.Example.com"); + assert!(idna_res.is_ok()); + assert_eq!(idna_res.unwrap(), "xn--hlo-bma.example.com"); } #[test] fn test_multiple_idna() { - let idna_res = to_idna("ns1.hÉlo.aç-éièè.example.com"); - assert!(idna_res.is_ok()); - assert_eq!( - idna_res.unwrap(), - "ns1.xn--hlo-bma.xn--a-i-2lahae.example.com" - ); + let idna_res = to_idna("ns1.hÉlo.aç-éièè.example.com"); + assert!(idna_res.is_ok()); + assert_eq!( + idna_res.unwrap(), + "ns1.xn--hlo-bma.xn--a-i-2lahae.example.com" + ); } #[test] fn test_already_idna() { - let idna_res = to_idna("xn--hlo-bma.example.com"); - assert!(idna_res.is_ok()); - assert_eq!(idna_res.unwrap(), "xn--hlo-bma.example.com"); + let idna_res = to_idna("xn--hlo-bma.example.com"); + assert!(idna_res.is_ok()); + assert_eq!(idna_res.unwrap(), "xn--hlo-bma.example.com"); } #[test] fn test_mixed_idna_parts() { - let idna_res = to_idna("ns1.xn--hlo-bma.aç-éièè.example.com"); - assert!(idna_res.is_ok()); - assert_eq!( - idna_res.unwrap(), - "ns1.xn--hlo-bma.xn--a-i-2lahae.example.com" - ); + let idna_res = to_idna("ns1.xn--hlo-bma.aç-éièè.example.com"); + assert!(idna_res.is_ok()); + assert_eq!( + idna_res.unwrap(), + "ns1.xn--hlo-bma.xn--a-i-2lahae.example.com" + ); } diff --git a/acme_common/src/tests/jws_signature_algorithm.rs b/acme_common/src/tests/jws_signature_algorithm.rs index 93b4a51..85d4d24 100644 --- a/acme_common/src/tests/jws_signature_algorithm.rs +++ b/acme_common/src/tests/jws_signature_algorithm.rs @@ -4,59 +4,59 @@ const TEST_DATA: &'static [u8] = &[72, 101, 108, 108, 111, 32, 119, 111, 114, 10 #[test] fn test_rs256_sign_rsa2048() { - let k = gen_keypair(KeyType::Rsa2048).unwrap(); - let _ = k.sign(&JwsSignatureAlgorithm::Rs256, TEST_DATA).unwrap(); + let k = gen_keypair(KeyType::Rsa2048).unwrap(); + let _ = k.sign(&JwsSignatureAlgorithm::Rs256, TEST_DATA).unwrap(); } #[test] fn test_rs256_sign_rsa4096() { - let k = gen_keypair(KeyType::Rsa4096).unwrap(); - let _ = k.sign(&JwsSignatureAlgorithm::Rs256, TEST_DATA).unwrap(); + let k = gen_keypair(KeyType::Rsa4096).unwrap(); + let _ = k.sign(&JwsSignatureAlgorithm::Rs256, TEST_DATA).unwrap(); } #[test] fn test_rs256_sign_ecdsa() { - let k = gen_keypair(KeyType::EcdsaP256).unwrap(); - let res = k.sign(&JwsSignatureAlgorithm::Rs256, TEST_DATA); - assert!(res.is_err()); + let k = gen_keypair(KeyType::EcdsaP256).unwrap(); + let res = k.sign(&JwsSignatureAlgorithm::Rs256, TEST_DATA); + assert!(res.is_err()); } #[test] fn test_es256_sign_p256() { - let k = gen_keypair(KeyType::EcdsaP256).unwrap(); - let _ = k.sign(&JwsSignatureAlgorithm::Es256, TEST_DATA).unwrap(); + let k = gen_keypair(KeyType::EcdsaP256).unwrap(); + let _ = k.sign(&JwsSignatureAlgorithm::Es256, TEST_DATA).unwrap(); } #[test] fn test_es256_sign_p384() { - let k = gen_keypair(KeyType::EcdsaP384).unwrap(); - let res = k.sign(&JwsSignatureAlgorithm::Es256, TEST_DATA); - assert!(res.is_err()); + let k = gen_keypair(KeyType::EcdsaP384).unwrap(); + let res = k.sign(&JwsSignatureAlgorithm::Es256, TEST_DATA); + assert!(res.is_err()); } #[test] fn test_es384_sign_p384() { - let k = gen_keypair(KeyType::EcdsaP384).unwrap(); - let _ = k.sign(&JwsSignatureAlgorithm::Es384, TEST_DATA).unwrap(); + let k = gen_keypair(KeyType::EcdsaP384).unwrap(); + let _ = k.sign(&JwsSignatureAlgorithm::Es384, TEST_DATA).unwrap(); } #[test] fn test_es384_sign_p256() { - let k = gen_keypair(KeyType::EcdsaP256).unwrap(); - let res = k.sign(&JwsSignatureAlgorithm::Es384, TEST_DATA); - assert!(res.is_err()); + let k = gen_keypair(KeyType::EcdsaP256).unwrap(); + let res = k.sign(&JwsSignatureAlgorithm::Es384, TEST_DATA); + assert!(res.is_err()); } #[cfg(ed25519)] #[test] fn test_ed25519_sign() { - let k = gen_keypair(KeyType::Ed25519).unwrap(); - let _ = k.sign(&JwsSignatureAlgorithm::Ed25519, TEST_DATA).unwrap(); + let k = gen_keypair(KeyType::Ed25519).unwrap(); + let _ = k.sign(&JwsSignatureAlgorithm::Ed25519, TEST_DATA).unwrap(); } #[cfg(ed448)] #[test] fn test_ed448_sign() { - let k = gen_keypair(KeyType::Ed448).unwrap(); - let _ = k.sign(&JwsSignatureAlgorithm::Ed448, TEST_DATA).unwrap(); + let k = gen_keypair(KeyType::Ed448).unwrap(); + let _ = k.sign(&JwsSignatureAlgorithm::Ed448, TEST_DATA).unwrap(); } diff --git a/acmed/build.rs b/acmed/build.rs index a43cc1c..2e0fed1 100644 --- a/acmed/build.rs +++ b/acmed/build.rs @@ -8,123 +8,123 @@ use std::io::prelude::*; use std::path::PathBuf; macro_rules! set_rustc_env_var { - ($name: expr, $value: expr) => {{ - println!("cargo:rustc-env={}={}", $name, $value); - }}; + ($name: expr, $value: expr) => {{ + println!("cargo:rustc-env={}={}", $name, $value); + }}; } macro_rules! set_env_var_if_absent { - ($name: expr, $default_value: expr) => {{ - if let Err(_) = env::var($name) { - set_rustc_env_var!($name, $default_value); - } - }}; + ($name: expr, $default_value: expr) => {{ + if let Err(_) = env::var($name) { + set_rustc_env_var!($name, $default_value); + } + }}; } macro_rules! set_specific_path_if_absent { - ($env_name: expr, $env_default: expr, $with_dir: expr, $name: expr, $default_value: expr) => {{ - let prefix = env::var($env_name).unwrap_or(String::from($env_default)); - let mut value = PathBuf::new(); - value.push(prefix); - if ($with_dir) { - value.push("acmed"); - } - value.push($default_value); - set_env_var_if_absent!($name, value.to_str().unwrap()); - }}; + ($env_name: expr, $env_default: expr, $with_dir: expr, $name: expr, $default_value: expr) => {{ + let prefix = env::var($env_name).unwrap_or(String::from($env_default)); + let mut value = PathBuf::new(); + value.push(prefix); + if ($with_dir) { + value.push("acmed"); + } + value.push($default_value); + set_env_var_if_absent!($name, value.to_str().unwrap()); + }}; } macro_rules! set_data_path_if_absent { - ($name: expr, $default_value: expr) => {{ - set_specific_path_if_absent!("VARLIBDIR", "/var/lib", true, $name, $default_value); - }}; + ($name: expr, $default_value: expr) => {{ + set_specific_path_if_absent!("VARLIBDIR", "/var/lib", true, $name, $default_value); + }}; } macro_rules! set_cfg_path_if_absent { - ($name: expr, $default_value: expr) => {{ - set_specific_path_if_absent!("SYSCONFDIR", "/etc", true, $name, $default_value); - }}; + ($name: expr, $default_value: expr) => {{ + set_specific_path_if_absent!("SYSCONFDIR", "/etc", true, $name, $default_value); + }}; } macro_rules! set_runstate_path_if_absent { - ($name: expr, $default_value: expr) => {{ - set_specific_path_if_absent!("RUNSTATEDIR", "/run", false, $name, $default_value); - }}; + ($name: expr, $default_value: expr) => {{ + set_specific_path_if_absent!("RUNSTATEDIR", "/run", false, $name, $default_value); + }}; } #[derive(Deserialize)] pub struct Lock { - package: Vec, + package: Vec, } #[derive(Deserialize)] struct Package { - name: String, - version: String, + name: String, + version: String, } struct Error; impl From for Error { - fn from(_error: std::io::Error) -> Self { - Error {} - } + fn from(_error: std::io::Error) -> Self { + Error {} + } } impl From for Error { - fn from(_error: toml::de::Error) -> Self { - Error {} - } + fn from(_error: toml::de::Error) -> Self { + Error {} + } } fn get_lock() -> Result { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.pop(); - path.push("Cargo.lock"); - let mut file = File::open(path)?; - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - let ret: Lock = toml::from_str(&contents)?; - Ok(ret) + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.pop(); + path.push("Cargo.lock"); + let mut file = File::open(path)?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + let ret: Lock = toml::from_str(&contents)?; + Ok(ret) } fn set_lock() { - let lock = match get_lock() { - Ok(l) => l, - Err(_) => { - return; - } - }; - for p in lock.package.iter() { - if p.name == "attohttpc" { - let agent = format!("{}/{}", p.name, p.version); - set_rustc_env_var!("ACMED_HTTP_LIB_AGENT", agent); - set_rustc_env_var!("ACMED_HTTP_LIB_NAME", p.name); - set_rustc_env_var!("ACMED_HTTP_LIB_VERSION", p.version); - return; - } - } + let lock = match get_lock() { + Ok(l) => l, + Err(_) => { + return; + } + }; + for p in lock.package.iter() { + if p.name == "attohttpc" { + let agent = format!("{}/{}", p.name, p.version); + set_rustc_env_var!("ACMED_HTTP_LIB_AGENT", agent); + set_rustc_env_var!("ACMED_HTTP_LIB_NAME", p.name); + set_rustc_env_var!("ACMED_HTTP_LIB_VERSION", p.version); + return; + } + } } fn set_target() { - if let Ok(target) = env::var("TARGET") { - set_rustc_env_var!("ACMED_TARGET", target); - }; + if let Ok(target) = env::var("TARGET") { + set_rustc_env_var!("ACMED_TARGET", target); + }; } fn set_default_values() { - set_data_path_if_absent!("ACMED_DEFAULT_ACCOUNTS_DIR", "accounts"); - set_data_path_if_absent!("ACMED_DEFAULT_CERT_DIR", "certs"); - set_env_var_if_absent!( - "ACMED_DEFAULT_CERT_FORMAT", - "{ name }_{ key_type }.{ file_type }.{ ext }" - ); - set_cfg_path_if_absent!("ACMED_DEFAULT_CONFIG_FILE", "acmed.toml"); - set_runstate_path_if_absent!("ACMED_DEFAULT_PID_FILE", "acmed.pid"); + set_data_path_if_absent!("ACMED_DEFAULT_ACCOUNTS_DIR", "accounts"); + set_data_path_if_absent!("ACMED_DEFAULT_CERT_DIR", "certs"); + set_env_var_if_absent!( + "ACMED_DEFAULT_CERT_FORMAT", + "{ name }_{ key_type }.{ file_type }.{ ext }" + ); + set_cfg_path_if_absent!("ACMED_DEFAULT_CONFIG_FILE", "acmed.toml"); + set_runstate_path_if_absent!("ACMED_DEFAULT_PID_FILE", "acmed.pid"); } fn main() { - set_target(); - set_lock(); - set_default_values(); + set_target(); + set_lock(); + set_default_values(); } diff --git a/acmed/src/account.rs b/acmed/src/account.rs index 694a42c..836119b 100644 --- a/acmed/src/account.rs +++ b/acmed/src/account.rs @@ -14,309 +14,309 @@ mod storage; #[derive(Clone, Debug)] pub struct ExternalAccount { - pub identifier: String, - pub key: Vec, - pub signature_algorithm: JwsSignatureAlgorithm, + pub identifier: String, + pub key: Vec, + pub signature_algorithm: JwsSignatureAlgorithm, } #[derive(Clone, Debug)] pub enum AccountContactType { - Mailfrom, + Mailfrom, } impl FromStr for AccountContactType { - type Err = Error; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "mailfrom" => Ok(AccountContactType::Mailfrom), - _ => Err(format!("{}: unknown contact type.", s).into()), - } - } + type Err = Error; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "mailfrom" => Ok(AccountContactType::Mailfrom), + _ => Err(format!("{}: unknown contact type.", s).into()), + } + } } impl fmt::Display for AccountContactType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match self { - AccountContactType::Mailfrom => "mailfrom", - }; - write!(f, "{}", s) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + AccountContactType::Mailfrom => "mailfrom", + }; + write!(f, "{}", s) + } } #[derive(Clone, Debug)] pub struct AccountKey { - pub creation_date: SystemTime, - pub key: KeyPair, - pub signature_algorithm: JwsSignatureAlgorithm, + pub creation_date: SystemTime, + pub key: KeyPair, + pub signature_algorithm: JwsSignatureAlgorithm, } impl AccountKey { - fn new(key_type: KeyType, signature_algorithm: JwsSignatureAlgorithm) -> Result { - Ok(AccountKey { - creation_date: SystemTime::now(), - key: gen_keypair(key_type)?, - signature_algorithm, - }) - } + fn new(key_type: KeyType, signature_algorithm: JwsSignatureAlgorithm) -> Result { + Ok(AccountKey { + creation_date: SystemTime::now(), + key: gen_keypair(key_type)?, + signature_algorithm, + }) + } } #[derive(Clone, Debug, Hash)] pub struct AccountEndpoint { - pub creation_date: SystemTime, - pub account_url: String, - pub orders_url: String, - pub key_hash: Vec, - pub contacts_hash: Vec, - pub external_account_hash: Vec, + pub creation_date: SystemTime, + pub account_url: String, + pub orders_url: String, + pub key_hash: Vec, + pub contacts_hash: Vec, + pub external_account_hash: Vec, } impl AccountEndpoint { - pub fn new() -> Self { - AccountEndpoint { - creation_date: SystemTime::UNIX_EPOCH, - account_url: String::new(), - orders_url: String::new(), - key_hash: Vec::new(), - contacts_hash: Vec::new(), - external_account_hash: Vec::new(), - } - } + pub fn new() -> Self { + AccountEndpoint { + creation_date: SystemTime::UNIX_EPOCH, + account_url: String::new(), + orders_url: String::new(), + key_hash: Vec::new(), + contacts_hash: Vec::new(), + external_account_hash: Vec::new(), + } + } } #[derive(Clone, Debug)] pub struct Account { - pub name: String, - pub endpoints: HashMap, - pub contacts: Vec, - pub current_key: AccountKey, - pub past_keys: Vec, - pub file_manager: FileManager, - pub external_account: Option, + pub name: String, + pub endpoints: HashMap, + pub contacts: Vec, + pub current_key: AccountKey, + pub past_keys: Vec, + pub file_manager: FileManager, + pub external_account: Option, } impl HasLogger for Account { - fn warn(&self, msg: &str) { - log::warn!("account \"{}\": {}", &self.name, msg); - } + fn warn(&self, msg: &str) { + log::warn!("account \"{}\": {}", &self.name, msg); + } - fn info(&self, msg: &str) { - log::info!("account \"{}\": {}", &self.name, msg); - } + fn info(&self, msg: &str) { + log::info!("account \"{}\": {}", &self.name, msg); + } - fn debug(&self, msg: &str) { - log::debug!("account \"{}\": {}", &self.name, msg); - } + fn debug(&self, msg: &str) { + log::debug!("account \"{}\": {}", &self.name, msg); + } - fn trace(&self, msg: &str) { - log::trace!("account \"{}\": {}", &self.name, msg); - } + fn trace(&self, msg: &str) { + log::trace!("account \"{}\": {}", &self.name, msg); + } } impl Account { - pub fn get_endpoint_mut(&mut self, endpoint_name: &str) -> Result<&mut AccountEndpoint, Error> { - match self.endpoints.get_mut(endpoint_name) { - Some(ep) => Ok(ep), - None => { - let msg = format!( - "\"{}\": unknown endpoint for account \"{}\"", - endpoint_name, self.name - ); - Err(msg.into()) - } - } - } - - pub fn get_endpoint(&self, endpoint_name: &str) -> Result<&AccountEndpoint, Error> { - match self.endpoints.get(endpoint_name) { - Some(ep) => Ok(ep), - None => { - let msg = format!( - "\"{}\": unknown endpoint for account \"{}\"", - endpoint_name, self.name - ); - Err(msg.into()) - } - } - } - - pub fn get_past_key(&self, key_hash: &[u8]) -> Result<&AccountKey, Error> { - let key_hash = key_hash.to_vec(); - for key in &self.past_keys { - let past_key_hash = hash_key(key)?; - if past_key_hash == key_hash { - return Ok(key); - } - } - Err("key not found".into()) - } - - pub fn load( - file_manager: &FileManager, - name: &str, - contacts: &[(String, String)], - key_type: &Option, - signature_algorithm: &Option, - external_account: &Option, - ) -> Result { - let contacts = contacts - .iter() - .map(|(k, v)| contact::AccountContact::new(k, v)) - .collect::, Error>>()?; - let key_type = match key_type { - Some(kt) => kt.parse()?, - None => crate::DEFAULT_ACCOUNT_KEY_TYPE, - }; - let signature_algorithm = match signature_algorithm { - Some(sa) => sa.parse()?, - None => key_type.get_default_signature_alg(), - }; - key_type.check_alg_compatibility(&signature_algorithm)?; - let account = match storage::fetch(file_manager, name)? { - Some(mut a) => { - a.update_keys(key_type, signature_algorithm)?; - a.contacts = contacts; - a.external_account = external_account.to_owned(); - a - } - None => { - let account = Account { - name: name.to_string(), - endpoints: HashMap::new(), - contacts, - current_key: AccountKey::new(key_type, signature_algorithm)?, - past_keys: Vec::new(), - file_manager: file_manager.clone(), - external_account: external_account.to_owned(), - }; - account.debug("initializing a new account"); - account - } - }; - Ok(account) - } - - pub fn add_endpoint_name(&mut self, endpoint_name: &str) { - self.endpoints - .entry(endpoint_name.to_string()) - .or_insert_with(AccountEndpoint::new); - } - - pub fn synchronize(&mut self, endpoint: &mut Endpoint) -> Result<(), Error> { - let acc_ep = self.get_endpoint(&endpoint.name)?; - if !acc_ep.account_url.is_empty() { - if let Some(ec) = &self.external_account { - let external_account_hash = hash_external_account(ec); - if external_account_hash != acc_ep.external_account_hash { - let msg = format!( - "external account changed on endpoint \"{}\"", - &endpoint.name - ); - self.info(&msg); - register_account(endpoint, self)?; - return Ok(()); - } - } - let ct_hash = hash_contacts(&self.contacts); - let key_hash = hash_key(&self.current_key)?; - let contacts_changed = ct_hash != acc_ep.contacts_hash; - let key_changed = key_hash != acc_ep.key_hash; - if contacts_changed { - update_account_contacts(endpoint, self)?; - } - if key_changed { - update_account_key(endpoint, self)?; - } - } else { - register_account(endpoint, self)?; - } - Ok(()) - } - - pub fn register(&mut self, endpoint: &mut Endpoint) -> Result<(), Error> { - register_account(endpoint, self) - } - - pub fn save(&self) -> Result<(), Error> { - storage::save(&self.file_manager, self) - } - - pub fn set_account_url(&mut self, endpoint_name: &str, account_url: &str) -> Result<(), Error> { - let mut ep = self.get_endpoint_mut(endpoint_name)?; - ep.account_url = account_url.to_string(); - Ok(()) - } - - pub fn set_orders_url(&mut self, endpoint_name: &str, orders_url: &str) -> Result<(), Error> { - let mut ep = self.get_endpoint_mut(endpoint_name)?; - ep.orders_url = orders_url.to_string(); - Ok(()) - } - - pub fn update_key_hash(&mut self, endpoint_name: &str) -> Result<(), Error> { - let key = self.current_key.clone(); - let mut ep = self.get_endpoint_mut(endpoint_name)?; - ep.key_hash = hash_key(&key)?; - Ok(()) - } - - pub fn update_contacts_hash(&mut self, endpoint_name: &str) -> Result<(), Error> { - let ct = self.contacts.clone(); - let mut ep = self.get_endpoint_mut(endpoint_name)?; - ep.contacts_hash = hash_contacts(&ct); - Ok(()) - } - - pub fn update_external_account_hash(&mut self, endpoint_name: &str) -> Result<(), Error> { - if let Some(ec) = &self.external_account { - let ec = ec.clone(); - let mut ep = self.get_endpoint_mut(endpoint_name)?; - ep.external_account_hash = hash_external_account(&ec); - } - Ok(()) - } - - fn update_keys( - &mut self, - key_type: KeyType, - signature_algorithm: JwsSignatureAlgorithm, - ) -> Result<(), Error> { - if self.current_key.key.key_type != key_type - || self.current_key.signature_algorithm != signature_algorithm - { - self.debug("account key has been changed in the configuration, creating a new one..."); - self.past_keys.push(self.current_key.to_owned()); - self.current_key = AccountKey::new(key_type, signature_algorithm)?; - self.save()?; - let msg = format!( - "new {} account key created, using {} as signing algorithm", - key_type, signature_algorithm - ); - self.info(&msg); - } else { - self.trace("account key is up to date"); - } - Ok(()) - } + pub fn get_endpoint_mut(&mut self, endpoint_name: &str) -> Result<&mut AccountEndpoint, Error> { + match self.endpoints.get_mut(endpoint_name) { + Some(ep) => Ok(ep), + None => { + let msg = format!( + "\"{}\": unknown endpoint for account \"{}\"", + endpoint_name, self.name + ); + Err(msg.into()) + } + } + } + + pub fn get_endpoint(&self, endpoint_name: &str) -> Result<&AccountEndpoint, Error> { + match self.endpoints.get(endpoint_name) { + Some(ep) => Ok(ep), + None => { + let msg = format!( + "\"{}\": unknown endpoint for account \"{}\"", + endpoint_name, self.name + ); + Err(msg.into()) + } + } + } + + pub fn get_past_key(&self, key_hash: &[u8]) -> Result<&AccountKey, Error> { + let key_hash = key_hash.to_vec(); + for key in &self.past_keys { + let past_key_hash = hash_key(key)?; + if past_key_hash == key_hash { + return Ok(key); + } + } + Err("key not found".into()) + } + + pub fn load( + file_manager: &FileManager, + name: &str, + contacts: &[(String, String)], + key_type: &Option, + signature_algorithm: &Option, + external_account: &Option, + ) -> Result { + let contacts = contacts + .iter() + .map(|(k, v)| contact::AccountContact::new(k, v)) + .collect::, Error>>()?; + let key_type = match key_type { + Some(kt) => kt.parse()?, + None => crate::DEFAULT_ACCOUNT_KEY_TYPE, + }; + let signature_algorithm = match signature_algorithm { + Some(sa) => sa.parse()?, + None => key_type.get_default_signature_alg(), + }; + key_type.check_alg_compatibility(&signature_algorithm)?; + let account = match storage::fetch(file_manager, name)? { + Some(mut a) => { + a.update_keys(key_type, signature_algorithm)?; + a.contacts = contacts; + a.external_account = external_account.to_owned(); + a + } + None => { + let account = Account { + name: name.to_string(), + endpoints: HashMap::new(), + contacts, + current_key: AccountKey::new(key_type, signature_algorithm)?, + past_keys: Vec::new(), + file_manager: file_manager.clone(), + external_account: external_account.to_owned(), + }; + account.debug("initializing a new account"); + account + } + }; + Ok(account) + } + + pub fn add_endpoint_name(&mut self, endpoint_name: &str) { + self.endpoints + .entry(endpoint_name.to_string()) + .or_insert_with(AccountEndpoint::new); + } + + pub fn synchronize(&mut self, endpoint: &mut Endpoint) -> Result<(), Error> { + let acc_ep = self.get_endpoint(&endpoint.name)?; + if !acc_ep.account_url.is_empty() { + if let Some(ec) = &self.external_account { + let external_account_hash = hash_external_account(ec); + if external_account_hash != acc_ep.external_account_hash { + let msg = format!( + "external account changed on endpoint \"{}\"", + &endpoint.name + ); + self.info(&msg); + register_account(endpoint, self)?; + return Ok(()); + } + } + let ct_hash = hash_contacts(&self.contacts); + let key_hash = hash_key(&self.current_key)?; + let contacts_changed = ct_hash != acc_ep.contacts_hash; + let key_changed = key_hash != acc_ep.key_hash; + if contacts_changed { + update_account_contacts(endpoint, self)?; + } + if key_changed { + update_account_key(endpoint, self)?; + } + } else { + register_account(endpoint, self)?; + } + Ok(()) + } + + pub fn register(&mut self, endpoint: &mut Endpoint) -> Result<(), Error> { + register_account(endpoint, self) + } + + pub fn save(&self) -> Result<(), Error> { + storage::save(&self.file_manager, self) + } + + pub fn set_account_url(&mut self, endpoint_name: &str, account_url: &str) -> Result<(), Error> { + let mut ep = self.get_endpoint_mut(endpoint_name)?; + ep.account_url = account_url.to_string(); + Ok(()) + } + + pub fn set_orders_url(&mut self, endpoint_name: &str, orders_url: &str) -> Result<(), Error> { + let mut ep = self.get_endpoint_mut(endpoint_name)?; + ep.orders_url = orders_url.to_string(); + Ok(()) + } + + pub fn update_key_hash(&mut self, endpoint_name: &str) -> Result<(), Error> { + let key = self.current_key.clone(); + let mut ep = self.get_endpoint_mut(endpoint_name)?; + ep.key_hash = hash_key(&key)?; + Ok(()) + } + + pub fn update_contacts_hash(&mut self, endpoint_name: &str) -> Result<(), Error> { + let ct = self.contacts.clone(); + let mut ep = self.get_endpoint_mut(endpoint_name)?; + ep.contacts_hash = hash_contacts(&ct); + Ok(()) + } + + pub fn update_external_account_hash(&mut self, endpoint_name: &str) -> Result<(), Error> { + if let Some(ec) = &self.external_account { + let ec = ec.clone(); + let mut ep = self.get_endpoint_mut(endpoint_name)?; + ep.external_account_hash = hash_external_account(&ec); + } + Ok(()) + } + + fn update_keys( + &mut self, + key_type: KeyType, + signature_algorithm: JwsSignatureAlgorithm, + ) -> Result<(), Error> { + if self.current_key.key.key_type != key_type + || self.current_key.signature_algorithm != signature_algorithm + { + self.debug("account key has been changed in the configuration, creating a new one..."); + self.past_keys.push(self.current_key.to_owned()); + self.current_key = AccountKey::new(key_type, signature_algorithm)?; + self.save()?; + let msg = format!( + "new {} account key created, using {} as signing algorithm", + key_type, signature_algorithm + ); + self.info(&msg); + } else { + self.trace("account key is up to date"); + } + Ok(()) + } } fn hash_contacts(contacts: &[contact::AccountContact]) -> Vec { - let msg = contacts - .iter() - .map(|v| v.to_string()) - .collect::>() - .join("") - .into_bytes(); - HashFunction::Sha256.hash(&msg) + let msg = contacts + .iter() + .map(|v| v.to_string()) + .collect::>() + .join("") + .into_bytes(); + HashFunction::Sha256.hash(&msg) } fn hash_key(key: &AccountKey) -> Result, Error> { - let pem = key.key.public_key_to_pem()?; - Ok(HashFunction::Sha256.hash(&pem)) + let pem = key.key.public_key_to_pem()?; + Ok(HashFunction::Sha256.hash(&pem)) } fn hash_external_account(ec: &ExternalAccount) -> Vec { - let mut msg = ec.key.clone(); - msg.extend(ec.identifier.as_bytes()); - HashFunction::Sha256.hash(&msg) + let mut msg = ec.key.clone(); + msg.extend(ec.identifier.as_bytes()); + HashFunction::Sha256.hash(&msg) } diff --git a/acmed/src/account/contact.rs b/acmed/src/account/contact.rs index e72bdcb..f4cb127 100644 --- a/acmed/src/account/contact.rs +++ b/acmed/src/account/contact.rs @@ -3,10 +3,10 @@ use std::fmt; use std::str::FromStr; fn clean_mailto(value: &str) -> Result { - // TODO: implement a simple RFC 6068 parser - // - no "hfields" - // - max one "addr-spec" in the "to" component - Ok(value.to_string()) + // TODO: implement a simple RFC 6068 parser + // - no "hfields" + // - max one "addr-spec" in the "to" component + Ok(value.to_string()) } // TODO: implement other URI shemes @@ -19,92 +19,92 @@ fn clean_mailto(value: &str) -> Result { // - P4: sip, sips #[derive(Clone, Debug, PartialEq)] pub enum ContactType { - Mailto, + Mailto, } impl ContactType { - pub fn clean_value(&self, value: &str) -> Result { - match self { - ContactType::Mailto => clean_mailto(value), - } - } + pub fn clean_value(&self, value: &str) -> Result { + match self { + ContactType::Mailto => clean_mailto(value), + } + } } impl FromStr for ContactType { - type Err = Error; + type Err = Error; - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "mailto" => Ok(ContactType::Mailto), - _ => Err(format!("{}: unknown contact type.", s).into()), - } - } + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "mailto" => Ok(ContactType::Mailto), + _ => Err(format!("{}: unknown contact type.", s).into()), + } + } } impl fmt::Display for ContactType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match self { - ContactType::Mailto => "mailto", - }; - write!(f, "{}", s) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + ContactType::Mailto => "mailto", + }; + write!(f, "{}", s) + } } #[derive(Clone, Debug, PartialEq)] pub struct AccountContact { - pub contact_type: ContactType, - pub value: String, + pub contact_type: ContactType, + pub value: String, } impl AccountContact { - pub fn new(contact_type: &str, value: &str) -> Result { - let contact_type: ContactType = contact_type.parse()?; - let value = contact_type.clean_value(value)?; - Ok(AccountContact { - contact_type, - value, - }) - } + pub fn new(contact_type: &str, value: &str) -> Result { + let contact_type: ContactType = contact_type.parse()?; + let value = contact_type.clean_value(value)?; + Ok(AccountContact { + contact_type, + value, + }) + } } impl fmt::Display for AccountContact { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}:{}", self.contact_type, self.value) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:{}", self.contact_type, self.value) + } } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn test_account_contact_eq() { - let c1 = AccountContact::new("mailto", "derp.derpson@example.com").unwrap(); - let c2 = AccountContact::new("mailto", "derp.derpson@example.com").unwrap(); - let c3 = AccountContact::new("mailto", "derp@example.com").unwrap(); - assert_eq!(c1, c2); - assert_eq!(c2, c1); - assert_ne!(c1, c3); - assert_ne!(c2, c3); - } + #[test] + fn test_account_contact_eq() { + let c1 = AccountContact::new("mailto", "derp.derpson@example.com").unwrap(); + let c2 = AccountContact::new("mailto", "derp.derpson@example.com").unwrap(); + let c3 = AccountContact::new("mailto", "derp@example.com").unwrap(); + assert_eq!(c1, c2); + assert_eq!(c2, c1); + assert_ne!(c1, c3); + assert_ne!(c2, c3); + } - #[test] - fn test_account_contact_in_vec() { - let contacts = vec![ - AccountContact::new("mailto", "derp.derpson@example.com").unwrap(), - AccountContact::new("mailto", "derp@example.com").unwrap(), - ]; - let c = AccountContact::new("mailto", "derp@example.com").unwrap(); - assert!(contacts.contains(&c)); - } + #[test] + fn test_account_contact_in_vec() { + let contacts = vec![ + AccountContact::new("mailto", "derp.derpson@example.com").unwrap(), + AccountContact::new("mailto", "derp@example.com").unwrap(), + ]; + let c = AccountContact::new("mailto", "derp@example.com").unwrap(); + assert!(contacts.contains(&c)); + } - #[test] - fn test_account_contact_not_in_vec() { - let contacts = vec![ - AccountContact::new("mailto", "derp.derpson@example.com").unwrap(), - AccountContact::new("mailto", "derp@example.com").unwrap(), - ]; - let c = AccountContact::new("mailto", "derpina@example.com").unwrap(); - assert!(!contacts.contains(&c)); - } + #[test] + fn test_account_contact_not_in_vec() { + let contacts = vec![ + AccountContact::new("mailto", "derp.derpson@example.com").unwrap(), + AccountContact::new("mailto", "derp@example.com").unwrap(), + ]; + let c = AccountContact::new("mailto", "derpina@example.com").unwrap(); + assert!(!contacts.contains(&c)); + } } diff --git a/acmed/src/account/storage.rs b/acmed/src/account/storage.rs index 5c95fda..0fa485e 100644 --- a/acmed/src/account/storage.rs +++ b/acmed/src/account/storage.rs @@ -9,180 +9,180 @@ use std::time::SystemTime; #[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct ExternalAccountStorage { - pub identifier: String, - pub key: Vec, - pub signature_algorithm: String, + pub identifier: String, + pub key: Vec, + pub signature_algorithm: String, } impl ExternalAccountStorage { - fn new(external_account: &ExternalAccount) -> Self { - ExternalAccountStorage { - identifier: external_account.identifier.to_owned(), - key: external_account.key.to_owned(), - signature_algorithm: external_account.signature_algorithm.to_string(), - } - } + fn new(external_account: &ExternalAccount) -> Self { + ExternalAccountStorage { + identifier: external_account.identifier.to_owned(), + key: external_account.key.to_owned(), + signature_algorithm: external_account.signature_algorithm.to_string(), + } + } - fn to_generic(&self) -> Result { - Ok(ExternalAccount { - identifier: self.identifier.to_owned(), - key: self.key.to_owned(), - signature_algorithm: self.signature_algorithm.parse()?, - }) - } + fn to_generic(&self) -> Result { + Ok(ExternalAccount { + identifier: self.identifier.to_owned(), + key: self.key.to_owned(), + signature_algorithm: self.signature_algorithm.parse()?, + }) + } } #[derive(Serialize, Deserialize, PartialEq, Debug)] struct AccountKeyStorage { - creation_date: SystemTime, - key: Vec, - signature_algorithm: String, + creation_date: SystemTime, + key: Vec, + signature_algorithm: String, } impl AccountKeyStorage { - fn new(key: &AccountKey) -> Result { - Ok(AccountKeyStorage { - creation_date: key.creation_date, - key: key.key.private_key_to_der()?, - signature_algorithm: key.signature_algorithm.to_string(), - }) - } + fn new(key: &AccountKey) -> Result { + Ok(AccountKeyStorage { + creation_date: key.creation_date, + key: key.key.private_key_to_der()?, + signature_algorithm: key.signature_algorithm.to_string(), + }) + } - fn to_generic(&self) -> Result { - Ok(AccountKey { - creation_date: self.creation_date, - key: KeyPair::from_der(&self.key)?, - signature_algorithm: self.signature_algorithm.parse()?, - }) - } + fn to_generic(&self) -> Result { + Ok(AccountKey { + creation_date: self.creation_date, + key: KeyPair::from_der(&self.key)?, + signature_algorithm: self.signature_algorithm.parse()?, + }) + } } #[derive(Serialize, Deserialize, PartialEq, Debug)] struct AccountEndpointStorage { - creation_date: SystemTime, - account_url: String, - orders_url: String, - key_hash: Vec, - contacts_hash: Vec, - external_account_hash: Vec, + creation_date: SystemTime, + account_url: String, + orders_url: String, + key_hash: Vec, + contacts_hash: Vec, + external_account_hash: Vec, } impl AccountEndpointStorage { - fn new(account_endpoint: &AccountEndpoint) -> Self { - AccountEndpointStorage { - creation_date: account_endpoint.creation_date, - account_url: account_endpoint.account_url.clone(), - orders_url: account_endpoint.orders_url.clone(), - key_hash: account_endpoint.key_hash.clone(), - contacts_hash: account_endpoint.contacts_hash.clone(), - external_account_hash: account_endpoint.external_account_hash.clone(), - } - } + fn new(account_endpoint: &AccountEndpoint) -> Self { + AccountEndpointStorage { + creation_date: account_endpoint.creation_date, + account_url: account_endpoint.account_url.clone(), + orders_url: account_endpoint.orders_url.clone(), + key_hash: account_endpoint.key_hash.clone(), + contacts_hash: account_endpoint.contacts_hash.clone(), + external_account_hash: account_endpoint.external_account_hash.clone(), + } + } - fn to_generic(&self) -> AccountEndpoint { - AccountEndpoint { - creation_date: self.creation_date, - account_url: self.account_url.clone(), - orders_url: self.orders_url.clone(), - key_hash: self.key_hash.clone(), - contacts_hash: self.contacts_hash.clone(), - external_account_hash: self.external_account_hash.clone(), - } - } + fn to_generic(&self) -> AccountEndpoint { + AccountEndpoint { + creation_date: self.creation_date, + account_url: self.account_url.clone(), + orders_url: self.orders_url.clone(), + key_hash: self.key_hash.clone(), + contacts_hash: self.contacts_hash.clone(), + external_account_hash: self.external_account_hash.clone(), + } + } } #[derive(Serialize, Deserialize, PartialEq, Debug)] struct AccountStorage { - name: String, - endpoints: HashMap, - contacts: Vec<(String, String)>, - current_key: AccountKeyStorage, - past_keys: Vec, - external_account: Option, + name: String, + endpoints: HashMap, + contacts: Vec<(String, String)>, + current_key: AccountKeyStorage, + past_keys: Vec, + external_account: Option, } fn do_fetch(file_manager: &FileManager, name: &str) -> Result, Error> { - if account_files_exists(file_manager) { - let data = get_account_data(file_manager)?; - let obj: AccountStorage = bincode::deserialize(&data[..]) - .map_err(|e| Error::from(&e.to_string()).prefix(name))?; - let endpoints = obj - .endpoints - .iter() - .map(|(k, v)| (k.clone(), v.to_generic())) - .collect(); - let contacts = obj - .contacts - .iter() - .map(|(t, v)| AccountContact::new(t, v)) - .collect::, Error>>()?; - let current_key = obj.current_key.to_generic()?; - let past_keys = obj - .past_keys - .iter() - .map(|k| k.to_generic()) - .collect::, Error>>()?; - let external_account = match obj.external_account { - Some(a) => Some(a.to_generic()?), - None => None, - }; - Ok(Some(Account { - name: obj.name, - endpoints, - contacts, - current_key, - past_keys, - file_manager: file_manager.clone(), - external_account, - })) - } else { - Ok(None) - } + if account_files_exists(file_manager) { + let data = get_account_data(file_manager)?; + let obj: AccountStorage = bincode::deserialize(&data[..]) + .map_err(|e| Error::from(&e.to_string()).prefix(name))?; + let endpoints = obj + .endpoints + .iter() + .map(|(k, v)| (k.clone(), v.to_generic())) + .collect(); + let contacts = obj + .contacts + .iter() + .map(|(t, v)| AccountContact::new(t, v)) + .collect::, Error>>()?; + let current_key = obj.current_key.to_generic()?; + let past_keys = obj + .past_keys + .iter() + .map(|k| k.to_generic()) + .collect::, Error>>()?; + let external_account = match obj.external_account { + Some(a) => Some(a.to_generic()?), + None => None, + }; + Ok(Some(Account { + name: obj.name, + endpoints, + contacts, + current_key, + past_keys, + file_manager: file_manager.clone(), + external_account, + })) + } else { + Ok(None) + } } fn do_save(file_manager: &FileManager, account: &Account) -> Result<(), Error> { - let endpoints: HashMap = account - .endpoints - .iter() - .map(|(k, v)| (k.to_owned(), AccountEndpointStorage::new(v))) - .collect(); - let contacts: Vec<(String, String)> = account - .contacts - .iter() - .map(|c| (c.contact_type.to_string(), c.value.to_owned())) - .collect(); - let past_keys = account - .past_keys - .iter() - .map(AccountKeyStorage::new) - .collect::, Error>>()?; - let external_account = account - .external_account - .as_ref() - .map(ExternalAccountStorage::new); - let account_storage = AccountStorage { - name: account.name.to_owned(), - endpoints, - contacts, - current_key: AccountKeyStorage::new(&account.current_key)?, - past_keys, - external_account, - }; - let encoded: Vec = bincode::serialize(&account_storage) - .map_err(|e| Error::from(&e.to_string()).prefix(&account.name))?; - set_account_data(file_manager, &encoded) + let endpoints: HashMap = account + .endpoints + .iter() + .map(|(k, v)| (k.to_owned(), AccountEndpointStorage::new(v))) + .collect(); + let contacts: Vec<(String, String)> = account + .contacts + .iter() + .map(|c| (c.contact_type.to_string(), c.value.to_owned())) + .collect(); + let past_keys = account + .past_keys + .iter() + .map(AccountKeyStorage::new) + .collect::, Error>>()?; + let external_account = account + .external_account + .as_ref() + .map(ExternalAccountStorage::new); + let account_storage = AccountStorage { + name: account.name.to_owned(), + endpoints, + contacts, + current_key: AccountKeyStorage::new(&account.current_key)?, + past_keys, + external_account, + }; + let encoded: Vec = bincode::serialize(&account_storage) + .map_err(|e| Error::from(&e.to_string()).prefix(&account.name))?; + set_account_data(file_manager, &encoded) } pub fn fetch(file_manager: &FileManager, name: &str) -> Result, Error> { - do_fetch(file_manager, name).map_err(|_| { - format!( - "account \"{}\": unable to load account file: file may be corrupted", - name - ) - .into() - }) + do_fetch(file_manager, name).map_err(|_| { + format!( + "account \"{}\": unable to load account file: file may be corrupted", + name + ) + .into() + }) } pub fn save(file_manager: &FileManager, account: &Account) -> Result<(), Error> { - do_save(file_manager, account).map_err(|e| format!("unable to save account file: {}", e).into()) + do_save(file_manager, account).map_err(|e| format!("unable to save account file: {}", e).into()) } diff --git a/acmed/src/acme_proto.rs b/acmed/src/acme_proto.rs index ab8ff84..88c0626 100644 --- a/acmed/src/acme_proto.rs +++ b/acmed/src/acme_proto.rs @@ -1,6 +1,6 @@ use crate::account::Account; use crate::acme_proto::structs::{ - AcmeError, ApiError, Authorization, AuthorizationStatus, NewOrder, Order, OrderStatus, + AcmeError, ApiError, Authorization, AuthorizationStatus, NewOrder, Order, OrderStatus, }; use crate::certificate::Certificate; use crate::endpoint::Endpoint; @@ -21,216 +21,216 @@ pub mod structs; #[derive(Clone, Copy, Debug, PartialEq)] pub enum Challenge { - Http01, - Dns01, - TlsAlpn01, + Http01, + Dns01, + TlsAlpn01, } impl Challenge { - pub fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "http-01" => Ok(Challenge::Http01), - "dns-01" => Ok(Challenge::Dns01), - "tls-alpn-01" => Ok(Challenge::TlsAlpn01), - _ => Err(format!("{}: unknown challenge.", s).into()), - } - } + pub fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "http-01" => Ok(Challenge::Http01), + "dns-01" => Ok(Challenge::Dns01), + "tls-alpn-01" => Ok(Challenge::TlsAlpn01), + _ => Err(format!("{}: unknown challenge.", s).into()), + } + } } impl fmt::Display for Challenge { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match self { - Challenge::Http01 => "http-01", - Challenge::Dns01 => "dns-01", - Challenge::TlsAlpn01 => "tls-alpn-01", - }; - write!(f, "{}", s) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + Challenge::Http01 => "http-01", + Challenge::Dns01 => "dns-01", + Challenge::TlsAlpn01 => "tls-alpn-01", + }; + write!(f, "{}", s) + } } impl PartialEq for Challenge { - fn eq(&self, other: &structs::Challenge) -> bool { - matches!( - (self, other), - (Challenge::Http01, structs::Challenge::Http01(_)) - | (Challenge::Dns01, structs::Challenge::Dns01(_)) - | (Challenge::TlsAlpn01, structs::Challenge::TlsAlpn01(_)) - ) - } + fn eq(&self, other: &structs::Challenge) -> bool { + matches!( + (self, other), + (Challenge::Http01, structs::Challenge::Http01(_)) + | (Challenge::Dns01, structs::Challenge::Dns01(_)) + | (Challenge::TlsAlpn01, structs::Challenge::TlsAlpn01(_)) + ) + } } #[macro_export] macro_rules! set_data_builder { - ($account: ident, $endpoint_name: ident, $data: expr) => { - |n: &str, url: &str| { - encode_kid( - &$account.current_key.key, - &$account.current_key.signature_algorithm, - &($account.get_endpoint(&$endpoint_name)?.account_url), - $data, - url, - n, - ) - } - }; + ($account: ident, $endpoint_name: ident, $data: expr) => { + |n: &str, url: &str| { + encode_kid( + &$account.current_key.key, + &$account.current_key.signature_algorithm, + &($account.get_endpoint(&$endpoint_name)?.account_url), + $data, + url, + n, + ) + } + }; } #[macro_export] macro_rules! set_empty_data_builder { - ($account: ident, $endpoint_name: ident) => { - set_data_builder!($account, $endpoint_name, b"") - }; + ($account: ident, $endpoint_name: ident) => { + set_data_builder!($account, $endpoint_name, b"") + }; } pub fn request_certificate( - cert: &Certificate, - endpoint: &mut Endpoint, - account: &mut Account, + cert: &Certificate, + endpoint: &mut Endpoint, + account: &mut Account, ) -> Result<(), Error> { - let mut hook_datas = vec![]; - let endpoint_name = endpoint.name.clone(); - - // Refresh the directory - http::refresh_directory(endpoint).map_err(HttpError::in_err)?; - - // Synchronize the account - account.synchronize(endpoint)?; - - // Create a new order - let mut new_reg = false; - let (order, order_url) = loop { - let new_order = NewOrder::new(&cert.identifiers); - let new_order = serde_json::to_string(&new_order)?; - let data_builder = set_data_builder!(account, endpoint_name, new_order.as_bytes()); - match http::new_order(endpoint, &data_builder) { - Ok((order, order_url)) => { - if let Some(e) = order.get_error() { - cert.warn(&e.prefix("Error").message); - } - break (order, order_url); - } - Err(e) => { - if !new_reg && e.is_acme_err(AcmeError::AccountDoesNotExist) { - account.register(endpoint)?; - new_reg = true; - } else { - return Err(HttpError::in_err(e)); - } - } - }; - }; - - // Begin iter over authorizations - for auth_url in order.authorizations.iter() { - // Fetch the authorization - let data_builder = set_empty_data_builder!(account, endpoint_name); - let auth = http::get_authorization(endpoint, &data_builder, auth_url) - .map_err(HttpError::in_err)?; - if let Some(e) = auth.get_error() { - cert.warn(&e.prefix("error").message); - } - if auth.status == AuthorizationStatus::Valid { - continue; - } - if auth.status != AuthorizationStatus::Pending { - let msg = format!( - "{}: authorization status is {}", - auth.identifier, auth.status - ); - return Err(msg.into()); - } - - // Fetch the associated challenges - let current_identifier = cert.get_identifier_from_str(&auth.identifier.value)?; - let current_challenge = current_identifier.challenge; - for challenge in auth.challenges.iter() { - if current_challenge == *challenge { - let proof = challenge.get_proof(&account.current_key.key)?; - let file_name = challenge.get_file_name(); - let identifier = auth.identifier.value.to_owned(); - - // Call the challenge hook in order to complete it - let mut data = cert.call_challenge_hooks(&file_name, &proof, &identifier)?; - data.0.is_clean_hook = true; - hook_datas.push(data); - - // Tell the server the challenge has been completed - let chall_url = challenge.get_url(); - let data_builder = set_data_builder!(account, endpoint_name, b"{}"); - http::post_jose_no_response(endpoint, &data_builder, &chall_url) - .map_err(HttpError::in_err)?; - } - } - - // Pool the authorization in order to see whether or not it is valid - let data_builder = set_empty_data_builder!(account, endpoint_name); - let break_fn = |a: &Authorization| a.status == AuthorizationStatus::Valid; - let _ = http::pool_authorization(endpoint, &data_builder, &break_fn, auth_url) - .map_err(HttpError::in_err)?; - for (data, hook_type) in hook_datas.iter() { - cert.call_challenge_hooks_clean(data, (*hook_type).to_owned())?; - } - hook_datas.clear(); - } - // End iter over authorizations - - // Pool the order in order to see whether or not it is ready - let data_builder = set_empty_data_builder!(account, endpoint_name); - let break_fn = |o: &Order| o.status == OrderStatus::Ready; - let order = http::pool_order(endpoint, &data_builder, &break_fn, &order_url) - .map_err(HttpError::in_err)?; - - // Finalize the order by sending the CSR - let key_pair = certificate::get_key_pair(cert)?; - let domains: Vec = cert - .identifiers - .iter() - .filter(|e| e.id_type == IdentifierType::Dns) - .map(|e| e.value.to_owned()) - .collect(); - let ips: Vec = cert - .identifiers - .iter() - .filter(|e| e.id_type == IdentifierType::Ip) - .map(|e| e.value.to_owned()) - .collect(); - let csr = Csr::new( - &key_pair, - cert.csr_digest, - domains.as_slice(), - ips.as_slice(), - &cert.subject_attributes, - )?; - cert.trace(&format!("new CSR:\n{}", csr.to_pem()?)); - let csr = json!({ - "csr": csr.to_der_base64()?, - }); - let csr = csr.to_string(); - let data_builder = set_data_builder!(account, endpoint_name, csr.as_bytes()); - let order = http::finalize_order(endpoint, &data_builder, &order.finalize) - .map_err(HttpError::in_err)?; - if let Some(e) = order.get_error() { - cert.warn(&e.prefix("error").message); - } - - // Pool the order in order to see whether or not it is valid - let data_builder = set_empty_data_builder!(account, endpoint_name); - let break_fn = |o: &Order| o.status == OrderStatus::Valid; - let order = http::pool_order(endpoint, &data_builder, &break_fn, &order_url) - .map_err(HttpError::in_err)?; - - // Download the certificate - let crt_url = order - .certificate - .ok_or_else(|| Error::from("no certificate available for download"))?; - let data_builder = set_empty_data_builder!(account, endpoint_name); - let crt = - http::get_certificate(endpoint, &data_builder, &crt_url).map_err(HttpError::in_err)?; - storage::write_certificate(&cert.file_manager, crt.as_bytes())?; - - cert.info(&format!( - "certificate renewed (identifiers: {})", - cert.identifier_list() - )); - Ok(()) + let mut hook_datas = vec![]; + let endpoint_name = endpoint.name.clone(); + + // Refresh the directory + http::refresh_directory(endpoint).map_err(HttpError::in_err)?; + + // Synchronize the account + account.synchronize(endpoint)?; + + // Create a new order + let mut new_reg = false; + let (order, order_url) = loop { + let new_order = NewOrder::new(&cert.identifiers); + let new_order = serde_json::to_string(&new_order)?; + let data_builder = set_data_builder!(account, endpoint_name, new_order.as_bytes()); + match http::new_order(endpoint, &data_builder) { + Ok((order, order_url)) => { + if let Some(e) = order.get_error() { + cert.warn(&e.prefix("Error").message); + } + break (order, order_url); + } + Err(e) => { + if !new_reg && e.is_acme_err(AcmeError::AccountDoesNotExist) { + account.register(endpoint)?; + new_reg = true; + } else { + return Err(HttpError::in_err(e)); + } + } + }; + }; + + // Begin iter over authorizations + for auth_url in order.authorizations.iter() { + // Fetch the authorization + let data_builder = set_empty_data_builder!(account, endpoint_name); + let auth = http::get_authorization(endpoint, &data_builder, auth_url) + .map_err(HttpError::in_err)?; + if let Some(e) = auth.get_error() { + cert.warn(&e.prefix("error").message); + } + if auth.status == AuthorizationStatus::Valid { + continue; + } + if auth.status != AuthorizationStatus::Pending { + let msg = format!( + "{}: authorization status is {}", + auth.identifier, auth.status + ); + return Err(msg.into()); + } + + // Fetch the associated challenges + let current_identifier = cert.get_identifier_from_str(&auth.identifier.value)?; + let current_challenge = current_identifier.challenge; + for challenge in auth.challenges.iter() { + if current_challenge == *challenge { + let proof = challenge.get_proof(&account.current_key.key)?; + let file_name = challenge.get_file_name(); + let identifier = auth.identifier.value.to_owned(); + + // Call the challenge hook in order to complete it + let mut data = cert.call_challenge_hooks(&file_name, &proof, &identifier)?; + data.0.is_clean_hook = true; + hook_datas.push(data); + + // Tell the server the challenge has been completed + let chall_url = challenge.get_url(); + let data_builder = set_data_builder!(account, endpoint_name, b"{}"); + http::post_jose_no_response(endpoint, &data_builder, &chall_url) + .map_err(HttpError::in_err)?; + } + } + + // Pool the authorization in order to see whether or not it is valid + let data_builder = set_empty_data_builder!(account, endpoint_name); + let break_fn = |a: &Authorization| a.status == AuthorizationStatus::Valid; + let _ = http::pool_authorization(endpoint, &data_builder, &break_fn, auth_url) + .map_err(HttpError::in_err)?; + for (data, hook_type) in hook_datas.iter() { + cert.call_challenge_hooks_clean(data, (*hook_type).to_owned())?; + } + hook_datas.clear(); + } + // End iter over authorizations + + // Pool the order in order to see whether or not it is ready + let data_builder = set_empty_data_builder!(account, endpoint_name); + let break_fn = |o: &Order| o.status == OrderStatus::Ready; + let order = http::pool_order(endpoint, &data_builder, &break_fn, &order_url) + .map_err(HttpError::in_err)?; + + // Finalize the order by sending the CSR + let key_pair = certificate::get_key_pair(cert)?; + let domains: Vec = cert + .identifiers + .iter() + .filter(|e| e.id_type == IdentifierType::Dns) + .map(|e| e.value.to_owned()) + .collect(); + let ips: Vec = cert + .identifiers + .iter() + .filter(|e| e.id_type == IdentifierType::Ip) + .map(|e| e.value.to_owned()) + .collect(); + let csr = Csr::new( + &key_pair, + cert.csr_digest, + domains.as_slice(), + ips.as_slice(), + &cert.subject_attributes, + )?; + cert.trace(&format!("new CSR:\n{}", csr.to_pem()?)); + let csr = json!({ + "csr": csr.to_der_base64()?, + }); + let csr = csr.to_string(); + let data_builder = set_data_builder!(account, endpoint_name, csr.as_bytes()); + let order = http::finalize_order(endpoint, &data_builder, &order.finalize) + .map_err(HttpError::in_err)?; + if let Some(e) = order.get_error() { + cert.warn(&e.prefix("error").message); + } + + // Pool the order in order to see whether or not it is valid + let data_builder = set_empty_data_builder!(account, endpoint_name); + let break_fn = |o: &Order| o.status == OrderStatus::Valid; + let order = http::pool_order(endpoint, &data_builder, &break_fn, &order_url) + .map_err(HttpError::in_err)?; + + // Download the certificate + let crt_url = order + .certificate + .ok_or_else(|| Error::from("no certificate available for download"))?; + let data_builder = set_empty_data_builder!(account, endpoint_name); + let crt = + http::get_certificate(endpoint, &data_builder, &crt_url).map_err(HttpError::in_err)?; + storage::write_certificate(&cert.file_manager, crt.as_bytes())?; + + cert.info(&format!( + "certificate renewed (identifiers: {})", + cert.identifier_list() + )); + Ok(()) } diff --git a/acmed/src/acme_proto/account.rs b/acmed/src/acme_proto/account.rs index 26af4a4..4a7d483 100644 --- a/acmed/src/acme_proto/account.rs +++ b/acmed/src/acme_proto/account.rs @@ -9,141 +9,141 @@ use crate::set_data_builder; use acme_common::error::Error; macro_rules! create_account_if_does_not_exist { - ($e: expr, $endpoint: ident, $account: ident) => { - match $e { - Ok(r) => Ok(r), - Err(he) => match he { - HttpError::ApiError(ref e) => match e.get_acme_type() { - AcmeError::AccountDoesNotExist => { - let msg = format!( - "account has been dropped by endpoint \"{}\"", - $endpoint.name - ); - $account.debug(&msg); - return register_account($endpoint, $account); - } - _ => Err(HttpError::in_err(he.to_owned())), - }, - HttpError::GenericError(e) => Err(e), - }, - } - }; + ($e: expr, $endpoint: ident, $account: ident) => { + match $e { + Ok(r) => Ok(r), + Err(he) => match he { + HttpError::ApiError(ref e) => match e.get_acme_type() { + AcmeError::AccountDoesNotExist => { + let msg = format!( + "account has been dropped by endpoint \"{}\"", + $endpoint.name + ); + $account.debug(&msg); + return register_account($endpoint, $account); + } + _ => Err(HttpError::in_err(he.to_owned())), + }, + HttpError::GenericError(e) => Err(e), + }, + } + }; } pub fn register_account(endpoint: &mut Endpoint, account: &mut BaseAccount) -> Result<(), Error> { - account.debug(&format!( - "creating account on endpoint \"{}\"...", - &endpoint.name - )); - let account_struct = Account::new(account, endpoint)?; - let account_struct = serde_json::to_string(&account_struct)?; - let acc_ref = &account_struct; - let kp_ref = &account.current_key.key; - let signature_algorithm = &account.current_key.signature_algorithm; - let data_builder = |n: &str, url: &str| { - encode_jwk( - kp_ref, - signature_algorithm, - acc_ref.as_bytes(), - url, - Some(n.to_string()), - ) - }; - let (acc_rep, account_url) = - http::new_account(endpoint, &data_builder).map_err(HttpError::in_err)?; - account.set_account_url(&endpoint.name, &account_url)?; - let orders_url = match acc_rep.orders { - Some(url) => url, - None => { - let msg = format!( + account.debug(&format!( + "creating account on endpoint \"{}\"...", + &endpoint.name + )); + let account_struct = Account::new(account, endpoint)?; + let account_struct = serde_json::to_string(&account_struct)?; + let acc_ref = &account_struct; + let kp_ref = &account.current_key.key; + let signature_algorithm = &account.current_key.signature_algorithm; + let data_builder = |n: &str, url: &str| { + encode_jwk( + kp_ref, + signature_algorithm, + acc_ref.as_bytes(), + url, + Some(n.to_string()), + ) + }; + let (acc_rep, account_url) = + http::new_account(endpoint, &data_builder).map_err(HttpError::in_err)?; + account.set_account_url(&endpoint.name, &account_url)?; + let orders_url = match acc_rep.orders { + Some(url) => url, + None => { + let msg = format!( "endpoint \"{}\": account \"{}\": the server has not provided an order URL upon account creation", &endpoint.name, &account.name ); - account.warn(&msg); - String::new() - } - }; - account.set_orders_url(&endpoint.name, &orders_url)?; - account.update_key_hash(&endpoint.name)?; - account.update_contacts_hash(&endpoint.name)?; - account.update_external_account_hash(&endpoint.name)?; - account.save()?; - account.info(&format!( - "account created on endpoint \"{}\"", - &endpoint.name - )); - Ok(()) + account.warn(&msg); + String::new() + } + }; + account.set_orders_url(&endpoint.name, &orders_url)?; + account.update_key_hash(&endpoint.name)?; + account.update_contacts_hash(&endpoint.name)?; + account.update_external_account_hash(&endpoint.name)?; + account.save()?; + account.info(&format!( + "account created on endpoint \"{}\"", + &endpoint.name + )); + Ok(()) } pub fn update_account_contacts( - endpoint: &mut Endpoint, - account: &mut BaseAccount, + endpoint: &mut Endpoint, + account: &mut BaseAccount, ) -> Result<(), Error> { - let endpoint_name = endpoint.name.clone(); - account.debug(&format!( - "updating account contacts on endpoint \"{}\"...", - &endpoint_name - )); - let new_contacts: Vec = account.contacts.iter().map(|c| c.to_string()).collect(); - let acc_up_struct = AccountUpdate::new(&new_contacts); - let acc_up_struct = serde_json::to_string(&acc_up_struct)?; - let data_builder = set_data_builder!(account, endpoint_name, acc_up_struct.as_bytes()); - let url = account.get_endpoint(&endpoint_name)?.account_url.clone(); - create_account_if_does_not_exist!( - http::post_jose_no_response(endpoint, &data_builder, &url), - endpoint, - account - )?; - account.update_contacts_hash(&endpoint_name)?; - account.save()?; - account.info(&format!( - "account contacts updated on endpoint \"{}\"", - &endpoint_name - )); - Ok(()) + let endpoint_name = endpoint.name.clone(); + account.debug(&format!( + "updating account contacts on endpoint \"{}\"...", + &endpoint_name + )); + let new_contacts: Vec = account.contacts.iter().map(|c| c.to_string()).collect(); + let acc_up_struct = AccountUpdate::new(&new_contacts); + let acc_up_struct = serde_json::to_string(&acc_up_struct)?; + let data_builder = set_data_builder!(account, endpoint_name, acc_up_struct.as_bytes()); + let url = account.get_endpoint(&endpoint_name)?.account_url.clone(); + create_account_if_does_not_exist!( + http::post_jose_no_response(endpoint, &data_builder, &url), + endpoint, + account + )?; + account.update_contacts_hash(&endpoint_name)?; + account.save()?; + account.info(&format!( + "account contacts updated on endpoint \"{}\"", + &endpoint_name + )); + Ok(()) } pub fn update_account_key(endpoint: &mut Endpoint, account: &mut BaseAccount) -> Result<(), Error> { - let endpoint_name = endpoint.name.clone(); - account.debug(&format!( - "updating account key on endpoint \"{}\"...", - &endpoint_name - )); - let url = endpoint.dir.key_change.clone(); - let ep = account.get_endpoint(&endpoint_name)?; - let old_account_key = account.get_past_key(&ep.key_hash)?; - let old_key = &old_account_key.key; - let account_url = account.get_endpoint(&endpoint_name)?.account_url.clone(); - let rollover_struct = AccountKeyRollover::new(&account_url, old_key)?; - let rollover_struct = serde_json::to_string(&rollover_struct)?; - let rollover_payload = encode_jwk( - &account.current_key.key, - &account.current_key.signature_algorithm, - rollover_struct.as_bytes(), - &url, - None, - )?; - let data_builder = |n: &str, url: &str| { - encode_kid( - old_key, - &old_account_key.signature_algorithm, - &account_url, - rollover_payload.as_bytes(), - url, - n, - ) - }; - create_account_if_does_not_exist!( - http::post_jose_no_response(endpoint, &data_builder, &url), - endpoint, - account - )?; - account.update_key_hash(&endpoint_name)?; - account.save()?; - account.info(&format!( - "account key updated on endpoint \"{}\"", - &endpoint_name - )); - Ok(()) + let endpoint_name = endpoint.name.clone(); + account.debug(&format!( + "updating account key on endpoint \"{}\"...", + &endpoint_name + )); + let url = endpoint.dir.key_change.clone(); + let ep = account.get_endpoint(&endpoint_name)?; + let old_account_key = account.get_past_key(&ep.key_hash)?; + let old_key = &old_account_key.key; + let account_url = account.get_endpoint(&endpoint_name)?.account_url.clone(); + let rollover_struct = AccountKeyRollover::new(&account_url, old_key)?; + let rollover_struct = serde_json::to_string(&rollover_struct)?; + let rollover_payload = encode_jwk( + &account.current_key.key, + &account.current_key.signature_algorithm, + rollover_struct.as_bytes(), + &url, + None, + )?; + let data_builder = |n: &str, url: &str| { + encode_kid( + old_key, + &old_account_key.signature_algorithm, + &account_url, + rollover_payload.as_bytes(), + url, + n, + ) + }; + create_account_if_does_not_exist!( + http::post_jose_no_response(endpoint, &data_builder, &url), + endpoint, + account + )?; + account.update_key_hash(&endpoint_name)?; + account.save()?; + account.info(&format!( + "account key updated on endpoint \"{}\"", + &endpoint_name + )); + Ok(()) } diff --git a/acmed/src/acme_proto/certificate.rs b/acmed/src/acme_proto/certificate.rs index 6e30eff..93cafdc 100644 --- a/acmed/src/acme_proto/certificate.rs +++ b/acmed/src/acme_proto/certificate.rs @@ -4,22 +4,22 @@ use acme_common::crypto::{gen_keypair, KeyPair}; use acme_common::error::Error; fn gen_key_pair(cert: &Certificate) -> Result { - let key_pair = gen_keypair(cert.key_type)?; - storage::set_keypair(&cert.file_manager, &key_pair)?; - Ok(key_pair) + let key_pair = gen_keypair(cert.key_type)?; + storage::set_keypair(&cert.file_manager, &key_pair)?; + Ok(key_pair) } fn read_key_pair(cert: &Certificate) -> Result { - storage::get_keypair(&cert.file_manager) + storage::get_keypair(&cert.file_manager) } pub fn get_key_pair(cert: &Certificate) -> Result { - if cert.kp_reuse { - match read_key_pair(cert) { - Ok(key_pair) => Ok(key_pair), - Err(_) => gen_key_pair(cert), - } - } else { - gen_key_pair(cert) - } + if cert.kp_reuse { + match read_key_pair(cert) { + Ok(key_pair) => Ok(key_pair), + Err(_) => gen_key_pair(cert), + } + } else { + gen_key_pair(cert) + } } diff --git a/acmed/src/acme_proto/http.rs b/acmed/src/acme_proto/http.rs index e4fa7b5..022ec42 100644 --- a/acmed/src/acme_proto/http.rs +++ b/acmed/src/acme_proto/http.rs @@ -5,144 +5,144 @@ use acme_common::error::Error; use std::{thread, time}; macro_rules! pool_object { - ($obj_type: ty, $obj_name: expr, $endpoint: expr, $url: expr, $data_builder: expr, $break: expr) => {{ - for _ in 0..crate::DEFAULT_POOL_NB_TRIES { - thread::sleep(time::Duration::from_secs(crate::DEFAULT_POOL_WAIT_SEC)); - let response = http::post_jose($endpoint, $url, $data_builder)?; - let obj = response.json::<$obj_type>()?; - if $break(&obj) { - return Ok(obj); - } - } - let msg = format!("{} pooling failed on {}", $obj_name, $url); - Err(msg.into()) - }}; + ($obj_type: ty, $obj_name: expr, $endpoint: expr, $url: expr, $data_builder: expr, $break: expr) => {{ + for _ in 0..crate::DEFAULT_POOL_NB_TRIES { + thread::sleep(time::Duration::from_secs(crate::DEFAULT_POOL_WAIT_SEC)); + let response = http::post_jose($endpoint, $url, $data_builder)?; + let obj = response.json::<$obj_type>()?; + if $break(&obj) { + return Ok(obj); + } + } + let msg = format!("{} pooling failed on {}", $obj_name, $url); + Err(msg.into()) + }}; } pub fn refresh_directory(endpoint: &mut Endpoint) -> Result<(), http::HttpError> { - let url = endpoint.url.clone(); - let response = http::get(endpoint, &url)?; - endpoint.dir = response.json::()?; - Ok(()) + let url = endpoint.url.clone(); + let response = http::get(endpoint, &url)?; + endpoint.dir = response.json::()?; + Ok(()) } pub fn post_jose_no_response( - endpoint: &mut Endpoint, - data_builder: &F, - url: &str, + endpoint: &mut Endpoint, + data_builder: &F, + url: &str, ) -> Result<(), http::HttpError> where - F: Fn(&str, &str) -> Result, + F: Fn(&str, &str) -> Result, { - let _ = http::post_jose(endpoint, url, data_builder)?; - Ok(()) + let _ = http::post_jose(endpoint, url, data_builder)?; + Ok(()) } pub fn new_account( - endpoint: &mut Endpoint, - data_builder: &F, + endpoint: &mut Endpoint, + data_builder: &F, ) -> Result<(AccountResponse, String), http::HttpError> where - F: Fn(&str, &str) -> Result, + F: Fn(&str, &str) -> Result, { - let url = endpoint.dir.new_account.clone(); - let response = http::post_jose(endpoint, &url, data_builder)?; - let acc_uri = response - .get_header(http::HEADER_LOCATION) - .ok_or_else(|| Error::from("no account location found"))?; - let acc_resp = response.json::()?; - Ok((acc_resp, acc_uri)) + let url = endpoint.dir.new_account.clone(); + let response = http::post_jose(endpoint, &url, data_builder)?; + let acc_uri = response + .get_header(http::HEADER_LOCATION) + .ok_or_else(|| Error::from("no account location found"))?; + let acc_resp = response.json::()?; + Ok((acc_resp, acc_uri)) } pub fn new_order( - endpoint: &mut Endpoint, - data_builder: &F, + endpoint: &mut Endpoint, + data_builder: &F, ) -> Result<(Order, String), http::HttpError> where - F: Fn(&str, &str) -> Result, + F: Fn(&str, &str) -> Result, { - let url = endpoint.dir.new_order.clone(); - let response = http::post_jose(endpoint, &url, data_builder)?; - let order_uri = response - .get_header(http::HEADER_LOCATION) - .ok_or_else(|| Error::from("no account location found"))?; - let order_resp = response.json::()?; - Ok((order_resp, order_uri)) + let url = endpoint.dir.new_order.clone(); + let response = http::post_jose(endpoint, &url, data_builder)?; + let order_uri = response + .get_header(http::HEADER_LOCATION) + .ok_or_else(|| Error::from("no account location found"))?; + let order_resp = response.json::()?; + Ok((order_resp, order_uri)) } pub fn get_authorization( - endpoint: &mut Endpoint, - data_builder: &F, - url: &str, + endpoint: &mut Endpoint, + data_builder: &F, + url: &str, ) -> Result where - F: Fn(&str, &str) -> Result, + F: Fn(&str, &str) -> Result, { - let response = http::post_jose(endpoint, url, data_builder)?; - let auth = response.json::()?; - Ok(auth) + let response = http::post_jose(endpoint, url, data_builder)?; + let auth = response.json::()?; + Ok(auth) } pub fn pool_authorization( - endpoint: &mut Endpoint, - data_builder: &F, - break_fn: &S, - url: &str, + endpoint: &mut Endpoint, + data_builder: &F, + break_fn: &S, + url: &str, ) -> Result where - F: Fn(&str, &str) -> Result, - S: Fn(&Authorization) -> bool, + F: Fn(&str, &str) -> Result, + S: Fn(&Authorization) -> bool, { - pool_object!( - Authorization, - "authorization", - endpoint, - url, - data_builder, - break_fn - ) + pool_object!( + Authorization, + "authorization", + endpoint, + url, + data_builder, + break_fn + ) } pub fn pool_order( - endpoint: &mut Endpoint, - data_builder: &F, - break_fn: &S, - url: &str, + endpoint: &mut Endpoint, + data_builder: &F, + break_fn: &S, + url: &str, ) -> Result where - F: Fn(&str, &str) -> Result, - S: Fn(&Order) -> bool, + F: Fn(&str, &str) -> Result, + S: Fn(&Order) -> bool, { - pool_object!(Order, "order", endpoint, url, data_builder, break_fn) + pool_object!(Order, "order", endpoint, url, data_builder, break_fn) } pub fn finalize_order( - endpoint: &mut Endpoint, - data_builder: &F, - url: &str, + endpoint: &mut Endpoint, + data_builder: &F, + url: &str, ) -> Result where - F: Fn(&str, &str) -> Result, + F: Fn(&str, &str) -> Result, { - let response = http::post_jose(endpoint, url, data_builder)?; - let order = response.json::()?; - Ok(order) + let response = http::post_jose(endpoint, url, data_builder)?; + let order = response.json::()?; + Ok(order) } pub fn get_certificate( - endpoint: &mut Endpoint, - data_builder: &F, - url: &str, + endpoint: &mut Endpoint, + data_builder: &F, + url: &str, ) -> Result where - F: Fn(&str, &str) -> Result, + F: Fn(&str, &str) -> Result, { - let response = http::post( - endpoint, - url, - data_builder, - http::CONTENT_TYPE_JOSE, - http::CONTENT_TYPE_PEM, - )?; - Ok(response.body) + let response = http::post( + endpoint, + url, + data_builder, + http::CONTENT_TYPE_JOSE, + http::CONTENT_TYPE_PEM, + )?; + Ok(response.body) } diff --git a/acmed/src/acme_proto/structs.rs b/acmed/src/acme_proto/structs.rs index ba048bb..cd64ea9 100644 --- a/acmed/src/acme_proto/structs.rs +++ b/acmed/src/acme_proto/structs.rs @@ -1,15 +1,15 @@ #[macro_export] macro_rules! deserialize_from_str { - ($t: ty) => { - impl FromStr for $t { - type Err = Error; + ($t: ty) => { + impl FromStr for $t { + type Err = Error; - fn from_str(data: &str) -> Result { - let res = serde_json::from_str(data)?; - Ok(res) - } - } - }; + fn from_str(data: &str) -> Result { + let res = serde_json::from_str(data)?; + Ok(res) + } + } + }; } mod account; @@ -19,7 +19,7 @@ mod error; mod order; pub use account::{ - Account, AccountDeactivation, AccountKeyRollover, AccountResponse, AccountUpdate, + Account, AccountDeactivation, AccountKeyRollover, AccountResponse, AccountUpdate, }; pub use authorization::{Authorization, AuthorizationStatus, Challenge}; pub use deserialize_from_str; diff --git a/acmed/src/acme_proto/structs/account.rs b/acmed/src/acme_proto/structs/account.rs index 129fb47..a0b6698 100644 --- a/acmed/src/acme_proto/structs/account.rs +++ b/acmed/src/acme_proto/structs/account.rs @@ -9,136 +9,136 @@ use std::str::FromStr; #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct Account { - pub contact: Vec, - pub terms_of_service_agreed: bool, - pub only_return_existing: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub external_account_binding: Option, + pub contact: Vec, + pub terms_of_service_agreed: bool, + pub only_return_existing: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub external_account_binding: Option, } impl Account { - pub fn new(account: &crate::account::Account, endpoint: &Endpoint) -> Result { - let external_account_binding = match &account.external_account { - Some(a) => { - let k_ref = &a.key; - let signature_algorithm = &a.signature_algorithm; - let kid = &a.identifier; - let payload = account.current_key.key.jwk_public_key()?; - let payload = serde_json::to_string(&payload)?; - let data = encode_kid_mac( - k_ref, - signature_algorithm, - kid, - payload.as_bytes(), - &endpoint.dir.new_account, - )?; - let data: Value = serde_json::from_str(&data)?; - Some(data) - } - None => None, - }; - Ok(Account { - contact: account.contacts.iter().map(|e| e.to_string()).collect(), - terms_of_service_agreed: endpoint.tos_agreed, - only_return_existing: false, - external_account_binding, - }) - } + pub fn new(account: &crate::account::Account, endpoint: &Endpoint) -> Result { + let external_account_binding = match &account.external_account { + Some(a) => { + let k_ref = &a.key; + let signature_algorithm = &a.signature_algorithm; + let kid = &a.identifier; + let payload = account.current_key.key.jwk_public_key()?; + let payload = serde_json::to_string(&payload)?; + let data = encode_kid_mac( + k_ref, + signature_algorithm, + kid, + payload.as_bytes(), + &endpoint.dir.new_account, + )?; + let data: Value = serde_json::from_str(&data)?; + Some(data) + } + None => None, + }; + Ok(Account { + contact: account.contacts.iter().map(|e| e.to_string()).collect(), + terms_of_service_agreed: endpoint.tos_agreed, + only_return_existing: false, + external_account_binding, + }) + } } #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct AccountResponse { - pub status: String, - pub contact: Option>, - pub terms_of_service_agreed: Option, - pub external_account_binding: Option, - pub orders: Option, + pub status: String, + pub contact: Option>, + pub terms_of_service_agreed: Option, + pub external_account_binding: Option, + pub orders: Option, } deserialize_from_str!(AccountResponse); #[derive(Serialize)] pub struct AccountUpdate { - pub contact: Vec, + pub contact: Vec, } impl AccountUpdate { - pub fn new(contact: &[String]) -> Self { - AccountUpdate { - contact: contact.into(), - } - } + pub fn new(contact: &[String]) -> Self { + AccountUpdate { + contact: contact.into(), + } + } } #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct AccountKeyRollover { - pub account: String, - pub old_key: Value, + pub account: String, + pub old_key: Value, } impl AccountKeyRollover { - pub fn new(account_str: &str, old_key: &KeyPair) -> Result { - Ok(AccountKeyRollover { - account: account_str.to_string(), - old_key: old_key.jwk_public_key()?, - }) - } + pub fn new(account_str: &str, old_key: &KeyPair) -> Result { + Ok(AccountKeyRollover { + account: account_str.to_string(), + old_key: old_key.jwk_public_key()?, + }) + } } // TODO: implement account deactivation #[allow(dead_code)] #[derive(Serialize)] pub struct AccountDeactivation { - pub status: String, + pub status: String, } impl AccountDeactivation { - #[allow(dead_code)] - pub fn new() -> Self { - AccountDeactivation { - status: "deactivated".into(), - } - } + #[allow(dead_code)] + pub fn new() -> Self { + AccountDeactivation { + status: "deactivated".into(), + } + } } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn test_account_new() { - let emails = vec![ - "mailto:derp@example.com".to_string(), - "mailto:derp.derpson@example.com".to_string(), - ]; - let a = Account { - contact: emails, - terms_of_service_agreed: true, - only_return_existing: false, - external_account_binding: None, - }; - assert_eq!(a.contact.len(), 2); - assert_eq!(a.terms_of_service_agreed, true); - assert_eq!(a.only_return_existing, false); - let a_str = serde_json::to_string(&a); - assert!(a_str.is_ok()); - let a_str = a_str.unwrap(); - assert!(a_str.starts_with("{")); - assert!(a_str.ends_with("}")); - assert!(a_str.contains("\"contact\"")); - assert!(a_str.contains("\"mailto:derp@example.com\"")); - assert!(a_str.contains("\"mailto:derp.derpson@example.com\"")); - assert!(a_str.contains("\"termsOfServiceAgreed\"")); - assert!(a_str.contains("\"onlyReturnExisting\"")); - assert!(a_str.contains("true")); - assert!(a_str.contains("false")); - } + #[test] + fn test_account_new() { + let emails = vec![ + "mailto:derp@example.com".to_string(), + "mailto:derp.derpson@example.com".to_string(), + ]; + let a = Account { + contact: emails, + terms_of_service_agreed: true, + only_return_existing: false, + external_account_binding: None, + }; + assert_eq!(a.contact.len(), 2); + assert_eq!(a.terms_of_service_agreed, true); + assert_eq!(a.only_return_existing, false); + let a_str = serde_json::to_string(&a); + assert!(a_str.is_ok()); + let a_str = a_str.unwrap(); + assert!(a_str.starts_with("{")); + assert!(a_str.ends_with("}")); + assert!(a_str.contains("\"contact\"")); + assert!(a_str.contains("\"mailto:derp@example.com\"")); + assert!(a_str.contains("\"mailto:derp.derpson@example.com\"")); + assert!(a_str.contains("\"termsOfServiceAgreed\"")); + assert!(a_str.contains("\"onlyReturnExisting\"")); + assert!(a_str.contains("true")); + assert!(a_str.contains("false")); + } - #[test] - fn test_account_response() { - let data = "{ + #[test] + fn test_account_response() { + let data = "{ \"status\": \"valid\", \"contact\": [ \"mailto:cert-admin@example.org\", @@ -147,52 +147,52 @@ mod tests { \"termsOfServiceAgreed\": true, \"orders\": \"https://example.com/acme/orders/rzGoeA\" }"; - let account_resp = AccountResponse::from_str(data); - assert!(account_resp.is_ok()); - let account_resp = account_resp.unwrap(); - assert_eq!(account_resp.status, "valid"); - assert!(account_resp.contact.is_some()); - let contacts = account_resp.contact.unwrap(); - assert_eq!(contacts.len(), 2); - assert_eq!(contacts[0], "mailto:cert-admin@example.org"); - assert_eq!(contacts[1], "mailto:admin@example.org"); - assert!(account_resp.external_account_binding.is_none()); - assert!(account_resp.terms_of_service_agreed.is_some()); - assert!(account_resp.terms_of_service_agreed.unwrap()); - assert_eq!( - account_resp.orders, - Some("https://example.com/acme/orders/rzGoeA".into()) - ); - } + let account_resp = AccountResponse::from_str(data); + assert!(account_resp.is_ok()); + let account_resp = account_resp.unwrap(); + assert_eq!(account_resp.status, "valid"); + assert!(account_resp.contact.is_some()); + let contacts = account_resp.contact.unwrap(); + assert_eq!(contacts.len(), 2); + assert_eq!(contacts[0], "mailto:cert-admin@example.org"); + assert_eq!(contacts[1], "mailto:admin@example.org"); + assert!(account_resp.external_account_binding.is_none()); + assert!(account_resp.terms_of_service_agreed.is_some()); + assert!(account_resp.terms_of_service_agreed.unwrap()); + assert_eq!( + account_resp.orders, + Some("https://example.com/acme/orders/rzGoeA".into()) + ); + } - #[test] - fn test_account_update() { - let emails = vec![ - "mailto:derp@example.com".to_string(), - "mailto:derp.derpson@example.com".to_string(), - ]; - let au = AccountUpdate::new(&emails); - assert_eq!(au.contact.len(), 2); - let au_str = serde_json::to_string(&au); - assert!(au_str.is_ok()); - let au_str = au_str.unwrap(); - assert!(au_str.starts_with("{")); - assert!(au_str.ends_with("}")); - assert!(au_str.contains("\"contact\"")); - assert!(au_str.contains("\"mailto:derp@example.com\"")); - assert!(au_str.contains("\"mailto:derp.derpson@example.com\"")); - } + #[test] + fn test_account_update() { + let emails = vec![ + "mailto:derp@example.com".to_string(), + "mailto:derp.derpson@example.com".to_string(), + ]; + let au = AccountUpdate::new(&emails); + assert_eq!(au.contact.len(), 2); + let au_str = serde_json::to_string(&au); + assert!(au_str.is_ok()); + let au_str = au_str.unwrap(); + assert!(au_str.starts_with("{")); + assert!(au_str.ends_with("}")); + assert!(au_str.contains("\"contact\"")); + assert!(au_str.contains("\"mailto:derp@example.com\"")); + assert!(au_str.contains("\"mailto:derp.derpson@example.com\"")); + } - #[test] - fn test_account_deactivation() { - let ad = AccountDeactivation::new(); - assert_eq!(ad.status, "deactivated"); - let ad_str = serde_json::to_string(&ad); - assert!(ad_str.is_ok()); - let ad_str = ad_str.unwrap(); - assert!(ad_str.starts_with("{")); - assert!(ad_str.ends_with("}")); - assert!(ad_str.contains("\"status\"")); - assert!(ad_str.contains("\"deactivated\"")); - } + #[test] + fn test_account_deactivation() { + let ad = AccountDeactivation::new(); + assert_eq!(ad.status, "deactivated"); + let ad_str = serde_json::to_string(&ad); + assert!(ad_str.is_ok()); + let ad_str = ad_str.unwrap(); + assert!(ad_str.starts_with("{")); + assert!(ad_str.ends_with("}")); + assert!(ad_str.contains("\"status\"")); + assert!(ad_str.contains("\"deactivated\"")); + } } diff --git a/acmed/src/acme_proto/structs/authorization.rs b/acmed/src/acme_proto/structs/authorization.rs index a6aab98..bf3a317 100644 --- a/acmed/src/acme_proto/structs/authorization.rs +++ b/acmed/src/acme_proto/structs/authorization.rs @@ -13,174 +13,174 @@ const DER_STRUCT_NAME: &str = "DER"; #[derive(Deserialize)] pub struct Authorization { - pub identifier: Identifier, - pub status: AuthorizationStatus, - pub expires: Option, - pub challenges: Vec, - pub wildcard: Option, + pub identifier: Identifier, + pub status: AuthorizationStatus, + pub expires: Option, + pub challenges: Vec, + pub wildcard: Option, } impl FromStr for Authorization { - type Err = Error; + type Err = Error; - fn from_str(data: &str) -> Result { - let mut res: Self = serde_json::from_str(data)?; - res.challenges.retain(|c| *c != Challenge::Unknown); - Ok(res) - } + fn from_str(data: &str) -> Result { + let mut res: Self = serde_json::from_str(data)?; + res.challenges.retain(|c| *c != Challenge::Unknown); + Ok(res) + } } impl ApiError for Authorization { - fn get_error(&self) -> Option { - for challenge in self.challenges.iter() { - let err = challenge.get_error(); - if err.is_some() { - return err; - } - } - None - } + fn get_error(&self) -> Option { + for challenge in self.challenges.iter() { + let err = challenge.get_error(); + if err.is_some() { + return err; + } + } + None + } } #[derive(Debug, PartialEq, Eq, Deserialize)] #[serde(rename_all = "lowercase")] pub enum AuthorizationStatus { - Pending, - Valid, - Invalid, - Deactivated, - Expired, - Revoked, + Pending, + Valid, + Invalid, + Deactivated, + Expired, + Revoked, } impl fmt::Display for AuthorizationStatus { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match self { - AuthorizationStatus::Pending => "pending", - AuthorizationStatus::Valid => "valid", - AuthorizationStatus::Invalid => "invalid", - AuthorizationStatus::Deactivated => "deactivated", - AuthorizationStatus::Expired => "expired", - AuthorizationStatus::Revoked => "revoked", - }; - write!(f, "{}", s) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + AuthorizationStatus::Pending => "pending", + AuthorizationStatus::Valid => "valid", + AuthorizationStatus::Invalid => "invalid", + AuthorizationStatus::Deactivated => "deactivated", + AuthorizationStatus::Expired => "expired", + AuthorizationStatus::Revoked => "revoked", + }; + write!(f, "{}", s) + } } #[derive(PartialEq, Deserialize)] #[serde(tag = "type")] pub enum Challenge { - #[serde(rename = "http-01")] - Http01(TokenChallenge), - #[serde(rename = "dns-01")] - Dns01(TokenChallenge), - #[serde(rename = "tls-alpn-01")] - TlsAlpn01(TokenChallenge), - #[serde(other)] - Unknown, + #[serde(rename = "http-01")] + Http01(TokenChallenge), + #[serde(rename = "dns-01")] + Dns01(TokenChallenge), + #[serde(rename = "tls-alpn-01")] + TlsAlpn01(TokenChallenge), + #[serde(other)] + Unknown, } deserialize_from_str!(Challenge); impl Challenge { - pub fn get_url(&self) -> String { - match self { - Challenge::Http01(tc) | Challenge::Dns01(tc) | Challenge::TlsAlpn01(tc) => { - tc.url.to_owned() - } - Challenge::Unknown => String::new(), - } - } + pub fn get_url(&self) -> String { + match self { + Challenge::Http01(tc) | Challenge::Dns01(tc) | Challenge::TlsAlpn01(tc) => { + tc.url.to_owned() + } + Challenge::Unknown => String::new(), + } + } - pub fn get_proof(&self, key_pair: &KeyPair) -> Result { - match self { - Challenge::Http01(tc) => tc.key_authorization(key_pair), - Challenge::Dns01(tc) => { - let ka = tc.key_authorization(key_pair)?; - let a = HashFunction::Sha256.hash(ka.as_bytes()); - let a = b64_encode(&a); - Ok(a) - } - Challenge::TlsAlpn01(tc) => { - let acme_ext_name = format!("{}.{}", ACME_OID, ID_PE_ACME_ID); - let ka = tc.key_authorization(key_pair)?; - let proof = HashFunction::Sha256.hash(ka.as_bytes()); - let proof_str = proof - .iter() - .map(|e| format!("{:02x}", e)) - .collect::>() - .join(":"); - let value = format!( - "critical,{}:{:02x}:{:02x}:{}", - DER_STRUCT_NAME, - DER_OCTET_STRING_ID, - proof.len(), - proof_str - ); - let acme_ext = format!("{}={}", acme_ext_name, value); - Ok(acme_ext) - } - Challenge::Unknown => Ok(String::new()), - } - } + pub fn get_proof(&self, key_pair: &KeyPair) -> Result { + match self { + Challenge::Http01(tc) => tc.key_authorization(key_pair), + Challenge::Dns01(tc) => { + let ka = tc.key_authorization(key_pair)?; + let a = HashFunction::Sha256.hash(ka.as_bytes()); + let a = b64_encode(&a); + Ok(a) + } + Challenge::TlsAlpn01(tc) => { + let acme_ext_name = format!("{}.{}", ACME_OID, ID_PE_ACME_ID); + let ka = tc.key_authorization(key_pair)?; + let proof = HashFunction::Sha256.hash(ka.as_bytes()); + let proof_str = proof + .iter() + .map(|e| format!("{:02x}", e)) + .collect::>() + .join(":"); + let value = format!( + "critical,{}:{:02x}:{:02x}:{}", + DER_STRUCT_NAME, + DER_OCTET_STRING_ID, + proof.len(), + proof_str + ); + let acme_ext = format!("{}={}", acme_ext_name, value); + Ok(acme_ext) + } + Challenge::Unknown => Ok(String::new()), + } + } - pub fn get_file_name(&self) -> String { - match self { - Challenge::Http01(tc) => tc.token.to_owned(), - Challenge::Dns01(_) | Challenge::TlsAlpn01(_) => String::new(), - Challenge::Unknown => String::new(), - } - } + pub fn get_file_name(&self) -> String { + match self { + Challenge::Http01(tc) => tc.token.to_owned(), + Challenge::Dns01(_) | Challenge::TlsAlpn01(_) => String::new(), + Challenge::Unknown => String::new(), + } + } } impl ApiError for Challenge { - fn get_error(&self) -> Option { - match self { - Challenge::Http01(tc) | Challenge::Dns01(tc) | Challenge::TlsAlpn01(tc) => { - tc.error.to_owned().map(Error::from) - } - Challenge::Unknown => None, - } - } + fn get_error(&self) -> Option { + match self { + Challenge::Http01(tc) | Challenge::Dns01(tc) | Challenge::TlsAlpn01(tc) => { + tc.error.to_owned().map(Error::from) + } + Challenge::Unknown => None, + } + } } #[derive(PartialEq, Deserialize)] pub struct TokenChallenge { - pub url: String, - pub status: Option, - pub validated: Option, - pub error: Option, - pub token: String, + pub url: String, + pub status: Option, + pub validated: Option, + pub error: Option, + pub token: String, } impl TokenChallenge { - fn key_authorization(&self, key_pair: &KeyPair) -> Result { - let thumbprint = key_pair.jwk_public_key_thumbprint()?; - let thumbprint = HashFunction::Sha256.hash(thumbprint.to_string().as_bytes()); - let thumbprint = b64_encode(&thumbprint); - let auth = format!("{}.{}", self.token, thumbprint); - Ok(auth) - } + fn key_authorization(&self, key_pair: &KeyPair) -> Result { + let thumbprint = key_pair.jwk_public_key_thumbprint()?; + let thumbprint = HashFunction::Sha256.hash(thumbprint.to_string().as_bytes()); + let thumbprint = b64_encode(&thumbprint); + let auth = format!("{}.{}", self.token, thumbprint); + Ok(auth) + } } #[derive(Debug, PartialEq, Eq, Deserialize)] #[serde(rename_all = "lowercase")] pub enum ChallengeStatus { - Pending, - Processing, - Valid, - Invalid, + Pending, + Processing, + Valid, + Invalid, } #[cfg(test)] mod tests { - use super::{Authorization, AuthorizationStatus, Challenge, ChallengeStatus}; - use crate::identifier::IdentifierType; - use std::str::FromStr; + use super::{Authorization, AuthorizationStatus, Challenge, ChallengeStatus}; + use crate::identifier::IdentifierType; + use std::str::FromStr; - #[test] - fn test_authorization() { - let data = "{ + #[test] + fn test_authorization() { + let data = "{ \"status\": \"pending\", \"identifier\": { \"type\": \"dns\", @@ -188,19 +188,19 @@ mod tests { }, \"challenges\": [] }"; - let a = Authorization::from_str(data); - assert!(a.is_ok()); - let a = a.unwrap(); - assert_eq!(a.status, AuthorizationStatus::Pending); - assert!(a.challenges.is_empty()); - let i = a.identifier; - assert_eq!(i.id_type, IdentifierType::Dns); - assert_eq!(i.value, "example.com".to_string()); - } + let a = Authorization::from_str(data); + assert!(a.is_ok()); + let a = a.unwrap(); + assert_eq!(a.status, AuthorizationStatus::Pending); + assert!(a.challenges.is_empty()); + let i = a.identifier; + assert_eq!(i.id_type, IdentifierType::Dns); + assert_eq!(i.value, "example.com".to_string()); + } - #[test] - fn test_authorization_challenge() { - let data = "{ + #[test] + fn test_authorization_challenge() { + let data = "{ \"status\": \"pending\", \"identifier\": { \"type\": \"dns\", @@ -215,19 +215,19 @@ mod tests { } ] }"; - let a = Authorization::from_str(data); - assert!(a.is_ok()); - let a = a.unwrap(); - assert_eq!(a.status, AuthorizationStatus::Pending); - assert_eq!(a.challenges.len(), 1); - let i = a.identifier; - assert_eq!(i.id_type, IdentifierType::Dns); - assert_eq!(i.value, "example.com".to_string()); - } + let a = Authorization::from_str(data); + assert!(a.is_ok()); + let a = a.unwrap(); + assert_eq!(a.status, AuthorizationStatus::Pending); + assert_eq!(a.challenges.len(), 1); + let i = a.identifier; + assert_eq!(i.id_type, IdentifierType::Dns); + assert_eq!(i.value, "example.com".to_string()); + } - #[test] - fn test_authorization_unknown_challenge() { - let data = "{ + #[test] + fn test_authorization_unknown_challenge() { + let data = "{ \"status\": \"pending\", \"identifier\": { \"type\": \"dns\", @@ -242,19 +242,19 @@ mod tests { } ] }"; - let a = Authorization::from_str(data); - assert!(a.is_ok()); - let a = a.unwrap(); - assert_eq!(a.status, AuthorizationStatus::Pending); - assert!(a.challenges.is_empty()); - let i = a.identifier; - assert_eq!(i.id_type, IdentifierType::Dns); - assert_eq!(i.value, "example.com".to_string()); - } + let a = Authorization::from_str(data); + assert!(a.is_ok()); + let a = a.unwrap(); + assert_eq!(a.status, AuthorizationStatus::Pending); + assert!(a.challenges.is_empty()); + let i = a.identifier; + assert_eq!(i.id_type, IdentifierType::Dns); + assert_eq!(i.value, "example.com".to_string()); + } - #[test] - fn test_invalid_authorization() { - let data = "{ + #[test] + fn test_invalid_authorization() { + let data = "{ \"status\": \"pending\", \"identifier\": { \"type\": \"foo\", @@ -262,85 +262,85 @@ mod tests { }, \"challenges\": [] }"; - let a = Authorization::from_str(data); - assert!(a.is_err()); - } + let a = Authorization::from_str(data); + assert!(a.is_err()); + } - #[test] - fn test_http01_challenge() { - let data = "{ + #[test] + fn test_http01_challenge() { + let data = "{ \"type\": \"http-01\", \"url\": \"https://example.com/acme/chall/prV_B7yEyA4\", \"status\": \"pending\", \"token\": \"LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0\" }"; - let challenge = Challenge::from_str(data); - assert!(challenge.is_ok()); - let challenge = challenge.unwrap(); - let c = match challenge { - Challenge::Http01(c) => c, - _ => { - assert!(false); - return; - } - }; - assert_eq!( - c.url, - "https://example.com/acme/chall/prV_B7yEyA4".to_string() - ); - assert_eq!(c.status, Some(ChallengeStatus::Pending)); - assert_eq!( - c.token, - "LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0".to_string() - ); - assert!(c.validated.is_none()); - assert!(c.error.is_none()); - } + let challenge = Challenge::from_str(data); + assert!(challenge.is_ok()); + let challenge = challenge.unwrap(); + let c = match challenge { + Challenge::Http01(c) => c, + _ => { + assert!(false); + return; + } + }; + assert_eq!( + c.url, + "https://example.com/acme/chall/prV_B7yEyA4".to_string() + ); + assert_eq!(c.status, Some(ChallengeStatus::Pending)); + assert_eq!( + c.token, + "LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0".to_string() + ); + assert!(c.validated.is_none()); + assert!(c.error.is_none()); + } - #[test] - fn test_dns01_challenge() { - let data = "{ + #[test] + fn test_dns01_challenge() { + let data = "{ \"type\": \"http-01\", \"url\": \"https://example.com/acme/chall/prV_B7yEyA4\", \"status\": \"valid\", \"token\": \"LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0\" }"; - let challenge = Challenge::from_str(data); - assert!(challenge.is_ok()); - let challenge = challenge.unwrap(); - let c = match challenge { - Challenge::Http01(c) => c, - _ => { - assert!(false); - return; - } - }; - assert_eq!( - c.url, - "https://example.com/acme/chall/prV_B7yEyA4".to_string() - ); - assert_eq!(c.status, Some(ChallengeStatus::Valid)); - assert_eq!( - c.token, - "LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0".to_string() - ); - assert!(c.validated.is_none()); - assert!(c.error.is_none()); - } + let challenge = Challenge::from_str(data); + assert!(challenge.is_ok()); + let challenge = challenge.unwrap(); + let c = match challenge { + Challenge::Http01(c) => c, + _ => { + assert!(false); + return; + } + }; + assert_eq!( + c.url, + "https://example.com/acme/chall/prV_B7yEyA4".to_string() + ); + assert_eq!(c.status, Some(ChallengeStatus::Valid)); + assert_eq!( + c.token, + "LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0".to_string() + ); + assert!(c.validated.is_none()); + assert!(c.error.is_none()); + } - #[test] - fn test_unknown_challenge_type() { - let data = "{ + #[test] + fn test_unknown_challenge_type() { + let data = "{ \"type\": \"invalid-01\", \"url\": \"https://example.com/acme/chall/prV_B7yEyA4\", \"status\": \"pending\", \"token\": \"LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0\" }"; - let challenge = Challenge::from_str(data); - assert!(challenge.is_ok()); - match challenge.unwrap() { - Challenge::Unknown => assert!(true), - _ => assert!(false), - } - } + let challenge = Challenge::from_str(data); + assert!(challenge.is_ok()); + match challenge.unwrap() { + Challenge::Unknown => assert!(true), + _ => assert!(false), + } + } } diff --git a/acmed/src/acme_proto/structs/directory.rs b/acmed/src/acme_proto/structs/directory.rs index 44b81be..242b8b7 100644 --- a/acmed/src/acme_proto/structs/directory.rs +++ b/acmed/src/acme_proto/structs/directory.rs @@ -5,34 +5,34 @@ use std::str::FromStr; #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct DirectoryMeta { - pub terms_of_service: Option, - pub website: Option, - pub caa_identities: Option>, - pub external_account_required: Option, + pub terms_of_service: Option, + pub website: Option, + pub caa_identities: Option>, + pub external_account_required: Option, } #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Directory { - pub meta: Option, - pub new_nonce: String, - pub new_account: String, - pub new_order: String, - pub new_authz: Option, - pub revoke_cert: String, - pub key_change: String, + pub meta: Option, + pub new_nonce: String, + pub new_account: String, + pub new_order: String, + pub new_authz: Option, + pub revoke_cert: String, + pub key_change: String, } deserialize_from_str!(Directory); #[cfg(test)] mod tests { - use super::Directory; - use std::str::FromStr; + use super::Directory; + use std::str::FromStr; - #[test] - fn test_directory() { - let data = "{ + #[test] + fn test_directory() { + let data = "{ \"newAccount\": \"https://example.org/acme/new-acct\", \"newNonce\": \"https://example.org/acme/new-nonce\", \"newOrder\": \"https://example.org/acme/new-order\", @@ -40,51 +40,51 @@ mod tests { \"newAuthz\": \"https://example.org/acme/new-authz\", \"keyChange\": \"https://example.org/acme/key-change\" }"; - let parsed_dir = Directory::from_str(data); - assert!(parsed_dir.is_ok()); - let parsed_dir = parsed_dir.unwrap(); - assert_eq!(parsed_dir.new_nonce, "https://example.org/acme/new-nonce"); - assert_eq!(parsed_dir.new_account, "https://example.org/acme/new-acct"); - assert_eq!(parsed_dir.new_order, "https://example.org/acme/new-order"); - assert_eq!( - parsed_dir.new_authz, - Some("https://example.org/acme/new-authz".to_string()) - ); - assert_eq!( - parsed_dir.revoke_cert, - "https://example.org/acme/revoke-cert" - ); - assert_eq!(parsed_dir.key_change, "https://example.org/acme/key-change"); - assert!(parsed_dir.meta.is_none()); - } + let parsed_dir = Directory::from_str(data); + assert!(parsed_dir.is_ok()); + let parsed_dir = parsed_dir.unwrap(); + assert_eq!(parsed_dir.new_nonce, "https://example.org/acme/new-nonce"); + assert_eq!(parsed_dir.new_account, "https://example.org/acme/new-acct"); + assert_eq!(parsed_dir.new_order, "https://example.org/acme/new-order"); + assert_eq!( + parsed_dir.new_authz, + Some("https://example.org/acme/new-authz".to_string()) + ); + assert_eq!( + parsed_dir.revoke_cert, + "https://example.org/acme/revoke-cert" + ); + assert_eq!(parsed_dir.key_change, "https://example.org/acme/key-change"); + assert!(parsed_dir.meta.is_none()); + } - #[test] - fn test_directory_no_authz() { - let data = "{ + #[test] + fn test_directory_no_authz() { + let data = "{ \"newAccount\": \"https://example.org/acme/new-acct\", \"newNonce\": \"https://example.org/acme/new-nonce\", \"newOrder\": \"https://example.org/acme/new-order\", \"revokeCert\": \"https://example.org/acme/revoke-cert\", \"keyChange\": \"https://example.org/acme/key-change\" }"; - let parsed_dir = Directory::from_str(data); - assert!(parsed_dir.is_ok()); - let parsed_dir = parsed_dir.unwrap(); - assert_eq!(parsed_dir.new_nonce, "https://example.org/acme/new-nonce"); - assert_eq!(parsed_dir.new_account, "https://example.org/acme/new-acct"); - assert_eq!(parsed_dir.new_order, "https://example.org/acme/new-order"); - assert!(parsed_dir.new_authz.is_none()); - assert_eq!( - parsed_dir.revoke_cert, - "https://example.org/acme/revoke-cert" - ); - assert_eq!(parsed_dir.key_change, "https://example.org/acme/key-change"); - assert!(parsed_dir.meta.is_none()); - } + let parsed_dir = Directory::from_str(data); + assert!(parsed_dir.is_ok()); + let parsed_dir = parsed_dir.unwrap(); + assert_eq!(parsed_dir.new_nonce, "https://example.org/acme/new-nonce"); + assert_eq!(parsed_dir.new_account, "https://example.org/acme/new-acct"); + assert_eq!(parsed_dir.new_order, "https://example.org/acme/new-order"); + assert!(parsed_dir.new_authz.is_none()); + assert_eq!( + parsed_dir.revoke_cert, + "https://example.org/acme/revoke-cert" + ); + assert_eq!(parsed_dir.key_change, "https://example.org/acme/key-change"); + assert!(parsed_dir.meta.is_none()); + } - #[test] - fn test_directory_meta() { - let data = "{ + #[test] + fn test_directory_meta() { + let data = "{ \"keyChange\": \"https://example.org/acme/key-change\", \"meta\": { \"caaIdentities\": [ @@ -98,26 +98,26 @@ mod tests { \"newOrder\": \"https://example.org/acme/new-order\", \"revokeCert\": \"https://example.org/acme/revoke-cert\" }"; - let parsed_dir = Directory::from_str(&data); - assert!(parsed_dir.is_ok()); - let parsed_dir = parsed_dir.unwrap(); - assert!(parsed_dir.meta.is_some()); - let meta = parsed_dir.meta.unwrap(); - assert_eq!( - meta.terms_of_service, - Some("https://example.org/documents/tos.pdf".to_string()) - ); - assert_eq!(meta.website, Some("https://example.org/".to_string())); - assert!(meta.caa_identities.is_some()); - let caa_identities = meta.caa_identities.unwrap(); - assert_eq!(caa_identities.len(), 1); - assert_eq!(caa_identities.first(), Some(&"example.org".to_string())); - assert!(meta.external_account_required.is_none()); - } + let parsed_dir = Directory::from_str(&data); + assert!(parsed_dir.is_ok()); + let parsed_dir = parsed_dir.unwrap(); + assert!(parsed_dir.meta.is_some()); + let meta = parsed_dir.meta.unwrap(); + assert_eq!( + meta.terms_of_service, + Some("https://example.org/documents/tos.pdf".to_string()) + ); + assert_eq!(meta.website, Some("https://example.org/".to_string())); + assert!(meta.caa_identities.is_some()); + let caa_identities = meta.caa_identities.unwrap(); + assert_eq!(caa_identities.len(), 1); + assert_eq!(caa_identities.first(), Some(&"example.org".to_string())); + assert!(meta.external_account_required.is_none()); + } - #[test] - fn test_directory_extra_fields() { - let data = "{ + #[test] + fn test_directory_extra_fields() { + let data = "{ \"foo\": \"bar\", \"keyChange\": \"https://example.org/acme/key-change\", \"newAccount\": \"https://example.org/acme/new-acct\", @@ -127,21 +127,21 @@ mod tests { \"newOrder\": \"https://example.org/acme/new-order\", \"revokeCert\": \"https://example.org/acme/revoke-cert\" }"; - let parsed_dir = Directory::from_str(&data); - assert!(parsed_dir.is_ok()); - let parsed_dir = parsed_dir.unwrap(); - assert_eq!(parsed_dir.new_nonce, "https://example.org/acme/new-nonce"); - assert_eq!(parsed_dir.new_account, "https://example.org/acme/new-acct"); - assert_eq!(parsed_dir.new_order, "https://example.org/acme/new-order"); - assert_eq!( - parsed_dir.new_authz, - Some("https://example.org/acme/new-authz".to_string()) - ); - assert_eq!( - parsed_dir.revoke_cert, - "https://example.org/acme/revoke-cert" - ); - assert_eq!(parsed_dir.key_change, "https://example.org/acme/key-change"); - assert!(parsed_dir.meta.is_none()); - } + let parsed_dir = Directory::from_str(&data); + assert!(parsed_dir.is_ok()); + let parsed_dir = parsed_dir.unwrap(); + assert_eq!(parsed_dir.new_nonce, "https://example.org/acme/new-nonce"); + assert_eq!(parsed_dir.new_account, "https://example.org/acme/new-acct"); + assert_eq!(parsed_dir.new_order, "https://example.org/acme/new-order"); + assert_eq!( + parsed_dir.new_authz, + Some("https://example.org/acme/new-authz".to_string()) + ); + assert_eq!( + parsed_dir.revoke_cert, + "https://example.org/acme/revoke-cert" + ); + assert_eq!(parsed_dir.key_change, "https://example.org/acme/key-change"); + assert!(parsed_dir.meta.is_none()); + } } diff --git a/acmed/src/acme_proto/structs/error.rs b/acmed/src/acme_proto/structs/error.rs index 9f4fe53..fbfb461 100644 --- a/acmed/src/acme_proto/structs/error.rs +++ b/acmed/src/acme_proto/structs/error.rs @@ -4,75 +4,75 @@ use std::fmt; use std::str::FromStr; pub trait ApiError { - fn get_error(&self) -> Option; + fn get_error(&self) -> Option; } #[derive(Clone, Debug, PartialEq)] pub enum AcmeError { - AccountDoesNotExist, - AlreadyRevoked, - BadCSR, - BadNonce, - BadPublicKey, - BadRevocationReason, - BadSignatureAlgorithm, - Caa, - Compound, - Connection, - Dns, - ExternalAccountRequired, - IncorrectResponse, - InvalidContact, - Malformed, - OrderNotReady, - RateLimited, - RejectedIdentifier, - ServerInternal, - Tls, - Unauthorized, - UnsupportedContact, - UnsupportedIdentifier, - UserActionRequired, - Unknown, + AccountDoesNotExist, + AlreadyRevoked, + BadCSR, + BadNonce, + BadPublicKey, + BadRevocationReason, + BadSignatureAlgorithm, + Caa, + Compound, + Connection, + Dns, + ExternalAccountRequired, + IncorrectResponse, + InvalidContact, + Malformed, + OrderNotReady, + RateLimited, + RejectedIdentifier, + ServerInternal, + Tls, + Unauthorized, + UnsupportedContact, + UnsupportedIdentifier, + UserActionRequired, + Unknown, } impl From for AcmeError { - fn from(error: String) -> Self { - match error.as_str() { - "urn:ietf:params:acme:error:accountDoesNotExist" => AcmeError::AccountDoesNotExist, - "urn:ietf:params:acme:error:alreadyRevoked" => AcmeError::AlreadyRevoked, - "urn:ietf:params:acme:error:badCSR" => AcmeError::BadCSR, - "urn:ietf:params:acme:error:badNonce" => AcmeError::BadNonce, - "urn:ietf:params:acme:error:badPublicKey" => AcmeError::BadPublicKey, - "urn:ietf:params:acme:error:badRevocationReason" => AcmeError::BadRevocationReason, - "urn:ietf:params:acme:error:badSignatureAlgorithm" => AcmeError::BadSignatureAlgorithm, - "urn:ietf:params:acme:error:caa" => AcmeError::Caa, - "urn:ietf:params:acme:error:compound" => AcmeError::Compound, - "urn:ietf:params:acme:error:connection" => AcmeError::Connection, - "urn:ietf:params:acme:error:dns" => AcmeError::Dns, - "urn:ietf:params:acme:error:externalAccountRequired" => { - AcmeError::ExternalAccountRequired - } - "urn:ietf:params:acme:error:incorrectResponse" => AcmeError::IncorrectResponse, - "urn:ietf:params:acme:error:invalidContact" => AcmeError::InvalidContact, - "urn:ietf:params:acme:error:malformed" => AcmeError::Malformed, - "urn:ietf:params:acme:error:orderNotReady" => AcmeError::OrderNotReady, - "urn:ietf:params:acme:error:rateLimited" => AcmeError::RateLimited, - "urn:ietf:params:acme:error:rejectedIdentifier" => AcmeError::RejectedIdentifier, - "urn:ietf:params:acme:error:serverInternal" => AcmeError::ServerInternal, - "urn:ietf:params:acme:error:tls" => AcmeError::Tls, - "urn:ietf:params:acme:error:unauthorized" => AcmeError::Unauthorized, - "urn:ietf:params:acme:error:unsupportedContact" => AcmeError::UnsupportedContact, - "urn:ietf:params:acme:error:unsupportedIdentifier" => AcmeError::UnsupportedIdentifier, - "urn:ietf:params:acme:error:userActionRequired" => AcmeError::UserActionRequired, - _ => AcmeError::Unknown, - } - } + fn from(error: String) -> Self { + match error.as_str() { + "urn:ietf:params:acme:error:accountDoesNotExist" => AcmeError::AccountDoesNotExist, + "urn:ietf:params:acme:error:alreadyRevoked" => AcmeError::AlreadyRevoked, + "urn:ietf:params:acme:error:badCSR" => AcmeError::BadCSR, + "urn:ietf:params:acme:error:badNonce" => AcmeError::BadNonce, + "urn:ietf:params:acme:error:badPublicKey" => AcmeError::BadPublicKey, + "urn:ietf:params:acme:error:badRevocationReason" => AcmeError::BadRevocationReason, + "urn:ietf:params:acme:error:badSignatureAlgorithm" => AcmeError::BadSignatureAlgorithm, + "urn:ietf:params:acme:error:caa" => AcmeError::Caa, + "urn:ietf:params:acme:error:compound" => AcmeError::Compound, + "urn:ietf:params:acme:error:connection" => AcmeError::Connection, + "urn:ietf:params:acme:error:dns" => AcmeError::Dns, + "urn:ietf:params:acme:error:externalAccountRequired" => { + AcmeError::ExternalAccountRequired + } + "urn:ietf:params:acme:error:incorrectResponse" => AcmeError::IncorrectResponse, + "urn:ietf:params:acme:error:invalidContact" => AcmeError::InvalidContact, + "urn:ietf:params:acme:error:malformed" => AcmeError::Malformed, + "urn:ietf:params:acme:error:orderNotReady" => AcmeError::OrderNotReady, + "urn:ietf:params:acme:error:rateLimited" => AcmeError::RateLimited, + "urn:ietf:params:acme:error:rejectedIdentifier" => AcmeError::RejectedIdentifier, + "urn:ietf:params:acme:error:serverInternal" => AcmeError::ServerInternal, + "urn:ietf:params:acme:error:tls" => AcmeError::Tls, + "urn:ietf:params:acme:error:unauthorized" => AcmeError::Unauthorized, + "urn:ietf:params:acme:error:unsupportedContact" => AcmeError::UnsupportedContact, + "urn:ietf:params:acme:error:unsupportedIdentifier" => AcmeError::UnsupportedIdentifier, + "urn:ietf:params:acme:error:userActionRequired" => AcmeError::UserActionRequired, + _ => AcmeError::Unknown, + } + } } impl fmt::Display for AcmeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let msg = match self { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let msg = match self { AcmeError::AccountDoesNotExist => "the request specified an account that does not exist", AcmeError::AlreadyRevoked => "the request specified a certificate to be revoked that has already been revoked", AcmeError::BadCSR => "the CSR is unacceptable (e.g., due to a short key)", @@ -99,75 +99,75 @@ impl fmt::Display for AcmeError { AcmeError::UserActionRequired => "visit the \"instance\" URL and take actions specified there", AcmeError::Unknown => "unknown error", }; - write!(f, "{}", msg) - } + write!(f, "{}", msg) + } } impl AcmeError { - pub fn is_recoverable(&self) -> bool { - *self == AcmeError::BadNonce - || *self == AcmeError::Connection - || *self == AcmeError::Dns - || *self == AcmeError::Malformed - || *self == AcmeError::RateLimited - || *self == AcmeError::ServerInternal - || *self == AcmeError::Tls - } + pub fn is_recoverable(&self) -> bool { + *self == AcmeError::BadNonce + || *self == AcmeError::Connection + || *self == AcmeError::Dns + || *self == AcmeError::Malformed + || *self == AcmeError::RateLimited + || *self == AcmeError::ServerInternal + || *self == AcmeError::Tls + } } impl From for AcmeError { - fn from(_error: Error) -> Self { - AcmeError::Unknown - } + fn from(_error: Error) -> Self { + AcmeError::Unknown + } } impl From for Error { - fn from(error: AcmeError) -> Self { - error.to_string().into() - } + fn from(error: AcmeError) -> Self { + error.to_string().into() + } } #[derive(Clone, Debug, PartialEq, Deserialize)] pub struct HttpApiError { - #[serde(rename = "type")] - error_type: Option, - // title: Option, - status: Option, - detail: Option, - // instance: Option, - // TODO: implement subproblems + #[serde(rename = "type")] + error_type: Option, + // title: Option, + status: Option, + detail: Option, + // instance: Option, + // TODO: implement subproblems } crate::acme_proto::structs::deserialize_from_str!(HttpApiError); impl fmt::Display for HttpApiError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let msg = self - .detail - .to_owned() - .unwrap_or_else(|| self.get_acme_type().to_string()); - let msg = match self.status { - Some(s) => format!("status {}: {}", s, msg), - None => msg, - }; - write!(f, "{}", msg) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let msg = self + .detail + .to_owned() + .unwrap_or_else(|| self.get_acme_type().to_string()); + let msg = match self.status { + Some(s) => format!("status {}: {}", s, msg), + None => msg, + }; + write!(f, "{}", msg) + } } impl HttpApiError { - pub fn get_type(&self) -> String { - self.error_type - .to_owned() - .unwrap_or_else(|| String::from("about:blank")) - } + pub fn get_type(&self) -> String { + self.error_type + .to_owned() + .unwrap_or_else(|| String::from("about:blank")) + } - pub fn get_acme_type(&self) -> AcmeError { - self.get_type().into() - } + pub fn get_acme_type(&self) -> AcmeError { + self.get_type().into() + } } impl From for Error { - fn from(error: HttpApiError) -> Self { - error.to_string().into() - } + fn from(error: HttpApiError) -> Self { + error.to_string().into() + } } diff --git a/acmed/src/acme_proto/structs/order.rs b/acmed/src/acme_proto/structs/order.rs index 6d9f6d1..d10dc8e 100644 --- a/acmed/src/acme_proto/structs/order.rs +++ b/acmed/src/acme_proto/structs/order.rs @@ -8,41 +8,41 @@ use std::str::FromStr; #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct NewOrder { - pub identifiers: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - pub not_before: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub not_after: Option, + pub identifiers: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub not_before: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub not_after: Option, } impl NewOrder { - pub fn new(identifiers: &[identifier::Identifier]) -> Self { - NewOrder { - identifiers: identifiers.iter().map(Identifier::from_generic).collect(), - not_before: None, - not_after: None, - } - } + pub fn new(identifiers: &[identifier::Identifier]) -> Self { + NewOrder { + identifiers: identifiers.iter().map(Identifier::from_generic).collect(), + not_before: None, + not_after: None, + } + } } #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct Order { - pub status: OrderStatus, - pub expires: Option, - pub identifiers: Vec, - pub not_before: Option, - pub not_after: Option, - pub error: Option, - pub authorizations: Vec, - pub finalize: String, - pub certificate: Option, + pub status: OrderStatus, + pub expires: Option, + pub identifiers: Vec, + pub not_before: Option, + pub not_after: Option, + pub error: Option, + pub authorizations: Vec, + pub finalize: String, + pub certificate: Option, } impl ApiError for Order { - fn get_error(&self) -> Option { - self.error.to_owned().map(Error::from) - } + fn get_error(&self) -> Option { + self.error.to_owned().map(Error::from) + } } deserialize_from_str!(Order); @@ -50,82 +50,82 @@ deserialize_from_str!(Order); #[derive(Debug, PartialEq, Eq, Deserialize)] #[serde(rename_all = "lowercase")] pub enum OrderStatus { - Pending, - Ready, - Processing, - Valid, - Invalid, + Pending, + Ready, + Processing, + Valid, + Invalid, } impl fmt::Display for OrderStatus { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match self { - OrderStatus::Pending => "pending", - OrderStatus::Ready => "ready", - OrderStatus::Processing => "processing", - OrderStatus::Valid => "valid", - OrderStatus::Invalid => "invalid", - }; - write!(f, "{}", s) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + OrderStatus::Pending => "pending", + OrderStatus::Ready => "ready", + OrderStatus::Processing => "processing", + OrderStatus::Valid => "valid", + OrderStatus::Invalid => "invalid", + }; + write!(f, "{}", s) + } } #[derive(Deserialize, Serialize)] pub struct Identifier { - #[serde(rename = "type")] - pub id_type: IdentifierType, - pub value: String, + #[serde(rename = "type")] + pub id_type: IdentifierType, + pub value: String, } impl Identifier { - pub fn from_generic(id: &identifier::Identifier) -> Self { - Identifier { - id_type: id.id_type.to_owned(), - value: id.value.to_owned(), - } - } + pub fn from_generic(id: &identifier::Identifier) -> Self { + Identifier { + id_type: id.id_type.to_owned(), + value: id.value.to_owned(), + } + } } deserialize_from_str!(Identifier); impl fmt::Display for Identifier { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}:{}", self.id_type, self.value) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:{}", self.id_type, self.value) + } } #[cfg(test)] mod tests { - use super::{Identifier, IdentifierType}; - use std::str::FromStr; + use super::{Identifier, IdentifierType}; + use std::str::FromStr; - #[test] - fn id_serialize() { - let reference = "{\"type\":\"dns\",\"value\":\"test.example.org\"}"; - let id = Identifier { - id_type: IdentifierType::Dns, - value: "test.example.org".to_string(), - }; - let id_json = serde_json::to_string(&id); - assert!(id_json.is_ok()); - let id_json = id_json.unwrap(); - assert_eq!(id_json, reference.to_string()); - } + #[test] + fn id_serialize() { + let reference = "{\"type\":\"dns\",\"value\":\"test.example.org\"}"; + let id = Identifier { + id_type: IdentifierType::Dns, + value: "test.example.org".to_string(), + }; + let id_json = serde_json::to_string(&id); + assert!(id_json.is_ok()); + let id_json = id_json.unwrap(); + assert_eq!(id_json, reference.to_string()); + } - #[test] - fn id_deserialize_valid() { - let id_str = "{\"type\":\"dns\",\"value\":\"test.example.org\"}"; - let id = Identifier::from_str(id_str); - assert!(id.is_ok()); - let id = id.unwrap(); - assert_eq!(id.id_type, IdentifierType::Dns); - assert_eq!(id.value, "test.example.org".to_string()); - } + #[test] + fn id_deserialize_valid() { + let id_str = "{\"type\":\"dns\",\"value\":\"test.example.org\"}"; + let id = Identifier::from_str(id_str); + assert!(id.is_ok()); + let id = id.unwrap(); + assert_eq!(id.id_type, IdentifierType::Dns); + assert_eq!(id.value, "test.example.org".to_string()); + } - #[test] - fn id_deserialize_invalid_type() { - let id_str = "{\"type\":\"trololo\",\"value\":\"test.example.org\"}"; - let id = Identifier::from_str(id_str); - assert!(id.is_err()); - } + #[test] + fn id_deserialize_invalid_type() { + let id_str = "{\"type\":\"trololo\",\"value\":\"test.example.org\"}"; + let id = Identifier::from_str(id_str); + assert!(id.is_err()); + } } diff --git a/acmed/src/certificate.rs b/acmed/src/certificate.rs index 752d399..bc69c8c 100644 --- a/acmed/src/certificate.rs +++ b/acmed/src/certificate.rs @@ -12,187 +12,187 @@ use std::time::Duration; #[derive(Clone, Debug)] pub struct Certificate { - pub account_name: String, - pub identifiers: Vec, - pub subject_attributes: HashMap, - pub key_type: KeyType, - pub csr_digest: HashFunction, - pub kp_reuse: bool, - pub endpoint_name: String, - pub hooks: Vec, - pub crt_name: String, - pub env: HashMap, - pub renew_delay: Duration, - pub file_manager: FileManager, + pub account_name: String, + pub identifiers: Vec, + pub subject_attributes: HashMap, + pub key_type: KeyType, + pub csr_digest: HashFunction, + pub kp_reuse: bool, + pub endpoint_name: String, + pub hooks: Vec, + pub crt_name: String, + pub env: HashMap, + pub renew_delay: Duration, + pub file_manager: FileManager, } impl fmt::Display for Certificate { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.get_id()) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.get_id()) + } } impl HasLogger for Certificate { - fn warn(&self, msg: &str) { - warn!("certificate \"{}\": {}", &self, msg); - } + fn warn(&self, msg: &str) { + warn!("certificate \"{}\": {}", &self, msg); + } - fn info(&self, msg: &str) { - info!("certificate \"{}\": {}", &self, msg); - } + fn info(&self, msg: &str) { + info!("certificate \"{}\": {}", &self, msg); + } - fn debug(&self, msg: &str) { - debug!("certificate \"{}\": {}", &self, msg); - } + fn debug(&self, msg: &str) { + debug!("certificate \"{}\": {}", &self, msg); + } - fn trace(&self, msg: &str) { - trace!("certificate \"{}\": {}", &self, msg); - } + fn trace(&self, msg: &str) { + trace!("certificate \"{}\": {}", &self, msg); + } } impl Certificate { - pub fn get_id(&self) -> String { - format!("{}_{}", self.crt_name, self.key_type) - } - - pub fn get_identifier_from_str(&self, identifier: &str) -> Result { - let identifier = identifier.to_string(); - for d in self.identifiers.iter() { - let val = match d.id_type { - // strip wildcards from domain before matching - IdentifierType::Dns => d.value.trim_start_matches("*.").to_string(), - IdentifierType::Ip => d.value.to_owned(), - }; - if identifier == val { - return Ok(d.clone()); - } - } - Err(format!("{}: identifier not found", identifier).into()) - } - - fn is_expiring(&self, cert: &X509Certificate) -> Result { - let expires_in = cert.expires_in()?; - self.debug(&format!( - "certificate expires in {} days ({} days delay)", - expires_in.as_secs() / 86400, - self.renew_delay.as_secs() / 86400, - )); - Ok(expires_in <= self.renew_delay) - } - - fn has_missing_identifiers(&self, cert: &X509Certificate) -> bool { - let cert_names = cert.subject_alt_names(); - let req_names = self - .identifiers - .iter() - .map(|v| v.value.to_owned()) - .collect::>(); - let has_miss = req_names.difference(&cert_names).count() != 0; - if has_miss { - let domains = req_names - .difference(&cert_names) - .map(std::borrow::ToOwned::to_owned) - .collect::>() - .join(", "); - self.debug(&format!( - "the certificate does not include the following domains: {}", - domains - )); - } - has_miss - } - - /// Return a comma-separated list of the domains this certificate is valid for. - pub fn identifier_list(&self) -> String { - self.identifiers - .iter() - .map(|d| d.value.as_str()) - .collect::>() - .join(",") - } - - pub fn should_renew(&self) -> Result { - self.debug(&format!( - "checking for renewal (identifiers: {})", - self.identifier_list() - )); - if !certificate_files_exists(&self.file_manager) { - self.debug("certificate does not exist: requesting one"); - return Ok(true); - } - let cert = get_certificate(&self.file_manager)?; - - let renew_ident = self.has_missing_identifiers(&cert); - if renew_ident { - self.debug("the current certificate doesn't include all the required identifiers"); - } - let renew_exp = self.is_expiring(&cert)?; - if renew_exp { - self.debug("the certificate is expiring"); - } - let renew = renew_ident || renew_exp; - - if renew { - self.debug("the certificate will be renewed now"); - } else { - self.debug("the certificate will not be renewed now"); - } - Ok(renew) - } - - pub fn call_challenge_hooks( - &self, - file_name: &str, - proof: &str, - identifier: &str, - ) -> Result<(ChallengeHookData, HookType), Error> { - let identifier = self.get_identifier_from_str(identifier)?; - let mut hook_data = ChallengeHookData { - challenge: identifier.challenge.to_string(), - identifier: identifier.value.to_owned(), - identifier_tls_alpn: identifier.get_tls_alpn_name().unwrap_or_default(), - file_name: file_name.to_string(), - proof: proof.to_string(), - is_clean_hook: false, - env: HashMap::new(), - }; - hook_data.set_env(&self.env); - hook_data.set_env(&identifier.env); - let hook_type = match identifier.challenge { - Challenge::Http01 => (HookType::ChallengeHttp01, HookType::ChallengeHttp01Clean), - Challenge::Dns01 => (HookType::ChallengeDns01, HookType::ChallengeDns01Clean), - Challenge::TlsAlpn01 => ( - HookType::ChallengeTlsAlpn01, - HookType::ChallengeTlsAlpn01Clean, - ), - }; - hooks::call(self, &self.hooks, &hook_data, hook_type.0)?; - Ok((hook_data, hook_type.1)) - } - - pub fn call_challenge_hooks_clean( - &self, - data: &ChallengeHookData, - hook_type: HookType, - ) -> Result<(), Error> { - hooks::call(self, &self.hooks, data, hook_type) - } - - pub fn call_post_operation_hooks(&self, status: &str, is_success: bool) -> Result<(), Error> { - let identifiers = self - .identifiers - .iter() - .map(|d| d.value.to_owned()) - .collect::>(); - let mut hook_data = PostOperationHookData { - identifiers, - key_type: self.key_type.to_string(), - status: status.to_string(), - is_success, - env: HashMap::new(), - }; - hook_data.set_env(&self.env); - hooks::call(self, &self.hooks, &hook_data, HookType::PostOperation)?; - Ok(()) - } + pub fn get_id(&self) -> String { + format!("{}_{}", self.crt_name, self.key_type) + } + + pub fn get_identifier_from_str(&self, identifier: &str) -> Result { + let identifier = identifier.to_string(); + for d in self.identifiers.iter() { + let val = match d.id_type { + // strip wildcards from domain before matching + IdentifierType::Dns => d.value.trim_start_matches("*.").to_string(), + IdentifierType::Ip => d.value.to_owned(), + }; + if identifier == val { + return Ok(d.clone()); + } + } + Err(format!("{}: identifier not found", identifier).into()) + } + + fn is_expiring(&self, cert: &X509Certificate) -> Result { + let expires_in = cert.expires_in()?; + self.debug(&format!( + "certificate expires in {} days ({} days delay)", + expires_in.as_secs() / 86400, + self.renew_delay.as_secs() / 86400, + )); + Ok(expires_in <= self.renew_delay) + } + + fn has_missing_identifiers(&self, cert: &X509Certificate) -> bool { + let cert_names = cert.subject_alt_names(); + let req_names = self + .identifiers + .iter() + .map(|v| v.value.to_owned()) + .collect::>(); + let has_miss = req_names.difference(&cert_names).count() != 0; + if has_miss { + let domains = req_names + .difference(&cert_names) + .map(std::borrow::ToOwned::to_owned) + .collect::>() + .join(", "); + self.debug(&format!( + "the certificate does not include the following domains: {}", + domains + )); + } + has_miss + } + + /// Return a comma-separated list of the domains this certificate is valid for. + pub fn identifier_list(&self) -> String { + self.identifiers + .iter() + .map(|d| d.value.as_str()) + .collect::>() + .join(",") + } + + pub fn should_renew(&self) -> Result { + self.debug(&format!( + "checking for renewal (identifiers: {})", + self.identifier_list() + )); + if !certificate_files_exists(&self.file_manager) { + self.debug("certificate does not exist: requesting one"); + return Ok(true); + } + let cert = get_certificate(&self.file_manager)?; + + let renew_ident = self.has_missing_identifiers(&cert); + if renew_ident { + self.debug("the current certificate doesn't include all the required identifiers"); + } + let renew_exp = self.is_expiring(&cert)?; + if renew_exp { + self.debug("the certificate is expiring"); + } + let renew = renew_ident || renew_exp; + + if renew { + self.debug("the certificate will be renewed now"); + } else { + self.debug("the certificate will not be renewed now"); + } + Ok(renew) + } + + pub fn call_challenge_hooks( + &self, + file_name: &str, + proof: &str, + identifier: &str, + ) -> Result<(ChallengeHookData, HookType), Error> { + let identifier = self.get_identifier_from_str(identifier)?; + let mut hook_data = ChallengeHookData { + challenge: identifier.challenge.to_string(), + identifier: identifier.value.to_owned(), + identifier_tls_alpn: identifier.get_tls_alpn_name().unwrap_or_default(), + file_name: file_name.to_string(), + proof: proof.to_string(), + is_clean_hook: false, + env: HashMap::new(), + }; + hook_data.set_env(&self.env); + hook_data.set_env(&identifier.env); + let hook_type = match identifier.challenge { + Challenge::Http01 => (HookType::ChallengeHttp01, HookType::ChallengeHttp01Clean), + Challenge::Dns01 => (HookType::ChallengeDns01, HookType::ChallengeDns01Clean), + Challenge::TlsAlpn01 => ( + HookType::ChallengeTlsAlpn01, + HookType::ChallengeTlsAlpn01Clean, + ), + }; + hooks::call(self, &self.hooks, &hook_data, hook_type.0)?; + Ok((hook_data, hook_type.1)) + } + + pub fn call_challenge_hooks_clean( + &self, + data: &ChallengeHookData, + hook_type: HookType, + ) -> Result<(), Error> { + hooks::call(self, &self.hooks, data, hook_type) + } + + pub fn call_post_operation_hooks(&self, status: &str, is_success: bool) -> Result<(), Error> { + let identifiers = self + .identifiers + .iter() + .map(|d| d.value.to_owned()) + .collect::>(); + let mut hook_data = PostOperationHookData { + identifiers, + key_type: self.key_type.to_string(), + status: status.to_string(), + is_success, + env: HashMap::new(), + }; + hook_data.set_env(&self.env); + hooks::call(self, &self.hooks, &hook_data, HookType::PostOperation)?; + Ok(()) + } } diff --git a/acmed/src/config.rs b/acmed/src/config.rs index 894a641..9f509cf 100644 --- a/acmed/src/config.rs +++ b/acmed/src/config.rs @@ -17,737 +17,737 @@ use std::result::Result; use std::time::Duration; macro_rules! set_cfg_attr { - ($to: expr, $from: expr) => { - if let Some(v) = $from { - $to = Some(v); - }; - }; + ($to: expr, $from: expr) => { + if let Some(v) = $from { + $to = Some(v); + }; + }; } macro_rules! push_subject_attr { - ($hm: expr, $attr: expr, $attr_type: ident) => { - if let Some(v) = &$attr { - $hm.insert(SubjectAttribute::$attr_type, v.to_owned()); - } - }; + ($hm: expr, $attr: expr, $attr_type: ident) => { + if let Some(v) = &$attr { + $hm.insert(SubjectAttribute::$attr_type, v.to_owned()); + } + }; } fn get_stdin(hook: &Hook) -> Result { - match &hook.stdin { - Some(file) => match &hook.stdin_str { - Some(_) => { - let msg = format!( - "{}: a hook cannot have both stdin and stdin_str", - &hook.name - ); - Err(msg.into()) - } - None => Ok(hooks::HookStdin::File(file.to_string())), - }, - None => match &hook.stdin_str { - Some(s) => Ok(hooks::HookStdin::Str(s.to_string())), - None => Ok(hooks::HookStdin::None), - }, - } + match &hook.stdin { + Some(file) => match &hook.stdin_str { + Some(_) => { + let msg = format!( + "{}: a hook cannot have both stdin and stdin_str", + &hook.name + ); + Err(msg.into()) + } + None => Ok(hooks::HookStdin::File(file.to_string())), + }, + None => match &hook.stdin_str { + Some(s) => Ok(hooks::HookStdin::Str(s.to_string())), + None => Ok(hooks::HookStdin::None), + }, + } } #[derive(Default, Deserialize)] #[serde(deny_unknown_fields)] pub struct Config { - pub global: Option, - #[serde(default)] - pub endpoint: Vec, - #[serde(default, rename = "rate-limit")] - pub rate_limit: Vec, - #[serde(default)] - pub hook: Vec, - #[serde(default)] - pub group: Vec, - #[serde(default)] - pub account: Vec, - #[serde(default)] - pub certificate: Vec, - #[serde(default)] - pub include: Vec, + pub global: Option, + #[serde(default)] + pub endpoint: Vec, + #[serde(default, rename = "rate-limit")] + pub rate_limit: Vec, + #[serde(default)] + pub hook: Vec, + #[serde(default)] + pub group: Vec, + #[serde(default)] + pub account: Vec, + #[serde(default)] + pub certificate: Vec, + #[serde(default)] + pub include: Vec, } impl Config { - fn get_rate_limit(&self, name: &str) -> Result<(usize, String), Error> { - for rl in self.rate_limit.iter() { - if rl.name == name { - return Ok((rl.number, rl.period.to_owned())); - } - } - Err(format!("{}: rate limit not found", name).into()) - } - - pub fn get_account_dir(&self) -> String { - let account_dir = match &self.global { - Some(g) => match &g.accounts_directory { - Some(d) => d, - None => crate::DEFAULT_ACCOUNTS_DIR, - }, - None => crate::DEFAULT_ACCOUNTS_DIR, - }; - account_dir.to_string() - } - - pub fn get_hook(&self, name: &str) -> Result, Error> { - for hook in self.hook.iter() { - if name == hook.name { - let h = hooks::Hook { - name: hook.name.to_owned(), - hook_type: hook.hook_type.iter().map(|e| e.to_owned()).collect(), - cmd: hook.cmd.to_owned(), - args: hook.args.to_owned(), - stdin: get_stdin(hook)?, - stdout: hook.stdout.to_owned(), - stderr: hook.stderr.to_owned(), - allow_failure: hook - .allow_failure - .unwrap_or(crate::DEFAULT_HOOK_ALLOW_FAILURE), - }; - return Ok(vec![h]); - } - } - for grp in self.group.iter() { - if name == grp.name { - let mut ret = vec![]; - for hook_name in grp.hooks.iter() { - let mut h = self.get_hook(hook_name)?; - ret.append(&mut h); - } - return Ok(ret); - } - } - Err(format!("{}: hook not found", name).into()) - } - - pub fn get_cert_file_mode(&self) -> u32 { - match &self.global { - Some(g) => match g.cert_file_mode { - Some(m) => m, - None => crate::DEFAULT_CERT_FILE_MODE, - }, - None => crate::DEFAULT_CERT_FILE_MODE, - } - } - - pub fn get_cert_file_user(&self) -> Option { - match &self.global { - Some(g) => g.cert_file_user.to_owned(), - None => None, - } - } - - pub fn get_cert_file_group(&self) -> Option { - match &self.global { - Some(g) => g.cert_file_group.to_owned(), - None => None, - } - } - - pub fn get_pk_file_mode(&self) -> u32 { - match &self.global { - Some(g) => match g.pk_file_mode { - Some(m) => m, - None => crate::DEFAULT_PK_FILE_MODE, - }, - None => crate::DEFAULT_PK_FILE_MODE, - } - } - - pub fn get_pk_file_user(&self) -> Option { - match &self.global { - Some(g) => g.pk_file_user.to_owned(), - None => None, - } - } - - pub fn get_pk_file_group(&self) -> Option { - match &self.global { - Some(g) => g.pk_file_group.to_owned(), - None => None, - } - } + fn get_rate_limit(&self, name: &str) -> Result<(usize, String), Error> { + for rl in self.rate_limit.iter() { + if rl.name == name { + return Ok((rl.number, rl.period.to_owned())); + } + } + Err(format!("{}: rate limit not found", name).into()) + } + + pub fn get_account_dir(&self) -> String { + let account_dir = match &self.global { + Some(g) => match &g.accounts_directory { + Some(d) => d, + None => crate::DEFAULT_ACCOUNTS_DIR, + }, + None => crate::DEFAULT_ACCOUNTS_DIR, + }; + account_dir.to_string() + } + + pub fn get_hook(&self, name: &str) -> Result, Error> { + for hook in self.hook.iter() { + if name == hook.name { + let h = hooks::Hook { + name: hook.name.to_owned(), + hook_type: hook.hook_type.iter().map(|e| e.to_owned()).collect(), + cmd: hook.cmd.to_owned(), + args: hook.args.to_owned(), + stdin: get_stdin(hook)?, + stdout: hook.stdout.to_owned(), + stderr: hook.stderr.to_owned(), + allow_failure: hook + .allow_failure + .unwrap_or(crate::DEFAULT_HOOK_ALLOW_FAILURE), + }; + return Ok(vec![h]); + } + } + for grp in self.group.iter() { + if name == grp.name { + let mut ret = vec![]; + for hook_name in grp.hooks.iter() { + let mut h = self.get_hook(hook_name)?; + ret.append(&mut h); + } + return Ok(ret); + } + } + Err(format!("{}: hook not found", name).into()) + } + + pub fn get_cert_file_mode(&self) -> u32 { + match &self.global { + Some(g) => match g.cert_file_mode { + Some(m) => m, + None => crate::DEFAULT_CERT_FILE_MODE, + }, + None => crate::DEFAULT_CERT_FILE_MODE, + } + } + + pub fn get_cert_file_user(&self) -> Option { + match &self.global { + Some(g) => g.cert_file_user.to_owned(), + None => None, + } + } + + pub fn get_cert_file_group(&self) -> Option { + match &self.global { + Some(g) => g.cert_file_group.to_owned(), + None => None, + } + } + + pub fn get_pk_file_mode(&self) -> u32 { + match &self.global { + Some(g) => match g.pk_file_mode { + Some(m) => m, + None => crate::DEFAULT_PK_FILE_MODE, + }, + None => crate::DEFAULT_PK_FILE_MODE, + } + } + + pub fn get_pk_file_user(&self) -> Option { + match &self.global { + Some(g) => g.pk_file_user.to_owned(), + None => None, + } + } + + pub fn get_pk_file_group(&self) -> Option { + match &self.global { + Some(g) => g.pk_file_group.to_owned(), + None => None, + } + } } #[derive(Clone, Deserialize)] #[serde(deny_unknown_fields)] pub struct GlobalOptions { - pub accounts_directory: Option, - pub cert_file_group: Option, - pub cert_file_mode: Option, - pub cert_file_user: Option, - pub certificates_directory: Option, - #[serde(default)] - pub env: HashMap, - pub file_name_format: Option, - pub pk_file_group: Option, - pub pk_file_mode: Option, - pub pk_file_user: Option, - pub renew_delay: Option, - pub root_certificates: Option>, + pub accounts_directory: Option, + pub cert_file_group: Option, + pub cert_file_mode: Option, + pub cert_file_user: Option, + pub certificates_directory: Option, + #[serde(default)] + pub env: HashMap, + pub file_name_format: Option, + pub pk_file_group: Option, + pub pk_file_mode: Option, + pub pk_file_user: Option, + pub renew_delay: Option, + pub root_certificates: Option>, } impl GlobalOptions { - pub fn get_renew_delay(&self) -> Result { - match &self.renew_delay { - Some(d) => parse_duration(d), - None => Ok(Duration::new(crate::DEFAULT_CERT_RENEW_DELAY, 0)), - } - } + pub fn get_renew_delay(&self) -> Result { + match &self.renew_delay { + Some(d) => parse_duration(d), + None => Ok(Duration::new(crate::DEFAULT_CERT_RENEW_DELAY, 0)), + } + } - pub fn get_crt_name_format(&self) -> String { - match &self.file_name_format { - Some(n) => n.to_string(), - None => crate::DEFAULT_CERT_FORMAT.to_string(), - } - } + pub fn get_crt_name_format(&self) -> String { + match &self.file_name_format { + Some(n) => n.to_string(), + None => crate::DEFAULT_CERT_FORMAT.to_string(), + } + } } #[derive(Clone, Deserialize)] #[serde(deny_unknown_fields)] pub struct Endpoint { - pub file_name_format: Option, - pub name: String, - #[serde(default)] - pub rate_limits: Vec, - pub renew_delay: Option, - pub root_certificates: Option>, - pub tos_agreed: bool, - pub url: String, + pub file_name_format: Option, + pub name: String, + #[serde(default)] + pub rate_limits: Vec, + pub renew_delay: Option, + pub root_certificates: Option>, + pub tos_agreed: bool, + pub url: String, } impl Endpoint { - pub fn get_renew_delay(&self, cnf: &Config) -> Result { - match &self.renew_delay { - Some(d) => parse_duration(d), - None => match &cnf.global { - Some(g) => g.get_renew_delay(), - None => Ok(Duration::new(crate::DEFAULT_CERT_RENEW_DELAY, 0)), - }, - } - } - - pub fn get_crt_name_format(&self, cnf: &Config) -> String { - match &self.file_name_format { - Some(n) => n.to_string(), - None => match &cnf.global { - Some(g) => g.get_crt_name_format(), - None => crate::DEFAULT_CERT_FORMAT.to_string(), - }, - } - } - - fn to_generic( - &self, - cnf: &Config, - root_certs: &[&str], - ) -> Result { - let mut limits = vec![]; - for rl_name in self.rate_limits.iter() { - let (nb, timeframe) = cnf.get_rate_limit(rl_name)?; - limits.push((nb, timeframe)); - } - let mut root_lst: Vec = vec![]; - root_lst.extend(root_certs.iter().map(|v| v.to_string())); - if let Some(crt_lst) = &self.root_certificates { - root_lst.extend(crt_lst.iter().map(|v| v.to_owned())); - } - if let Some(glob) = &cnf.global { - if let Some(crt_lst) = &glob.root_certificates { - root_lst.extend(crt_lst.iter().map(|v| v.to_owned())); - } - } - crate::endpoint::Endpoint::new( - &self.name, - &self.url, - self.tos_agreed, - &limits, - root_lst.as_slice(), - ) - } + pub fn get_renew_delay(&self, cnf: &Config) -> Result { + match &self.renew_delay { + Some(d) => parse_duration(d), + None => match &cnf.global { + Some(g) => g.get_renew_delay(), + None => Ok(Duration::new(crate::DEFAULT_CERT_RENEW_DELAY, 0)), + }, + } + } + + pub fn get_crt_name_format(&self, cnf: &Config) -> String { + match &self.file_name_format { + Some(n) => n.to_string(), + None => match &cnf.global { + Some(g) => g.get_crt_name_format(), + None => crate::DEFAULT_CERT_FORMAT.to_string(), + }, + } + } + + fn to_generic( + &self, + cnf: &Config, + root_certs: &[&str], + ) -> Result { + let mut limits = vec![]; + for rl_name in self.rate_limits.iter() { + let (nb, timeframe) = cnf.get_rate_limit(rl_name)?; + limits.push((nb, timeframe)); + } + let mut root_lst: Vec = vec![]; + root_lst.extend(root_certs.iter().map(|v| v.to_string())); + if let Some(crt_lst) = &self.root_certificates { + root_lst.extend(crt_lst.iter().map(|v| v.to_owned())); + } + if let Some(glob) = &cnf.global { + if let Some(crt_lst) = &glob.root_certificates { + root_lst.extend(crt_lst.iter().map(|v| v.to_owned())); + } + } + crate::endpoint::Endpoint::new( + &self.name, + &self.url, + self.tos_agreed, + &limits, + root_lst.as_slice(), + ) + } } #[derive(Clone, Deserialize)] #[serde(deny_unknown_fields)] pub struct RateLimit { - pub name: String, - pub number: usize, - pub period: String, + pub name: String, + pub number: usize, + pub period: String, } #[derive(Deserialize)] #[serde(deny_unknown_fields)] pub struct Hook { - pub allow_failure: Option, - pub args: Option>, - pub cmd: String, - pub name: String, - pub stderr: Option, - pub stdin: Option, - pub stdin_str: Option, - pub stdout: Option, - #[serde(rename = "type")] - pub hook_type: Vec, + pub allow_failure: Option, + pub args: Option>, + pub cmd: String, + pub name: String, + pub stderr: Option, + pub stdin: Option, + pub stdin_str: Option, + pub stdout: Option, + #[serde(rename = "type")] + pub hook_type: Vec, } #[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum HookType { - FilePreCreate, - FilePostCreate, - FilePreEdit, - FilePostEdit, - #[serde(rename = "challenge-http-01")] - ChallengeHttp01, - #[serde(rename = "challenge-http-01-clean")] - ChallengeHttp01Clean, - #[serde(rename = "challenge-dns-01")] - ChallengeDns01, - #[serde(rename = "challenge-dns-01-clean")] - ChallengeDns01Clean, - #[serde(rename = "challenge-tls-alpn-01")] - ChallengeTlsAlpn01, - #[serde(rename = "challenge-tls-alpn-01-clean")] - ChallengeTlsAlpn01Clean, - PostOperation, + FilePreCreate, + FilePostCreate, + FilePreEdit, + FilePostEdit, + #[serde(rename = "challenge-http-01")] + ChallengeHttp01, + #[serde(rename = "challenge-http-01-clean")] + ChallengeHttp01Clean, + #[serde(rename = "challenge-dns-01")] + ChallengeDns01, + #[serde(rename = "challenge-dns-01-clean")] + ChallengeDns01Clean, + #[serde(rename = "challenge-tls-alpn-01")] + ChallengeTlsAlpn01, + #[serde(rename = "challenge-tls-alpn-01-clean")] + ChallengeTlsAlpn01Clean, + PostOperation, } #[derive(Deserialize)] #[serde(deny_unknown_fields)] pub struct Group { - pub hooks: Vec, - pub name: String, + pub hooks: Vec, + pub name: String, } #[derive(Clone, Debug, Deserialize)] #[serde(deny_unknown_fields)] pub struct ExternalAccount { - pub identifier: String, - pub key: String, - pub signature_algorithm: Option, + pub identifier: String, + pub key: String, + pub signature_algorithm: Option, } impl ExternalAccount { - pub fn to_generic(&self) -> Result { - let signature_algorithm = match &self.signature_algorithm { - Some(a) => a.parse()?, - None => crate::DEFAULT_EXTERNAL_ACCOUNT_JWA, - }; - match signature_algorithm { - JwsSignatureAlgorithm::Hs256 - | JwsSignatureAlgorithm::Hs384 - | JwsSignatureAlgorithm::Hs512 => {} - _ => { - return Err(format!( - "{}: invalid signature algorithm for external account binding", - signature_algorithm - ) - .into()); - } - }; - Ok(crate::account::ExternalAccount { - identifier: self.identifier.to_owned(), - key: b64_decode(&self.key)?, - signature_algorithm, - }) - } + pub fn to_generic(&self) -> Result { + let signature_algorithm = match &self.signature_algorithm { + Some(a) => a.parse()?, + None => crate::DEFAULT_EXTERNAL_ACCOUNT_JWA, + }; + match signature_algorithm { + JwsSignatureAlgorithm::Hs256 + | JwsSignatureAlgorithm::Hs384 + | JwsSignatureAlgorithm::Hs512 => {} + _ => { + return Err(format!( + "{}: invalid signature algorithm for external account binding", + signature_algorithm + ) + .into()); + } + }; + Ok(crate::account::ExternalAccount { + identifier: self.identifier.to_owned(), + key: b64_decode(&self.key)?, + signature_algorithm, + }) + } } #[derive(Clone, Debug, Deserialize)] #[serde(deny_unknown_fields)] pub struct Account { - pub contacts: Vec, - #[serde(default)] - pub env: HashMap, - pub external_account: Option, - pub hooks: Option>, - pub key_type: Option, - pub name: String, - pub signature_algorithm: Option, + pub contacts: Vec, + #[serde(default)] + pub env: HashMap, + pub external_account: Option, + pub hooks: Option>, + pub key_type: Option, + pub name: String, + pub signature_algorithm: Option, } impl Account { - pub fn get_hooks(&self, cnf: &Config) -> Result, Error> { - let lst = match &self.hooks { - Some(h) => { - let mut res = vec![]; - for name in h.iter() { - let mut h = cnf.get_hook(name)?; - res.append(&mut h); - } - res - } - None => vec![], - }; - Ok(lst) - } - - pub fn to_generic(&self, file_manager: &FileManager) -> Result { - let contacts: Vec<(String, String)> = self - .contacts - .iter() - .map(|e| (e.get_type(), e.get_value())) - .collect(); - let external_account = match &self.external_account { - Some(a) => Some(a.to_generic()?), - None => None, - }; - crate::account::Account::load( - file_manager, - &self.name, - &contacts, - &self.key_type, - &self.signature_algorithm, - &external_account, - ) - } + pub fn get_hooks(&self, cnf: &Config) -> Result, Error> { + let lst = match &self.hooks { + Some(h) => { + let mut res = vec![]; + for name in h.iter() { + let mut h = cnf.get_hook(name)?; + res.append(&mut h); + } + res + } + None => vec![], + }; + Ok(lst) + } + + pub fn to_generic(&self, file_manager: &FileManager) -> Result { + let contacts: Vec<(String, String)> = self + .contacts + .iter() + .map(|e| (e.get_type(), e.get_value())) + .collect(); + let external_account = match &self.external_account { + Some(a) => Some(a.to_generic()?), + None => None, + }; + crate::account::Account::load( + file_manager, + &self.name, + &contacts, + &self.key_type, + &self.signature_algorithm, + &external_account, + ) + } } #[derive(Clone, Debug, Deserialize)] #[serde(deny_unknown_fields)] pub struct AccountContact { - pub mailto: String, + pub mailto: String, } impl AccountContact { - pub fn get_type(&self) -> String { - "mailto".to_string() - } + pub fn get_type(&self) -> String { + "mailto".to_string() + } - pub fn get_value(&self) -> String { - self.mailto.clone() - } + pub fn get_value(&self) -> String { + self.mailto.clone() + } } #[derive(Deserialize)] #[serde(deny_unknown_fields)] pub struct Certificate { - pub account: String, - pub csr_digest: Option, - pub directory: Option, - pub endpoint: String, - #[serde(default)] - pub env: HashMap, - pub file_name_format: Option, - pub hooks: Vec, - pub identifiers: Vec, - pub key_type: Option, - pub kp_reuse: Option, - pub name: Option, - pub renew_delay: Option, - #[serde(default)] - pub subject_attributes: SubjectAttributes, + pub account: String, + pub csr_digest: Option, + pub directory: Option, + pub endpoint: String, + #[serde(default)] + pub env: HashMap, + pub file_name_format: Option, + pub hooks: Vec, + pub identifiers: Vec, + pub key_type: Option, + pub kp_reuse: Option, + pub name: Option, + pub renew_delay: Option, + #[serde(default)] + pub subject_attributes: SubjectAttributes, } impl Certificate { - pub fn get_key_type(&self) -> Result { - match &self.key_type { - Some(a) => a.parse(), - None => Ok(crate::DEFAULT_CERT_KEY_TYPE), - } - } - - pub fn get_csr_digest(&self) -> Result { - match &self.csr_digest { - Some(d) => d.parse(), - None => Ok(crate::DEFAULT_CSR_DIGEST), - } - } - - pub fn get_identifiers(&self) -> Result, Error> { - let mut ret = vec![]; - for id in self.identifiers.iter() { - ret.push(id.to_generic()?); - } - Ok(ret) - } - - pub fn get_kp_reuse(&self) -> bool { - match self.kp_reuse { - Some(b) => b, - None => crate::DEFAULT_KP_REUSE, - } - } - - pub fn get_crt_name(&self) -> Result { - let name = match &self.name { - Some(n) => n.to_string(), - None => { - let id = self - .identifiers - .first() - .ok_or_else(|| Error::from("certificate has no identifiers"))?; - id.to_string() - } - }; - let name = name.replace(['*', ':', '/'], "_"); - Ok(name) - } - - pub fn get_crt_name_format(&self, cnf: &Config) -> Result { - match &self.file_name_format { - Some(n) => Ok(n.to_string()), - None => { - let ep = self.do_get_endpoint(cnf)?; - Ok(ep.get_crt_name_format(cnf)) - } - } - } - - pub fn get_crt_dir(&self, cnf: &Config) -> String { - let crt_directory = match &self.directory { - Some(d) => d, - None => match &cnf.global { - Some(g) => match &g.certificates_directory { - Some(d) => d, - None => crate::DEFAULT_CERT_DIR, - }, - None => crate::DEFAULT_CERT_DIR, - }, - }; - crt_directory.to_string() - } - - fn do_get_endpoint(&self, cnf: &Config) -> Result { - for endpoint in cnf.endpoint.iter() { - if endpoint.name == self.endpoint { - return Ok(endpoint.clone()); - } - } - Err(format!("{}: unknown endpoint", self.endpoint).into()) - } - - pub fn get_endpoint( - &self, - cnf: &Config, - root_certs: &[&str], - ) -> Result { - let endpoint = self.do_get_endpoint(cnf)?; - endpoint.to_generic(cnf, root_certs) - } - - pub fn get_hooks(&self, cnf: &Config) -> Result, Error> { - let mut res = vec![]; - for name in self.hooks.iter() { - let mut h = cnf.get_hook(name)?; - res.append(&mut h); - } - Ok(res) - } - - pub fn get_renew_delay(&self, cnf: &Config) -> Result { - match &self.renew_delay { - Some(d) => parse_duration(d), - None => { - let endpoint = self.do_get_endpoint(cnf)?; - endpoint.get_renew_delay(cnf) - } - } - } + pub fn get_key_type(&self) -> Result { + match &self.key_type { + Some(a) => a.parse(), + None => Ok(crate::DEFAULT_CERT_KEY_TYPE), + } + } + + pub fn get_csr_digest(&self) -> Result { + match &self.csr_digest { + Some(d) => d.parse(), + None => Ok(crate::DEFAULT_CSR_DIGEST), + } + } + + pub fn get_identifiers(&self) -> Result, Error> { + let mut ret = vec![]; + for id in self.identifiers.iter() { + ret.push(id.to_generic()?); + } + Ok(ret) + } + + pub fn get_kp_reuse(&self) -> bool { + match self.kp_reuse { + Some(b) => b, + None => crate::DEFAULT_KP_REUSE, + } + } + + pub fn get_crt_name(&self) -> Result { + let name = match &self.name { + Some(n) => n.to_string(), + None => { + let id = self + .identifiers + .first() + .ok_or_else(|| Error::from("certificate has no identifiers"))?; + id.to_string() + } + }; + let name = name.replace(['*', ':', '/'], "_"); + Ok(name) + } + + pub fn get_crt_name_format(&self, cnf: &Config) -> Result { + match &self.file_name_format { + Some(n) => Ok(n.to_string()), + None => { + let ep = self.do_get_endpoint(cnf)?; + Ok(ep.get_crt_name_format(cnf)) + } + } + } + + pub fn get_crt_dir(&self, cnf: &Config) -> String { + let crt_directory = match &self.directory { + Some(d) => d, + None => match &cnf.global { + Some(g) => match &g.certificates_directory { + Some(d) => d, + None => crate::DEFAULT_CERT_DIR, + }, + None => crate::DEFAULT_CERT_DIR, + }, + }; + crt_directory.to_string() + } + + fn do_get_endpoint(&self, cnf: &Config) -> Result { + for endpoint in cnf.endpoint.iter() { + if endpoint.name == self.endpoint { + return Ok(endpoint.clone()); + } + } + Err(format!("{}: unknown endpoint", self.endpoint).into()) + } + + pub fn get_endpoint( + &self, + cnf: &Config, + root_certs: &[&str], + ) -> Result { + let endpoint = self.do_get_endpoint(cnf)?; + endpoint.to_generic(cnf, root_certs) + } + + pub fn get_hooks(&self, cnf: &Config) -> Result, Error> { + let mut res = vec![]; + for name in self.hooks.iter() { + let mut h = cnf.get_hook(name)?; + res.append(&mut h); + } + Ok(res) + } + + pub fn get_renew_delay(&self, cnf: &Config) -> Result { + match &self.renew_delay { + Some(d) => parse_duration(d), + None => { + let endpoint = self.do_get_endpoint(cnf)?; + endpoint.get_renew_delay(cnf) + } + } + } } #[derive(Clone, Debug, Deserialize)] #[serde(remote = "Self")] #[serde(deny_unknown_fields)] pub struct Identifier { - pub challenge: String, - pub dns: Option, - #[serde(default)] - pub env: HashMap, - pub ip: Option, + pub challenge: String, + pub dns: Option, + #[serde(default)] + pub env: HashMap, + pub ip: Option, } impl<'de> Deserialize<'de> for Identifier { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let unchecked = Identifier::deserialize(deserializer)?; - let filled_nb: u8 = [unchecked.dns.is_some(), unchecked.ip.is_some()] - .iter() - .copied() - .map(u8::from) - .sum(); - if filled_nb != 1 { - return Err(de::Error::custom( - "one and only one of `dns` or `ip` must be specified", - )); - } - Ok(unchecked) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let unchecked = Identifier::deserialize(deserializer)?; + let filled_nb: u8 = [unchecked.dns.is_some(), unchecked.ip.is_some()] + .iter() + .copied() + .map(u8::from) + .sum(); + if filled_nb != 1 { + return Err(de::Error::custom( + "one and only one of `dns` or `ip` must be specified", + )); + } + Ok(unchecked) + } } impl fmt::Display for Identifier { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = String::new(); - let msg = self.dns.as_ref().or(self.ip.as_ref()).unwrap_or(&s); - write!(f, "{}", msg) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = String::new(); + let msg = self.dns.as_ref().or(self.ip.as_ref()).unwrap_or(&s); + write!(f, "{}", msg) + } } impl Identifier { - fn to_generic(&self) -> Result { - let (t, v) = match &self.dns { - Some(d) => (IdentifierType::Dns, d), - None => match &self.ip { - Some(ip) => (IdentifierType::Ip, ip), - None => { - return Err("no identifier found".into()); - } - }, - }; - crate::identifier::Identifier::new(t, v, &self.challenge, &self.env) - } + fn to_generic(&self) -> Result { + let (t, v) = match &self.dns { + Some(d) => (IdentifierType::Dns, d), + None => match &self.ip { + Some(ip) => (IdentifierType::Ip, ip), + None => { + return Err("no identifier found".into()); + } + }, + }; + crate::identifier::Identifier::new(t, v, &self.challenge, &self.env) + } } #[derive(Clone, Debug, Default, Deserialize)] #[serde(deny_unknown_fields)] pub struct SubjectAttributes { - pub country_name: Option, - pub generation_qualifier: Option, - pub given_name: Option, - pub initials: Option, - pub locality_name: Option, - pub name: Option, - pub organization_name: Option, - pub organizational_unit_name: Option, - pub pkcs9_email_address: Option, - pub postal_address: Option, - pub postal_code: Option, - pub state_or_province_name: Option, - pub street: Option, - pub surname: Option, - pub title: Option, + pub country_name: Option, + pub generation_qualifier: Option, + pub given_name: Option, + pub initials: Option, + pub locality_name: Option, + pub name: Option, + pub organization_name: Option, + pub organizational_unit_name: Option, + pub pkcs9_email_address: Option, + pub postal_address: Option, + pub postal_code: Option, + pub state_or_province_name: Option, + pub street: Option, + pub surname: Option, + pub title: Option, } impl SubjectAttributes { - pub fn to_generic(&self) -> HashMap { - let mut ret = HashMap::new(); - push_subject_attr!(ret, self.country_name, CountryName); - push_subject_attr!(ret, self.generation_qualifier, GenerationQualifier); - push_subject_attr!(ret, self.given_name, GivenName); - push_subject_attr!(ret, self.initials, Initials); - push_subject_attr!(ret, self.locality_name, LocalityName); - push_subject_attr!(ret, self.name, Name); - push_subject_attr!(ret, self.organization_name, OrganizationName); - push_subject_attr!(ret, self.organizational_unit_name, OrganizationalUnitName); - push_subject_attr!(ret, self.pkcs9_email_address, Pkcs9EmailAddress); - push_subject_attr!(ret, self.postal_address, PostalAddress); - push_subject_attr!(ret, self.postal_code, PostalCode); - push_subject_attr!(ret, self.state_or_province_name, StateOrProvinceName); - push_subject_attr!(ret, self.street, Street); - push_subject_attr!(ret, self.surname, Surname); - push_subject_attr!(ret, self.title, Title); - ret - } + pub fn to_generic(&self) -> HashMap { + let mut ret = HashMap::new(); + push_subject_attr!(ret, self.country_name, CountryName); + push_subject_attr!(ret, self.generation_qualifier, GenerationQualifier); + push_subject_attr!(ret, self.given_name, GivenName); + push_subject_attr!(ret, self.initials, Initials); + push_subject_attr!(ret, self.locality_name, LocalityName); + push_subject_attr!(ret, self.name, Name); + push_subject_attr!(ret, self.organization_name, OrganizationName); + push_subject_attr!(ret, self.organizational_unit_name, OrganizationalUnitName); + push_subject_attr!(ret, self.pkcs9_email_address, Pkcs9EmailAddress); + push_subject_attr!(ret, self.postal_address, PostalAddress); + push_subject_attr!(ret, self.postal_code, PostalCode); + push_subject_attr!(ret, self.state_or_province_name, StateOrProvinceName); + push_subject_attr!(ret, self.street, Street); + push_subject_attr!(ret, self.surname, Surname); + push_subject_attr!(ret, self.title, Title); + ret + } } fn create_dir(path: &str) -> Result<(), Error> { - if Path::new(path).is_dir() { - Ok(()) - } else { - fs::create_dir_all(path)?; - Ok(()) - } + if Path::new(path).is_dir() { + Ok(()) + } else { + fs::create_dir_all(path)?; + Ok(()) + } } fn init_directories(config: &Config) -> Result<(), Error> { - create_dir(&config.get_account_dir())?; - for crt in config.certificate.iter() { - create_dir(&crt.get_crt_dir(config))?; - } - Ok(()) + create_dir(&config.get_account_dir())?; + for crt in config.certificate.iter() { + create_dir(&crt.get_crt_dir(config))?; + } + Ok(()) } fn get_cnf_path(from: &Path, file: &str) -> Result, Error> { - let mut path = from.to_path_buf().canonicalize()?; - path.pop(); - path.push(file); - let err = format!("{:?}: invalid UTF-8 path", path); - let raw_path = path.to_str().ok_or(err)?; - let g = glob(raw_path)? - .filter_map(Result::ok) - .collect::>(); - if g.is_empty() { - log::warn!( - "pattern `{}` (expanded as `{}`): no matching configuration file found", - file, - raw_path - ); - } - Ok(g) + let mut path = from.to_path_buf().canonicalize()?; + path.pop(); + path.push(file); + let err = format!("{:?}: invalid UTF-8 path", path); + let raw_path = path.to_str().ok_or(err)?; + let g = glob(raw_path)? + .filter_map(Result::ok) + .collect::>(); + if g.is_empty() { + log::warn!( + "pattern `{}` (expanded as `{}`): no matching configuration file found", + file, + raw_path + ); + } + Ok(g) } fn read_cnf(path: &Path, loaded_files: &mut BTreeSet) -> Result { - let path = path - .canonicalize() - .map_err(|e| Error::from(e).prefix(&path.display().to_string()))?; - if loaded_files.contains(&path) { - info!("{}: configuration file already loaded", path.display()); - return Ok(Config::default()); - } - loaded_files.insert(path.clone()); - info!("{}: loading configuration file", &path.display()); - let mut file = - File::open(&path).map_err(|e| Error::from(e).prefix(&path.display().to_string()))?; - let mut contents = String::new(); - file.read_to_string(&mut contents) - .map_err(|e| Error::from(e).prefix(&path.display().to_string()))?; - let mut config: Config = toml::from_str(&contents) - .map_err(|e| Error::from(e).prefix(&path.display().to_string()))?; - for cnf_name in config.include.iter() { - for cnf_path in get_cnf_path(&path, cnf_name)? { - let mut add_cnf = read_cnf(&cnf_path, loaded_files)?; - config.endpoint.append(&mut add_cnf.endpoint); - config.rate_limit.append(&mut add_cnf.rate_limit); - config.hook.append(&mut add_cnf.hook); - config.group.append(&mut add_cnf.group); - config.account.append(&mut add_cnf.account); - config.certificate.append(&mut add_cnf.certificate); - if config.global.is_none() { - config.global = add_cnf.global; - } else if let Some(new_glob) = add_cnf.global { - let mut tmp_glob = config.global.clone().unwrap(); - set_cfg_attr!(tmp_glob.accounts_directory, new_glob.accounts_directory); - set_cfg_attr!( - tmp_glob.certificates_directory, - new_glob.certificates_directory - ); - set_cfg_attr!(tmp_glob.cert_file_mode, new_glob.cert_file_mode); - set_cfg_attr!(tmp_glob.cert_file_user, new_glob.cert_file_user); - set_cfg_attr!(tmp_glob.cert_file_group, new_glob.cert_file_group); - set_cfg_attr!(tmp_glob.pk_file_mode, new_glob.pk_file_mode); - set_cfg_attr!(tmp_glob.pk_file_user, new_glob.pk_file_user); - set_cfg_attr!(tmp_glob.pk_file_group, new_glob.pk_file_group); - config.global = Some(tmp_glob); - } - } - } - Ok(config) + let path = path + .canonicalize() + .map_err(|e| Error::from(e).prefix(&path.display().to_string()))?; + if loaded_files.contains(&path) { + info!("{}: configuration file already loaded", path.display()); + return Ok(Config::default()); + } + loaded_files.insert(path.clone()); + info!("{}: loading configuration file", &path.display()); + let mut file = + File::open(&path).map_err(|e| Error::from(e).prefix(&path.display().to_string()))?; + let mut contents = String::new(); + file.read_to_string(&mut contents) + .map_err(|e| Error::from(e).prefix(&path.display().to_string()))?; + let mut config: Config = toml::from_str(&contents) + .map_err(|e| Error::from(e).prefix(&path.display().to_string()))?; + for cnf_name in config.include.iter() { + for cnf_path in get_cnf_path(&path, cnf_name)? { + let mut add_cnf = read_cnf(&cnf_path, loaded_files)?; + config.endpoint.append(&mut add_cnf.endpoint); + config.rate_limit.append(&mut add_cnf.rate_limit); + config.hook.append(&mut add_cnf.hook); + config.group.append(&mut add_cnf.group); + config.account.append(&mut add_cnf.account); + config.certificate.append(&mut add_cnf.certificate); + if config.global.is_none() { + config.global = add_cnf.global; + } else if let Some(new_glob) = add_cnf.global { + let mut tmp_glob = config.global.clone().unwrap(); + set_cfg_attr!(tmp_glob.accounts_directory, new_glob.accounts_directory); + set_cfg_attr!( + tmp_glob.certificates_directory, + new_glob.certificates_directory + ); + set_cfg_attr!(tmp_glob.cert_file_mode, new_glob.cert_file_mode); + set_cfg_attr!(tmp_glob.cert_file_user, new_glob.cert_file_user); + set_cfg_attr!(tmp_glob.cert_file_group, new_glob.cert_file_group); + set_cfg_attr!(tmp_glob.pk_file_mode, new_glob.pk_file_mode); + set_cfg_attr!(tmp_glob.pk_file_user, new_glob.pk_file_user); + set_cfg_attr!(tmp_glob.pk_file_group, new_glob.pk_file_group); + config.global = Some(tmp_glob); + } + } + } + Ok(config) } fn dispatch_global_env_vars(config: &mut Config) { - if let Some(glob) = &config.global { - if !glob.env.is_empty() { - for mut cert in config.certificate.iter_mut() { - let mut new_vars = glob.env.clone(); - for (k, v) in cert.env.iter() { - new_vars.insert(k.to_string(), v.to_string()); - } - cert.env = new_vars; - } - } - } + if let Some(glob) = &config.global { + if !glob.env.is_empty() { + for mut cert in config.certificate.iter_mut() { + let mut new_vars = glob.env.clone(); + for (k, v) in cert.env.iter() { + new_vars.insert(k.to_string(), v.to_string()); + } + cert.env = new_vars; + } + } + } } pub fn from_file(file_name: &str) -> Result { - let path = PathBuf::from(file_name); - let mut loaded_files = BTreeSet::new(); - let mut config = read_cnf(&path, &mut loaded_files)?; - dispatch_global_env_vars(&mut config); - init_directories(&config)?; - Ok(config) + let path = PathBuf::from(file_name); + let mut loaded_files = BTreeSet::new(); + let mut config = read_cnf(&path, &mut loaded_files)?; + dispatch_global_env_vars(&mut config); + init_directories(&config)?; + Ok(config) } diff --git a/acmed/src/duration.rs b/acmed/src/duration.rs index fd3d637..983c267 100644 --- a/acmed/src/duration.rs +++ b/acmed/src/duration.rs @@ -7,45 +7,45 @@ use nom::IResult; use std::time::Duration; fn is_duration_chr(c: char) -> bool { - c == 's' || c == 'm' || c == 'h' || c == 'd' || c == 'w' + c == 's' || c == 'm' || c == 'h' || c == 'd' || c == 'w' } fn get_multiplicator(input: &str) -> IResult<&str, u64> { - let (input, nb) = take_while_m_n(1, 1, is_duration_chr)(input)?; - let mult = match nb.chars().next() { - Some('s') => 1, - Some('m') => 60, - Some('h') => 3_600, - Some('d') => 86_400, - Some('w') => 604_800, - _ => 0, - }; - Ok((input, mult)) + let (input, nb) = take_while_m_n(1, 1, is_duration_chr)(input)?; + let mult = match nb.chars().next() { + Some('s') => 1, + Some('m') => 60, + Some('h') => 3_600, + Some('d') => 86_400, + Some('w') => 604_800, + _ => 0, + }; + Ok((input, mult)) } fn get_duration_part(input: &str) -> IResult<&str, Duration> { - let (input, nb) = map_res(digit1, |s: &str| s.parse::())(input)?; - let (input, mult) = get_multiplicator(input)?; - Ok((input, Duration::from_secs(nb * mult))) + let (input, nb) = map_res(digit1, |s: &str| s.parse::())(input)?; + let (input, mult) = get_multiplicator(input)?; + Ok((input, Duration::from_secs(nb * mult))) } fn get_duration(input: &str) -> IResult<&str, Duration> { - fold_many1( - get_duration_part, - || Duration::new(0, 0), - |mut acc: Duration, item| { - acc += item; - acc - }, - )(input) + fold_many1( + get_duration_part, + || Duration::new(0, 0), + |mut acc: Duration, item| { + acc += item; + acc + }, + )(input) } pub fn parse_duration(input: &str) -> Result { - match get_duration(input) { - Ok((r, d)) => match r.len() { - 0 => Ok(d), - _ => Err(format!("{}: invalid duration", input).into()), - }, - Err(_) => Err(format!("{}: invalid duration", input).into()), - } + match get_duration(input) { + Ok((r, d)) => match r.len() { + 0 => Ok(d), + _ => Err(format!("{}: invalid duration", input).into()), + }, + Err(_) => Err(format!("{}: invalid duration", input).into()), + } } diff --git a/acmed/src/endpoint.rs b/acmed/src/endpoint.rs index 99c0442..d9ec144 100644 --- a/acmed/src/endpoint.rs +++ b/acmed/src/endpoint.rs @@ -7,117 +7,117 @@ use std::time::{Duration, Instant}; #[derive(Clone, Debug)] pub struct Endpoint { - pub name: String, - pub url: String, - pub tos_agreed: bool, - pub nonce: Option, - pub rl: RateLimit, - pub dir: Directory, - pub root_certificates: Vec, + pub name: String, + pub url: String, + pub tos_agreed: bool, + pub nonce: Option, + pub rl: RateLimit, + pub dir: Directory, + pub root_certificates: Vec, } impl Endpoint { - pub fn new( - name: &str, - url: &str, - tos_agreed: bool, - limits: &[(usize, String)], - root_certs: &[String], - ) -> Result { - Ok(Self { - name: name.to_string(), - url: url.to_string(), - tos_agreed, - nonce: None, - rl: RateLimit::new(limits)?, - dir: Directory { - meta: None, - new_nonce: String::new(), - new_account: String::new(), - new_order: String::new(), - new_authz: None, - revoke_cert: String::new(), - key_change: String::new(), - }, - root_certificates: root_certs.to_vec(), - }) - } + pub fn new( + name: &str, + url: &str, + tos_agreed: bool, + limits: &[(usize, String)], + root_certs: &[String], + ) -> Result { + Ok(Self { + name: name.to_string(), + url: url.to_string(), + tos_agreed, + nonce: None, + rl: RateLimit::new(limits)?, + dir: Directory { + meta: None, + new_nonce: String::new(), + new_account: String::new(), + new_order: String::new(), + new_authz: None, + revoke_cert: String::new(), + key_change: String::new(), + }, + root_certificates: root_certs.to_vec(), + }) + } } #[derive(Clone, Debug)] pub struct RateLimit { - limits: Vec<(usize, Duration)>, - query_log: Vec, + limits: Vec<(usize, Duration)>, + query_log: Vec, } impl RateLimit { - pub fn new(raw_limits: &[(usize, String)]) -> Result { - let mut limits = vec![]; - for (nb, raw_duration) in raw_limits.iter() { - let parsed_duration = parse_duration(raw_duration)?; - limits.push((*nb, parsed_duration)); - } - limits.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); - limits.reverse(); - Ok(Self { - limits, - query_log: vec![], - }) - } + pub fn new(raw_limits: &[(usize, String)]) -> Result { + let mut limits = vec![]; + for (nb, raw_duration) in raw_limits.iter() { + let parsed_duration = parse_duration(raw_duration)?; + limits.push((*nb, parsed_duration)); + } + limits.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); + limits.reverse(); + Ok(Self { + limits, + query_log: vec![], + }) + } - pub fn block_until_allowed(&mut self) { - if self.limits.is_empty() { - return; - } - let sleep_duration = self.get_sleep_duration(); - loop { - self.prune_log(); - if self.request_allowed() { - self.query_log.push(Instant::now()); - return; - } - // TODO: find a better sleep duration - thread::sleep(sleep_duration); - } - } + pub fn block_until_allowed(&mut self) { + if self.limits.is_empty() { + return; + } + let sleep_duration = self.get_sleep_duration(); + loop { + self.prune_log(); + if self.request_allowed() { + self.query_log.push(Instant::now()); + return; + } + // TODO: find a better sleep duration + thread::sleep(sleep_duration); + } + } - fn get_sleep_duration(&self) -> Duration { - let (nb_req, min_duration) = match self.limits.last() { - Some((n, d)) => (*n as u64, *d), - None => { - return Duration::from_millis(0); - } - }; - let nb_mili = match min_duration.as_secs() { - 0 | 1 => crate::MIN_RATE_LIMIT_SLEEP_MILISEC, - n => { - let a = n * 200 / nb_req; - let a = cmp::min(a, crate::MAX_RATE_LIMIT_SLEEP_MILISEC); - cmp::max(a, crate::MIN_RATE_LIMIT_SLEEP_MILISEC) - } - }; - Duration::from_millis(nb_mili) - } + fn get_sleep_duration(&self) -> Duration { + let (nb_req, min_duration) = match self.limits.last() { + Some((n, d)) => (*n as u64, *d), + None => { + return Duration::from_millis(0); + } + }; + let nb_mili = match min_duration.as_secs() { + 0 | 1 => crate::MIN_RATE_LIMIT_SLEEP_MILISEC, + n => { + let a = n * 200 / nb_req; + let a = cmp::min(a, crate::MAX_RATE_LIMIT_SLEEP_MILISEC); + cmp::max(a, crate::MIN_RATE_LIMIT_SLEEP_MILISEC) + } + }; + Duration::from_millis(nb_mili) + } - fn request_allowed(&self) -> bool { - for (max_allowed, duration) in self.limits.iter() { - let max_date = Instant::now() - *duration; - let nb_req = self - .query_log - .iter() - .filter(move |x| **x > max_date) - .count(); - if nb_req >= *max_allowed { - return false; - } - } - true - } + fn request_allowed(&self) -> bool { + for (max_allowed, duration) in self.limits.iter() { + let max_date = Instant::now() - *duration; + let nb_req = self + .query_log + .iter() + .filter(move |x| **x > max_date) + .count(); + if nb_req >= *max_allowed { + return false; + } + } + true + } - fn prune_log(&mut self) { - if let Some((_, max_limit)) = self.limits.first() { - let prune_date = Instant::now() - *max_limit; - self.query_log.retain(move |&d| d > prune_date); - } - } + fn prune_log(&mut self) { + if let Some((_, max_limit)) = self.limits.first() { + let prune_date = Instant::now() - *max_limit; + self.query_log.retain(move |&d| d > prune_date); + } + } } diff --git a/acmed/src/hooks.rs b/acmed/src/hooks.rs index f42df9c..26de269 100644 --- a/acmed/src/hooks.rs +++ b/acmed/src/hooks.rs @@ -13,201 +13,201 @@ use std::process::{Command, Stdio}; use std::{env, fmt}; pub trait HookEnvData { - fn set_env(&mut self, env: &HashMap); - fn get_env(&self) -> Iter; + fn set_env(&mut self, env: &HashMap); + fn get_env(&self) -> Iter; } fn deref(t: (&F, &G)) -> (F, G) where - F: Clone, - G: Clone, + F: Clone, + G: Clone, { - ((*(t.0)).to_owned(), (*(t.1)).to_owned()) + ((*(t.0)).to_owned(), (*(t.1)).to_owned()) } macro_rules! imple_hook_data_env { - ($t: ty) => { - impl HookEnvData for $t { - fn set_env(&mut self, env: &HashMap) { - for (key, value) in env::vars().chain(env.iter().map(deref)) { - self.env.insert(key, value); - } - } - - fn get_env(&self) -> Iter { - self.env.iter() - } - } - }; + ($t: ty) => { + impl HookEnvData for $t { + fn set_env(&mut self, env: &HashMap) { + for (key, value) in env::vars().chain(env.iter().map(deref)) { + self.env.insert(key, value); + } + } + + fn get_env(&self) -> Iter { + self.env.iter() + } + } + }; } #[derive(Clone, Serialize)] pub struct PostOperationHookData { - pub identifiers: Vec, - pub key_type: String, - pub status: String, - pub is_success: bool, - pub env: HashMap, + pub identifiers: Vec, + pub key_type: String, + pub status: String, + pub is_success: bool, + pub env: HashMap, } imple_hook_data_env!(PostOperationHookData); #[derive(Clone, Serialize)] pub struct ChallengeHookData { - pub identifier: String, - pub identifier_tls_alpn: String, - pub challenge: String, - pub file_name: String, - pub proof: String, - pub is_clean_hook: bool, - pub env: HashMap, + pub identifier: String, + pub identifier_tls_alpn: String, + pub challenge: String, + pub file_name: String, + pub proof: String, + pub is_clean_hook: bool, + pub env: HashMap, } imple_hook_data_env!(ChallengeHookData); #[derive(Clone, Serialize)] pub struct FileStorageHookData { - // TODO: add the current operation (create/edit) - pub file_name: String, - pub file_directory: String, - pub file_path: PathBuf, - pub env: HashMap, + // TODO: add the current operation (create/edit) + pub file_name: String, + pub file_directory: String, + pub file_path: PathBuf, + pub env: HashMap, } imple_hook_data_env!(FileStorageHookData); #[derive(Clone, Debug)] pub enum HookStdin { - File(String), - Str(String), - None, + File(String), + Str(String), + None, } #[derive(Clone, Debug)] pub struct Hook { - pub name: String, - pub hook_type: HashSet, - pub cmd: String, - pub args: Option>, - pub stdin: HookStdin, - pub stdout: Option, - pub stderr: Option, - pub allow_failure: bool, + pub name: String, + pub hook_type: HashSet, + pub cmd: String, + pub args: Option>, + pub stdin: HookStdin, + pub stdout: Option, + pub stderr: Option, + pub allow_failure: bool, } impl fmt::Display for Hook { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.name) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.name) + } } macro_rules! get_hook_output { - ($logger: expr, $out: expr, $data: expr, $hook_name: expr, $out_name: expr) => {{ - match $out { - Some(path) => { - let path = render_template(path, $data)?; - $logger.trace(&format!( - "hook \"{}\": {}: {}", - $hook_name, $out_name, &path - )); - let file = File::create(&path)?; - Stdio::from(file) - } - None => Stdio::null(), - } - }}; + ($logger: expr, $out: expr, $data: expr, $hook_name: expr, $out_name: expr) => {{ + match $out { + Some(path) => { + let path = render_template(path, $data)?; + $logger.trace(&format!( + "hook \"{}\": {}: {}", + $hook_name, $out_name, &path + )); + let file = File::create(&path)?; + Stdio::from(file) + } + None => Stdio::null(), + } + }}; } fn call_single(logger: &L, data: &T, hook: &Hook) -> Result<(), Error> where - L: HasLogger, - T: Clone + HookEnvData + Serialize, + L: HasLogger, + T: Clone + HookEnvData + Serialize, { - logger.debug(&format!("calling hook \"{}\"", hook.name)); - let mut v = vec![]; - let args = match &hook.args { - Some(lst) => { - for fmt in lst.iter() { - let s = render_template(fmt, &data)?; - v.push(s); - } - v.as_slice() - } - None => &[], - }; - logger.trace(&format!("hook \"{}\": cmd: {}", hook.name, hook.cmd)); - logger.trace(&format!("hook \"{}\": args: {:?}", hook.name, args)); - let mut cmd = Command::new(&hook.cmd) - .envs(data.get_env()) - .args(args) - .stdout(get_hook_output!( - logger, - &hook.stdout, - &data, - &hook.name, - "stdout" - )) - .stderr(get_hook_output!( - logger, - &hook.stderr, - &data, - &hook.name, - "stderr" - )) - .stdin(match &hook.stdin { - HookStdin::Str(_) | HookStdin::File(_) => Stdio::piped(), - HookStdin::None => Stdio::null(), - }) - .spawn()?; - match &hook.stdin { - HookStdin::Str(s) => { - let data_in = render_template(s, &data)?; - logger.trace(&format!( - "hook \"{}\": string stdin: {}", - hook.name, &data_in - )); - let stdin = cmd.stdin.as_mut().ok_or("stdin not found")?; - stdin.write_all(data_in.as_bytes())?; - } - HookStdin::File(f) => { - let file_name = render_template(f, &data)?; - logger.trace(&format!( - "hook \"{}\": file stdin: {}", - hook.name, &file_name - )); - let stdin = cmd.stdin.as_mut().ok_or("stdin not found")?; - let file = File::open(&file_name).map_err(|e| Error::from(e).prefix(&file_name))?; - let buf_reader = BufReader::new(file); - for line in buf_reader.lines() { - let line = format!("{}\n", line?); - stdin.write_all(line.as_bytes())?; - } - } - HookStdin::None => {} - } - // TODO: add a timeout - let status = cmd.wait()?; - if !status.success() && !hook.allow_failure { - let msg = match status.code() { - Some(code) => format!("unrecoverable failure: code {}", code).into(), - None => "unrecoverable failure".into(), - }; - return Err(msg); - } - match status.code() { - Some(code) => logger.debug(&format!("hook \"{}\": exited: code {}", hook.name, code)), - None => logger.debug(&format!("hook \"{}\": exited", hook.name)), - }; - Ok(()) + logger.debug(&format!("calling hook \"{}\"", hook.name)); + let mut v = vec![]; + let args = match &hook.args { + Some(lst) => { + for fmt in lst.iter() { + let s = render_template(fmt, &data)?; + v.push(s); + } + v.as_slice() + } + None => &[], + }; + logger.trace(&format!("hook \"{}\": cmd: {}", hook.name, hook.cmd)); + logger.trace(&format!("hook \"{}\": args: {:?}", hook.name, args)); + let mut cmd = Command::new(&hook.cmd) + .envs(data.get_env()) + .args(args) + .stdout(get_hook_output!( + logger, + &hook.stdout, + &data, + &hook.name, + "stdout" + )) + .stderr(get_hook_output!( + logger, + &hook.stderr, + &data, + &hook.name, + "stderr" + )) + .stdin(match &hook.stdin { + HookStdin::Str(_) | HookStdin::File(_) => Stdio::piped(), + HookStdin::None => Stdio::null(), + }) + .spawn()?; + match &hook.stdin { + HookStdin::Str(s) => { + let data_in = render_template(s, &data)?; + logger.trace(&format!( + "hook \"{}\": string stdin: {}", + hook.name, &data_in + )); + let stdin = cmd.stdin.as_mut().ok_or("stdin not found")?; + stdin.write_all(data_in.as_bytes())?; + } + HookStdin::File(f) => { + let file_name = render_template(f, &data)?; + logger.trace(&format!( + "hook \"{}\": file stdin: {}", + hook.name, &file_name + )); + let stdin = cmd.stdin.as_mut().ok_or("stdin not found")?; + let file = File::open(&file_name).map_err(|e| Error::from(e).prefix(&file_name))?; + let buf_reader = BufReader::new(file); + for line in buf_reader.lines() { + let line = format!("{}\n", line?); + stdin.write_all(line.as_bytes())?; + } + } + HookStdin::None => {} + } + // TODO: add a timeout + let status = cmd.wait()?; + if !status.success() && !hook.allow_failure { + let msg = match status.code() { + Some(code) => format!("unrecoverable failure: code {}", code).into(), + None => "unrecoverable failure".into(), + }; + return Err(msg); + } + match status.code() { + Some(code) => logger.debug(&format!("hook \"{}\": exited: code {}", hook.name, code)), + None => logger.debug(&format!("hook \"{}\": exited", hook.name)), + }; + Ok(()) } pub fn call(logger: &L, hooks: &[Hook], data: &T, hook_type: HookType) -> Result<(), Error> where - L: HasLogger, - T: Clone + HookEnvData + Serialize, + L: HasLogger, + T: Clone + HookEnvData + Serialize, { - for hook in hooks.iter().filter(|h| h.hook_type.contains(&hook_type)) { - call_single(logger, data, hook).map_err(|e| e.prefix(&hook.name))?; - } - Ok(()) + for hook in hooks.iter().filter(|h| h.hook_type.contains(&hook_type)) { + call_single(logger, data, hook).map_err(|e| e.prefix(&hook.name))?; + } + Ok(()) } diff --git a/acmed/src/http.rs b/acmed/src/http.rs index 14a8274..7e7eb7a 100644 --- a/acmed/src/http.rs +++ b/acmed/src/http.rs @@ -16,263 +16,263 @@ pub const HEADER_NONCE: &str = "Replay-Nonce"; pub const HEADER_LOCATION: &str = "Location"; pub struct ValidHttpResponse { - headers: attohttpc::header::HeaderMap, - pub body: String, + headers: attohttpc::header::HeaderMap, + pub body: String, } impl ValidHttpResponse { - pub fn get_header(&self, name: &str) -> Option { - match self.headers.get(name) { - Some(r) => match header_to_string(r) { - Ok(h) => Some(h), - Err(_) => None, - }, - None => None, - } - } + pub fn get_header(&self, name: &str) -> Option { + match self.headers.get(name) { + Some(r) => match header_to_string(r) { + Ok(h) => Some(h), + Err(_) => None, + }, + None => None, + } + } - pub fn json(&self) -> Result - where - T: serde::de::DeserializeOwned, - { - serde_json::from_str(&self.body).map_err(Error::from) - } + pub fn json(&self) -> Result + where + T: serde::de::DeserializeOwned, + { + serde_json::from_str(&self.body).map_err(Error::from) + } - fn from_response(response: Response) -> Result { - let (_status, headers, body) = response.split(); - let body = body.text()?; - log::trace!("HTTP response headers: {:?}", headers); - log::trace!("HTTP response body: {}", body); - Ok(ValidHttpResponse { headers, body }) - } + fn from_response(response: Response) -> Result { + let (_status, headers, body) = response.split(); + let body = body.text()?; + log::trace!("HTTP response headers: {:?}", headers); + log::trace!("HTTP response body: {}", body); + Ok(ValidHttpResponse { headers, body }) + } } #[derive(Clone, Debug)] pub enum HttpError { - ApiError(HttpApiError), - GenericError(Error), + ApiError(HttpApiError), + GenericError(Error), } impl HttpError { - pub fn in_err(error: HttpError) -> Error { - match error { - HttpError::ApiError(e) => e.to_string().into(), - HttpError::GenericError(e) => e, - } - } + pub fn in_err(error: HttpError) -> Error { + match error { + HttpError::ApiError(e) => e.to_string().into(), + HttpError::GenericError(e) => e, + } + } - pub fn is_acme_err(&self, acme_error: AcmeError) -> bool { - match self { - HttpError::ApiError(aerr) => aerr.get_acme_type() == acme_error, - HttpError::GenericError(_) => false, - } - } + pub fn is_acme_err(&self, acme_error: AcmeError) -> bool { + match self { + HttpError::ApiError(aerr) => aerr.get_acme_type() == acme_error, + HttpError::GenericError(_) => false, + } + } } impl From for HttpError { - fn from(error: Error) -> Self { - HttpError::GenericError(error) - } + fn from(error: Error) -> Self { + HttpError::GenericError(error) + } } impl From for HttpError { - fn from(error: HttpApiError) -> Self { - HttpError::ApiError(error) - } + fn from(error: HttpApiError) -> Self { + HttpError::ApiError(error) + } } impl From<&str> for HttpError { - fn from(error: &str) -> Self { - HttpError::GenericError(error.into()) - } + fn from(error: &str) -> Self { + HttpError::GenericError(error.into()) + } } impl From for HttpError { - fn from(error: String) -> Self { - HttpError::GenericError(error.into()) - } + fn from(error: String) -> Self { + HttpError::GenericError(error.into()) + } } impl From for HttpError { - fn from(error: attohttpc::Error) -> Self { - HttpError::GenericError(error.into()) - } + fn from(error: attohttpc::Error) -> Self { + HttpError::GenericError(error.into()) + } } fn is_nonce(data: &str) -> bool { - !data.is_empty() - && data - .bytes() - .all(|c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_') + !data.is_empty() + && data + .bytes() + .all(|c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_') } fn new_nonce(endpoint: &mut Endpoint) -> Result<(), HttpError> { - rate_limit(endpoint); - let url = endpoint.dir.new_nonce.clone(); - let _ = get(endpoint, &url)?; - Ok(()) + rate_limit(endpoint); + let url = endpoint.dir.new_nonce.clone(); + let _ = get(endpoint, &url)?; + Ok(()) } fn update_nonce(endpoint: &mut Endpoint, response: &Response) -> Result<(), Error> { - if let Some(nonce) = response.headers().get(HEADER_NONCE) { - let nonce = header_to_string(nonce)?; - if !is_nonce(&nonce) { - let msg = format!("{}: invalid nonce.", &nonce); - return Err(msg.into()); - } - endpoint.nonce = Some(nonce); - } - Ok(()) + if let Some(nonce) = response.headers().get(HEADER_NONCE) { + let nonce = header_to_string(nonce)?; + if !is_nonce(&nonce) { + let msg = format!("{}: invalid nonce.", &nonce); + return Err(msg.into()); + } + endpoint.nonce = Some(nonce); + } + Ok(()) } fn check_status(response: &Response) -> Result<(), Error> { - if !response.is_success() { - let status = response.status(); - let msg = format!("HTTP error: {}: {}", status.as_u16(), status.as_str()); - return Err(msg.into()); - } - Ok(()) + if !response.is_success() { + let status = response.status(); + let msg = format!("HTTP error: {}: {}", status.as_u16(), status.as_str()); + return Err(msg.into()); + } + Ok(()) } fn rate_limit(endpoint: &mut Endpoint) { - endpoint.rl.block_until_allowed(); + endpoint.rl.block_until_allowed(); } fn header_to_string(header_value: &header::HeaderValue) -> Result { - let s = header_value - .to_str() - .map_err(|_| Error::from("invalid header format"))?; - Ok(s.to_string()) + let s = header_value + .to_str() + .map_err(|_| Error::from("invalid header format"))?; + Ok(s.to_string()) } fn get_session(root_certs: &[String]) -> Result { - let useragent = format!( - "{}/{} ({}) {}", - crate::APP_NAME, - crate::APP_VERSION, - env!("ACMED_TARGET"), - env!("ACMED_HTTP_LIB_AGENT") - ); - // TODO: allow to change the language - let mut session = Session::new(); - session.default_charset(Some(charsets::UTF_8)); - session.try_header(header::ACCEPT_LANGUAGE, "en-US,en;q=0.5")?; - session.try_header(header::USER_AGENT, &useragent)?; - for crt_file in root_certs.iter() { - #[cfg(feature = "crypto_openssl")] - { - let mut buff = Vec::new(); - File::open(crt_file) - .map_err(|e| Error::from(e).prefix(crt_file))? - .read_to_end(&mut buff)?; - let crt = X509Certificate::from_pem_native(&buff)?; - session.add_root_certificate(crt); - } - } - Ok(session) + let useragent = format!( + "{}/{} ({}) {}", + crate::APP_NAME, + crate::APP_VERSION, + env!("ACMED_TARGET"), + env!("ACMED_HTTP_LIB_AGENT") + ); + // TODO: allow to change the language + let mut session = Session::new(); + session.default_charset(Some(charsets::UTF_8)); + session.try_header(header::ACCEPT_LANGUAGE, "en-US,en;q=0.5")?; + session.try_header(header::USER_AGENT, &useragent)?; + for crt_file in root_certs.iter() { + #[cfg(feature = "crypto_openssl")] + { + let mut buff = Vec::new(); + File::open(crt_file) + .map_err(|e| Error::from(e).prefix(crt_file))? + .read_to_end(&mut buff)?; + let crt = X509Certificate::from_pem_native(&buff)?; + session.add_root_certificate(crt); + } + } + Ok(session) } pub fn get(endpoint: &mut Endpoint, url: &str) -> Result { - let mut session = get_session(&endpoint.root_certificates)?; - session.try_header(header::ACCEPT, CONTENT_TYPE_JSON)?; - rate_limit(endpoint); - let response = session.get(url).send()?; - update_nonce(endpoint, &response)?; - check_status(&response)?; - ValidHttpResponse::from_response(response).map_err(HttpError::from) + let mut session = get_session(&endpoint.root_certificates)?; + session.try_header(header::ACCEPT, CONTENT_TYPE_JSON)?; + rate_limit(endpoint); + let response = session.get(url).send()?; + update_nonce(endpoint, &response)?; + check_status(&response)?; + ValidHttpResponse::from_response(response).map_err(HttpError::from) } pub fn post( - endpoint: &mut Endpoint, - url: &str, - data_builder: &F, - content_type: &str, - accept: &str, + endpoint: &mut Endpoint, + url: &str, + data_builder: &F, + content_type: &str, + accept: &str, ) -> Result where - F: Fn(&str, &str) -> Result, + F: Fn(&str, &str) -> Result, { - let mut session = get_session(&endpoint.root_certificates)?; - session.try_header(header::ACCEPT, accept)?; - session.try_header(header::CONTENT_TYPE, content_type)?; - if endpoint.nonce.is_none() { - let _ = new_nonce(endpoint); - } - for _ in 0..crate::DEFAULT_HTTP_FAIL_NB_RETRY { - let nonce = &endpoint.nonce.clone().unwrap_or_default(); - let body = data_builder(nonce, url)?; - rate_limit(endpoint); - log::trace!("POST request body: {}", body); - let response = session.post(url).text(&body).send()?; - update_nonce(endpoint, &response)?; - match check_status(&response) { - Ok(_) => { - return ValidHttpResponse::from_response(response).map_err(HttpError::from); - } - Err(_) => { - let resp = ValidHttpResponse::from_response(response)?; - let api_err = resp.json::()?; - let acme_err = api_err.get_acme_type(); - if !acme_err.is_recoverable() { - return Err(api_err.into()); - } - } - } - thread::sleep(time::Duration::from_secs(crate::DEFAULT_HTTP_FAIL_WAIT_SEC)); - } - Err("too much errors, will not retry".into()) + let mut session = get_session(&endpoint.root_certificates)?; + session.try_header(header::ACCEPT, accept)?; + session.try_header(header::CONTENT_TYPE, content_type)?; + if endpoint.nonce.is_none() { + let _ = new_nonce(endpoint); + } + for _ in 0..crate::DEFAULT_HTTP_FAIL_NB_RETRY { + let nonce = &endpoint.nonce.clone().unwrap_or_default(); + let body = data_builder(nonce, url)?; + rate_limit(endpoint); + log::trace!("POST request body: {}", body); + let response = session.post(url).text(&body).send()?; + update_nonce(endpoint, &response)?; + match check_status(&response) { + Ok(_) => { + return ValidHttpResponse::from_response(response).map_err(HttpError::from); + } + Err(_) => { + let resp = ValidHttpResponse::from_response(response)?; + let api_err = resp.json::()?; + let acme_err = api_err.get_acme_type(); + if !acme_err.is_recoverable() { + return Err(api_err.into()); + } + } + } + thread::sleep(time::Duration::from_secs(crate::DEFAULT_HTTP_FAIL_WAIT_SEC)); + } + Err("too much errors, will not retry".into()) } pub fn post_jose( - endpoint: &mut Endpoint, - url: &str, - data_builder: &F, + endpoint: &mut Endpoint, + url: &str, + data_builder: &F, ) -> Result where - F: Fn(&str, &str) -> Result, + F: Fn(&str, &str) -> Result, { - post( - endpoint, - url, - data_builder, - CONTENT_TYPE_JOSE, - CONTENT_TYPE_JSON, - ) + post( + endpoint, + url, + data_builder, + CONTENT_TYPE_JOSE, + CONTENT_TYPE_JSON, + ) } #[cfg(test)] mod tests { - use super::is_nonce; + use super::is_nonce; - #[test] - fn test_nonce_valid() { - let lst = [ - "XFHw3qcgFNZAdw", - "XFHw3qcg-NZAdw", - "XFHw3qcg_NZAdw", - "XFHw3qcg-_ZAdw", - "a", - "1", - "-", - "_", - ]; - for n in lst.iter() { - assert!(is_nonce(n)); - } - } + #[test] + fn test_nonce_valid() { + let lst = [ + "XFHw3qcgFNZAdw", + "XFHw3qcg-NZAdw", + "XFHw3qcg_NZAdw", + "XFHw3qcg-_ZAdw", + "a", + "1", + "-", + "_", + ]; + for n in lst.iter() { + assert!(is_nonce(n)); + } + } - #[test] - fn test_nonce_invalid() { - let lst = [ - "", - "rdo9x8gS4K/mZg==", - "rdo9x8gS4K/mZg", - "rdo9x8gS4K+mZg", - "৬", - "京", - ]; - for n in lst.iter() { - assert!(!is_nonce(n)); - } - } + #[test] + fn test_nonce_invalid() { + let lst = [ + "", + "rdo9x8gS4K/mZg==", + "rdo9x8gS4K/mZg", + "rdo9x8gS4K+mZg", + "৬", + "京", + ]; + for n in lst.iter() { + assert!(!is_nonce(n)); + } + } } diff --git a/acmed/src/identifier.rs b/acmed/src/identifier.rs index f3e89ef..7713c1e 100644 --- a/acmed/src/identifier.rs +++ b/acmed/src/identifier.rs @@ -9,141 +9,141 @@ use std::str::FromStr; // RFC 3596, section 2.5 fn u8_to_nibbles_string(value: &u8) -> String { - let bytes = value.to_ne_bytes(); - let first = bytes[0] & 0x0f; - let second = (bytes[0] >> 4) & 0x0f; - format!("{:x}.{:x}", first, second) + let bytes = value.to_ne_bytes(); + let first = bytes[0] & 0x0f; + let second = (bytes[0] >> 4) & 0x0f; + format!("{:x}.{:x}", first, second) } #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] pub enum IdentifierType { - #[serde(rename = "dns")] - Dns, - #[serde(rename = "ip")] - Ip, + #[serde(rename = "dns")] + Dns, + #[serde(rename = "ip")] + Ip, } impl IdentifierType { - pub fn supported_challenges(&self) -> Vec { - match self { - IdentifierType::Dns => vec![Challenge::Http01, Challenge::Dns01, Challenge::TlsAlpn01], - IdentifierType::Ip => vec![Challenge::Http01, Challenge::TlsAlpn01], - } - } + pub fn supported_challenges(&self) -> Vec { + match self { + IdentifierType::Dns => vec![Challenge::Http01, Challenge::Dns01, Challenge::TlsAlpn01], + IdentifierType::Ip => vec![Challenge::Http01, Challenge::TlsAlpn01], + } + } } impl fmt::Display for IdentifierType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let name = match self { - IdentifierType::Dns => "dns", - IdentifierType::Ip => "ip", - }; - write!(f, "{}", name) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let name = match self { + IdentifierType::Dns => "dns", + IdentifierType::Ip => "ip", + }; + write!(f, "{}", name) + } } #[derive(Clone, Debug)] pub struct Identifier { - pub id_type: IdentifierType, - pub value: String, - pub challenge: Challenge, - pub env: HashMap, + pub id_type: IdentifierType, + pub value: String, + pub challenge: Challenge, + pub env: HashMap, } impl Identifier { - pub fn new( - id_type: IdentifierType, - value: &str, - challenge: &str, - env: &HashMap, - ) -> Result { - let value = match id_type { - IdentifierType::Dns => to_idna(value)?, - IdentifierType::Ip => IpAddr::from_str(value)?.to_string(), - }; - let challenge = Challenge::from_str(challenge)?; - if !id_type.supported_challenges().contains(&challenge) { - let msg = format!( - "challenge {} cannot be used with identifier of type {}", - challenge, id_type - ); - return Err(msg.into()); - } - Ok(Identifier { - id_type, - value, - challenge, - env: env.clone(), - }) - } + pub fn new( + id_type: IdentifierType, + value: &str, + challenge: &str, + env: &HashMap, + ) -> Result { + let value = match id_type { + IdentifierType::Dns => to_idna(value)?, + IdentifierType::Ip => IpAddr::from_str(value)?.to_string(), + }; + let challenge = Challenge::from_str(challenge)?; + if !id_type.supported_challenges().contains(&challenge) { + let msg = format!( + "challenge {} cannot be used with identifier of type {}", + challenge, id_type + ); + return Err(msg.into()); + } + Ok(Identifier { + id_type, + value, + challenge, + env: env.clone(), + }) + } - pub fn get_tls_alpn_name(&self) -> Result { - match &self.id_type { - IdentifierType::Dns => Ok(self.value.to_owned()), - IdentifierType::Ip => match IpAddr::from_str(&self.value)? { - IpAddr::V4(ip) => { - let dn = ip - .octets() - .iter() - .rev() - .map(|v| v.to_string()) - .collect::>() - .join("."); - let dn = format!("{}.in-addr.arpa", dn); - Ok(dn) - } - IpAddr::V6(ip) => { - let dn = ip - .octets() - .iter() - .rev() - .map(u8_to_nibbles_string) - .collect::>() - .join("."); - let dn = format!("{}.ip6.arpa", dn); - Ok(dn) - } - }, - } - } + pub fn get_tls_alpn_name(&self) -> Result { + match &self.id_type { + IdentifierType::Dns => Ok(self.value.to_owned()), + IdentifierType::Ip => match IpAddr::from_str(&self.value)? { + IpAddr::V4(ip) => { + let dn = ip + .octets() + .iter() + .rev() + .map(|v| v.to_string()) + .collect::>() + .join("."); + let dn = format!("{}.in-addr.arpa", dn); + Ok(dn) + } + IpAddr::V6(ip) => { + let dn = ip + .octets() + .iter() + .rev() + .map(u8_to_nibbles_string) + .collect::>() + .join("."); + let dn = format!("{}.ip6.arpa", dn); + Ok(dn) + } + }, + } + } } impl fmt::Display for Identifier { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: {} ({})", self.id_type, self.value, self.challenge) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}: {} ({})", self.id_type, self.value, self.challenge) + } } #[cfg(test)] mod tests { - use super::*; - use std::collections::HashMap; + use super::*; + use std::collections::HashMap; - #[test] - fn test_ipv4_tls_alpn_name() { - let env = HashMap::new(); - let id = Identifier::new(IdentifierType::Ip, "203.0.113.1", "http-01", &env).unwrap(); - assert_eq!(&id.get_tls_alpn_name().unwrap(), "1.113.0.203.in-addr.arpa"); - } + #[test] + fn test_ipv4_tls_alpn_name() { + let env = HashMap::new(); + let id = Identifier::new(IdentifierType::Ip, "203.0.113.1", "http-01", &env).unwrap(); + assert_eq!(&id.get_tls_alpn_name().unwrap(), "1.113.0.203.in-addr.arpa"); + } - #[test] - fn test_ipv6_tls_alpn_name() { - let env = HashMap::new(); - let id = Identifier::new(IdentifierType::Ip, "2001:db8::1", "http-01", &env).unwrap(); - assert_eq!( - &id.get_tls_alpn_name().unwrap(), - "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa" - ); - let id = Identifier::new( - IdentifierType::Ip, - "4321:0:1:2:3:4:567:89ab", - "http-01", - &env, - ) - .unwrap(); - assert_eq!( - &id.get_tls_alpn_name().unwrap(), - "b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa" - ); - } + #[test] + fn test_ipv6_tls_alpn_name() { + let env = HashMap::new(); + let id = Identifier::new(IdentifierType::Ip, "2001:db8::1", "http-01", &env).unwrap(); + assert_eq!( + &id.get_tls_alpn_name().unwrap(), + "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa" + ); + let id = Identifier::new( + IdentifierType::Ip, + "4321:0:1:2:3:4:567:89ab", + "http-01", + &env, + ) + .unwrap(); + assert_eq!( + &id.get_tls_alpn_name().unwrap(), + "b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa" + ); + } } diff --git a/acmed/src/jws.rs b/acmed/src/jws.rs index b4e42c0..cc07d39 100644 --- a/acmed/src/jws.rs +++ b/acmed/src/jws.rs @@ -6,186 +6,186 @@ use serde_json::value::Value; #[derive(Serialize)] struct JwsData { - protected: String, - payload: String, - signature: String, + protected: String, + payload: String, + signature: String, } #[derive(Serialize)] struct JwsProtectedHeader { - alg: String, - #[serde(skip_serializing_if = "Option::is_none")] - jwk: Option, - #[serde(skip_serializing_if = "Option::is_none")] - kid: Option, - #[serde(skip_serializing_if = "Option::is_none")] - nonce: Option, - url: String, + alg: String, + #[serde(skip_serializing_if = "Option::is_none")] + jwk: Option, + #[serde(skip_serializing_if = "Option::is_none")] + kid: Option, + #[serde(skip_serializing_if = "Option::is_none")] + nonce: Option, + url: String, } fn get_jws_data( - key_pair: &KeyPair, - sign_alg: &JwsSignatureAlgorithm, - protected: &str, - payload: &[u8], + key_pair: &KeyPair, + sign_alg: &JwsSignatureAlgorithm, + protected: &str, + payload: &[u8], ) -> Result { - let protected = b64_encode(protected); - let payload = b64_encode(payload); - let signing_input = format!("{}.{}", protected, payload); - let signature = key_pair.sign(sign_alg, signing_input.as_bytes())?; - let signature = b64_encode(&signature); - let data = JwsData { - protected, - payload, - signature, - }; - let str_data = serde_json::to_string(&data)?; - Ok(str_data) + let protected = b64_encode(protected); + let payload = b64_encode(payload); + let signing_input = format!("{}.{}", protected, payload); + let signature = key_pair.sign(sign_alg, signing_input.as_bytes())?; + let signature = b64_encode(&signature); + let data = JwsData { + protected, + payload, + signature, + }; + let str_data = serde_json::to_string(&data)?; + Ok(str_data) } pub fn encode_jwk( - key_pair: &KeyPair, - sign_alg: &JwsSignatureAlgorithm, - payload: &[u8], - url: &str, - nonce: Option, + key_pair: &KeyPair, + sign_alg: &JwsSignatureAlgorithm, + payload: &[u8], + url: &str, + nonce: Option, ) -> Result { - let protected = JwsProtectedHeader { - alg: sign_alg.to_string(), - jwk: Some(key_pair.jwk_public_key()?), - kid: None, - nonce, - url: url.into(), - }; - let protected = serde_json::to_string(&protected)?; - get_jws_data(key_pair, sign_alg, &protected, payload) + let protected = JwsProtectedHeader { + alg: sign_alg.to_string(), + jwk: Some(key_pair.jwk_public_key()?), + kid: None, + nonce, + url: url.into(), + }; + let protected = serde_json::to_string(&protected)?; + get_jws_data(key_pair, sign_alg, &protected, payload) } pub fn encode_kid( - key_pair: &KeyPair, - sign_alg: &JwsSignatureAlgorithm, - key_id: &str, - payload: &[u8], - url: &str, - nonce: &str, + key_pair: &KeyPair, + sign_alg: &JwsSignatureAlgorithm, + key_id: &str, + payload: &[u8], + url: &str, + nonce: &str, ) -> Result { - let protected = JwsProtectedHeader { - alg: sign_alg.to_string(), - jwk: None, - kid: Some(key_id.to_string()), - nonce: Some(nonce.into()), - url: url.into(), - }; - let protected = serde_json::to_string(&protected)?; - get_jws_data(key_pair, sign_alg, &protected, payload) + let protected = JwsProtectedHeader { + alg: sign_alg.to_string(), + jwk: None, + kid: Some(key_id.to_string()), + nonce: Some(nonce.into()), + url: url.into(), + }; + let protected = serde_json::to_string(&protected)?; + get_jws_data(key_pair, sign_alg, &protected, payload) } pub fn encode_kid_mac( - key: &[u8], - sign_alg: &JwsSignatureAlgorithm, - key_id: &str, - payload: &[u8], - url: &str, + key: &[u8], + sign_alg: &JwsSignatureAlgorithm, + key_id: &str, + payload: &[u8], + url: &str, ) -> Result { - let protected = JwsProtectedHeader { - alg: sign_alg.to_string(), - jwk: None, - kid: Some(key_id.to_string()), - nonce: None, - url: url.into(), - }; - let protected = serde_json::to_string(&protected)?; - let protected = b64_encode(&protected); - let payload = b64_encode(payload); - let signing_input = format!("{}.{}", protected, payload); - let hash_func = match sign_alg { - JwsSignatureAlgorithm::Hs256 => HashFunction::Sha256, - JwsSignatureAlgorithm::Hs384 => HashFunction::Sha384, - JwsSignatureAlgorithm::Hs512 => HashFunction::Sha512, - _ => { - return Err(format!("{}: not a HMAC-based signature algorithm", sign_alg).into()); - } - }; - let signature = hash_func.hmac(key, signing_input.as_bytes())?; - let signature = b64_encode(&signature); - let data = JwsData { - protected, - payload, - signature, - }; - let str_data = serde_json::to_string(&data)?; - Ok(str_data) + let protected = JwsProtectedHeader { + alg: sign_alg.to_string(), + jwk: None, + kid: Some(key_id.to_string()), + nonce: None, + url: url.into(), + }; + let protected = serde_json::to_string(&protected)?; + let protected = b64_encode(&protected); + let payload = b64_encode(payload); + let signing_input = format!("{}.{}", protected, payload); + let hash_func = match sign_alg { + JwsSignatureAlgorithm::Hs256 => HashFunction::Sha256, + JwsSignatureAlgorithm::Hs384 => HashFunction::Sha384, + JwsSignatureAlgorithm::Hs512 => HashFunction::Sha512, + _ => { + return Err(format!("{}: not a HMAC-based signature algorithm", sign_alg).into()); + } + }; + let signature = hash_func.hmac(key, signing_input.as_bytes())?; + let signature = b64_encode(&signature); + let data = JwsData { + protected, + payload, + signature, + }; + let str_data = serde_json::to_string(&data)?; + Ok(str_data) } #[cfg(test)] mod tests { - use super::{encode_jwk, encode_kid}; - use acme_common::crypto::{gen_keypair, KeyType}; + use super::{encode_jwk, encode_kid}; + use acme_common::crypto::{gen_keypair, KeyType}; - #[test] - fn test_default_jwk() { - let key_type = KeyType::EcdsaP256; - let key_pair = gen_keypair(key_type).unwrap(); - let payload = "Dummy payload 1"; - let payload_b64 = "RHVtbXkgcGF5bG9hZCAx"; - let s = encode_jwk( - &key_pair, - &key_type.get_default_signature_alg(), - payload.as_bytes(), - "", - Some(String::new()), - ); - assert!(s.is_ok()); - let s = s.unwrap(); - assert!(s.contains("\"protected\"")); - assert!(s.contains("\"payload\"")); - assert!(s.contains("\"signature\"")); - assert!(s.contains(payload_b64)); - } + #[test] + fn test_default_jwk() { + let key_type = KeyType::EcdsaP256; + let key_pair = gen_keypair(key_type).unwrap(); + let payload = "Dummy payload 1"; + let payload_b64 = "RHVtbXkgcGF5bG9hZCAx"; + let s = encode_jwk( + &key_pair, + &key_type.get_default_signature_alg(), + payload.as_bytes(), + "", + Some(String::new()), + ); + assert!(s.is_ok()); + let s = s.unwrap(); + assert!(s.contains("\"protected\"")); + assert!(s.contains("\"payload\"")); + assert!(s.contains("\"signature\"")); + assert!(s.contains(payload_b64)); + } - #[test] - fn test_default_nopad_jwk() { - let key_type = KeyType::EcdsaP256; - let key_pair = gen_keypair(key_type).unwrap(); - let payload = "Dummy payload"; - let payload_b64 = "RHVtbXkgcGF5bG9hZA"; - let payload_b64_pad = "RHVtbXkgcGF5bG9hZA=="; - let s = encode_jwk( - &key_pair, - &key_type.get_default_signature_alg(), - payload.as_bytes(), - "", - Some(String::new()), - ); - assert!(s.is_ok()); - let s = s.unwrap(); - assert!(s.contains("\"protected\"")); - assert!(s.contains("\"payload\"")); - assert!(s.contains("\"signature\"")); - assert!(s.contains(payload_b64)); - assert!(!s.contains(payload_b64_pad)); - } + #[test] + fn test_default_nopad_jwk() { + let key_type = KeyType::EcdsaP256; + let key_pair = gen_keypair(key_type).unwrap(); + let payload = "Dummy payload"; + let payload_b64 = "RHVtbXkgcGF5bG9hZA"; + let payload_b64_pad = "RHVtbXkgcGF5bG9hZA=="; + let s = encode_jwk( + &key_pair, + &key_type.get_default_signature_alg(), + payload.as_bytes(), + "", + Some(String::new()), + ); + assert!(s.is_ok()); + let s = s.unwrap(); + assert!(s.contains("\"protected\"")); + assert!(s.contains("\"payload\"")); + assert!(s.contains("\"signature\"")); + assert!(s.contains(payload_b64)); + assert!(!s.contains(payload_b64_pad)); + } - #[test] - fn test_default_kid() { - let key_type = KeyType::EcdsaP256; - let key_pair = gen_keypair(key_type).unwrap(); - let payload = "Dummy payload 1"; - let payload_b64 = "RHVtbXkgcGF5bG9hZCAx"; - let key_id = "0x2a"; - let s = encode_kid( - &key_pair, - &key_type.get_default_signature_alg(), - key_id, - payload.as_bytes(), - "", - "", - ); - assert!(s.is_ok()); - let s = s.unwrap(); - assert!(s.contains("\"protected\"")); - assert!(s.contains("\"payload\"")); - assert!(s.contains("\"signature\"")); - assert!(s.contains(payload_b64)); - } + #[test] + fn test_default_kid() { + let key_type = KeyType::EcdsaP256; + let key_pair = gen_keypair(key_type).unwrap(); + let payload = "Dummy payload 1"; + let payload_b64 = "RHVtbXkgcGF5bG9hZCAx"; + let key_id = "0x2a"; + let s = encode_kid( + &key_pair, + &key_type.get_default_signature_alg(), + key_id, + payload.as_bytes(), + "", + "", + ); + assert!(s.is_ok()); + let s = s.unwrap(); + assert!(s.contains("\"protected\"")); + assert!(s.contains("\"payload\"")); + assert!(s.contains("\"signature\"")); + assert!(s.contains(payload_b64)); + } } diff --git a/acmed/src/logs.rs b/acmed/src/logs.rs index d32a313..16d8c85 100644 --- a/acmed/src/logs.rs +++ b/acmed/src/logs.rs @@ -1,6 +1,6 @@ pub trait HasLogger { - fn warn(&self, msg: &str); - fn info(&self, msg: &str); - fn debug(&self, msg: &str); - fn trace(&self, msg: &str); + fn warn(&self, msg: &str); + fn info(&self, msg: &str); + fn debug(&self, msg: &str); + fn trace(&self, msg: &str); } diff --git a/acmed/src/main.rs b/acmed/src/main.rs index 47f6d89..151ade7 100644 --- a/acmed/src/main.rs +++ b/acmed/src/main.rs @@ -1,6 +1,6 @@ use crate::main_event_loop::MainEventLoop; use acme_common::crypto::{ - get_lib_name, get_lib_version, HashFunction, JwsSignatureAlgorithm, KeyType, + get_lib_name, get_lib_version, HashFunction, JwsSignatureAlgorithm, KeyType, }; use acme_common::logs::{set_log_system, DEFAULT_LOG_LEVEL}; use acme_common::{clean_pid_file, init_server}; @@ -49,117 +49,117 @@ pub const MAX_RATE_LIMIT_SLEEP_MILISEC: u64 = 3_600_000; pub const MIN_RATE_LIMIT_SLEEP_MILISEC: u64 = 100; fn main() { - let full_version = format!( - "{} built for {}\n\nCryptographic library:\n - {} {}\nHTTP client library:\n - {} {}", - APP_VERSION, - env!("ACMED_TARGET"), - get_lib_name(), - get_lib_version(), - env!("ACMED_HTTP_LIB_NAME"), - env!("ACMED_HTTP_LIB_VERSION") - ); - let default_log_level = DEFAULT_LOG_LEVEL.to_string().to_lowercase(); - let matches = Command::new(APP_NAME) - .version(APP_VERSION) - .long_version(full_version) - .arg( - Arg::new("config") - .short('c') - .long("config") - .help("Path to the main configuration file") - .num_args(1) - .value_name("FILE") - .default_value(DEFAULT_CONFIG_FILE), - ) - .arg( - Arg::new("log-level") - .long("log-level") - .help("Specify the log level") - .num_args(1) - .value_name("LEVEL") - .value_parser(["error", "warn", "info", "debug", "trace"]) - .default_value(default_log_level), - ) - .arg( - Arg::new("to-syslog") - .long("log-syslog") - .help("Sends log messages via syslog") - .conflicts_with("to-stderr") - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new("to-stderr") - .long("log-stderr") - .help("Prints log messages to the standard error output") - .conflicts_with("to-syslog") - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new("foreground") - .short('f') - .long("foreground") - .help("Runs in the foreground") - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new("pid-file") - .long("pid-file") - .help("Path to the PID file") - .num_args(1) - .value_name("FILE") - .default_value(DEFAULT_PID_FILE) - .default_value_if("no-pid-file", clap::builder::ArgPredicate::IsPresent, None) - .conflicts_with("no-pid-file"), - ) - .arg( - Arg::new("no-pid-file") - .long("no-pid-file") - .help("Do not create any PID file") - .conflicts_with("pid-file") - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new("root-cert") - .long("root-cert") - .help("Add a root certificate to the trust store (can be set multiple times)") - .num_args(1) - .action(ArgAction::Append) - .value_name("FILE"), - ) - .get_matches(); + let full_version = format!( + "{} built for {}\n\nCryptographic library:\n - {} {}\nHTTP client library:\n - {} {}", + APP_VERSION, + env!("ACMED_TARGET"), + get_lib_name(), + get_lib_version(), + env!("ACMED_HTTP_LIB_NAME"), + env!("ACMED_HTTP_LIB_VERSION") + ); + let default_log_level = DEFAULT_LOG_LEVEL.to_string().to_lowercase(); + let matches = Command::new(APP_NAME) + .version(APP_VERSION) + .long_version(full_version) + .arg( + Arg::new("config") + .short('c') + .long("config") + .help("Path to the main configuration file") + .num_args(1) + .value_name("FILE") + .default_value(DEFAULT_CONFIG_FILE), + ) + .arg( + Arg::new("log-level") + .long("log-level") + .help("Specify the log level") + .num_args(1) + .value_name("LEVEL") + .value_parser(["error", "warn", "info", "debug", "trace"]) + .default_value(default_log_level), + ) + .arg( + Arg::new("to-syslog") + .long("log-syslog") + .help("Sends log messages via syslog") + .conflicts_with("to-stderr") + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new("to-stderr") + .long("log-stderr") + .help("Prints log messages to the standard error output") + .conflicts_with("to-syslog") + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new("foreground") + .short('f') + .long("foreground") + .help("Runs in the foreground") + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new("pid-file") + .long("pid-file") + .help("Path to the PID file") + .num_args(1) + .value_name("FILE") + .default_value(DEFAULT_PID_FILE) + .default_value_if("no-pid-file", clap::builder::ArgPredicate::IsPresent, None) + .conflicts_with("no-pid-file"), + ) + .arg( + Arg::new("no-pid-file") + .long("no-pid-file") + .help("Do not create any PID file") + .conflicts_with("pid-file") + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new("root-cert") + .long("root-cert") + .help("Add a root certificate to the trust store (can be set multiple times)") + .num_args(1) + .action(ArgAction::Append) + .value_name("FILE"), + ) + .get_matches(); - match set_log_system( - matches.get_one::("log-level").map(|e| e.as_str()), - matches.get_flag("to-syslog"), - matches.get_flag("to-stderr"), - ) { - Ok(_) => {} - Err(e) => { - eprintln!("Error: {}", e); - std::process::exit(2); - } - }; + match set_log_system( + matches.get_one::("log-level").map(|e| e.as_str()), + matches.get_flag("to-syslog"), + matches.get_flag("to-stderr"), + ) { + Ok(_) => {} + Err(e) => { + eprintln!("Error: {}", e); + std::process::exit(2); + } + }; - let root_certs = match matches.get_many::("root-cert") { - Some(v) => v.map(|e| e.as_str()).collect(), - None => vec![], - }; + let root_certs = match matches.get_many::("root-cert") { + Some(v) => v.map(|e| e.as_str()).collect(), + None => vec![], + }; - let config_file = matches - .get_one::("config") - .map(|e| e.as_str()) - .unwrap_or(DEFAULT_CONFIG_FILE); - let pid_file = matches.get_one::("pid-file").map(|e| e.as_str()); + let config_file = matches + .get_one::("config") + .map(|e| e.as_str()) + .unwrap_or(DEFAULT_CONFIG_FILE); + let pid_file = matches.get_one::("pid-file").map(|e| e.as_str()); - init_server(matches.get_flag("foreground"), pid_file); + init_server(matches.get_flag("foreground"), pid_file); - let mut srv = match MainEventLoop::new(config_file, &root_certs) { - Ok(s) => s, - Err(e) => { - error!("{}", e); - let _ = clean_pid_file(pid_file); - std::process::exit(1); - } - }; - srv.run(); + let mut srv = match MainEventLoop::new(config_file, &root_certs) { + Ok(s) => s, + Err(e) => { + error!("{}", e); + let _ = clean_pid_file(pid_file); + std::process::exit(1); + } + }; + srv.run(); } diff --git a/acmed/src/main_event_loop.rs b/acmed/src/main_event_loop.rs index 30c2c97..35285cc 100644 --- a/acmed/src/main_event_loop.rs +++ b/acmed/src/main_event_loop.rs @@ -16,194 +16,194 @@ type AccountSync = Arc>; type EndpointSync = Arc>; fn renew_certificate(crt: &Certificate, endpoint: &mut Endpoint, account: &mut Account) { - let (status, is_success) = match request_certificate(crt, endpoint, account) { - Ok(_) => ("success".to_string(), true), - Err(e) => { - let e = e.prefix("unable to renew the certificate"); - crt.warn(&e.message); - (e.message, false) - } - }; - match crt.call_post_operation_hooks(&status, is_success) { - Ok(_) => {} - Err(e) => { - let e = e.prefix("post-operation hook error"); - crt.warn(&e.message); - } - }; + let (status, is_success) = match request_certificate(crt, endpoint, account) { + Ok(_) => ("success".to_string(), true), + Err(e) => { + let e = e.prefix("unable to renew the certificate"); + crt.warn(&e.message); + (e.message, false) + } + }; + match crt.call_post_operation_hooks(&status, is_success) { + Ok(_) => {} + Err(e) => { + let e = e.prefix("post-operation hook error"); + crt.warn(&e.message); + } + }; } pub struct MainEventLoop { - certs: Vec, - accounts: HashMap, - endpoints: HashMap, + certs: Vec, + accounts: HashMap, + endpoints: HashMap, } impl MainEventLoop { - pub fn new(config_file: &str, root_certs: &[&str]) -> Result { - let cnf = config::from_file(config_file)?; - let file_hooks = vec![ - HookType::FilePreCreate, - HookType::FilePostCreate, - HookType::FilePreEdit, - HookType::FilePostEdit, - ] - .into_iter() - .collect(); - let cert_hooks = vec![ - HookType::ChallengeHttp01, - HookType::ChallengeHttp01Clean, - HookType::ChallengeDns01, - HookType::ChallengeDns01Clean, - HookType::ChallengeTlsAlpn01, - HookType::ChallengeTlsAlpn01Clean, - HookType::PostOperation, - ] - .into_iter() - .collect(); + pub fn new(config_file: &str, root_certs: &[&str]) -> Result { + let cnf = config::from_file(config_file)?; + let file_hooks = vec![ + HookType::FilePreCreate, + HookType::FilePostCreate, + HookType::FilePreEdit, + HookType::FilePostEdit, + ] + .into_iter() + .collect(); + let cert_hooks = vec![ + HookType::ChallengeHttp01, + HookType::ChallengeHttp01Clean, + HookType::ChallengeDns01, + HookType::ChallengeDns01Clean, + HookType::ChallengeTlsAlpn01, + HookType::ChallengeTlsAlpn01Clean, + HookType::PostOperation, + ] + .into_iter() + .collect(); - let mut accounts = HashMap::new(); - for acc in cnf.account.iter() { - let fm = FileManager { - account_directory: cnf.get_account_dir(), - account_name: acc.name.clone(), - crt_name: String::new(), - crt_name_format: String::new(), - crt_directory: String::new(), - crt_key_type: String::new(), - cert_file_mode: cnf.get_cert_file_mode(), - cert_file_owner: cnf.get_cert_file_user(), - cert_file_group: cnf.get_cert_file_group(), - pk_file_mode: cnf.get_pk_file_mode(), - pk_file_owner: cnf.get_pk_file_user(), - pk_file_group: cnf.get_pk_file_group(), - hooks: acc - .get_hooks(&cnf)? - .iter() - .filter(|h| !h.hook_type.is_disjoint(&file_hooks)) - .map(|e| e.to_owned()) - .collect(), - env: acc.env.clone(), - }; - let account = acc.to_generic(&fm)?; - accounts.insert(acc.name.clone(), account); - } + let mut accounts = HashMap::new(); + for acc in cnf.account.iter() { + let fm = FileManager { + account_directory: cnf.get_account_dir(), + account_name: acc.name.clone(), + crt_name: String::new(), + crt_name_format: String::new(), + crt_directory: String::new(), + crt_key_type: String::new(), + cert_file_mode: cnf.get_cert_file_mode(), + cert_file_owner: cnf.get_cert_file_user(), + cert_file_group: cnf.get_cert_file_group(), + pk_file_mode: cnf.get_pk_file_mode(), + pk_file_owner: cnf.get_pk_file_user(), + pk_file_group: cnf.get_pk_file_group(), + hooks: acc + .get_hooks(&cnf)? + .iter() + .filter(|h| !h.hook_type.is_disjoint(&file_hooks)) + .map(|e| e.to_owned()) + .collect(), + env: acc.env.clone(), + }; + let account = acc.to_generic(&fm)?; + accounts.insert(acc.name.clone(), account); + } - let mut certs: Vec = Vec::new(); - let mut endpoints = HashMap::new(); - for crt in cnf.certificate.iter() { - let endpoint = crt.get_endpoint(&cnf, root_certs)?; - let endpoint_name = endpoint.name.clone(); - let crt_name = crt.get_crt_name()?; - let key_type = crt.get_key_type()?; - let hooks = crt.get_hooks(&cnf)?; - let fm = FileManager { - account_directory: cnf.get_account_dir(), - account_name: crt.account.clone(), - crt_name: crt_name.clone(), - crt_name_format: crt.get_crt_name_format(&cnf)?, - crt_directory: crt.get_crt_dir(&cnf), - crt_key_type: key_type.to_string(), - cert_file_mode: cnf.get_cert_file_mode(), - cert_file_owner: cnf.get_cert_file_user(), - cert_file_group: cnf.get_cert_file_group(), - pk_file_mode: cnf.get_pk_file_mode(), - pk_file_owner: cnf.get_pk_file_user(), - pk_file_group: cnf.get_pk_file_group(), - hooks: hooks - .iter() - .filter(|h| !h.hook_type.is_disjoint(&file_hooks)) - .map(|e| e.to_owned()) - .collect(), - env: crt.env.clone(), - }; - let cert = Certificate { - account_name: crt.account.clone(), - identifiers: crt.get_identifiers()?, - subject_attributes: crt.subject_attributes.to_generic(), - key_type, - csr_digest: crt.get_csr_digest()?, - kp_reuse: crt.get_kp_reuse(), - endpoint_name: endpoint_name.clone(), - hooks: hooks - .iter() - .filter(|h| !h.hook_type.is_disjoint(&cert_hooks)) - .map(|e| e.to_owned()) - .collect(), - crt_name, - env: crt.env.to_owned(), - renew_delay: crt.get_renew_delay(&cnf)?, - file_manager: fm, - }; - let crt_id = cert.get_id(); - if certs.iter().any(|c| c.get_id() == crt_id) { - let msg = format!("{}: duplicate certificate id", crt_id); - return Err(msg.into()); - } - match accounts.get_mut(&crt.account) { - Some(acc) => acc.add_endpoint_name(&endpoint_name), - None => { - let msg = format!("{}: account not found", &crt.account); - return Err(msg.into()); - } - }; - endpoints.entry(endpoint_name).or_insert(endpoint); - certs.push(cert); - } + let mut certs: Vec = Vec::new(); + let mut endpoints = HashMap::new(); + for crt in cnf.certificate.iter() { + let endpoint = crt.get_endpoint(&cnf, root_certs)?; + let endpoint_name = endpoint.name.clone(); + let crt_name = crt.get_crt_name()?; + let key_type = crt.get_key_type()?; + let hooks = crt.get_hooks(&cnf)?; + let fm = FileManager { + account_directory: cnf.get_account_dir(), + account_name: crt.account.clone(), + crt_name: crt_name.clone(), + crt_name_format: crt.get_crt_name_format(&cnf)?, + crt_directory: crt.get_crt_dir(&cnf), + crt_key_type: key_type.to_string(), + cert_file_mode: cnf.get_cert_file_mode(), + cert_file_owner: cnf.get_cert_file_user(), + cert_file_group: cnf.get_cert_file_group(), + pk_file_mode: cnf.get_pk_file_mode(), + pk_file_owner: cnf.get_pk_file_user(), + pk_file_group: cnf.get_pk_file_group(), + hooks: hooks + .iter() + .filter(|h| !h.hook_type.is_disjoint(&file_hooks)) + .map(|e| e.to_owned()) + .collect(), + env: crt.env.clone(), + }; + let cert = Certificate { + account_name: crt.account.clone(), + identifiers: crt.get_identifiers()?, + subject_attributes: crt.subject_attributes.to_generic(), + key_type, + csr_digest: crt.get_csr_digest()?, + kp_reuse: crt.get_kp_reuse(), + endpoint_name: endpoint_name.clone(), + hooks: hooks + .iter() + .filter(|h| !h.hook_type.is_disjoint(&cert_hooks)) + .map(|e| e.to_owned()) + .collect(), + crt_name, + env: crt.env.to_owned(), + renew_delay: crt.get_renew_delay(&cnf)?, + file_manager: fm, + }; + let crt_id = cert.get_id(); + if certs.iter().any(|c| c.get_id() == crt_id) { + let msg = format!("{}: duplicate certificate id", crt_id); + return Err(msg.into()); + } + match accounts.get_mut(&crt.account) { + Some(acc) => acc.add_endpoint_name(&endpoint_name), + None => { + let msg = format!("{}: account not found", &crt.account); + return Err(msg.into()); + } + }; + endpoints.entry(endpoint_name).or_insert(endpoint); + certs.push(cert); + } - Ok(MainEventLoop { - certs, - accounts: accounts - .iter() - .map(|(k, v)| (k.to_owned(), Arc::new(RwLock::new(v.to_owned())))) - .collect(), - endpoints: endpoints - .iter() - .map(|(k, v)| (k.to_owned(), Arc::new(RwLock::new(v.to_owned())))) - .collect(), - }) - } + Ok(MainEventLoop { + certs, + accounts: accounts + .iter() + .map(|(k, v)| (k.to_owned(), Arc::new(RwLock::new(v.to_owned())))) + .collect(), + endpoints: endpoints + .iter() + .map(|(k, v)| (k.to_owned(), Arc::new(RwLock::new(v.to_owned())))) + .collect(), + }) + } - pub fn run(&mut self) { - loop { - self.renew_certificates(); - thread::sleep(Duration::from_secs(crate::DEFAULT_SLEEP_TIME)); - } - } + pub fn run(&mut self) { + loop { + self.renew_certificates(); + thread::sleep(Duration::from_secs(crate::DEFAULT_SLEEP_TIME)); + } + } - fn renew_certificates(&mut self) { - let mut handles = vec![]; - for (ep_name, endpoint_lock) in self.endpoints.iter_mut() { - let mut certs_to_renew = vec![]; - for crt in self.certs.iter() { - if crt.endpoint_name == *ep_name { - match crt.should_renew() { - Ok(true) => { - let crt_arc = Arc::new(crt.clone()); - certs_to_renew.push(crt_arc); - } - Ok(false) => {} - Err(e) => { - crt.warn(&e.message); - } - } - } - } - let mut accounts_lock = self.accounts.clone(); - let ep_lock = endpoint_lock.clone(); - let handle = thread::spawn(move || { - let mut endpoint = ep_lock.write().unwrap(); - for crt in certs_to_renew { - if let Some(acc_lock) = accounts_lock.get_mut(&crt.account_name) { - let mut account = acc_lock.write().unwrap(); - renew_certificate(&crt, &mut endpoint, &mut account); - }; - } - }); - handles.push(handle); - } - for handle in handles { - let _ = handle.join(); - } - } + fn renew_certificates(&mut self) { + let mut handles = vec![]; + for (ep_name, endpoint_lock) in self.endpoints.iter_mut() { + let mut certs_to_renew = vec![]; + for crt in self.certs.iter() { + if crt.endpoint_name == *ep_name { + match crt.should_renew() { + Ok(true) => { + let crt_arc = Arc::new(crt.clone()); + certs_to_renew.push(crt_arc); + } + Ok(false) => {} + Err(e) => { + crt.warn(&e.message); + } + } + } + } + let mut accounts_lock = self.accounts.clone(); + let ep_lock = endpoint_lock.clone(); + let handle = thread::spawn(move || { + let mut endpoint = ep_lock.write().unwrap(); + for crt in certs_to_renew { + if let Some(acc_lock) = accounts_lock.get_mut(&crt.account_name) { + let mut account = acc_lock.write().unwrap(); + renew_certificate(&crt, &mut endpoint, &mut account); + }; + } + }); + handles.push(handle); + } + for handle in handles { + let _ = handle.join(); + } + } } diff --git a/acmed/src/storage.rs b/acmed/src/storage.rs index 5f71a1f..c42a65a 100644 --- a/acmed/src/storage.rs +++ b/acmed/src/storage.rs @@ -16,280 +16,280 @@ use std::os::unix::fs::OpenOptionsExt; #[derive(Clone, Debug)] pub struct FileManager { - pub account_name: String, - pub account_directory: String, - pub crt_name: String, - pub crt_name_format: String, - pub crt_directory: String, - pub crt_key_type: String, - pub cert_file_mode: u32, - pub cert_file_owner: Option, - pub cert_file_group: Option, - pub pk_file_mode: u32, - pub pk_file_owner: Option, - pub pk_file_group: Option, - pub hooks: Vec, - pub env: HashMap, + pub account_name: String, + pub account_directory: String, + pub crt_name: String, + pub crt_name_format: String, + pub crt_directory: String, + pub crt_key_type: String, + pub cert_file_mode: u32, + pub cert_file_owner: Option, + pub cert_file_group: Option, + pub pk_file_mode: u32, + pub pk_file_owner: Option, + pub pk_file_group: Option, + pub hooks: Vec, + pub env: HashMap, } impl HasLogger for FileManager { - fn warn(&self, msg: &str) { - log::warn!("{}: {}", self, msg); - } + fn warn(&self, msg: &str) { + log::warn!("{}: {}", self, msg); + } - fn info(&self, msg: &str) { - log::info!("{}: {}", self, msg); - } + fn info(&self, msg: &str) { + log::info!("{}: {}", self, msg); + } - fn debug(&self, msg: &str) { - log::debug!("{}: {}", self, msg); - } + fn debug(&self, msg: &str) { + log::debug!("{}: {}", self, msg); + } - fn trace(&self, msg: &str) { - log::trace!("{}: {}", self, msg); - } + fn trace(&self, msg: &str) { + log::trace!("{}: {}", self, msg); + } } impl fmt::Display for FileManager { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = if !self.crt_name.is_empty() { - format!("certificate \"{}_{}\"", self.crt_name, self.crt_key_type) - } else { - format!("account \"{}\"", self.account_name) - }; - write!(f, "{}", s) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = if !self.crt_name.is_empty() { + format!("certificate \"{}_{}\"", self.crt_name, self.crt_key_type) + } else { + format!("account \"{}\"", self.account_name) + }; + write!(f, "{}", s) + } } #[derive(Clone)] enum FileType { - Account, - PrivateKey, - Certificate, + Account, + PrivateKey, + Certificate, } impl fmt::Display for FileType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match self { - FileType::Account => "account", - FileType::PrivateKey => "pk", - FileType::Certificate => "crt", - }; - write!(f, "{}", s) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + FileType::Account => "account", + FileType::PrivateKey => "pk", + FileType::Certificate => "crt", + }; + write!(f, "{}", s) + } } #[derive(Clone, Serialize)] pub struct CertFileFormat { - pub ext: String, - pub file_type: String, - pub key_type: String, - pub name: String, + pub ext: String, + pub file_type: String, + pub key_type: String, + pub name: String, } fn get_file_full_path( - fm: &FileManager, - file_type: FileType, + fm: &FileManager, + file_type: FileType, ) -> Result<(String, String, PathBuf), Error> { - let base_path = match file_type { - FileType::Account => &fm.account_directory, - FileType::PrivateKey => &fm.crt_directory, - FileType::Certificate => &fm.crt_directory, - }; - let file_name = match file_type { - FileType::Account => format!( - "{account}.{file_type}.{ext}", - account = b64_encode(&fm.account_name), - file_type = file_type, - ext = "bin" - ), - FileType::PrivateKey | FileType::Certificate => { - let fmt_data = CertFileFormat { - key_type: fm.crt_key_type.to_string(), - ext: "pem".into(), - file_type: file_type.to_string(), - name: fm.crt_name.to_owned(), - }; - render_template(&fm.crt_name_format, &fmt_data)? - } - }; - let mut path = PathBuf::from(&base_path); - path.push(&file_name); - Ok((base_path.to_string(), file_name, path)) + let base_path = match file_type { + FileType::Account => &fm.account_directory, + FileType::PrivateKey => &fm.crt_directory, + FileType::Certificate => &fm.crt_directory, + }; + let file_name = match file_type { + FileType::Account => format!( + "{account}.{file_type}.{ext}", + account = b64_encode(&fm.account_name), + file_type = file_type, + ext = "bin" + ), + FileType::PrivateKey | FileType::Certificate => { + let fmt_data = CertFileFormat { + key_type: fm.crt_key_type.to_string(), + ext: "pem".into(), + file_type: file_type.to_string(), + name: fm.crt_name.to_owned(), + }; + render_template(&fm.crt_name_format, &fmt_data)? + } + }; + let mut path = PathBuf::from(&base_path); + path.push(&file_name); + Ok((base_path.to_string(), file_name, path)) } fn get_file_path(fm: &FileManager, file_type: FileType) -> Result { - let (_, _, path) = get_file_full_path(fm, file_type)?; - Ok(path) + let (_, _, path) = get_file_full_path(fm, file_type)?; + Ok(path) } fn read_file(fm: &FileManager, path: &Path) -> Result, Error> { - fm.trace(&format!("reading file {:?}", path)); - let mut file = - File::open(path).map_err(|e| Error::from(e).prefix(&path.display().to_string()))?; - let mut contents = vec![]; - file.read_to_end(&mut contents)?; - Ok(contents) + fm.trace(&format!("reading file {:?}", path)); + let mut file = + File::open(path).map_err(|e| Error::from(e).prefix(&path.display().to_string()))?; + let mut contents = vec![]; + file.read_to_end(&mut contents)?; + Ok(contents) } #[cfg(unix)] fn set_owner(fm: &FileManager, path: &Path, file_type: FileType) -> Result<(), Error> { - let (uid, gid) = match file_type { - FileType::Certificate => (fm.cert_file_owner.to_owned(), fm.cert_file_group.to_owned()), - FileType::PrivateKey => (fm.pk_file_owner.to_owned(), fm.pk_file_group.to_owned()), - FileType::Account => { - // The account file does not need to be accessible to users other different from the current one. - return Ok(()); - } - }; - let uid = match uid { - Some(u) => { - if u.bytes().all(|b| b.is_ascii_digit()) { - let raw_uid = u - .parse::() - .map_err(|_| Error::from("unable to parse the UID"))?; - let nix_uid = nix::unistd::Uid::from_raw(raw_uid); - Some(nix_uid) - } else { - let user = nix::unistd::User::from_name(&u)?; - user.map(|u| u.uid) - } - } - None => None, - }; - let gid = match gid { - Some(g) => { - if g.bytes().all(|b| b.is_ascii_digit()) { - let raw_gid = g - .parse::() - .map_err(|_| Error::from("unable to parse the GID"))?; - let nix_gid = nix::unistd::Gid::from_raw(raw_gid); - Some(nix_gid) - } else { - let grp = nix::unistd::Group::from_name(&g)?; - grp.map(|g| g.gid) - } - } - None => None, - }; - match uid { - Some(u) => fm.trace(&format!("{:?}: setting the uid to {}", path, u.as_raw())), - None => fm.trace(&format!("{:?}: uid unchanged", path)), - }; - match gid { - Some(g) => fm.trace(&format!("{:?}: setting the gid to {}", path, g.as_raw())), - None => fm.trace(&format!("{:?}: gid unchanged", path)), - }; - match nix::unistd::chown(path, uid, gid) { - Ok(_) => Ok(()), - Err(e) => Err(format!("{}", e).into()), - } + let (uid, gid) = match file_type { + FileType::Certificate => (fm.cert_file_owner.to_owned(), fm.cert_file_group.to_owned()), + FileType::PrivateKey => (fm.pk_file_owner.to_owned(), fm.pk_file_group.to_owned()), + FileType::Account => { + // The account file does not need to be accessible to users other different from the current one. + return Ok(()); + } + }; + let uid = match uid { + Some(u) => { + if u.bytes().all(|b| b.is_ascii_digit()) { + let raw_uid = u + .parse::() + .map_err(|_| Error::from("unable to parse the UID"))?; + let nix_uid = nix::unistd::Uid::from_raw(raw_uid); + Some(nix_uid) + } else { + let user = nix::unistd::User::from_name(&u)?; + user.map(|u| u.uid) + } + } + None => None, + }; + let gid = match gid { + Some(g) => { + if g.bytes().all(|b| b.is_ascii_digit()) { + let raw_gid = g + .parse::() + .map_err(|_| Error::from("unable to parse the GID"))?; + let nix_gid = nix::unistd::Gid::from_raw(raw_gid); + Some(nix_gid) + } else { + let grp = nix::unistd::Group::from_name(&g)?; + grp.map(|g| g.gid) + } + } + None => None, + }; + match uid { + Some(u) => fm.trace(&format!("{:?}: setting the uid to {}", path, u.as_raw())), + None => fm.trace(&format!("{:?}: uid unchanged", path)), + }; + match gid { + Some(g) => fm.trace(&format!("{:?}: setting the gid to {}", path, g.as_raw())), + None => fm.trace(&format!("{:?}: gid unchanged", path)), + }; + match nix::unistd::chown(path, uid, gid) { + Ok(_) => Ok(()), + Err(e) => Err(format!("{}", e).into()), + } } fn write_file(fm: &FileManager, file_type: FileType, data: &[u8]) -> Result<(), Error> { - let (file_directory, file_name, path) = get_file_full_path(fm, file_type.clone())?; - let mut hook_data = FileStorageHookData { - file_name, - file_directory, - file_path: path.to_owned(), - env: HashMap::new(), - }; - hook_data.set_env(&fm.env); - let is_new = !path.is_file(); + let (file_directory, file_name, path) = get_file_full_path(fm, file_type.clone())?; + let mut hook_data = FileStorageHookData { + file_name, + file_directory, + file_path: path.to_owned(), + env: HashMap::new(), + }; + hook_data.set_env(&fm.env); + let is_new = !path.is_file(); - if is_new { - hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePreCreate)?; - } else { - hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePreEdit)?; - } + if is_new { + hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePreCreate)?; + } else { + hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePreEdit)?; + } - fm.trace(&format!("writing file {:?}", path)); - let mut file = if cfg!(unix) { - let mut options = OpenOptions::new(); - options.mode(match &file_type { - FileType::Certificate => fm.cert_file_mode, - FileType::PrivateKey => fm.pk_file_mode, - FileType::Account => crate::DEFAULT_ACCOUNT_FILE_MODE, - }); - options - .write(true) - .create(true) - .open(&path) - .map_err(|e| Error::from(e).prefix(&path.display().to_string()))? - } else { - File::create(&path).map_err(|e| Error::from(e).prefix(&path.display().to_string()))? - }; - file.write_all(data) - .map_err(|e| Error::from(e).prefix(&path.display().to_string()))?; - if cfg!(unix) { - set_owner(fm, &path, file_type).map_err(|e| e.prefix(&path.display().to_string()))?; - } + fm.trace(&format!("writing file {:?}", path)); + let mut file = if cfg!(unix) { + let mut options = OpenOptions::new(); + options.mode(match &file_type { + FileType::Certificate => fm.cert_file_mode, + FileType::PrivateKey => fm.pk_file_mode, + FileType::Account => crate::DEFAULT_ACCOUNT_FILE_MODE, + }); + options + .write(true) + .create(true) + .open(&path) + .map_err(|e| Error::from(e).prefix(&path.display().to_string()))? + } else { + File::create(&path).map_err(|e| Error::from(e).prefix(&path.display().to_string()))? + }; + file.write_all(data) + .map_err(|e| Error::from(e).prefix(&path.display().to_string()))?; + if cfg!(unix) { + set_owner(fm, &path, file_type).map_err(|e| e.prefix(&path.display().to_string()))?; + } - if is_new { - hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePostCreate)?; - } else { - hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePostEdit)?; - } - Ok(()) + if is_new { + hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePostCreate)?; + } else { + hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePostEdit)?; + } + Ok(()) } pub fn get_account_data(fm: &FileManager) -> Result, Error> { - let path = get_file_path(fm, FileType::Account)?; - read_file(fm, &path) + let path = get_file_path(fm, FileType::Account)?; + read_file(fm, &path) } pub fn set_account_data(fm: &FileManager, data: &[u8]) -> Result<(), Error> { - write_file(fm, FileType::Account, data) + write_file(fm, FileType::Account, data) } pub fn get_keypair(fm: &FileManager) -> Result { - let path = get_file_path(fm, FileType::PrivateKey)?; - let raw_key = read_file(fm, &path)?; - let key = KeyPair::from_pem(&raw_key)?; - Ok(key) + let path = get_file_path(fm, FileType::PrivateKey)?; + let raw_key = read_file(fm, &path)?; + let key = KeyPair::from_pem(&raw_key)?; + Ok(key) } pub fn set_keypair(fm: &FileManager, key_pair: &KeyPair) -> Result<(), Error> { - let data = key_pair.private_key_to_pem()?; - write_file(fm, FileType::PrivateKey, &data) + let data = key_pair.private_key_to_pem()?; + write_file(fm, FileType::PrivateKey, &data) } pub fn get_certificate(fm: &FileManager) -> Result { - let path = get_file_path(fm, FileType::Certificate)?; - let raw_crt = read_file(fm, &path)?; - let crt = X509Certificate::from_pem(&raw_crt)?; - Ok(crt) + let path = get_file_path(fm, FileType::Certificate)?; + let raw_crt = read_file(fm, &path)?; + let crt = X509Certificate::from_pem(&raw_crt)?; + Ok(crt) } pub fn write_certificate(fm: &FileManager, data: &[u8]) -> Result<(), Error> { - write_file(fm, FileType::Certificate, data) + write_file(fm, FileType::Certificate, data) } fn check_files(fm: &FileManager, file_types: &[FileType]) -> bool { - for t in file_types.iter().cloned() { - let path = match get_file_path(fm, t) { - Ok(p) => p, - Err(_) => { - return false; - } - }; - fm.trace(&format!( - "testing file path: {}", - path.to_str().unwrap_or_default() - )); - if !path.is_file() { - return false; - } - } - true + for t in file_types.iter().cloned() { + let path = match get_file_path(fm, t) { + Ok(p) => p, + Err(_) => { + return false; + } + }; + fm.trace(&format!( + "testing file path: {}", + path.to_str().unwrap_or_default() + )); + if !path.is_file() { + return false; + } + } + true } pub fn account_files_exists(fm: &FileManager) -> bool { - let file_types = vec![FileType::Account]; - check_files(fm, &file_types) + let file_types = vec![FileType::Account]; + check_files(fm, &file_types) } pub fn certificate_files_exists(fm: &FileManager) -> bool { - let file_types = vec![FileType::PrivateKey, FileType::Certificate]; - check_files(fm, &file_types) + let file_types = vec![FileType::PrivateKey, FileType::Certificate]; + check_files(fm, &file_types) } diff --git a/acmed/src/template.rs b/acmed/src/template.rs index 61d605a..976f79e 100644 --- a/acmed/src/template.rs +++ b/acmed/src/template.rs @@ -4,70 +4,70 @@ use serde_json::Value; use tinytemplate::TinyTemplate; macro_rules! default_format { - ($value: ident, $output: ident) => {{ - $output.push_str(&$value.to_string()); - Ok(()) - }}; + ($value: ident, $output: ident) => {{ + $output.push_str(&$value.to_string()); + Ok(()) + }}; } fn formatter_rev_labels(value: &Value, output: &mut String) -> tinytemplate::error::Result<()> { - match value { - Value::Null => Ok(()), - Value::Bool(v) => default_format!(v, output), - Value::Number(v) => default_format!(v, output), - Value::String(v) => { - let s = v.rsplit('.').collect::>().join("."); - output.push_str(&s); - Ok(()) - } - _ => Ok(()), - } + match value { + Value::Null => Ok(()), + Value::Bool(v) => default_format!(v, output), + Value::Number(v) => default_format!(v, output), + Value::String(v) => { + let s = v.rsplit('.').collect::>().join("."); + output.push_str(&s); + Ok(()) + } + _ => Ok(()), + } } pub fn render_template(template: &str, data: &T) -> Result where - T: Serialize, + T: Serialize, { - let mut reg = TinyTemplate::new(); - reg.add_formatter("rev_labels", formatter_rev_labels); - reg.add_template("reg", template)?; - Ok(reg.render("reg", data)?) + let mut reg = TinyTemplate::new(); + reg.add_formatter("rev_labels", formatter_rev_labels); + reg.add_template("reg", template)?; + Ok(reg.render("reg", data)?) } #[cfg(test)] mod tests { - use super::render_template; - use serde::Serialize; + use super::render_template; + use serde::Serialize; - #[derive(Serialize)] - struct TplTest { - foo: String, - bar: u64, - } + #[derive(Serialize)] + struct TplTest { + foo: String, + bar: u64, + } - #[test] - fn test_basic_template() { - let c = TplTest { - foo: String::from("test"), - bar: 42, - }; - let tpl = "This is { foo } { bar -} !"; - let rendered = render_template(tpl, &c); - assert!(rendered.is_ok()); - let rendered = rendered.unwrap(); - assert_eq!(rendered, "This is test 42!"); - } + #[test] + fn test_basic_template() { + let c = TplTest { + foo: String::from("test"), + bar: 42, + }; + let tpl = "This is { foo } { bar -} !"; + let rendered = render_template(tpl, &c); + assert!(rendered.is_ok()); + let rendered = rendered.unwrap(); + assert_eq!(rendered, "This is test 42!"); + } - #[test] - fn test_formatter_rev_labels() { - let c = TplTest { - foo: String::from("mx1.example.org"), - bar: 42, - }; - let tpl = "{ foo } - { foo | rev_labels }"; - let rendered = render_template(tpl, &c); - assert!(rendered.is_ok()); - let rendered = rendered.unwrap(); - assert_eq!(rendered, "mx1.example.org - org.example.mx1"); - } + #[test] + fn test_formatter_rev_labels() { + let c = TplTest { + foo: String::from("mx1.example.org"), + bar: 42, + }; + let tpl = "{ foo } - { foo | rev_labels }"; + let rendered = render_template(tpl, &c); + assert!(rendered.is_ok()); + let rendered = rendered.unwrap(); + assert_eq!(rendered, "mx1.example.org - org.example.mx1"); + } } diff --git a/rustfmt.toml b/rustfmt.toml index e69de29..218e203 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/tacd/build.rs b/tacd/build.rs index c9f0947..0aa9929 100644 --- a/tacd/build.rs +++ b/tacd/build.rs @@ -2,39 +2,39 @@ use std::env; use std::path::PathBuf; macro_rules! set_rustc_env_var { - ($name: expr, $value: expr) => {{ - println!("cargo:rustc-env={}={}", $name, $value); - }}; + ($name: expr, $value: expr) => {{ + println!("cargo:rustc-env={}={}", $name, $value); + }}; } macro_rules! set_env_var_if_absent { - ($name: expr, $default_value: expr) => {{ - if let Err(_) = env::var($name) { - set_rustc_env_var!($name, $default_value); - } - }}; + ($name: expr, $default_value: expr) => {{ + if let Err(_) = env::var($name) { + set_rustc_env_var!($name, $default_value); + } + }}; } macro_rules! set_specific_path_if_absent { - ($env_name: expr, $env_default: expr, $name: expr, $default_value: expr) => {{ - let prefix = env::var($env_name).unwrap_or(String::from($env_default)); - let mut value = PathBuf::new(); - value.push(prefix); - value.push($default_value); - set_env_var_if_absent!($name, value.to_str().unwrap()); - }}; + ($env_name: expr, $env_default: expr, $name: expr, $default_value: expr) => {{ + let prefix = env::var($env_name).unwrap_or(String::from($env_default)); + let mut value = PathBuf::new(); + value.push(prefix); + value.push($default_value); + set_env_var_if_absent!($name, value.to_str().unwrap()); + }}; } macro_rules! set_runstate_path_if_absent { - ($name: expr, $default_value: expr) => {{ - set_specific_path_if_absent!("RUNSTATEDIR", "/run", $name, $default_value); - }}; + ($name: expr, $default_value: expr) => {{ + set_specific_path_if_absent!("RUNSTATEDIR", "/run", $name, $default_value); + }}; } fn main() { - if let Ok(target) = env::var("TARGET") { - println!("cargo:rustc-env=TACD_TARGET={}", target); - }; + if let Ok(target) = env::var("TARGET") { + println!("cargo:rustc-env=TACD_TARGET={}", target); + }; - set_runstate_path_if_absent!("TACD_DEFAULT_PID_FILE", "tacd.pid"); + set_runstate_path_if_absent!("TACD_DEFAULT_PID_FILE", "tacd.pid"); } diff --git a/tacd/src/main.rs b/tacd/src/main.rs index b47e13a..6e91be7 100644 --- a/tacd/src/main.rs +++ b/tacd/src/main.rs @@ -22,69 +22,69 @@ const DEFAULT_CRT_DIGEST: HashFunction = HashFunction::Sha256; const ALPN_ACME_PROTO_NAME: &[u8] = b"\x0aacme-tls/1"; fn read_line(path: Option<&String>) -> Result { - let mut input = String::new(); - match path { - Some(p) => File::open(p)?.read_to_string(&mut input)?, - None => io::stdin().read_line(&mut input)?, - }; - let line = input.trim().to_string(); - Ok(line) + let mut input = String::new(); + match path { + Some(p) => File::open(p)?.read_to_string(&mut input)?, + None => io::stdin().read_line(&mut input)?, + }; + let line = input.trim().to_string(); + Ok(line) } fn get_acme_value(cnf: &ArgMatches, opt: &str, opt_file: &str) -> Result { - match cnf.get_one::(opt) { - Some(v) => Ok(v.to_string()), - None => { - debug!( - "reading {} from {}", - opt, - cnf.get_one::(opt_file) - .map(|e| e.as_str()) - .unwrap_or("stdin") - ); - read_line(cnf.get_one::(opt_file)) - } - } + match cnf.get_one::(opt) { + Some(v) => Ok(v.to_string()), + None => { + debug!( + "reading {} from {}", + opt, + cnf.get_one::(opt_file) + .map(|e| e.as_str()) + .unwrap_or("stdin") + ); + read_line(cnf.get_one::(opt_file)) + } + } } fn init(cnf: &ArgMatches) -> Result<(), Error> { - acme_common::init_server( - cnf.get_flag("foreground"), - cnf.get_one::("pid-file").map(|e| e.as_str()), - ); - let domain = get_acme_value(cnf, "domain", "domain-file")?; - let domain = to_idna(&domain)?; - let ext = get_acme_value(cnf, "acme-ext", "acme-ext-file")?; - let listen_addr = cnf - .get_one::("listen") - .map(|e| e.as_str()) - .unwrap_or(DEFAULT_LISTEN_ADDR); - let crt_signature_alg = match cnf.get_one::<&str>("crt-signature-alg") { - Some(alg) => alg.parse()?, - None => DEFAULT_CRT_KEY_TYPE, - }; - let crt_digest = match cnf.get_one::<&str>("crt-digest") { - Some(alg) => alg.parse()?, - None => DEFAULT_CRT_DIGEST, - }; - let (pk, cert) = X509Certificate::from_acme_ext(&domain, &ext, crt_signature_alg, crt_digest)?; - info!("starting {} on {} for {}", APP_NAME, listen_addr, domain); - server_start(listen_addr, &cert, &pk)?; - Ok(()) + acme_common::init_server( + cnf.get_flag("foreground"), + cnf.get_one::("pid-file").map(|e| e.as_str()), + ); + let domain = get_acme_value(cnf, "domain", "domain-file")?; + let domain = to_idna(&domain)?; + let ext = get_acme_value(cnf, "acme-ext", "acme-ext-file")?; + let listen_addr = cnf + .get_one::("listen") + .map(|e| e.as_str()) + .unwrap_or(DEFAULT_LISTEN_ADDR); + let crt_signature_alg = match cnf.get_one::<&str>("crt-signature-alg") { + Some(alg) => alg.parse()?, + None => DEFAULT_CRT_KEY_TYPE, + }; + let crt_digest = match cnf.get_one::<&str>("crt-digest") { + Some(alg) => alg.parse()?, + None => DEFAULT_CRT_DIGEST, + }; + let (pk, cert) = X509Certificate::from_acme_ext(&domain, &ext, crt_signature_alg, crt_digest)?; + info!("starting {} on {} for {}", APP_NAME, listen_addr, domain); + server_start(listen_addr, &cert, &pk)?; + Ok(()) } fn main() { - let full_version = format!( - "{} built for {}\n\nCryptographic library:\n - {} {}", - APP_VERSION, - env!("TACD_TARGET"), - get_lib_name(), - get_lib_version(), - ); - let default_crt_key_type = DEFAULT_CRT_KEY_TYPE.to_string(); - let default_crt_digest = DEFAULT_CRT_DIGEST.to_string(); - let default_log_level = DEFAULT_LOG_LEVEL.to_string().to_lowercase(); - let matches = Command::new(APP_NAME) + let full_version = format!( + "{} built for {}\n\nCryptographic library:\n - {} {}", + APP_VERSION, + env!("TACD_TARGET"), + get_lib_name(), + get_lib_version(), + ); + let default_crt_key_type = DEFAULT_CRT_KEY_TYPE.to_string(); + let default_crt_digest = DEFAULT_CRT_DIGEST.to_string(); + let default_log_level = DEFAULT_LOG_LEVEL.to_string().to_lowercase(); + let matches = Command::new(APP_NAME) .version(APP_VERSION) .long_version(full_version) .arg( @@ -197,25 +197,25 @@ fn main() { ) .get_matches(); - match set_log_system( - matches.get_one::("log-level").map(|e| e.as_str()), - matches.get_flag("to-syslog"), - matches.get_flag("to-stderr"), - ) { - Ok(_) => {} - Err(e) => { - eprintln!("Error: {}", e); - std::process::exit(2); - } - }; + match set_log_system( + matches.get_one::("log-level").map(|e| e.as_str()), + matches.get_flag("to-syslog"), + matches.get_flag("to-stderr"), + ) { + Ok(_) => {} + Err(e) => { + eprintln!("Error: {}", e); + std::process::exit(2); + } + }; - match init(&matches) { - Ok(_) => {} - Err(e) => { - error!("{}", e); - let pid_file = matches.get_one::("pid-file").map(|e| e.as_str()); - let _ = clean_pid_file(pid_file); - std::process::exit(1); - } - }; + match init(&matches) { + Ok(_) => {} + Err(e) => { + error!("{}", e); + let pid_file = matches.get_one::("pid-file").map(|e| e.as_str()); + let _ = clean_pid_file(pid_file); + std::process::exit(1); + } + }; } diff --git a/tacd/src/openssl_server.rs b/tacd/src/openssl_server.rs index 2213e41..df26cb1 100644 --- a/tacd/src/openssl_server.rs +++ b/tacd/src/openssl_server.rs @@ -15,41 +15,41 @@ const ALPN_ERROR: AlpnError = AlpnError::ALERT_FATAL; const ALPN_ERROR: AlpnError = AlpnError::NOACK; macro_rules! listen_and_accept { - ($lt: ident, $addr: ident, $acceptor: ident) => { - let listener = $lt::bind($addr)?; - for stream in listener.incoming() { - if let Ok(stream) = stream { - let acceptor = $acceptor.clone(); - thread::spawn(move || { - debug!("new client"); - let _ = acceptor.accept(stream).unwrap(); - }); - }; - } - }; + ($lt: ident, $addr: ident, $acceptor: ident) => { + let listener = $lt::bind($addr)?; + for stream in listener.incoming() { + if let Ok(stream) = stream { + let acceptor = $acceptor.clone(); + thread::spawn(move || { + debug!("new client"); + let _ = acceptor.accept(stream).unwrap(); + }); + }; + } + }; } pub fn start( - listen_addr: &str, - certificate: &X509Certificate, - key_pair: &KeyPair, + listen_addr: &str, + certificate: &X509Certificate, + key_pair: &KeyPair, ) -> Result<(), Error> { - let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?; - acceptor.set_alpn_select_callback(|_, client| { - debug!("ALPN negociation"); - ssl::select_next_proto(crate::ALPN_ACME_PROTO_NAME, client).ok_or(ALPN_ERROR) - }); - acceptor.set_private_key(&key_pair.inner_key)?; - acceptor.set_certificate(&certificate.inner_cert)?; - acceptor.check_private_key()?; - let acceptor = Arc::new(acceptor.build()); - if cfg!(unix) && listen_addr.starts_with("unix:") { - let listen_addr = &listen_addr[5..]; - debug!("listening on unix socket {}", listen_addr); - listen_and_accept!(UnixListener, listen_addr, acceptor); - } else { - debug!("listening on {}", listen_addr); - listen_and_accept!(TcpListener, listen_addr, acceptor); - } - Err("main thread loop unexpectedly exited".into()) + let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?; + acceptor.set_alpn_select_callback(|_, client| { + debug!("ALPN negociation"); + ssl::select_next_proto(crate::ALPN_ACME_PROTO_NAME, client).ok_or(ALPN_ERROR) + }); + acceptor.set_private_key(&key_pair.inner_key)?; + acceptor.set_certificate(&certificate.inner_cert)?; + acceptor.check_private_key()?; + let acceptor = Arc::new(acceptor.build()); + if cfg!(unix) && listen_addr.starts_with("unix:") { + let listen_addr = &listen_addr[5..]; + debug!("listening on unix socket {}", listen_addr); + listen_and_accept!(UnixListener, listen_addr, acceptor); + } else { + debug!("listening on {}", listen_addr); + listen_and_accept!(TcpListener, listen_addr, acceptor); + } + Err("main thread loop unexpectedly exited".into()) }