opensmtpd-filter-dkimout/src/algorithm.rs
2023-04-30 11:25:07 +02:00

128 lines
3.6 KiB
Rust

use anyhow::Result;
use base64::{engine::general_purpose, Engine as _};
use ed25519_dalek::ed25519::SignatureEncoding;
use ed25519_dalek::{Signer, SigningKey as Ed25519SigningKey};
use rand::thread_rng;
use rsa::pkcs1v15::SigningKey as RsaSigningKey;
use rsa::pkcs8::{DecodePrivateKey, EncodePrivateKey, EncodePublicKey};
use rsa::sha2::Sha256;
use rsa::signature::hazmat::PrehashSigner;
use rsa::{RsaPrivateKey, RsaPublicKey};
use std::str::FromStr;
#[derive(Clone, Copy, Debug)]
pub enum Algorithm {
Ed25519Sha256,
Rsa2048Sha256,
Rsa3072Sha256,
Rsa4096Sha256,
}
impl Algorithm {
pub fn display(&self) -> String {
match self {
Self::Ed25519Sha256 => String::from("ed25519-sha256"),
Self::Rsa2048Sha256 | Self::Rsa3072Sha256 | Self::Rsa4096Sha256 => {
String::from("rsa-sha256")
}
}
}
pub fn key_type(&self) -> String {
match self {
Self::Ed25519Sha256 => String::from("ed25519"),
Self::Rsa2048Sha256 | Self::Rsa3072Sha256 | Self::Rsa4096Sha256 => String::from("rsa"),
}
}
pub fn gen_keys(&self) -> (String, String) {
match self {
Self::Ed25519Sha256 => gen_ed25519_kp(),
Self::Rsa2048Sha256 => gen_rsa_kp(2048),
Self::Rsa3072Sha256 => gen_rsa_kp(3072),
Self::Rsa4096Sha256 => gen_rsa_kp(4096),
}
}
pub fn sign(&self, encoded_pk: &str, data: &[u8]) -> Result<Vec<u8>> {
let pk = general_purpose::STANDARD.decode(encoded_pk)?;
match self {
Self::Ed25519Sha256 => {
let signing_key = Ed25519SigningKey::from_bytes(pk.as_slice().try_into()?);
let signature = signing_key.try_sign(data)?;
Ok(signature.to_vec())
}
Self::Rsa2048Sha256 | Self::Rsa3072Sha256 | Self::Rsa4096Sha256 => {
let private_key = RsaPrivateKey::from_pkcs8_der(&pk)?;
let signing_key = RsaSigningKey::<Sha256>::new(private_key);
let signature = signing_key.sign_prehash(data)?;
Ok(signature.to_vec())
}
}
}
}
impl Default for Algorithm {
fn default() -> Self {
crate::DEFAULT_CNF_ALGORITHM
}
}
impl ToString for Algorithm {
fn to_string(&self) -> String {
match self {
Self::Ed25519Sha256 => String::from("ed25519-sha256"),
Self::Rsa2048Sha256 => String::from("rsa2048-sha256"),
Self::Rsa3072Sha256 => String::from("rsa3072-sha256"),
Self::Rsa4096Sha256 => String::from("rsa4096-sha256"),
}
}
}
impl FromStr for Algorithm {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"ed25519-sha256" => Ok(Self::Ed25519Sha256),
"rsa2048-sha256" => Ok(Self::Rsa2048Sha256),
"rsa3072-sha256" => Ok(Self::Rsa3072Sha256),
"rsa4096-sha256" => Ok(Self::Rsa4096Sha256),
_ => Err(format!("{s}: invalid signing algorithm")),
}
}
}
fn gen_ed25519_kp() -> (String, String) {
let mut csprng = thread_rng();
let priv_key = Ed25519SigningKey::generate(&mut csprng);
let pub_key = priv_key.verifying_key();
let priv_key = general_purpose::STANDARD.encode(priv_key.to_bytes());
let pub_key = general_purpose::STANDARD.encode(pub_key.to_bytes());
(priv_key, pub_key)
}
fn gen_rsa_kp(bits: usize) -> (String, String) {
let mut csprng = thread_rng();
loop {
if let Ok(priv_key) = RsaPrivateKey::new(&mut csprng, bits) {
let pub_key = RsaPublicKey::from(&priv_key);
let priv_key = match priv_key.to_pkcs8_der() {
Ok(d) => d,
Err(_) => {
continue;
}
};
let pub_key = match pub_key.to_public_key_der() {
Ok(d) => d,
Err(_) => {
continue;
}
};
let priv_key = general_purpose::STANDARD.encode(priv_key.as_bytes());
let pub_key = general_purpose::STANDARD.encode(pub_key.as_bytes());
return (priv_key, pub_key);
}
log::trace!("failed to generate an RSA-{bits} key");
}
}