dkimverify 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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.