mini_mail_auth/dkim/
sign.rs1use super::{canonicalize::CanonicalHeaders, DkimSigner, Done, Signature};
2use crate::{
3 common::{
4 crypto::SigningKey,
5 headers::{HeaderIterator, HeaderStream, Writable, Writer},
6 },
7 Error,
8};
9use base64::{engine::general_purpose::STANDARD as BASE64_STANDARD, Engine as _};
10use std::time::SystemTime;
11
12impl<T: SigningKey> DkimSigner<T, Done> {
13 pub fn sign(&self, message: &[u8]) -> crate::Result<Signature> {
14 self.sign_stream(
15 HeaderIterator::new(message),
16 SystemTime::now()
17 .duration_since(SystemTime::UNIX_EPOCH)
18 .map(|d| d.as_secs())
19 .unwrap_or(0),
20 )
21 }
22
23 fn sign_stream<'x>(
24 &self,
25 message: impl HeaderStream<'x>,
26 now: u64,
27 ) -> crate::Result<Signature> {
28 let (body_len, canonical_headers, signed_headers, canonical_body) =
29 self.template.canonicalize(message);
30
31 if signed_headers.is_empty() {
32 return Err(Error::NoHeadersFound);
33 }
34
35 let mut signature = self.template.clone();
36 let body_hash = self.key.hash(canonical_body);
37 signature.bh = BASE64_STANDARD.encode(body_hash.as_ref()).into_bytes();
38 signature.t = now;
39 signature.h = signed_headers;
40 if signature.l > 0 {
41 signature.l = body_len as u64;
42 }
43
44 let b = self.key.sign(SignableMessage {
45 headers: canonical_headers,
46 signature: &signature,
47 })?;
48
49 signature.b = BASE64_STANDARD.encode(&b).into_bytes();
50
51 Ok(signature)
52 }
53}
54
55pub(super) struct SignableMessage<'a> {
56 headers: CanonicalHeaders<'a>,
57 signature: &'a Signature,
58}
59
60impl Writable for SignableMessage<'_> {
61 fn write(self, writer: &mut impl Writer) {
62 self.headers.write(writer);
63 self.signature.write(writer, false);
64 }
65}