mail_auth/common/crypto/
mod.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use super::headers::{Writable, Writer};
8use crate::{Result, dkim::Canonicalization};
9#[cfg(feature = "sha1")]
10use sha1::{Digest, digest::Output};
11
12#[cfg(feature = "rust-crypto")]
13mod rust_crypto;
14#[cfg(feature = "rust-crypto")]
15pub use rust_crypto::{Ed25519Key, RsaKey};
16#[cfg(feature = "rust-crypto")]
17pub(crate) use rust_crypto::{Ed25519PublicKey, RsaPublicKey};
18
19#[cfg(all(feature = "ring", not(feature = "rust-crypto")))]
20mod ring_impls;
21#[cfg(all(feature = "ring", not(feature = "rust-crypto")))]
22pub use ring_impls::{Ed25519Key, RsaKey};
23#[cfg(all(feature = "ring", not(feature = "rust-crypto")))]
24pub(crate) use ring_impls::{Ed25519PublicKey, RsaPublicKey};
25
26pub trait SigningKey {
27    type Hasher: HashImpl;
28
29    fn sign(&self, input: impl Writable) -> Result<Vec<u8>>;
30
31    fn hash(&self, data: impl Writable) -> HashOutput {
32        let mut hasher = <Self::Hasher as HashImpl>::hasher();
33        data.write(&mut hasher);
34        hasher.complete()
35    }
36
37    fn algorithm(&self) -> Algorithm;
38}
39
40pub trait VerifyingKey {
41    fn verify<'a>(
42        &self,
43        headers: &mut dyn Iterator<Item = (&'a [u8], &'a [u8])>,
44        signature: &[u8],
45        canonicalication: Canonicalization,
46        algorithm: Algorithm,
47    ) -> Result<()>;
48}
49
50pub(crate) enum VerifyingKeyType {
51    Rsa,
52    Ed25519,
53}
54
55impl VerifyingKeyType {
56    pub(crate) fn verifying_key(
57        &self,
58        bytes: &[u8],
59    ) -> Result<Box<dyn VerifyingKey + Send + Sync>> {
60        match self {
61            #[cfg(feature = "rust-crypto")]
62            Self::Rsa => RsaPublicKey::verifying_key_from_bytes(bytes),
63            #[cfg(feature = "rust-crypto")]
64            Self::Ed25519 => Ed25519PublicKey::verifying_key_from_bytes(bytes),
65            #[cfg(all(feature = "ring", not(feature = "rust-crypto")))]
66            Self::Rsa => RsaPublicKey::verifying_key_from_bytes(bytes),
67            #[cfg(all(feature = "ring", not(feature = "rust-crypto")))]
68            Self::Ed25519 => Ed25519PublicKey::verifying_key_from_bytes(bytes),
69        }
70    }
71}
72
73pub trait HashContext: Writer + Sized {
74    fn complete(self) -> HashOutput;
75}
76
77pub trait HashImpl {
78    type Context: HashContext;
79
80    fn hasher() -> Self::Context;
81}
82
83#[derive(Clone, Copy)]
84pub struct Sha1;
85
86#[derive(Clone, Copy)]
87pub struct Sha256;
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90#[repr(u64)]
91pub enum HashAlgorithm {
92    Sha1 = R_HASH_SHA1,
93    Sha256 = R_HASH_SHA256,
94}
95
96impl HashAlgorithm {
97    pub fn hash(&self, data: impl Writable) -> HashOutput {
98        match self {
99            #[cfg(feature = "sha1")]
100            Self::Sha1 => {
101                let mut hasher = sha1::Sha1::new();
102                data.write(&mut hasher);
103                HashOutput::RustCryptoSha1(hasher.finalize())
104            }
105            #[cfg(feature = "sha2")]
106            Self::Sha256 => {
107                let mut hasher = sha2::Sha256::new();
108                data.write(&mut hasher);
109                HashOutput::RustCryptoSha256(hasher.finalize())
110            }
111            #[cfg(all(feature = "ring", not(feature = "sha1")))]
112            Self::Sha1 => {
113                let mut hasher =
114                    ring::digest::Context::new(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY);
115                data.write(&mut hasher);
116                HashOutput::Ring(hasher.finish())
117            }
118            #[cfg(all(feature = "ring", not(feature = "sha2")))]
119            Self::Sha256 => {
120                let mut hasher = ring::digest::Context::new(&ring::digest::SHA256);
121                data.write(&mut hasher);
122                HashOutput::Ring(hasher.finish())
123            }
124        }
125    }
126}
127
128#[non_exhaustive]
129pub enum HashOutput {
130    #[cfg(feature = "ring")]
131    Ring(ring::digest::Digest),
132    #[cfg(feature = "sha1")]
133    RustCryptoSha1(Output<sha1::Sha1>),
134    #[cfg(feature = "sha2")]
135    RustCryptoSha256(Output<sha2::Sha256>),
136}
137
138impl AsRef<[u8]> for HashOutput {
139    fn as_ref(&self) -> &[u8] {
140        match self {
141            #[cfg(feature = "ring")]
142            Self::Ring(output) => output.as_ref(),
143            #[cfg(feature = "sha1")]
144            Self::RustCryptoSha1(output) => output.as_ref(),
145            #[cfg(feature = "sha2")]
146            Self::RustCryptoSha256(output) => output.as_ref(),
147        }
148    }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
152pub enum Algorithm {
153    RsaSha1,
154    #[default]
155    RsaSha256,
156    Ed25519Sha256,
157}
158
159pub(crate) const R_HASH_SHA1: u64 = 0x01;
160pub(crate) const R_HASH_SHA256: u64 = 0x02;