Skip to main content

Dkim2Signer

Struct Dkim2Signer 

Source
pub struct Dkim2Signer<State = NeedDomain> { /* private fields */ }
Expand description

DKIM2 message signer (§12).

Built in typestate steps. Dkim2Signer::from_key takes the first private key, domain sets the signing domain (d=) and selector sets the selector of the first key, which yields a Dkim2Signer<Ready>. Only a ready signer can sign. Optional settings on a ready signer: additional_key adds an alternative signature under another selector (|), hybrid_key adds a signature that must pass together with the previous one (,), hashes selects the Message-Instance hash algorithms, flags sets the f= tag, nonce sets the n= tag and out_of_band_arrangement allows signing messages that are not Fully Verified DKIM2 Messages.

The signing methods (sign, sign_revised, sign_with_recipe and sign_with_message_instance) return a Dkim2Signed holding the header fields to prepend to the message. A message that already carries DKIM2 header fields is signed only with the Dkim2Output of a full verification that passed (§12).

§Example

use mail_auth::{
    crypto::{Ed25519Key, RsaKey, Sha256},
    dkim2::{Dkim2Signer, Flag, Hop},
};
use rustls_pki_types::{PrivateKeyDer, pem::PemObject};

let rsa_key = RsaKey::<Sha256>::from_key_der(PrivateKeyDer::from_pem_slice(rsa_pem.as_bytes())?)?;
let signed = Dkim2Signer::from_key(rsa_key)
    .domain("example.com")
    .selector("rsa-sel")
    .additional_key(Ed25519Key::from_pkcs8_der(ed25519_pkcs8)?, "ed-sel")
    .flags([Flag::Feedback])
    .sign(
        message.as_bytes(),
        Hop::real("sender@example.com", ["recipient@example.org"]),
        None,
    )?;

let signed_message = format!("{}{}", signed.to_header(), message);

Implementations§

Source§

impl Dkim2Signer<NeedDomain>

Source

pub fn from_key(key: impl Into<DkimKey>) -> Dkim2Signer<NeedDomain>

Starts a signer with its first private key (RSA-SHA256, Ed25519-SHA256 or ML-DSA-44). Continue with domain.

Source

pub fn domain(self, domain: impl Into<String>) -> Dkim2Signer<NeedSelector>

Sets the signing domain (d=). Its public keys are looked up under <selector>._domainkey.<domain>. Continue with selector.

Source§

impl Dkim2Signer<NeedSelector>

Source

pub fn selector(self, selector: impl Into<String>) -> Dkim2Signer<Ready>

Sets the selector of the first key, which completes the signer.

Source§

impl Dkim2Signer<Ready>

Source

pub fn additional_key( self, key: impl Into<DkimKey>, selector: impl Into<String>, ) -> Self

Adds another signing key under its own selector as an alternative to the keys added before it.

The key adds a new |-separated group to the s= tag of the same signature (algorithmic agility, §10.9): the signature passes when either this key or an earlier group verifies, so a Verifier that does not implement one of the algorithms can still check the others. Selectors must be distinct and a signature holds at most eight keys; signing fails with Dkim2Error::DuplicateSelector or Dkim2Error::TooManySelectors otherwise.

Source

pub fn hybrid_key( self, key: impl Into<DkimKey>, selector: impl Into<String>, ) -> Self

Adds another signing key under its own selector that must verify together with the key added before it.

The key joins the last |-separated group of the s= tag with a , (a “hybrid” signature, §10.9): that group passes only when every key in it verifies. The same limits as additional_key apply.

Source

pub fn hashes(self, hashes: impl IntoIterator<Item = Dkim2Hash>) -> Self

Sets the hash algorithms of the Message-Instance header fields this signer adds (§3.1 and §9.3). They form a single ,-separated group, so all of them must match. Repeated algorithms are added once; an empty list keeps the default, SHA-256 only.

Source

