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