diff --git a/acmed/src/account.rs b/acmed/src/account.rs new file mode 100644 index 0000000..ea4042c --- /dev/null +++ b/acmed/src/account.rs @@ -0,0 +1,35 @@ +use acme_common::crypto::{JwsSignatureAlgorithm, KeyType}; +use acme_common::error::Error; +use std::str::FromStr; + +#[derive(Clone, Debug)] +pub struct Account { + pub name: String, + pub email: String, + pub key_type: KeyType, + pub signature_algorithm: JwsSignatureAlgorithm, +} + +impl Account { + pub fn new( + name: &str, + email: &str, + key_type: &Option, + signature_algorithm: &Option, + ) -> Result { + let key_type = match key_type { + Some(kt) => KeyType::from_str(&kt)?, + None => crate::DEFAULT_ACCOUNT_KEY_TYPE, + }; + let signature_algorithm = match signature_algorithm { + Some(sa) => JwsSignatureAlgorithm::from_str(&sa)?, + None => key_type.get_default_signature_alg(), + }; + Ok(crate::account::Account { + name: name.to_string(), + email: email.to_string(), + key_type, + signature_algorithm, + }) + } +} diff --git a/acmed/src/acme_proto/account.rs b/acmed/src/acme_proto/account.rs index 2e955fd..c24ae4a 100644 --- a/acmed/src/acme_proto/account.rs +++ b/acmed/src/acme_proto/account.rs @@ -22,7 +22,7 @@ impl AccountManager { ) -> Result { // TODO: store the key id (account url) let key_pair = storage::get_account_keypair(cert)?; - let signature_algorithm = endpoint.signature_algorithm; + let signature_algorithm = cert.account.signature_algorithm; let kp_ref = &key_pair; let account = Account::new(cert, endpoint); let account = serde_json::to_string(&account)?; @@ -42,20 +42,28 @@ impl AccountManager { } } -pub fn init_account(cert: &Certificate, endpoint: &Endpoint) -> Result<(), Error> { +pub fn init_account(cert: &Certificate) -> Result<(), Error> { if !storage::account_files_exists(cert) { - let key_pair = gen_keypair(endpoint.key_type)?; + cert.info(&format!( + "Account {} does not exists. Creating it.", + &cert.account.name + )); + let key_pair = gen_keypair(cert.account.key_type)?; storage::set_account_keypair(cert, &key_pair)?; - cert.info(&format!("Account {} created", &cert.account.name)); + cert.debug(&format!("Account {} created.", &cert.account.name)); } else { let key_pair = storage::get_account_keypair(cert)?; - if key_pair.key_type != endpoint.key_type { - cert.debug(&format!("Account {} has a key pair of type {} while {} was expected. Creating a new {} key pair.", &cert.account.name, key_pair.key_type, endpoint.key_type, endpoint.key_type)); - // TODO: Do a propper key rollover? - let key_pair = gen_keypair(endpoint.key_type)?; + if key_pair.key_type != cert.account.key_type { + cert.info(&format!("Account {name} has a key pair of type {kt_has} while {kt_want} was expected. Creating a new {kt_want} key pair.", name=&cert.account.name, kt_has=key_pair.key_type, kt_want=cert.account.key_type)); + // TODO: Do a propper key rollover + let key_pair = gen_keypair(cert.account.key_type)?; storage::set_account_keypair(cert, &key_pair)?; + cert.debug(&format!( + "Account {} updated with a new {} key pair.", + &cert.account.name, cert.account.key_type + )); } else { - cert.debug(&format!("Account {} already exists", &cert.account.name)); + cert.debug(&format!("Account {} already exists.", &cert.account.name)); } } Ok(()) diff --git a/acmed/src/certificate.rs b/acmed/src/certificate.rs index 96233b0..bd1e72e 100644 --- a/acmed/src/certificate.rs +++ b/acmed/src/certificate.rs @@ -1,5 +1,5 @@ +use crate::account::Account; use crate::acme_proto::Challenge; -use crate::config::Account; use crate::hooks::{self, ChallengeHookData, Hook, HookEnvData, HookType, PostOperationHookData}; use crate::identifier::{Identifier, IdentifierType}; use crate::storage::{certificate_files_exists, get_certificate}; diff --git a/acmed/src/config.rs b/acmed/src/config.rs index 3b1e8ea..0064fd8 100644 --- a/acmed/src/config.rs +++ b/acmed/src/config.rs @@ -195,8 +195,6 @@ pub struct Endpoint { pub tos_agreed: bool, #[serde(default)] pub rate_limits: Vec, - pub key_type: Option, - pub signature_algorithm: Option, pub renew_delay: Option, } @@ -217,14 +215,7 @@ impl Endpoint { let (nb, timeframe) = cnf.get_rate_limit(&rl_name)?; limits.push((nb, timeframe)); } - crate::endpoint::Endpoint::new( - &self.name, - &self.url, - self.tos_agreed, - &limits, - &self.key_type, - &self.signature_algorithm, - ) + crate::endpoint::Endpoint::new(&self.name, &self.url, self.tos_agreed, &limits) } } @@ -285,6 +276,19 @@ pub struct Group { pub struct Account { pub name: String, pub email: String, + pub key_type: Option, + pub signature_algorithm: Option, +} + +impl Account { + pub fn to_generic(&self) -> Result { + crate::account::Account::new( + &self.name, + &self.email, + &self.key_type, + &self.signature_algorithm, + ) + } } #[derive(Deserialize)] @@ -306,10 +310,11 @@ pub struct Certificate { } impl Certificate { - pub fn get_account(&self, cnf: &Config) -> Result { + pub fn get_account(&self, cnf: &Config) -> Result { for account in cnf.account.iter() { if account.name == self.account { - return Ok(account.clone()); + let acc = account.to_generic()?; + return Ok(acc); } } Err(format!("{}: account not found", self.account).into()) diff --git a/acmed/src/endpoint.rs b/acmed/src/endpoint.rs index 68a9317..db7c239 100644 --- a/acmed/src/endpoint.rs +++ b/acmed/src/endpoint.rs @@ -1,9 +1,7 @@ use crate::acme_proto::structs::Directory; use crate::duration::parse_duration; -use acme_common::crypto::{JwsSignatureAlgorithm, KeyType}; use acme_common::error::Error; use std::cmp; -use std::str::FromStr; use std::thread; use std::time::{Duration, Instant}; @@ -15,8 +13,6 @@ pub struct Endpoint { pub nonce: Option, pub rl: RateLimit, pub dir: Directory, - pub key_type: KeyType, - pub signature_algorithm: JwsSignatureAlgorithm, } impl Endpoint { @@ -25,25 +21,13 @@ impl Endpoint { url: &str, tos_agreed: bool, limits: &[(usize, String)], - key_type: &Option, - signature_algorithm: &Option, ) -> Result { - let rl = RateLimit::new(limits)?; - let key_type = match key_type { - Some(kt) => KeyType::from_str(&kt)?, - None => crate::DEFAULT_ACCOUNT_KEY_TYPE, - }; - let signature_algorithm = match signature_algorithm { - Some(sa) => JwsSignatureAlgorithm::from_str(&sa)?, - None => key_type.get_default_signature_alg(), - }; - let _ = key_type.check_alg_compatibility(&signature_algorithm)?; Ok(Self { name: name.to_string(), url: url.to_string(), tos_agreed, nonce: None, - rl, + rl: RateLimit::new(limits)?, dir: Directory { meta: None, new_nonce: String::new(), @@ -53,8 +37,6 @@ impl Endpoint { revoke_cert: String::new(), key_change: String::new(), }, - key_type, - signature_algorithm, }) } } diff --git a/acmed/src/main.rs b/acmed/src/main.rs index d9af971..0eae67f 100644 --- a/acmed/src/main.rs +++ b/acmed/src/main.rs @@ -3,6 +3,7 @@ use acme_common::{clean_pid_file, crypto, init_server}; use clap::{App, Arg}; use log::error; +mod account; mod acme_proto; mod certificate; mod config; diff --git a/acmed/src/main_event_loop.rs b/acmed/src/main_event_loop.rs index 00488fa..76f0d6d 100644 --- a/acmed/src/main_event_loop.rs +++ b/acmed/src/main_event_loop.rs @@ -65,10 +65,10 @@ impl MainEventLoop { id: i + 1, renew_delay: crt.get_renew_delay(&cnf)?, }; - init_account(&cert, &endpoint)?; endpoints .entry(endpoint_name) .or_insert_with(|| Arc::new(RwLock::new(endpoint))); + init_account(&cert)?; certs.push(cert); } diff --git a/man/en/acmed.toml.5 b/man/en/acmed.toml.5 index f5aa4ce..56bdc2c 100644 --- a/man/en/acmed.toml.5 +++ b/man/en/acmed.toml.5 @@ -95,29 +95,6 @@ Set whether or not the user agrees to the Terms Of Service .Pq TOS . .It Cm url Ar string The endpoint's directory URL. -.It Cm key_type Ar string -Name of the asymmetric cryptography algorithm used to generate the client account's key pair. Possible values are : -.Bl -dash -compact -.It -rsa2048 -.It -rsa4096 -.It -ecdsa_p256 -.Aq default -.It -ecdsa_p384 -.El -.It Cm signature_algorithm Ar string -Name of the signature algorithm used to sign the messages sent to the endpoint. The default value is derived from the key type. Possible values are: -.Bl -dash -compact -.It -RS256 -.It -ES256 -.It -ES384 -.El .It Cm renew_delay Ar string Period of time between the certificate renewal and its expiration date. The format is described in the .Sx TIME PERIODS @@ -182,12 +159,37 @@ The name the group is registered under. This name is considered as a hook name. Array containing the names of the hooks that are grouped. The hooks are guaranteed to be called sequentially in the declaration order. .El .It Ic account -Array of table representing an account on one or several CA. +Array of table representing an account on one or several endpoint. .Bl -tag .It Ic name Ar string The name the account is registered under. Must be unique. .It Ic email Ar string The email address used to contact the account's holder. +.It Cm key_type Ar string +Name of the asymmetric cryptography algorithm used to generate the key pair. Possible values are : +.Bl -dash -compact +.It +rsa2048 +.It +rsa4096 +.It +ecdsa_p256 +.Aq default +.It +ecdsa_p384 +.El +.It Cm signature_algorithm Ar string +Name of the signature algorithm used to sign the messages sent to the endpoint as defined in +.Em RFC 7518 . +The default value is derived from the key type. Possible values are: +.Bl -dash -compact +.It +RS256 +.It +ES256 +.It +ES384 +.El .El .It Ic certificate Array of table representing a certificate that will be requested to a CA.