dkimverify 0.0.4 → 0.0.6

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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -12
  3. data/dkimverify.gemspec +2 -3
  4. data/dkimverify.rb +81 -26
  5. metadata +3 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ac5d9f13f3025786b6a19e4bd8e6147ca6adbcd8
4
- data.tar.gz: 461833996fc77a81882190b1011f1355764e5d5c
3
+ metadata.gz: 0a30e99530c7485db52e9583ad67b4565b22eadd
4
+ data.tar.gz: 0876d9ad6008badfc29bf3c05057b2894af84771
5
5
  SHA512:
6
- metadata.gz: 145a1d41927294da41a5d61ef1e8bdbb631f3fa4ef9e5860e49e1eabf060b66aa3e394c521c55939b79bbb7d2cf3396bbe81a9f88251d7fb3b14d436ecbe3dfa
7
- data.tar.gz: a6822d5f82d8fb5e43ec6078db1d6d67ef05a4f83cc2c07dcc834afb7184456f25f2e34e51544d1e35fcc2bfdac65e490d21df20a61df9bfa334721c23c095c3
6
+ metadata.gz: a4334b8fb2321ca976c25911447d1f95e3fd2fd7752640f93aa0c19f6622dde2c6d6da27e00ef75cdc3a9089e159d8f1df3d94e6eb4f8171b4333475d28aa574
7
+ data.tar.gz: 1819a0bef43d2aa4c95e2f69f4c3ec2b037d9c7a85eb6efe14b0bcbda6ab9257f8fcc2b5e0391df41053ef10e84e8a856edb21631ac947bc8f1a2d471cf95ab3
data/README.md CHANGED
@@ -5,7 +5,7 @@ a gem for verifying DKIM signatures in Ruby
5
5
 
6
6
  this gem does not sign mail messages (but a PR to enable it would likely be accepted, I just have no use for it.)
7
7
 
8
- **this gem doesn't work right yet!!!**
8
+ I'm pretty sure this actually works and I'm using it in production.
9
9
 
10
10
  how to use
11
11
  -----------
@@ -17,16 +17,10 @@ the `verify!` method will return:
17
17
  - `false` if no signature is present, and,
18
18
  - raise `Dkim::DkimError` (or a child error) if the signature is present but does not verify.
19
19
 
20
- loading emails from a string is not yet implemented, but would be really easy (send me a PR!)
21
-
22
-
23
-
24
-
25
20
  with a debt of gratitude to:
26
21
  ----------------------------
27
22
 
