dachs/mail-auth

Framework-neutral PHP mail-auth package for DKIM, ARC, SPF, and DMARC evidence.

Maintainers

Package info

gitlab.com/dachscon/mail-auth

Issues

pkg:composer/dachs/mail-auth

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

v0.1.1 2026-06-11 08:06 UTC

This package is auto-updated.

Last update: 2026-06-11 08:07:38 UTC


README

Framework-neutral PHP mail-auth package for DKIM, ARC, SPF, and DMARC evidence.

This package is maintained by Dachs Consulting for standards-based inbound email evidence. It operates on raw RFC 5322 messages and explicit protocol inputs and can be used as a standalone mail-auth library.

Scope

This package verifies and creates DKIM signatures, validates and seals ARC chains, verifies SPF, and evaluates DMARC organizational-domain alignment.

Implemented and regression-tested coverage:

  • DKIM-Signature parsing, including folded signature headers
  • mandatory From header signing enforcement from RFC 6376
  • rsa-sha256 verification/signing via OpenSSL
  • ed25519-sha256 verification/signing via Sodium/RFC 8463 key records
  • simple and relaxed header/body canonicalization
  • DKIM body hash validation (bh=)
  • optional body length tag (l=)
  • signature timestamp/expiry policy (t=/x=)
  • signing identity domain validation (i=)
  • DNS TXT public-key lookup through injectable resolvers
  • multi-string DNS TXT record concatenation
  • DKIM key-record policy checks for version, key type, hash algorithms, service type, and granularity (v=, k=, h=, s=, g=)
  • optional relaxed domain alignment against the visible sender domain
  • multiple signatures, accepting the first verified aligned signature
  • DKIM signing with RSA and Ed25519
  • SPF verification via a maintained SPF engine behind package-owned DNS/result boundaries
  • DMARC organizational-domain resolution using Public Suffix List rules
  • ARC structural and cryptographic chain validation (none|pass|fail) for set continuity, instance rules, cv=, AMS verification, AS verification, and ARC-Seal-only tag constraints
  • ARC sealing for application-owned outbound/forwarded evidence flows

Security posture:

  • rsa-sha256 and ed25519-sha256 are accepted.
  • weaker algorithms such as rsa-sha1 are intentionally rejected for production use.
  • ARC cryptographic chain validation/sealing is implemented for the package boundary; third-party and self-generated ARC fixtures are cross-checked against dkimpy.

Architecture

  • DkimVerifier: orchestrates DKIM parsing, canonicalization, DNS key lookup, crypto verification, and alignment evidence.
  • DkimSigner: creates DKIM signatures without mutating MIME content.
  • DkimMessageParser: splits raw RFC 5322 headers/body without MIME mutation.
  • DkimSignatureHeaderParser: parses folded DKIM-Signature fields into tags.
  • DkimCanonicalizer: implements simple/relaxed header and body canonicalization.
  • DkimDnsResolver: injectable DNS TXT resolver boundary.
  • MailAuthDnsResolver: TXT/A/AAAA/MX/PTR/reverse-DNS boundary for SPF and other mail-auth protocols.
  • DkimPublicKeyParser: parses and validates DKIM DNS key records.
  • DkimCryptoVerifier: performs OpenSSL/Sodium signature verification and signing.
  • DkimAlignmentChecker: evaluates relaxed sender-domain alignment.
  • SpfVerifier: SPF checker wrapper returning protocol evidence values.
  • DmarcOrganizationalDomainResolver: Public-Suffix-List based organizational domain/alignment helper.
  • DmarcEvaluator: evaluates SPF/DKIM identifier alignment and DMARC disposition.
  • ArcChainValidator: ARC structural and cryptographic chain validator.
  • ArcSealer: creates ARC sets for application-owned sealing flows.

Installation

composer require dachs/mail-auth

Usage

DKIM

use Dachs\MailAuth\DkimVerifier;

$dkim = new DkimVerifier();
$dkimResult = $dkim->verifyAlignedFromHeader($rawRfc822Message);

if (! $dkimResult->passed) {
    throw new RuntimeException($dkimResult->error?->value ?? 'DKIM verification failed');
}

$evidence = [
    'dkim' => 'pass',
    'alignment' => 'relaxed',
    'dkim_domain' => $dkimResult->domain,
    'selector' => $dkimResult->selector,
    'algorithm' => $dkimResult->algorithm,
];

SPF

SPF verification is evaluated from the SMTP transaction inputs, not from the message body.

use Dachs\MailAuth\SpfResult;
use Dachs\MailAuth\SpfVerifier;

$spf = new SpfVerifier();
$spfResult = $spf->verify(
    ip: '203.0.113.10',
    heloDomain: 'mx.sender.example',
    mailFrom: 'bounce@sender.example',
    receiverDomain: 'receiver.example',
);

if ($spfResult->result === SpfResult::Pass) {
    $evidence['spf'] = 'pass';
    $evidence['spf_domain'] = 'sender.example';
}

DMARC

DMARC combines identifier alignment from DKIM and SPF with the visible From domain policy.

use Dachs\MailAuth\DmarcEvaluationInput;
use Dachs\MailAuth\DmarcEvaluator;
use Dachs\MailAuth\DmarcPolicy;

$dmarc = new DmarcEvaluator();
$dmarcResult = $dmarc->evaluate(new DmarcEvaluationInput(
    fromDomain: 'sender.example',
    dkimPassed: $dkimResult->passed,
    dkimDomain: $dkimResult->domain,
    spfPassed: $spfResult->result === SpfResult::Pass,
    spfDomain: 'sender.example',
    policy: DmarcPolicy::Reject,
));

$evidence['dmarc'] = $dmarcResult->disposition->value;
$evidence['dmarc_dkim_aligned'] = $dmarcResult->dkimAligned;
$evidence['dmarc_spf_aligned'] = $dmarcResult->spfAligned;

ARC

ARC can preserve authentication results across forwarding hops by validating or sealing an ARC chain.

use Dachs\MailAuth\ArcChainValidator;
use Dachs\MailAuth\ArcValidationStatus;

$arc = new ArcChainValidator();
$arcResult = $arc->validateStructure($rawRfc822Message);

if ($arcResult->status === ArcValidationStatus::Pass) {
    $evidence['arc'] = 'pass';
    $evidence['arc_sets'] = $arcResult->sets;
}

For tests or custom DNS infrastructure, inject DkimDnsResolver / MailAuthDnsResolver.

Public status and error values are PHP backed enums, not free-form strings:

  • DkimVerificationError
  • ArcValidationStatus
  • ArcValidationError
  • SpfResult
  • DmarcDisposition
  • DmarcPolicy

Use ->value when serializing evidence payloads.

use Dachs\MailAuth\ArrayDkimDnsResolver;
use Dachs\MailAuth\ArrayMailAuthDnsResolver;
use Dachs\MailAuth\DkimVerifier;
use Dachs\MailAuth\SpfVerifier;

$dkim = new DkimVerifier(new ArrayDkimDnsResolver([
    'test._domainkey.example.com' => 'v=DKIM1; k=rsa; p=...',
]));

$spf = new SpfVerifier(new ArrayMailAuthDnsResolver(txt: [
    'example.com' => 'v=spf1 ip4:203.0.113.10 -all',
]));

Development

composer install
composer validate --strict
composer test

Interop fixtures are committed as static PHPUnit fixtures. Regenerate them with dkimpy/authres when changing DKIM or ARC signing semantics:

uv run --with dkimpy --with authres python tests/fixtures/interop/generate_fixtures.py
composer test