Module: MailAuth::Arc

Defined in:
lib/mailauth/arc.rb,
lib/mailauth/arc/set.rb,
lib/mailauth/arc/seal.rb,
lib/mailauth/arc/chain.rb,
lib/mailauth/arc/message_signature.rb

Overview

RFC 8617. An Authenticated Received Chain records what each intermediary concluded about a message as it passed through, sealed so a later reader can tell the record has not been touched since. Forwarding routinely breaks SPF and DKIM; a chain says what they said before it did.

What a valid chain proves is only that each hop sealed what it sealed. Sealing a chain over one's own message costs nothing, so whether any sealer is to be believed is a decision for the caller and has no answer here.

Defined Under Namespace

Classes: Chain, MessageSignature, Seal, Set, Validation

Constant Summary collapse

CRLF =
"\r\n".b
MAXIMUM_SETS =

§4.2.1. The ceiling is enforced before any key is fetched: each set otherwise costs a lookup, and a long chain is cheap to write.

50
CHAIN_STATUSES =
%w[ none fail pass ].freeze
CANONICALIZATIONS =
Dkim::CANONICALIZATIONS
HASHES =
%w[ sha256 ].freeze
SIGNING_ALGORITHMS =
Dkim::SIGNING_ALGORITHMS
INSTANCE_PREFIX =

§4.1.1 puts the instance ahead of the payload an AAR carries, where the other two hold it among their tags.

/\A[ \t\r\n]*i[ \t\r\n]*=[ \t\r\n]*(\d+)[ \t\r\n]*;/n

Class Method Summary collapse

Class Method Details

.hash_algorithm(algorithm) ⇒ Object



56
57
58
# File 'lib/mailauth/arc.rb', line 56

def self.hash_algorithm(algorithm)
  algorithm.to_s.split("-").last.to_s.downcase
end

.signing_algorithm(algorithm) ⇒ Object



52
53
54
# File 'lib/mailauth/arc.rb', line 52

def self.signing_algorithm(algorithm)
  algorithm.to_s.split("-").first.to_s.downcase
end

.signing_algorithm?(algorithm) ⇒ Boolean

RFC 8617 never registered sha1; DKIM's list matches only since retiring it.

Returns:

  • (Boolean)


48
49
50
# File 'lib/mailauth/arc.rb', line 48

def self.signing_algorithm?(algorithm)
  SIGNING_ALGORITHMS.include?(signing_algorithm(algorithm)) && HASHES.include?(hash_algorithm(algorithm))
end

.tags_in(field) ⇒ Object



43
44
45
# File 'lib/mailauth/arc.rb', line 43

def self.tags_in(field)
  Dkim.parse_tags(value_of(field).to_s)
end

.value_of(field) ⇒ Object



39
40
41
# File 'lib/mailauth/arc.rb', line 39

def self.value_of(field)
  field&.split(":", 2)&.last
end

.verify(message, resolver: Resolver.new, now: Time.now) ⇒ Object

now is taken for symmetry with the other checks and not consulted: ARC gives a chain no lifetime, and a message can sit in a queue for days without that saying anything about the hops it already crossed.



35
36
37
# File 'lib/mailauth/arc.rb', line 35

def self.verify(message, resolver: Resolver.new, now: Time.now)
  Validation.new(message, resolver: resolver).result
end