Documentation
¶
Overview ¶
Package arc implements Authenticated Received Chain (ARC) per RFC 8617.
ARC provides a way to preserve email authentication results across intermediaries that may modify messages (such as mailing lists). When a message passes through an intermediary, ARC creates a chain of custody that allows receivers to validate that the message was authenticated when received by each intermediary.
ARC consists of three header types:
- ARC-Authentication-Results: Contains authentication results from the intermediary
- ARC-Message-Signature: A DKIM-like signature over the message
- ARC-Seal: A signature that seals the entire ARC chain
Basic Usage ¶
Verifying an ARC chain:
verifier := arc.Verifier{
Resolver: resolver,
}
result, err := verifier.Verify(ctx, message)
if result.Status == arc.StatusPass {
// ARC chain is valid
}
Sealing a message (as an intermediary):
sealer := arc.Sealer{
Domain: "example.com",
Selector: "arc1",
PrivateKey: privateKey,
}
headers, err := sealer.Seal(message, "mx.example.com", authResults, arc.ChainValidationNone)
Chain Validation States ¶
RFC 8617 defines the following chain validation states:
- none: No ARC headers present
- pass: All ARC sets validated successfully
- fail: ARC validation failed
Integration with DMARC ¶
ARC results can be used by DMARC to override authentication failures when a trusted ARC chain indicates the message was originally authenticated. This is particularly useful for mailing lists that modify messages.
References ¶
Index ¶
- Constants
- Variables
- func EvaluateARCForDMARC(result *Result, trustedDomains []string) (trusted bool, oldestTrustedPass int)
- func QuickSeal(mail *ravenmail.Mail, domain, selector string, privateKey crypto.Signer, ...) error
- func SignMail(mail *ravenmail.Mail, sealer *Sealer, authServID, authResults string, ...) error
- type Algorithm
- type AuthenticationResults
- type Canonicalization
- type ChainValidationStatus
- type DKIMRecord
- type MessageSignature
- type Result
- type Seal
- type SealResult
- type Sealer
- type Set
- type Status
- type Verifier
Constants ¶
const MaxInstance = 50
MaxInstance is the maximum allowed ARC instance number per RFC 8617.
Variables ¶
var ( // ErrNoARCHeaders indicates no ARC headers were found in the message. ErrNoARCHeaders = errors.New("arc: no ARC headers found") // ErrInvalidChain indicates the ARC chain is structurally invalid. ErrInvalidChain = errors.New("arc: invalid ARC chain structure") // ErrMissingSet indicates a required ARC set is missing. ErrMissingSet = errors.New("arc: missing ARC set") // ErrDuplicateSet indicates duplicate ARC sets with the same instance number. ErrDuplicateSet = errors.New("arc: duplicate ARC set instance") // ErrGapInChain indicates a gap in the ARC chain instance numbers. ErrGapInChain = errors.New("arc: gap in ARC chain instance numbers") // ErrInvalidInstance indicates an invalid instance number. ErrInvalidInstance = errors.New("arc: invalid instance number") // ErrInstanceTooHigh indicates the instance number exceeds the limit. ErrInstanceTooHigh = errors.New("arc: instance number exceeds limit (50)") // ErrSealFailed indicates the ARC-Seal verification failed. ErrSealFailed = errors.New("arc: seal verification failed") // ErrMessageSignatureFailed indicates the ARC-Message-Signature verification failed. ErrMessageSignatureFailed = errors.New("arc: message signature verification failed") // ErrChainValidationMismatch indicates the cv= tag doesn't match the actual chain state. ErrChainValidationMismatch = errors.New("arc: chain validation status mismatch") // ErrDNS indicates a DNS lookup error occurred. ErrDNS = errors.New("arc: DNS lookup error") // ErrNoRecord indicates no DNS record was found. ErrNoRecord = errors.New("arc: no DNS record found") // ErrSyntax indicates a syntax error in an ARC header. ErrSyntax = errors.New("arc: syntax error") // ErrMissingTag indicates a required tag is missing. ErrMissingTag = errors.New("arc: missing required tag") // ErrInvalidVersion indicates an invalid version tag. ErrInvalidVersion = errors.New("arc: invalid version") // ErrAlgorithmUnknown indicates an unknown signing algorithm. ErrAlgorithmUnknown = errors.New("arc: unknown algorithm") // ErrHashUnknown indicates an unknown hash algorithm. ErrHashUnknown = errors.New("arc: unknown hash algorithm") // ErrCanonicalizationUnknown indicates an unknown canonicalization algorithm. ErrCanonicalizationUnknown = errors.New("arc: unknown canonicalization") // ErrKeyRevoked indicates the signing key has been revoked. ErrKeyRevoked = errors.New("arc: key has been revoked") // ErrWeakKey indicates the key is too weak. ErrWeakKey = errors.New("arc: key is too weak") // ErrExpired indicates the signature has expired. ErrExpired = errors.New("arc: signature expired") // ErrBodyHashMismatch indicates the body hash doesn't match. ErrBodyHashMismatch = errors.New("arc: body hash mismatch") // ErrSignatureFailed indicates signature verification failed. ErrSignatureFailed = errors.New("arc: signature verification failed") // ErrInvalidAuthResults indicates invalid Authentication-Results header. ErrInvalidAuthResults = errors.New("arc: invalid Authentication-Results") // ErrTLD indicates the domain is a top-level domain. ErrTLD = errors.New("arc: domain is a top-level domain") // ErrFromRequired indicates the From header must be signed. ErrFromRequired = errors.New("arc: From header must be signed") )
Common errors for ARC processing.
var DefaultSignedHeaders = []string{
"From",
"To",
"Cc",
"Subject",
"Date",
"Message-ID",
"In-Reply-To",
"References",
"MIME-Version",
"Content-Type",
"Content-Transfer-Encoding",
"Content-Disposition",
"Reply-To",
"Received",
"DKIM-Signature",
}
DefaultSignedHeaders is the default list of headers to sign in ARC-Message-Signature.
Functions ¶
func EvaluateARCForDMARC ¶
func EvaluateARCForDMARC(result *Result, trustedDomains []string) (trusted bool, oldestTrustedPass int)
EvaluateARCForDMARC helps DMARC evaluation by providing ARC chain information. This can be used to override DMARC failures when a trusted ARC chain exists.
Parameters:
- result: The ARC verification result
- trustedDomains: List of domains whose ARC seals are trusted
Returns:
- trusted: Whether the ARC chain was sealed by a trusted domain
- oldestTrustedPass: The oldest instance where a trusted domain passed ARC
func QuickSeal ¶
func QuickSeal(mail *ravenmail.Mail, domain, selector string, privateKey crypto.Signer, authServID, authResults string, chainValidation ChainValidationStatus) error
QuickSeal is a simplified sealing function for common use cases.
func SignMail ¶
func SignMail(mail *ravenmail.Mail, sealer *Sealer, authServID, authResults string, chainValidation ChainValidationStatus) error
SignMail adds ARC headers to a mail message (sealing it). This is a convenience function for sealing mail objects.
Parameters:
- mail: The mail message to seal
- sealer: The sealer configuration
- authServID: The authentication service identifier (your domain)
- authResults: The authentication results string (from SPF, DKIM, DMARC)
- chainValidation: The status of any existing ARC chain
The function will prepend the three ARC headers to the message: ARC-Seal, ARC-Message-Signature, and ARC-Authentication-Results.
Types ¶
type Algorithm ¶
type Algorithm string
Algorithm represents an ARC signing algorithm. ARC uses the same algorithms as DKIM.
const ( // AlgRSASHA256 is the RSA-SHA256 algorithm (required by RFC 8617). AlgRSASHA256 Algorithm = "rsa-sha256" // AlgRSASHA1 is the deprecated RSA-SHA1 algorithm. AlgRSASHA1 Algorithm = "rsa-sha1" // AlgEd25519SHA256 is the Ed25519-SHA256 algorithm (RFC 8463). AlgEd25519SHA256 Algorithm = "ed25519-sha256" )
type AuthenticationResults ¶
type AuthenticationResults struct {
// Instance is the ARC set instance number (i= tag).
Instance int
// AuthServID is the authentication service identifier (required).
AuthServID string
// Results contains the authentication results string.
// This is the raw results string as it appears in the header.
Results string
// Raw is the complete raw header value.
Raw string
}
AuthenticationResults represents a parsed ARC-Authentication-Results header. Per RFC 8617, this header preserves the authentication results observed by an intermediary.
func ParseAuthenticationResults ¶
func ParseAuthenticationResults(value string) (*AuthenticationResults, error)
ParseAuthenticationResults parses an ARC-Authentication-Results header value.
func (*AuthenticationResults) Header ¶
func (aar *AuthenticationResults) Header() string
Header generates the ARC-Authentication-Results header string.
type Canonicalization ¶
type Canonicalization string
Canonicalization represents header/body canonicalization algorithms. ARC uses the same canonicalization as DKIM.
const ( // CanonSimple uses the "simple" canonicalization algorithm. CanonSimple Canonicalization = "simple" // CanonRelaxed uses the "relaxed" canonicalization algorithm. CanonRelaxed Canonicalization = "relaxed" )
type ChainValidationStatus ¶
type ChainValidationStatus string
ChainValidationStatus represents the chain validation status (cv= tag).
const ( // ChainValidationNone indicates no prior ARC chain. ChainValidationNone ChainValidationStatus = "none" // ChainValidationPass indicates the prior ARC chain validated. ChainValidationPass ChainValidationStatus = "pass" // ChainValidationFail indicates the prior ARC chain failed validation. ChainValidationFail ChainValidationStatus = "fail" )
func GetARCChainStatus ¶
func GetARCChainStatus(result *Result) ChainValidationStatus
GetARCChainStatus determines the validation status for sealing based on verification results. This is useful when an intermediary needs to add its own ARC set.
Usage:
result, err := VerifyMailContext(ctx, mail, resolver) chainStatus := GetARCChainStatus(result) err = SignMail(mail, sealer, authServID, authResults, chainStatus)
type DKIMRecord ¶
DKIMRecord represents a DKIM public key record.
type MessageSignature ¶
type MessageSignature struct {
// Instance is the ARC set instance number (i= tag). Required.
Instance int
// Version is the signature version (v= tag). Must be 1.
Version int
// Algorithm is the signing algorithm (a= tag). Required.
Algorithm string
// Signature is the message signature (b= tag). Required.
Signature []byte
// BodyHash is the body hash (bh= tag). Required.
BodyHash []byte
// Domain is the signing domain (d= tag). Required.
Domain string
// SignedHeaders is the list of signed headers (h= tag). Required.
SignedHeaders []string
// Selector is the selector (s= tag). Required.
Selector string
// Canonicalization is the canonicalization (c= tag).
// Format: "header/body" (e.g., "relaxed/relaxed").
Canonicalization string
// Length is the body length limit (l= tag). -1 if not set.
Length int64
// Timestamp is the signature timestamp (t= tag). -1 if not set.
Timestamp int64
// Expiration is the signature expiration (x= tag). -1 if not set.
Expiration int64
// Raw is the complete raw header value.
Raw string
}
MessageSignature represents a parsed ARC-Message-Signature header. This is similar to a DKIM-Signature but with an instance number.
func ParseMessageSignature ¶
func ParseMessageSignature(value string) (*MessageSignature, []byte, error)
ParseMessageSignature parses an ARC-Message-Signature header value.
func (*MessageSignature) AlgorithmHash ¶
func (ms *MessageSignature) AlgorithmHash() string
AlgorithmHash returns the hash algorithm part (e.g., "sha256" from "rsa-sha256").
func (*MessageSignature) AlgorithmSign ¶
func (ms *MessageSignature) AlgorithmSign() string
AlgorithmSign returns the signing algorithm part (e.g., "rsa" from "rsa-sha256").
func (*MessageSignature) BodyCanon ¶
func (ms *MessageSignature) BodyCanon() Canonicalization
BodyCanon returns the body canonicalization algorithm.
func (*MessageSignature) Header ¶
func (ms *MessageSignature) Header(includeSignature bool) string
Header generates the ARC-Message-Signature header string. If includeSignature is false, the b= value is left empty for signing.
func (*MessageSignature) HeaderCanon ¶
func (ms *MessageSignature) HeaderCanon() Canonicalization
HeaderCanon returns the header canonicalization algorithm.
type Result ¶
type Result struct {
// Status is the overall chain validation status.
Status Status
// OldestPass is the instance number of the oldest passing ARC set.
// This is useful for policy decisions about trusted intermediaries.
// Zero if no sets passed.
OldestPass int
// Sets contains the parsed ARC sets, ordered by instance number.
Sets []*Set
// Err contains any error that occurred during validation.
Err error
// FailedInstance is the instance number where validation failed.
// Zero if validation passed or no sets were present.
FailedInstance int
// FailedReason provides details about why validation failed.
FailedReason string
}
Result represents the result of ARC chain validation.
type Seal ¶
type Seal struct {
// Instance is the ARC set instance number (i= tag). Required.
Instance int
// Version is the seal version (v= tag). Must be 1.
Version int
// Algorithm is the signing algorithm (a= tag). Required.
Algorithm string
// Signature is the seal signature (b= tag). Required.
Signature []byte
// Domain is the signing domain (d= tag). Required.
Domain string
// Selector is the selector (s= tag). Required.
Selector string
// ChainValidation is the chain validation status (cv= tag). Required.
ChainValidation ChainValidationStatus
// Timestamp is the signature timestamp (t= tag). -1 if not set.
Timestamp int64
// Raw is the complete raw header value.
Raw string
}
Seal represents a parsed ARC-Seal header. The seal signs the ARC chain to prevent tampering.
func (*Seal) AlgorithmHash ¶
AlgorithmHash returns the hash algorithm part for the seal.
func (*Seal) AlgorithmSign ¶
AlgorithmSign returns the signing algorithm part for the seal.
type SealResult ¶
type SealResult struct {
// AuthenticationResults is the ARC-Authentication-Results header.
AuthenticationResults string
// MessageSignature is the ARC-Message-Signature header.
MessageSignature string
// Seal is the ARC-Seal header.
Seal string
// Instance is the ARC set instance number.
Instance int
}
SealResult contains the headers generated by sealing.
type Sealer ¶
type Sealer struct {
// Domain is the signing domain (d= tag).
Domain string
// Selector is the selector for the signing key (s= tag).
Selector string
// PrivateKey is the signing key.
// Supported types: *rsa.PrivateKey, ed25519.PrivateKey
PrivateKey crypto.Signer
// Headers is the list of headers to sign in ARC-Message-Signature.
// If empty, DefaultSignedHeaders is used.
Headers []string
// HeaderCanonicalization is the header canonicalization algorithm.
// Default is CanonRelaxed.
HeaderCanonicalization Canonicalization
// BodyCanonicalization is the body canonicalization algorithm.
// Default is CanonRelaxed.
BodyCanonicalization Canonicalization
// Clock is used for timestamp generation.
// If nil, time.Now is used.
Clock func() time.Time
}
Sealer provides ARC message sealing.
func (*Sealer) Seal ¶
func (s *Sealer) Seal(message []byte, authServID, authResults string, chainValidation ChainValidationStatus) (*SealResult, error)
Seal creates a new ARC set for the message. The authResults parameter should contain the authentication results observed by this intermediary. The chainValidation parameter indicates the validation status of any existing ARC chain.
Returns the three ARC headers that should be prepended to the message.
type Set ¶
type Set struct {
// Instance is the ARC set instance number (i= tag).
// Must be between 1 and MaxInstance (50).
Instance int
// AuthenticationResults is the parsed ARC-Authentication-Results header.
AuthenticationResults *AuthenticationResults
// MessageSignature is the parsed ARC-Message-Signature header.
MessageSignature *MessageSignature
// Seal is the parsed ARC-Seal header.
Seal *Seal
}
Set represents a complete ARC set for a single instance. Each set contains exactly one of each header type with matching instance numbers.
type Verifier ¶
type Verifier struct {
// Resolver is the DNS resolver for key lookups.
Resolver ravendns.Resolver
// MinRSAKeyBits is the minimum RSA key size to accept.
// Default is 1024.
MinRSAKeyBits int
// IgnoreExpired allows verification of expired signatures.
// Default is false.
IgnoreExpired bool
// Clock is used for timestamp verification.
// If nil, time.Now is used.
Clock func() time.Time
}
Verifier provides ARC chain verification.