pub fn flags(self, flags: impl IntoIterator<Item = Flag>) -> Self

Adds flags to the f= tag of the signature (§10.10). Repeated flags are added once. Flag::Unknown values must be textstrings (letters, digits, - and _), or signing fails with Dkim2Error::SignatureSyntax; unknown values that spell a defined flag are treated as that flag.

The exploded flag is not allowed on the first signature of a message (§12.5). It is added automatically to a later signature whose Hop has more than one RCPT TO; a Forwarder that sends separate copies to several recipients marks each copy’s hop with Hop::exploded instead.

Source

pub fn nonce(self, nonce: impl Into<String>) -> Self

Sets the nonce (n= tag, §10.3): an opaque value meaningful only to the signer, for example a key to match a returned DSN. It must be at most 64 printable ASCII characters without semicolons; signing fails with Dkim2Error::SignatureSyntax otherwise. An empty nonce means no n= tag.

Source

pub fn out_of_band_arrangement(self) -> Self

Declares an out-of-band arrangement (§12 and §12.3) that allows this signer to sign messages that are not Fully Verified DKIM2 Messages, such as a message whose most recent signature carries nd= for an imaginary hop made by the same system. Without it, a message that already has DKIM2 header fields is signed only with a passing full verification of it. The chain of custody, mf=/d= and exploded checks still apply.

Source§

impl Dkim2Signer<Ready>

Source

pub fn sign<'x, M, A, R, I>( &self, message: M, hop: Hop<A, R, I>, verified: Option<&Dkim2Output<'_>>, ) -> Result<Dkim2Signed>
where M: TryInto<AuthenticatedMessage<'x>, Error = Error>, A: AsRef<str>, R: IntoIterator<Item: AsRef<str>>, I: Into<String>,

Signs a message whose content has not changed, as an Originator or a Forwarder that does not modify the message.

message is the raw message, including any DKIM2 header fields added by earlier hops. hop is the next hop the signature binds to. verified is the Dkim2Output of a full verification of message that passed (§12): required when the message already has DKIM2 header fields, unless the signer has an out_of_band_arrangement; pass None for a new message. The result always holds a new DKIM2-Signature, and holds a new Message-Instance (m=1) only if the message has none yet.

§Errors

Returns Error::Parse if the message cannot be parsed, Dkim2Error::NotFullyVerified if the validity proof is missing or does not match the message, Dkim2Error::FromNotAuthentic if the message is new (or its From changed) and its From domain is not the signing domain or a subdomain of it (§12.4), Dkim2Error::HeaderHashMismatch or Dkim2Error::BodyHashMismatch if the message changed since its most recent Message-Instance (use sign_revised), Dkim2Error::SignatureSyntax if the signing domain, the nd= domain or a selector is not an RFC 5321 domain (U-labels are mapped to A-labels first), Dkim2Error::SignatureTagMissing for a Hop without RCPT TO addresses, Dkim2Error::SignatureSyntax for an empty or null RCPT TO address, a chain of custody, mf=/d=, selector, nonce or exploded flag error for a signature that would not be valid (§12.2 to §12.5), Dkim2Error::SequenceOverflow if the next i= would exceed u32::MAX, or the signing key’s error if signing fails.

Source

pub fn sign_revised<'x, O, M, A, R, I>( &self, original: O, modified: M, hop: Hop<A, R, I>, verified: Option<&Dkim2Output<'_>>, ) -> Result<Dkim2Signed>
where O: TryInto<AuthenticatedMessage<'x>, Error = Error>, M: TryInto<AuthenticatedMessage<'x>, Error = Error>, A: AsRef<str>, R: IntoIterator<Item: AsRef<str>>, I: Into<String>,

Signs a message whose content changed, as a Reviser.

original is the message as received and verified the passing full verification of it (see sign). modified is the message to be sent, which keeps the DKIM2 header fields of original. The recipe that recreates original from modified is computed with Recipe::diff. If the signed content changed, the result holds a new Message-Instance with the next m=, the new hashes and the recipe; otherwise it holds only the DKIM2-Signature, as with sign.

