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 +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +20 -18
- data/lib/mailauth/arc.rb +1 -1
- data/lib/mailauth/dkim.rb +18 -3
- data/lib/mailauth/resolver.rb +3 -0
- data/lib/mailauth/version.rb +1 -1
- data/mailauth.gemspec +3 -4
- metadata +5 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 49dfab12d1bcd9e138fd09d613b66d5ffa150cbd4d73ce381e10a07a6aa3e3f2
|
|
4
|
+
data.tar.gz: 99e7290474ee3fe5054e05b23041cdf93b9f91d174f7d7ed23dd7ded0743b9e1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
5
|
+
Answers one question about a received message: is it really from who it says it's from?
|
|
6
6
|
|
|
7
|
-
|
|
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.
|
|
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/
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
data/lib/mailauth/resolver.rb
CHANGED
|
@@ -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.
|
data/lib/mailauth/version.rb
CHANGED
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 = "
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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:
|
|
113
|
+
summary: Email authentication for Ruby.
|
|
116
114
|
test_files: []
|