Something went wrong. Try again.
DKIM (RFC 6376) signing and verification for Zig 0.16: both canonicalizations, RSA and Ed25519, no I/O or allocation.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! DKIM — DomainKeys Identified Mail, RFC 6376.//!//! DKIM answers exactly one question: **which domain takes responsibility for//! this message?** Not who wrote it, not whether it is honest, not whether the//! `From` address is real. A domain signs a message with a key it publishes in//! DNS, and a verifier that checks the signature learns that *that domain* put//! its name to *these bytes*. Everything else — whether the domain is one you//! trust, whether it matches the `From` header, what to do when it does not —//! is DMARC's business, and DMARC is a different specification.//!//! ```zig//! const dkim = @import("dkim");//!//! // Signing: hand back the header field to put at the top of the message.//! var buf: [dkim.signing.max_field_len]u8 = undefined;//! const field = try dkim.signing.sign(&buf, raw_message, .{//! .domain = "example.com",//! .selector = "mail",//! .key = .{ .rsa = secret_key },//! .timestamp = now,//! });//!//! // Verifying: two steps, because the middle one is a DNS lookup.//! var it = dkim.signatures(raw_message);//! while (it.next()) |result| {//! const signature = try result;//! var name_buf: [253]u8 = undefined;//! const name = try signature.recordName(&name_buf); // what to look up//! const txt = try yourResolver.txt(name); // your resolver//! const record = try dkim.Record.parse(txt);//! const verified = try dkim.verify(raw_message, signature, record, .{});//! std.log.info("signed by {s}", .{verified.domain});//! }//! ```//!//! ## Two things this library will not do//!//! **It does no I/O and it allocates nothing.** Not a socket, not a file, not//! an allocator anywhere in the signing or verification path — a message is a//! `[]const u8` that the caller already has, and everything else fits in fixed//! buffers whose sizes are public constants. That is not asceticism: it means//! the whole of DKIM's logic is reachable from a unit test, that a milter can//! run it without deciding what to do about allocation failure mid-message,//! and that it cannot stall on a slow network while an SMTP transaction waits.//!//! **It does not resolve DNS.** Verification is split in two around the//! lookup, as above. A library that resolved names would need an `Io`, a//! timeout policy, a cache and an opinion about DNSSEC, and would be//! untestable without a network; a caller running a mail server already has a//! resolver it would rather use. The consequence is that `TEMPFAIL` is not a//! result this library can return — a DNS failure happens before anything here//! runs — so every failure below is permanent.//!//! **It does not read the clock.** A signature's `t=` and a verifier's idea of//! "now" are both passed in. A signer that read the clock could not be made to//! produce the same bytes twice and so could not be tested against a fixed//! vector, and a verifier that read it could not be tested against a signature//! that expired in 2019.//!//! ## What is checked, and what is left to the caller//!//! `verify` returns an error unless the signature is cryptographically sound//! *and* the key record permits it: the algorithm is one RFC 8301 still allows,//! the key is large enough, the record has not revoked it, its `h=` and `s=`//! permit this use, and `t=s` is respected. What it does not do is decide what//! a pass *means*. A `Verified` says which domain signed, whether the key is//! flagged as testing, and how much of the body `l=` actually covered — and//! all three change what a sensible caller does next. A pass over the first//! 200 bytes of a 40 KB message, from a key whose owner has said it is still//! testing, is not the same thing as a pass.
const std = @import("std");const testing = std.testing;
/// The `tag=value` syntax both the signature and the key record are written/// in, RFC 6376 §3.2.pub const tag_list = @import("tag_list.zig");
/// Taking a message apart: where the headers stop, and where each field/// begins and ends.pub const message = @import("message.zig");
/// The two canonicalization algorithms, `simple` and `relaxed`, which are/// what lets a signature survive being relayed.pub const canonical = @import("canonical.zig");
/// The `DKIM-Signature` header field.pub const Signature = @import("Signature.zig");
/// The public key record, which arrives as the text of a DNS TXT record.pub const Record = @import("Record.zig");
/// What actually gets hashed, RFC 6376 §3.7. Shared by the signer and the/// verifier so that the two cannot drift apart.pub const digest = @import("digest.zig");
/// Verifying a signature.pub const verification = @import("verify.zig");
/// Making one.pub const signing = @import("sign.zig");
/// Verify one signature against the key record it names. See/// `verification.verify`.pub const verify = verification.verify;
/// The last step of verifying anything signed the way DKIM signs: a/// digest, a signature and a key. Exposed for ARC, whose `ARC-Seal`/// hashes a different thing and then checks it exactly this way.pub const verifyDigest = verification.verifyDigest;
/// Walk the signatures on a message. See `verification.signatures`.pub const signatures = verification.signatures;
/// Sign a message, returning the header field to prepend. See `signing.sign`.pub const sign = signing.sign;
/// The last step of producing anything signed the way DKIM signs: a/// digest, a key, and a base64 signature. The mirror of `verifyDigest`,/// exposed for ARC.pub const signDigest = signing.signDigest;
test { testing.refAllDecls(@This()); // Without these the tests in each file would never be analysed, let alone // run: a test executable covers the module it is given and reaches a file // only through a reference to it. _ = tag_list; _ = message; _ = canonical; _ = Signature; _ = Record; _ = digest; _ = verification; _ = signing;}