28
- - [pydkim](https://github.com/ghewgill/pydkim) by @ghewgill which I used as a reference implementation
29
- - [mail](https://github.com/mikel/mail) by @mikel
23
+ - [pydkim](https://github.com/ghewgill/pydkim) by @ghewgill which I used as a more-or-less literal source of translation
30
24
  - [carsonreinke's fork of the jhawthorne's dkim gem](https://github.com/carsonreinke/dkim/tree/feature_verification) which I wish I had found before I started this.
31
25
  - [rfc6376 authors](https://tools.ietf.org/html/rfc6376)
32
26
 
@@ -36,11 +30,10 @@ checking expiration dates (x=, t=)
36
30
  accounting for length limits (l= tag)
37
31
  tests (which I really ought to add)
38
32
  checking multiple dkim signature header lines (probably easy)
39
- dealing with the "simple" canonicalization method (because I need to strip out the `mail` gem and instead write my own RFC822 parser that is better for maintaining the exact original string)
40
33
 
41
34
  by
42
35
  --
43
36
 
44
- Jeremy B. Merrill
45
- The New York Times
46
- January 2017
37
+ Jeremy B. Merrill
38
+ The New York Times
39
+ April 2017
data/dkimverify.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = "dkimverify"
5
- gem.version = '0.0.4'
5
+ gem.version = '0.0.6'
6
6
  gem.authors = ["Jeremy B. Merrill"]
7
7
  gem.license = "MIT"
8
8
  gem.email = ["jeremybmerrill@gmail.com"]
@@ -10,7 +10,6 @@ Gem::Specification.new do |gem|
10
10
  gem.summary = %q{ A pure-Ruby library for validating/verifying DKIM signatures. }
11
11
  gem.homepage = "https://github.com/jeremybmerrill/dkimverify"
12
12
  gem.files = `git ls-files`.split($/)
13
- gem.require_paths = ["dkim-query", "."]
14
- gem.add_dependency "mail", "2.6.4"
13
+ gem.require_paths = ["."]
15
14
  gem.add_dependency "parslet", "~> 1.6"
16
15
  end
data/dkimverify.rb CHANGED
@@ -5,12 +5,72 @@ require 'resolv'
5
5
 
6
6
  # TODO make this an option somehow
7
7
  $debuglog = nil #STDERR # nil # alternatively, set this to `STDERR` to log to stdout.
8
- require 'mail'
9
8
 
10
9
  module Mail
11
- class Header
12
- def first_field(name)
13
- self[name].class == Array ? self[name].first : self[name]
10
+ class MessageFormatError < StandardError; end
11
+
12
+ class HeaderHash < Hash
13
+ def get(header_name)
14
+ self[get_name(header_name)]
15
+ end
16
+
17
+ def get_name(header_name)
18
+ keys.find{|k| k.downcase == header_name.downcase }
19
+ end
20
+ end
21
+
22
+ class Message
23
+ def initialize(msg)
24
+ @raw_message = msg
25
+ @raw_headers = []
26
+ @body = nil
27
+ @parsed = false
28
+ end
29
+
30
+ def headers
31
+ self.parse! unless @parsed
32
+ @headers
33
+ end
34
+
35
+ def body
36
+ self.parse! unless @parsed
37
+ @body
38
+ end
39
+
40
+ def parse!
41
+ """Parse a message in RFC822 format.
42
+
43
+ @param message: The message in RFC822 format. Either CRLF or LF is an accepted line separator.
44
+
45
+ @return Returns a tuple of (headers, body) where headers is a list of (name, value) pairs.
46
+ The body is a CRLF-separated string.
47
+
48
+ """
49
+
50
+ lines = @raw_message.split(/\r?\n/)
51
+ i = 0
52
+ while i < lines.size
53
+ if lines[i].size == 0
54
+ # End of headers, return what we have plus the body, excluding the blank line.
55
+ i += 1
56
+ break
57
+ end
58
+ if /[\x09\x20]/.match lines[i][0]
59
+ @raw_headers[-1][1] += lines[i]+"\r\n"
60
+ else
61
+ m = /([\x21-\x7e]+?):/.match lines[i]
62
+ if m
63
+ @raw_headers << [m[1], lines[i][m.end(0)..-1]+"\r\n"]
64
+ elsif lines[i].start_with?("From ")
65
+
66
+ else
67
+ raise MessageFormatError.new("Unexpected characters in RFC822 header: #{lines[i]}")
68
+ end
69
+ end
70
+ i += 1
71
+ end
72
+ @body = lines[i..-1].join("\r\n") + "\r\n"
73
+ @headers = HeaderHash[*@raw_headers.reverse.flatten(1)]
14
74
  end
15
75
  end
16
76
  end
@@ -35,8 +95,8 @@ module Dkim
35
95
  #TODO: what is this kind of key-value string even called?
36
96
  def self.parse_header_kv(input_str)
37
97
  parsed = {}
38
- input_str.split(/\s*;\s*/).each do |key_val|
39
- if m = key_val.match(/(\w+)\s*=\s*(.*)/)
98
+ input_str.split(/\s*;\s*/m).each do |key_val|
99
+ if m = key_val.match(/(\w+)\s*=\s*(.*)/m)
40
100
  parsed[m[1]] = m[2]
41
101
  end
42
102
  end
@@ -44,17 +104,17 @@ module Dkim
44
104
  end
45
105
 
46
106
  class Verifier
47
- def initialize(email_filename)
48
- mail = Mail.read(email_filename) # TODO make this `mail` not `@mail`
49
- @headers = mail.header
50
- @body = mail.body.raw_source
107
+ def initialize(email_stringy_thing)
108
+ mail = Mail::Message.new(email_stringy_thing)
109
+ @headers = mail.headers
110
+ @body = mail.body
51
111
  end
52
112
 
53
113
 
54
114
  def verify!
55
- return false if @headers["DKIM-Signature"].nil?
115
+ return false if @headers.get("DKIM-Signature").nil?
56
116
 
57
- dkim_signature_str = @headers.first_field("DKIM-Signature").value.to_s
117
+ dkim_signature_str = @headers.get("DKIM-Signature").to_s
58
118
  @dkim_signature = Dkim.parse_header_kv(dkim_signature_str)
59
119
  validate_signature! # just checking to make sure we have all the ingredients we need to actually verify the signature
60
120
 
@@ -110,7 +170,7 @@ module Dkim
110
170
  def figure_out_canonicalization_methods!
111
171
  c_match = @dkim_signature['c'].match(/(\w+)(?:\/(\w+))?$/)
112
172
  if not c_match
113
- puts "can't figure out canonicalization ('c=')"
173
+ $debuglog.puts "can't figure out canonicalization ('c=')"
114
174
  return false
115
175
  end
116
176
  @how_to_canonicalize_headers = c_match[1]
@@ -142,20 +202,15 @@ module Dkim
142
202
  $debuglog.puts "header_fields_to_include: #{header_fields_to_include}" unless $debuglog.nil?
143
203
  canonicalized_headers = []
144
204
  header_fields_to_include_with_values = header_fields_to_include.map do |header_name|
145
- puts @headers.first_field(header_name).inspect
146
- [header_name, (hstr = @headers.first_field(header_name).instance_variable_get("@raw_value")).nil? ? '' : hstr.split(":")[1..-1].join(":") ]
147
- # .value and .instance_eval { unfold(split(@raw_value)[1]) } return subtly different values
148
- # if the value of the Date header is a date with a single-digit day.
149
- # see https://github.com/mikel/mail/issues/1075
150
- # incidentally, .instance_variable_get("@value") gives a third subtly different value in a way that I don't understand.
205
+ header_val = (hstr = @headers.get(header_name)).nil? ? '' : hstr #.split(":")[1..-1].join(":")
206
+ [header_name, header_val ]
151
207
  end
152
208
  canonicalized_headers = Dkim.canonicalize_headers(header_fields_to_include_with_values, @how_to_canonicalize_headers)
153
- puts @headers.first_field("DKIM-Signature").inspect
154
209
 
155
210
  canonicalized_headers += Dkim.canonicalize_headers([
156
211
  [
157
- @headers.first_field("DKIM-Signature").name.to_s,
158
- @headers.first_field("DKIM-Signature").value.to_s.split(@dkim_signature['b']).join('')
212
+ @headers.get_name("DKIM-Signature").to_s,
213
+ @headers.get("DKIM-Signature").to_s.split(@dkim_signature['b']).join('')
159
214
  ]
160
215
  ], @how_to_canonicalize_headers).map{|x| [x[0], x[1].rstrip()] }
161
216
 
@@ -272,12 +327,12 @@ module Dkim
272
327
  end
273
328
 
274
329
  if __FILE__ == $0
275
- emlfn = ARGV[0] || "/Users/204434/code/stevedore-uploader-internal/inputs/podesta-part36/59250.eml"
330
+ eml = ARGF.read
276
331
  begin
277
- ret = Dkim::Verifier.new(emlfn).verify!
332
+ ret = Dkim::Verifier.new(eml).verify!
278
333
  rescue Dkim::DkimPermFail
279
- puts "uh oh, something went wrong, the signature did not verify correctly"
334
+ STDERR.puts "uh oh, something went wrong, the signature did not verify correctly"
280
335
  exit 1
281
336
  end
282
- puts ret ? "DKIM signature verified correctly" : "DKIM signature absent"
337
+ STDERR.puts ret ? "DKIM signature verified correctly" : "DKIM signature absent"
283
338
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dkimverify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy B. Merrill
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-14 00:00:00.000000000 Z
11
+ date: 2017-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: mail
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - '='
18
- - !ruby/object:Gem::Version
19
- version: 2.6.4
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - '='
25
- - !ruby/object:Gem::Version
26
- version: 2.6.4
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: parslet
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -58,7 +44,6 @@ metadata: {}
58
44
  post_install_message:
59
45
  rdoc_options: []
60
46
  require_paths:
61
- - dkim-query
62
47
  - "."
63
48
  required_ruby_version: !ruby/object:Gem::Requirement
64
49
  requirements:
@@ -72,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
57
  version: '0'
73
58
  requirements: []
74
59
  rubyforge_project:
75
- rubygems_version: 2.5.2
60
+ rubygems_version: 2.5.1
76
61
  signing_key:
77
62
  specification_version: 4
78
63
  summary: A pure-Ruby library for validating/verifying DKIM signatures.