use std::{fs, io::Cursor, path::PathBuf};
use native::{
crypto::{hash::HashAlgorithm, sym::SymmetricKeyAlgorithm},
types::{CompressionAlgorithm, SecretKeyTrait},
Deserializable, KeyType, SecretKeyParamsBuilder, SignedPublicKey, SignedSecretKey,
StandaloneSignature, SubkeyParamsBuilder,
};
use smallvec::smallvec;
use crate::{Error, Result};
pub async fn gen_key_pair(
email: impl ToString,
passphrase: impl ToString,
) -> Result<(SignedSecretKey, SignedPublicKey)> {
let email = email.to_string();
let passphrase = passphrase.to_string();
let passphrase = if passphrase.trim().is_empty() {
None
} else {
Some(passphrase)
};
spawn_blocking(move || {
let key_params = SecretKeyParamsBuilder::default()
.key_type(KeyType::EdDSA)
.can_create_certificates(true)
.can_sign(true)
.primary_user_id(email)
.passphrase(passphrase.clone())
.preferred_symmetric_algorithms(smallvec![SymmetricKeyAlgorithm::AES256])
.preferred_hash_algorithms(smallvec![HashAlgorithm::SHA2_256])
.preferred_compression_algorithms(smallvec![CompressionAlgorithm::ZLIB])
.subkey(
SubkeyParamsBuilder::default()
.key_type(KeyType::ECDH)
.can_encrypt(true)
.passphrase(passphrase)
.build()
.map_err(Error::BuildPublicKeyParamsError)?,
)
.build()
.map_err(Error::BuildSecretKeyParamsError)?;
let skey = key_params
.generate()
.map_err(Error::GenerateSecretKeyError)?;
let skey = skey.sign(String::new).map_err(Error::SignSecretKeyError)?;
skey.verify().map_err(Error::VerifySecretKeyError)?;
let pkey = skey.public_key();
let pkey = pkey
.sign(&skey, String::new)
.map_err(Error::SignPublicKeyError)?;
pkey.verify().map_err(Error::VerifyPublicKeyError)?;
Ok((skey, pkey))
})
.await?
}
pub async fn read_pkey_from_path(path: PathBuf) -> Result<SignedPublicKey> {
spawn_blocking(move || {
let data =
fs::read(&path).map_err(|err| Error::ReadArmoredPublicKeyError(err, path.clone()))?;
let (pkey, _) = SignedPublicKey::from_armor_single(Cursor::new(data))
.map_err(|err| Error::ParseArmoredPublicKeyError(err, path.clone()))?;
Ok(pkey)
})
.await?
}
pub async fn read_skey_from_file(path: PathBuf) -> Result<SignedSecretKey> {
spawn_blocking(move || {
let data = fs::read(&path)
.map_err(|err| Error::ReadArmoredSecretKeyFromPathError(err, path.clone()))?;
let (skey, _) = SignedSecretKey::from_armor_single(Cursor::new(data))
.map_err(|err| Error::ParseArmoredSecretKeyFromPathError(err, path.clone()))?;
Ok(skey)
})
.await?
}
pub async fn read_skey_from_string(string: String) -> Result<SignedSecretKey> {
spawn_blocking(move || {
let (skey, _) = SignedSecretKey::from_armor_single(Cursor::new(string))
.map_err(Error::ParseArmoredSecretKeyFromStringError)?;
Ok(skey)
})
.await?
}
pub async fn read_sig_from_bytes(bytes: Vec<u8>) -> Result<StandaloneSignature> {
spawn_blocking(move || {
let (sig, _) = StandaloneSignature::from_armor_single(Cursor::new(&bytes))
.map_err(Error::ReadStandaloneSignatureFromArmoredBytesError)?;
Ok(sig)
})
.await?
}
pub(crate) async fn spawn_blocking<F, T>(f: F) -> Result<T>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
Ok(tokio::task::spawn_blocking(f).await?)
}