mailauth 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77dfb8831d86e9be13316fe465a7655bf0aedf4da6164cdd0d2c4ab51cb5f6a4
4
- data.tar.gz: dd67a9d118d2410deeb00d57f907075c9d77e58637f69b2989ed7c1066e01fd8
3
+ metadata.gz: 49dfab12d1bcd9e138fd09d613b66d5ffa150cbd4d73ce381e10a07a6aa3e3f2
4
+ data.tar.gz: 99e7290474ee3fe5054e05b23041cdf93b9f91d174f7d7ed23dd7ded0743b9e1
5
5
  SHA512:
6
- metadata.gz: 3ebf36b61620326e742d838d3472dc32f8e9fb43812126e27e00daed54e947711167ab42742622432f28344b5ceeeee3d1d2348f1187212a4708c2138a821770
7
- data.tar.gz: c35623984649fb47802e1a61386bbb8cdb91f192b2597209d5556ccafba35c97099afc482fa531837d94e54ce29a72131b82f6f25fdb2f19e446bbe03b7effd9
6
+ metadata.gz: 5381265ba7b84d464781bbca0428c1ceec111b93eea103095545301d381a4b4bfbfc55fb34bf47e5ffd8921700a531bb83d965dc3c3d087739a72452e53a7e3b
7
+ data.tar.gz: '06093aba36c61e9a818afc9c82749416cee178345136cc3473877d5bc5354dd460189bfa02963d82354e38a4ce168b8a07412421c7b78f7c19cf2d826a8835e5'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.2
4
+
5
+ - `permerror` rather than `pass` for an `rsa-sha1` signature, retired by RFC 8301 §3.1
6
+ - At most ten DKIM signatures are verified per message
7
+
8
+ ## 0.3.1
9
+
10
+ - `temperror` when no nameserver is usable, rather than a raise
11
+ - Documentation
12
+
3
13
  ## 0.3.0
4
14
 
5
15
  **A nameserver failing is no longer read as a domain publishing nothing.** Expect
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # mailauth
2
2
 
3
- ✉️ Email authentication for Ruby
3
+ ✉️ Email authentication for Ruby.
4
4
 
5
- **mailauth** verifies SPF, DKIM, DMARC, and ARC on received mail, and signs outgoing mail with DKIM. Pure Ruby, no dependencies beyond `dnsruby`.
5
+ Answers one question about a received message: is it really from who it says it's from?
6
6
 
7
- It answers one question about a message: is it really from who it says it's from?
7
+ **mailauth** verifies SPF, DKIM, DMARC, and ARC on received mail, and signs outgoing mail with DKIM.
8
8
 
9
9
  ## Features
10
10
 
@@ -16,10 +16,22 @@ It answers one question about a message: is it really from who it says it's from
16
16
  - ARC verification (RFC 8617), reporting what each hop sealed and left to you to trust
17
17
  - Injectable resolver, so tests run against a hash instead of a network
18
18
 
19
- Not planned: MTA-STS (outbound TLS policy, not message auth), `Received-SPF` (Authentication-Results already covers it), `%{p}` (RFC 7208 discourages PTR).
20
-
21
19
  > Not a spam filter.
22
20
 