§Errors

Same as sign; either message can fail to parse.

§Example
use mail_auth::{
    AuthenticatedMessage, MessageAuthenticator,
    crypto::Ed25519Key,
    dkim2::{Dkim2Signer, Envelope, Hop},
};

let authenticator = MessageAuthenticator::new_cloudflare_tls().unwrap();
let message = AuthenticatedMessage::parse(received).unwrap();
let verified = authenticator
    .verify_dkim2(&message, Envelope::new("sender@example.com", ["list@example.org"]))
    .await;

let signed = Dkim2Signer::from_key(Ed25519Key::from_pkcs8_der(pkcs8)?)
    .domain("example.org")
    .selector("ed25519")
    .sign_revised(
        received,
        modified,
        Hop::real("list-bounces@example.org", ["member@example.net"]),
        Some(&verified),
    )?;

let mut outgoing = Vec::new();
signed.write(&mut outgoing);
outgoing.extend_from_slice(modified);
Source

pub fn sign_with_recipe<'x, M, A, R, I>( &self, modified: M, recipe: Recipe, hop: Hop<A, R, I>, verified: Option<&Dkim2Output<'_>>, ) -> Result<Dkim2Signed>
where M: TryInto<AuthenticatedMessage<'x>, Error = Error>, A: AsRef<str>, R: IntoIterator<Item: AsRef<str>>, I: Into<String>,

Signs a changed message with a caller-supplied recipe.

recipe recreates the previous revision from modified, which keeps the DKIM2 header fields of the received message; verified is the passing full verification of the received message (see sign). The result holds a new Message-Instance with the next m=, the hashes of modified and recipe.

§Errors

Same as sign, and Dkim2Error::RecipeSyntax if recipe fails Recipe::validate.

Source

pub fn sign_with_message_instance<'x, A, R, I>( &self, modified: &AuthenticatedMessage<'x>, instance: Option<&MessageInstance>, hop: Hop<A, R, I>, verified: Option<&Dkim2Output<'_>>, ) -> Result<Dkim2Signed>
where A: AsRef<str>, R: IntoIterator<Item: AsRef<str>>, I: Into<String>,

Signs a message with a caller-supplied Message-Instance.

instance is signed as the newest Message-Instance of modified; with None, only the instances already present are signed. The returned Dkim2Signed::message_instance is always None: the caller writes instance to the message itself, next to the returned DKIM2-Signature. verified is as in sign.

§Errors

Same as sign, except that parsing cannot fail; Dkim2Error::InstanceSyntax if instance is not numbered one more than the highest existing m=, Dkim2Error::HeaderHashMismatch or Dkim2Error::BodyHashMismatch if its hashes do not match modified, and Dkim2Error::RecipeSyntax if its recipe fails Recipe::validate.

Source§

impl<State> Dkim2Signer<State>

Source

pub fn heap_size(&self) -> usize

Approximate heap memory owned by this signer, in bytes: the domain, selectors, flags, nonce and private key material.

Auto Trait Implementations§

§

impl<State> Freeze for Dkim2Signer<State>
where PhantomData<State>: Freeze,

§

impl<State> RefUnwindSafe for Dkim2Signer<State>
where PhantomData<State>: RefUnwindSafe,

§

impl<State> Send for Dkim2Signer<State>
where PhantomData<State>: Send,

§

impl<State> Sync for Dkim2Signer<State>
where PhantomData<State>: Sync,

§

impl<State> Unpin for Dkim2Signer<State>
where PhantomData<State>: Unpin,

§

impl<State> UnsafeUnpin for Dkim2Signer<State>
where PhantomData<State>: UnsafeUnpin,

§

impl<State> UnwindSafe for Dkim2Signer<State>
where PhantomData<State>: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more