Browse Source

Bring async to the hooks and the file storage

pull/88/head
Rodolphe Bréard 2 years ago
parent
commit
a2084c10a8
  1. 1
      acmed/Cargo.toml
  2. 28
      acmed/src/account.rs
  3. 18
      acmed/src/account/storage.rs
  4. 25
      acmed/src/acme_proto.rs
  5. 20
      acmed/src/acme_proto/account.rs
  6. 16
      acmed/src/acme_proto/certificate.rs
  7. 20
      acmed/src/certificate.rs
  8. 6
      acmed/src/config.rs
  9. 22
      acmed/src/hooks.rs
  10. 2
      acmed/src/main.rs
  11. 11
      acmed/src/main_event_loop.rs
  12. 56
      acmed/src/storage.rs

1
acmed/Cargo.toml

@ -22,6 +22,7 @@ openssl_vendored = ["crypto_openssl", "acme_common/openssl_vendored", "attohttpc
[dependencies]
acme_common = { path = "../acme_common" }
async-lock = "2.6"
async-process = "1.6"
attohttpc = { version = "0.24", default-features = false, features = ["charsets", "json"] }
bincode = "1.3"
clap = { version = "4.0", features = ["string"] }

28
acmed/src/account.rs

@ -151,7 +151,7 @@ impl Account {
Err("key not found".into())
}
pub fn load(
pub async fn load(
file_manager: &FileManager,
name: &str,
contacts: &[(String, String)],
@ -172,9 +172,9 @@ impl Account {
None => key_type.get_default_signature_alg(),
};
key_type.check_alg_compatibility(&signature_algorithm)?;
let account = match storage::fetch(file_manager, name)? {
let account = match storage::fetch(file_manager, name).await? {
Some(mut a) => {
a.update_keys(key_type, signature_algorithm)?;
a.update_keys(key_type, signature_algorithm).await?;
a.contacts = contacts;
a.external_account = external_account.to_owned();
a
@ -202,7 +202,7 @@ impl Account {
.or_insert_with(AccountEndpoint::new);
}
pub fn synchronize(&mut self, endpoint: &mut Endpoint) -> Result<(), Error> {
pub async 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 {
@ -213,7 +213,7 @@ impl Account {
&endpoint.name
);
self.info(&msg);
register_account(endpoint, self)?;
register_account(endpoint, self).await?;
return Ok(());
}
}
@ -222,23 +222,23 @@ impl Account {
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)?;
update_account_contacts(endpoint, self).await?;
}
if key_changed {
update_account_key(endpoint, self)?;
update_account_key(endpoint, self).await?;
}
} else {
register_account(endpoint, self)?;
register_account(endpoint, self).await?;
}
Ok(())
}
pub fn register(&mut self, endpoint: &mut Endpoint) -> Result<(), Error> {
register_account(endpoint, self)
pub async fn register(&mut self, endpoint: &mut Endpoint) -> Result<(), Error> {
register_account(endpoint, self).await
}
pub fn save(&self) -> Result<(), Error> {
storage::save(&self.file_manager, self)
pub async fn save(&self) -> Result<(), Error> {
storage::save(&self.file_manager, self).await
}
pub fn set_account_url(&mut self, endpoint_name: &str, account_url: &str) -> Result<(), Error> {
@ -276,7 +276,7 @@ impl Account {
Ok(())
}
fn update_keys(
async fn update_keys(
&mut self,
key_type: KeyType,
signature_algorithm: JwsSignatureAlgorithm,
@ -287,7 +287,7 @@ impl Account {
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()?;
self.save().await?;
let msg = format!("new {key_type} account key created, using {signature_algorithm} as signing algorithm");
self.info(&msg);
} else {

18
acmed/src/account/storage.rs

@ -101,9 +101,9 @@ struct AccountStorage {
external_account: Option<ExternalAccountStorage>,
}
fn do_fetch(file_manager: &FileManager, name: &str) -> Result<Option<Account>, Error> {
async fn do_fetch(file_manager: &FileManager, name: &str) -> Result<Option<Account>, Error> {
if account_files_exists(file_manager) {
let data = get_account_data(file_manager)?;
let data = get_account_data(file_manager).await?;
let obj: AccountStorage = bincode::deserialize(&data[..])
.map_err(|e| Error::from(&e.to_string()).prefix(name))?;
let endpoints = obj
@ -140,7 +140,7 @@ fn do_fetch(file_manager: &FileManager, name: &str) -> Result<Option<Account>, E
}
}
fn do_save(file_manager: &FileManager, account: &Account) -> Result<(), Error> {
async fn do_save(file_manager: &FileManager, account: &Account) -> Result<(), Error> {
let endpoints: HashMap<String, AccountEndpointStorage> = account
.endpoints
.iter()
@ -170,15 +170,17 @@ fn do_save(file_manager: &FileManager, account: &Account) -> Result<(), Error> {
};
let encoded: Vec<u8> = bincode::serialize(&account_storage)
.map_err(|e| Error::from(&e.to_string()).prefix(&account.name))?;
set_account_data(file_manager, &encoded)
set_account_data(file_manager, &encoded).await
}
pub fn fetch(file_manager: &FileManager, name: &str) -> Result<Option<Account>, Error> {
do_fetch(file_manager, name).map_err(|_| {
pub async fn fetch(file_manager: &FileManager, name: &str) -> Result<Option<Account>, Error> {
do_fetch(file_manager, name).await.map_err(|_| {
format!("account \"{name}\": unable to load account file: file may be corrupted").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())
pub async fn save(file_manager: &FileManager, account: &Account) -> Result<(), Error> {
do_save(file_manager, account)
.await
.map_err(|e| format!("unable to save account file: {e}").into())
}

25
acmed/src/acme_proto.rs

@ -100,7 +100,8 @@ pub async fn request_certificate(
account_s
.write()
.await
.synchronize(&mut *(endpoint_s.write().await))?;
.synchronize(&mut *(endpoint_s.write().await))
.await?;
// Create a new order
let mut new_reg = false;
@ -117,10 +118,12 @@ pub async fn request_certificate(
}
Err(e) => {
if !new_reg && e.is_acme_err(AcmeError::AccountDoesNotExist) {
drop(data_builder);
account_s
.write()
.await
.register(&mut *(endpoint_s.write().await))?;
.register(&mut *(endpoint_s.write().await))
.await?;
new_reg = true;
} else {
return Err(HttpError::in_err(e));
@ -136,6 +139,7 @@ pub async fn request_certificate(
let auth =
http::get_authorization(&mut *(endpoint_s.write().await), &data_builder, auth_url)
.map_err(HttpError::in_err)?;
drop(data_builder);
if let Some(e) = auth.get_error() {
cert.warn(&e.prefix("error").message);
}
@ -160,7 +164,9 @@ pub async fn request_certificate(
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)?;
let mut data = cert
.call_challenge_hooks(&file_name, &proof, &identifier)
.await?;
data.0.is_clean_hook = true;
hook_datas.push(data);
@ -173,6 +179,7 @@ pub async fn request_certificate(
&chall_url,
)
.map_err(HttpError::in_err)?;
drop(data_builder);
}
}
@ -186,8 +193,10 @@ pub async fn request_certificate(
auth_url,
)
.map_err(HttpError::in_err)?;
drop(data_builder);
for (data, hook_type) in hook_datas.iter() {
cert.call_challenge_hooks_clean(data, (*hook_type).to_owned())?;
cert.call_challenge_hooks_clean(data, (*hook_type).to_owned())
.await?;
}
hook_datas.clear();
}
@ -203,9 +212,10 @@ pub async fn request_certificate(
&order_url,
)
.map_err(HttpError::in_err)?;
drop(data_builder);
// Finalize the order by sending the CSR
let key_pair = certificate::get_key_pair(cert)?;
let key_pair = certificate::get_key_pair(cert).await?;
let domains: Vec<String> = cert
.identifiers
.iter()
@ -237,6 +247,7 @@ pub async fn request_certificate(
&order.finalize,
)
.map_err(HttpError::in_err)?;
drop(data_builder);
if let Some(e) = order.get_error() {
cert.warn(&e.prefix("error").message);
}
@ -251,6 +262,7 @@ pub async fn request_certificate(
&order_url,
)
.map_err(HttpError::in_err)?;
drop(data_builder);
// Download the certificate
let crt_url = order
@ -259,7 +271,8 @@ pub async fn request_certificate(
let data_builder = set_data_builder!(account_s, endpoint_name, b"").await;
let crt = http::get_certificate(&mut *(endpoint_s.write().await), &data_builder, &crt_url)
.map_err(HttpError::in_err)?;
storage::write_certificate(&cert.file_manager, crt.as_bytes())?;
drop(data_builder);
storage::write_certificate(&cert.file_manager, crt.as_bytes()).await?;
cert.info(&format!(
"certificate renewed (identifiers: {})",

20
acmed/src/acme_proto/account.rs

@ -20,7 +20,7 @@ macro_rules! create_account_if_does_not_exist {
$endpoint.name
);
$account.debug(&msg);
return register_account($endpoint, $account);
return register_account($endpoint, $account).await;
}
_ => Err(HttpError::in_err(he.to_owned())),
},
@ -30,7 +30,10 @@ macro_rules! create_account_if_does_not_exist {
};
}
pub fn register_account(endpoint: &mut Endpoint, account: &mut BaseAccount) -> Result<(), Error> {
pub async fn register_account(
endpoint: &mut Endpoint,
account: &mut BaseAccount,
) -> Result<(), Error> {
account.debug(&format!(
"creating account on endpoint \"{}\"...",
&endpoint.name
@ -68,7 +71,7 @@ pub fn register_account(endpoint: &mut Endpoint, account: &mut BaseAccount) -> R
account.update_key_hash(&endpoint.name)?;
account.update_contacts_hash(&endpoint.name)?;
account.update_external_account_hash(&endpoint.name)?;
account.save()?;
account.save().await?;
account.info(&format!(
"account created on endpoint \"{}\"",
&endpoint.name
@ -76,7 +79,7 @@ pub fn register_account(endpoint: &mut Endpoint, account: &mut BaseAccount) -> R
Ok(())
}
pub fn update_account_contacts(
pub async fn update_account_contacts(
endpoint: &mut Endpoint,
account: &mut BaseAccount,
) -> Result<(), Error> {
@ -97,14 +100,17 @@ pub fn update_account_contacts(
account
)?;
account.update_contacts_hash(&endpoint_name)?;
account.save()?;
account.save().await?;
account.info(&format!(
"account contacts updated on endpoint \"{endpoint_name}\""
));
Ok(())
}
pub fn update_account_key(endpoint: &mut Endpoint, account: &mut BaseAccount) -> Result<(), Error> {
pub async 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}\"..."
@ -139,7 +145,7 @@ pub fn update_account_key(endpoint: &mut Endpoint, account: &mut BaseAccount) ->
account
)?;
account.update_key_hash(&endpoint_name)?;
account.save()?;
account.save().await?;
account.info(&format!(
"account key updated on endpoint \"{endpoint_name}\""
));

16
acmed/src/acme_proto/certificate.rs

@ -3,23 +3,23 @@ use crate::storage;
use acme_common::crypto::{gen_keypair, KeyPair};
use acme_common::error::Error;
fn gen_key_pair(cert: &Certificate) -> Result<KeyPair, Error> {
async fn gen_key_pair(cert: &Certificate) -> Result<KeyPair, Error> {
let key_pair = gen_keypair(cert.key_type)?;
storage::set_keypair(&cert.file_manager, &key_pair)?;
storage::set_keypair(&cert.file_manager, &key_pair).await?;
Ok(key_pair)
}
fn read_key_pair(cert: &Certificate) -> Result<KeyPair, Error> {
storage::get_keypair(&cert.file_manager)
async fn read_key_pair(cert: &Certificate) -> Result<KeyPair, Error> {
storage::get_keypair(&cert.file_manager).await
}
pub fn get_key_pair(cert: &Certificate) -> Result<KeyPair, Error> {
pub async fn get_key_pair(cert: &Certificate) -> Result<KeyPair, Error> {
if cert.kp_reuse {
match read_key_pair(cert) {
match read_key_pair(cert).await {
Ok(key_pair) => Ok(key_pair),
Err(_) => gen_key_pair(cert),
Err(_) => gen_key_pair(cert).await,
}
} else {
gen_key_pair(cert)
gen_key_pair(cert).await
}
}

20
acmed/src/certificate.rs

@ -110,7 +110,7 @@ impl Certificate {
.join(",")
}
pub fn should_renew(&self) -> Result<bool, Error> {
pub async fn should_renew(&self) -> Result<bool, Error> {
self.debug(&format!(
"checking for renewal (identifiers: {})",
self.identifier_list()
@ -119,7 +119,7 @@ impl Certificate {
self.debug("certificate does not exist: requesting one");
return Ok(true);
}
let cert = get_certificate(&self.file_manager)?;
let cert = get_certificate(&self.file_manager).await?;
let renew_ident = self.has_missing_identifiers(&cert);
if renew_ident {
@ -139,7 +139,7 @@ impl Certificate {
Ok(renew)
}
pub fn call_challenge_hooks(
pub async fn call_challenge_hooks(
&self,
file_name: &str,
proof: &str,
@ -165,19 +165,23 @@ impl Certificate {
HookType::ChallengeTlsAlpn01Clean,
),
};
hooks::call(self, &self.hooks, &hook_data, hook_type.0)?;
hooks::call(self, &self.hooks, &hook_data, hook_type.0).await?;
Ok((hook_data, hook_type.1))
}
pub fn call_challenge_hooks_clean(
pub async fn call_challenge_hooks_clean(
&self,
data: &ChallengeHookData,
hook_type: HookType,
) -> Result<(), Error> {
hooks::call(self, &self.hooks, data, hook_type)
hooks::call(self, &self.hooks, data, hook_type).await
}
pub fn call_post_operation_hooks(&self, status: &str, is_success: bool) -> Result<(), Error> {
pub async fn call_post_operation_hooks(
&self,
status: &str,
is_success: bool,
) -> Result<(), Error> {
let identifiers = self
.identifiers
.iter()
@ -191,7 +195,7 @@ impl Certificate {
env: HashMap::new(),
};
hook_data.set_env(&self.env);
hooks::call(self, &self.hooks, &hook_data, HookType::PostOperation)?;
hooks::call(self, &self.hooks, &hook_data, HookType::PostOperation).await?;
Ok(())
}
}

6
acmed/src/config.rs

@ -381,7 +381,10 @@ impl Account {
Ok(lst)
}
pub fn to_generic(&self, file_manager: &FileManager) -> Result<crate::account::Account, Error> {
pub async fn to_generic(
&self,
file_manager: &FileManager,
) -> Result<crate::account::Account, Error> {
let contacts: Vec<(String, String)> = self
.contacts
.iter()
@ -399,6 +402,7 @@ impl Account {
&self.signature_algorithm,
&external_account,
)
.await
}
}

22
acmed/src/hooks.rs

@ -2,6 +2,8 @@ pub use crate::config::HookType;
use crate::logs::HasLogger;
use crate::template::render_template;
use acme_common::error::Error;
use async_process::{Command, Stdio};
use futures::AsyncWriteExt;
use serde::Serialize;
use std::collections::hash_map::Iter;
use std::collections::{HashMap, HashSet};
@ -9,7 +11,6 @@ use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::{env, fmt};
pub trait HookEnvData {
@ -115,7 +116,7 @@ macro_rules! get_hook_output {
}};
}
fn call_single<L, T>(logger: &L, data: &T, hook: &Hook) -> Result<(), Error>
async fn call_single<L, T>(logger: &L, data: &T, hook: &Hook) -> Result<(), Error>
where
L: HasLogger,
T: Clone + HookEnvData + Serialize,
@ -161,7 +162,7 @@ where
let data_in = render_template(s, &data)?;
logger.trace(&format!("hook \"{}\": string stdin: {data_in}", hook.name));
let stdin = cmd.stdin.as_mut().ok_or("stdin not found")?;
stdin.write_all(data_in.as_bytes())?;
stdin.write_all(data_in.as_bytes()).await?;
}
HookStdin::File(f) => {
let file_name = render_template(f, &data)?;
@ -171,13 +172,13 @@ where
let buf_reader = BufReader::new(file);
for line in buf_reader.lines() {
let line = format!("{}\n", line?);
stdin.write_all(line.as_bytes())?;
stdin.write_all(line.as_bytes()).await?;
}
}
HookStdin::None => {}
}
// TODO: add a timeout
let status = cmd.wait()?;
let status = cmd.status().await?;
if !status.success() && !hook.allow_failure {
let msg = match status.code() {
Some(code) => format!("unrecoverable failure: code {code}").into(),
@ -192,13 +193,20 @@ where
Ok(())
}
pub fn call<L, T>(logger: &L, hooks: &[Hook], data: &T, hook_type: HookType) -> Result<(), Error>
pub async fn call<L, T>(
logger: &L,
hooks: &[Hook],
data: &T,
hook_type: HookType,
) -> Result<(), Error>
where
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))?;
call_single(logger, data, hook)
.await
.map_err(|e| e.prefix(&hook.name))?;
}
Ok(())
}

2
acmed/src/main.rs

@ -168,7 +168,7 @@ async fn inner_main() {
init_server(matches.get_flag("foreground"), pid_file);
let mut srv = match MainEventLoop::new(config_file, &root_certs) {
let mut srv = match MainEventLoop::new(config_file, &root_certs).await {
Ok(s) => s,
Err(e) => {
error!("{e}");

11
acmed/src/main_event_loop.rs

@ -23,7 +23,7 @@ pub struct MainEventLoop {
}
impl MainEventLoop {
pub fn new(config_file: &str, root_certs: &[&str]) -> Result<Self, Error> {
pub async fn new(config_file: &str, root_certs: &[&str]) -> Result<Self, Error> {
let cnf = config::from_file(config_file)?;
let file_hooks = vec![
HookType::FilePreCreate,
@ -68,7 +68,7 @@ impl MainEventLoop {
.collect(),
env: acc.env.clone(),
};
let account = acc.to_generic(&fm)?;
let account = acc.to_generic(&fm).await?;
let name = acc.name.clone();
accounts.insert(name, account);
}
@ -180,7 +180,7 @@ async fn renew_certificate(
endpoint_s: EndpointSync,
) -> (&mut Certificate, AccountSync, EndpointSync) {
loop {
match certificate.should_renew() {
match certificate.should_renew().await {
Ok(true) => break,
Ok(false) => {}
Err(e) => {
@ -198,7 +198,10 @@ async fn renew_certificate(
(e.message, false)
}
};
match certificate.call_post_operation_hooks(&status, is_success) {
match certificate
.call_post_operation_hooks(&status, is_success)
.await
{
Ok(_) => {}
Err(e) => {
let e = e.prefix("post-operation hook error");

56
acmed/src/storage.rs

@ -7,12 +7,9 @@ use acme_common::error::Error;
use serde::Serialize;
use std::collections::HashMap;
use std::fmt;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
#[cfg(target_family = "unix")]
use std::os::unix::fs::OpenOptionsExt;
use tokio::fs::{File, OpenOptions};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[derive(Clone, Debug)]
pub struct FileManager {
@ -123,12 +120,13 @@ fn get_file_path(fm: &FileManager, file_type: FileType) -> Result<PathBuf, Error
Ok(path)
}
fn read_file(fm: &FileManager, path: &Path) -> Result<Vec<u8>, Error> {
async fn read_file(fm: &FileManager, path: &Path) -> Result<Vec<u8>, 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 file = File::open(path)
.await
.map_err(|e| Error::from(e).prefix(&path.display().to_string()))?;
let mut contents = vec![];
file.read_to_end(&mut contents)?;
file.read_to_end(&mut contents).await?;
Ok(contents)
}
@ -186,7 +184,7 @@ fn set_owner(fm: &FileManager, path: &Path, file_type: FileType) -> Result<(), E
}
}
fn write_file(fm: &FileManager, file_type: FileType, data: &[u8]) -> Result<(), Error> {
async 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,
@ -198,9 +196,9 @@ fn write_file(fm: &FileManager, file_type: FileType, data: &[u8]) -> Result<(),
let is_new = !path.is_file();
if is_new {
hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePreCreate)?;
hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePreCreate).await?;
} else {
hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePreEdit)?;
hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePreEdit).await?;
}
fm.trace(&format!("writing file {path:?}"));
@ -215,54 +213,58 @@ fn write_file(fm: &FileManager, file_type: FileType, data: &[u8]) -> Result<(),
.write(true)
.create(true)
.open(&path)
.await
.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::create(&path)
.await
.map_err(|e| Error::from(e).prefix(&path.display().to_string()))?
};
file.write_all(data)
.await
.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)?;
hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePostCreate).await?;
} else {
hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePostEdit)?;
hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePostEdit).await?;
}
Ok(())
}
pub fn get_account_data(fm: &FileManager) -> Result<Vec<u8>, Error> {
pub async fn get_account_data(fm: &FileManager) -> Result<Vec<u8>, Error> {
let path = get_file_path(fm, FileType::Account)?;
read_file(fm, &path)
read_file(fm, &path).await
}
pub fn set_account_data(fm: &FileManager, data: &[u8]) -> Result<(), Error> {
write_file(fm, FileType::Account, data)
pub async fn set_account_data(fm: &FileManager, data: &[u8]) -> Result<(), Error> {
write_file(fm, FileType::Account, data).await
}
pub fn get_keypair(fm: &FileManager) -> Result<KeyPair, Error> {
pub async fn get_keypair(fm: &FileManager) -> Result<KeyPair, Error> {
let path = get_file_path(fm, FileType::PrivateKey)?;
let raw_key = read_file(fm, &path)?;
let raw_key = read_file(fm, &path).await?;
let key = KeyPair::from_pem(&raw_key)?;
Ok(key)
}
pub fn set_keypair(fm: &FileManager, key_pair: &KeyPair) -> Result<(), Error> {
pub async fn set_keypair(fm: &FileManager, key_pair: &KeyPair) -> Result<(), Error> {
let data = key_pair.private_key_to_pem()?;
write_file(fm, FileType::PrivateKey, &data)
write_file(fm, FileType::PrivateKey, &data).await
}
pub fn get_certificate(fm: &FileManager) -> Result<X509Certificate, Error> {
pub async fn get_certificate(fm: &FileManager) -> Result<X509Certificate, Error> {
let path = get_file_path(fm, FileType::Certificate)?;
let raw_crt = read_file(fm, &path)?;
let raw_crt = read_file(fm, &path).await?;
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)
pub async fn write_certificate(fm: &FileManager, data: &[u8]) -> Result<(), Error> {
write_file(fm, FileType::Certificate, data).await
}
fn check_files(fm: &FileManager, file_types: &[FileType]) -> bool {

Loading…
Cancel
Save