21
+ ## Contents
22
+
23
+ - [Getting Started](#getting-started)
24
+ - [Authentication](#authentication)
25
+ - [Results](#results)
26
+ - [Individual Checks](#individual-checks)
27
+ - [When It Doesn't Pass](#when-it-doesnt-pass)
28
+ - [Resolver](#resolver)
29
+ - [ARC](#arc)
30
+ - [Signing](#signing)
31
+ - [Testing](#testing)
32
+ - [History](#history)
33
+ - [Contributing](#contributing)
34
+ - [License](#license)
23
35
 
24
36
  ## Getting Started
25
37
 
@@ -125,8 +137,6 @@ end
125
137
  - `permerror` - the record or signature is broken; retrying changes nothing
126
138
  - `none` - the domain published no policy, or the message carried no signature
127
139
 
128
-
129
-
130
140
  ## Resolver
131
141
 
132
142
  Every lookup goes through one object with five methods - `txt`, `a`, `aaaa`, `mx`, `ptr`:
@@ -137,7 +147,7 @@ MailAuth.authenticate(raw, ip: ip, resolver: MyCachingResolver.new)
137
147
 
138
148
  Raise `NotFound` for a void lookup (SPF counts these against a limit), `Timeout` or `ServerFailure` for no answer, which produces a `temperror` that stops evaluation. Collapsing those into empty arrays loses a distinction SPF spends much of its rulebook on.
139
149
 
140
- The default resolver uses [dnsruby](https://github.com/alexdalitz/dnsruby) rather than `Resolv`, which collapses SERVFAIL/REFUSED into the same empty answer as NXDOMAIN. It caches nothing and does not validate DNSSEC.
150
+ The default resolver uses [dnsruby](https://github.com/alexdalitz/dnsruby) rather than `Resolv`, which collapses SERVFAIL/REFUSED into the same empty answer as NXDOMAIN.
141
151
 
142
152
  ## ARC
143
153
 
@@ -185,27 +195,19 @@ bundle install
185
195
  rake
186
196
  ```
187
197
 
188
- SPF runs against the [RFC 7208 test suite](http://www.openspf.org/Test_Suite) - 200 official cases covering every mechanism, the lookup limits, and the error semantics. One case is skipped: `p-macro-multiple`, which needs PTR forward-confirmation this library doesn't do.
189
-
190
- DKIM runs against the sample message in RFC 8463 Appendix A, which carries both an RSA and an Ed25519 signature over the same body. Signing is held to the same standard: both algorithms are deterministic, so signing the sample message with its published private keys must reproduce the published signatures byte for byte.
191
-
192
- ARC runs against the [ValiMail ARC test suite](https://github.com/ValiMail/arc_test_suite) - 171 cases over chain validation, header structure, tag grammar, and key strength.
193
-
194
198
  ## History
195
199
 
196
- View the changelog
200
+ View the [changelog](CHANGELOG.md).
197
201
 
198
202
  ## Contributing
199
203
 
200
204
  Everyone is encouraged to help improve this project:
201
205
 
202
- - [Report bugs](https://github.com/example/mailauth/issues)
206
+ - [Report bugs](https://github.com/mailpiece/mailauth/issues)
203
207
  - Fix bugs and submit pull requests
204
208
  - Write, clarify, or fix documentation
205
209
  - Suggest or add new features
206
210
 
207
-
208
-
209
211
  ## License
210
212
 
211
213
  MIT. See [LICENSE](LICENSE).
data/lib/mailauth/arc.rb CHANGED
@@ -44,7 +44,7 @@ module MailAuth
44
44
  Dkim.parse_tags(value_of(field).to_s)
45
45
  end
46
46
 
47
- # sha1 is still legal for a DKIM signature and never was for ARC.
47
+ # RFC 8617 never registered sha1; DKIM's list matches only since retiring it.
48
48
  def self.signing_algorithm?(algorithm)
49
49
  SIGNING_ALGORITHMS.include?(signing_algorithm(algorithm)) && HASHES.include?(hash_algorithm(algorithm))
50
50
  end
data/lib/mailauth/dkim.rb CHANGED
@@ -17,7 +17,8 @@ module MailAuth
17
17
  # the algorithm is unsupported, or the key is unreadable.
18
18
  class Malformed < StandardError; end
19
19
 
20
- HASHES = { "sha1" => OpenSSL::Digest::SHA1, "sha256" => OpenSSL::Digest::SHA256 }.freeze
20
+ # RFC 8301 §3.1 retired rsa-sha1: a verifier must not consider one valid.
21
+ HASHES = { "sha256" => OpenSSL::Digest::SHA256 }.freeze
21
22
  SIGNING_ALGORITHMS = %w[ rsa ed25519 ].freeze
22
23
 
23
24
  # What OpenSSL calls a key, mapped to what a= calls it.
@@ -29,6 +30,10 @@ module MailAuth
29
30
  # RFC 6376 §3.2: ALPHA *ALNUMPUNC.
30
31
  TAG_NAME = /\A[a-zA-Z][a-zA-Z0-9_]*\z/n
31
32
 
33
+ # RFC 6376 §8.4. Each signature costs a lookup for a key its sender named,
34
+ # and headers are cheap to write, so only the first so many are answered.
35
+ MAXIMUM_SIGNATURES = 10
36
+
32
37
  # Shorter RSA keys are factorable cheaply enough to prove nothing.
33
38
  MINIMUM_RSA_BITS = 1024
34
39
 
@@ -37,10 +42,20 @@ module MailAuth
37
42
  ED25519_SPKI_PREFIX = [ "302a300506032b6570032100" ].pack("H*")
38
43
 
39
44
  def self.verify(message, resolver: Resolver.new, now: Time.now)
40
- signatures = message.fields_named("dkim-signature").map do |field|
45
+ fields = message.fields_named("dkim-signature")
46
+
47
+ signatures = fields.first(MAXIMUM_SIGNATURES).map do |field|
41
48
  Verification.new(message, field, resolver:, now:).result
42
49
  end
43
- signatures << Signature.new(status: Status::NONE, comment: "message not signed") if signatures.empty?
50
+
51
+ # The ones left over are reported rather than dropped: a verifier that
52
+ # quietly ignores a signature says a message carried fewer than it did.
53
+ if fields.empty?
54
+ signatures << Signature.new(status: Status::NONE, comment: "message not signed")
55
+ elsif fields.length > MAXIMUM_SIGNATURES
56
+ signatures << Signature.new(status: Status::PERMERROR,
57
+ comment: "#{fields.length - MAXIMUM_SIGNATURES} more signatures not evaluated")
58
+ end
44
59
 
45
60
  DkimResult.new(signatures:)
46
61
  end
@@ -82,6 +82,9 @@ module MailAuth
82
82
  dns.do_caching = false
83
83
  dns.query_timeout = @timeout
84
84
  end
85
+ rescue ArgumentError => error
86
+ # Dnsruby raises this when no configured nameserver is usable.
87
+ raise ServerFailure, "no usable nameserver: #{error.message}"
85
88
  end
86
89
 
87
90
  # Override in tests to point at a loopback nameserver.
@@ -1,3 +1,3 @@
1
1
  module MailAuth
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.2"
3
3
  end
data/mailauth.gemspec CHANGED
@@ -7,10 +7,9 @@ Gem::Specification.new do |spec|
7
7
  spec.platform = Gem::Platform::RUBY
8
8
  spec.required_ruby_version = ">= 3.3.4"
9
9
  spec.authors = [ "Simon Lev" ]
10
- spec.email = [ "support@postrider.dev" ]
11
10
 
12
- spec.summary = "SPF, DKIM, DMARC, and ARC verification for received mail, in pure Ruby."
13
- spec.description = "Answers one question about a received message: is it really from who it says it's from?"
11
+ spec.summary = "Email authentication for Ruby."
12
+ spec.description = "Answers one question about a received message: is it really from who it says it's from? Implements DKIM signing and verification, SPF, DMARC, and ARC."
14
13
 
15
14
  spec.homepage = "https://github.com/mailpiece/mailauth"
16
15
  spec.license = "MIT"
@@ -33,7 +32,7 @@ Gem::Specification.new do |spec|
33
32
  spec.require_paths = [ "lib" ]
34
33
 
35
34
  # Resolv collapses SERVFAIL/REFUSED into empty answers; dnsruby does not.
36
- spec.add_dependency "dnsruby", "~> 1.72"
35
+ spec.add_dependency "dnsruby", "~> 1.74"
37
36
  spec.add_dependency "public_suffix", ">= 5.0"
38
37
  spec.add_dependency "simpleidn", "~> 0.2"
39
38
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mailauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Lev
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '1.72'
18
+ version: '1.74'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '1.72'
25
+ version: '1.74'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: public_suffix
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -52,9 +52,7 @@ dependencies:
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0.2'
54
54
  description: 'Answers one question about a received message: is it really from who
55
- it says it''s from?'
56
- email:
57
- - support@postrider.dev
55
+ it says it''s from? Implements DKIM signing and verification, SPF, DMARC, and ARC.'
58
56
  executables: []
59
57
  extensions: []
60
58
  extra_rdoc_files: []
@@ -112,5 +110,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
110
  requirements: []
113
111
  rubygems_version: 4.0.3
114
112
  specification_version: 4
115
- summary: SPF, DKIM, DMARC, and ARC verification for received mail, in pure Ruby.
113
+ summary: Email authentication for Ruby.
116
114
  test_files: []