dkim 0.0.1 → 0.0.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.
@@ -0,0 +1,14 @@
1
+ # dkim Changelog
2
+
3
+ ## 2011.06.01, Version 0.0.2
4
+
5
+ * add convenience method Dkim.sign
6
+ * support for the simple canonicalization algorithm
7
+ * domain now must be specified as an option
8
+ * correct handling of an empty message body
9
+
10
+
11
+ ## 2011.05.10, Version 0.0.1
12
+
13
+ * Initial release
14
+
@@ -0,0 +1,123 @@
1
+ dkim
2
+ ====
3
+
4
+ A DKIM signing library in ruby.
5
+
6
+ Installation
7
+ ============
8
+
9
+ sudo gem install dkim
10
+
11
+ Usage
12
+ =====
13
+
14
+ Calling `Dkim.sign` on a string representing an email message returns the message with a DKIM signature inserted.
15
+
16
+ For example
17
+
18
+ mail = <<eos
19
+ To: someone@example.com
20
+ From: john@example.com
21
+ Subject: hi
22
+
23
+ Howdy
24
+ eos
25
+
26
+ Dkim.sign(mail)
27
+
28
+ # =>
29
+ # To: someone@example.com
30
+ # From: john@example.com
31
+ # Subject: hi
32
+ # DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; q=dns/txt; s=mail; t=1305917829;
33
+ # bh=qZxwTnSM1ywsrq0Ag9UhQSOtVIG+sW5zDkB+hPbuX08=; h=from:subject:to;
34
+ # b=0mKnNOkxFGiww63Zu4t46J7eZc3Uak3I9km3IH2Le3XcnSNtWJgxiwBX26IZ5yzcT
35
+ # VwJzcCnPKCScIJMQ7yfbfXmNsKVIOV6eSUqu1YvJ1fgzlSAXuDEMNFTjoto5rrdA+
36
+ # BgX849hEY/bWHDl1JJgNpiwtpl4t0Q7M4BVJUd7Lo=
37
+ #
38
+ # Howdy
39
+
40
+ Necessary configuration
41
+ -----------------------
42
+ A private key, a domain, and a selector need to be specified in order to sign messages.
43
+
44
+ These can be specified globally
45
+
46
+ Dkim::domain = 'example.com'
47
+ Dkim::selector = 'mail'
48
+ Dkim::private_key = open('private.pem').read
49
+
50
+ Options can be overridden per message.
51
+
52
+ Dkim.sign(mail, :selector => 'mail2', :private_key => open('private2.pem').read)
53
+
54
+ Additional configuration
55
+ ------------------------
56
+
57
+ The following is the default configuration
58
+
59
+ Dkim::signable_headers = Dkim::DefaultHeaders # Sign only the specified headers
60
+ Dkim::signing_algorithm = 'rsa-sha256' # can be rsa-sha1 or rsa-sha256 (default)
61
+ Dkim::header_canonicalization = 'relaxed' # Can be simple or relaxed (default)
62
+ Dkim::body_canonicalization = 'relaxed' # Can be simple or relaxed (default)
63
+
64
+ The defaults should fit most users needs; however, certain use cases will need them to be customized.
65
+
66
+ For example, for sending mesages through amazon SES, certain headers should not be signed
67
+
68
+ Dkim::signable_headers = Dkim::DefaultHeaders - %w{Message-Id Resent-Message-ID Date Return-Path Bounces-To}
69
+
70
+ rfc4871 states that signers SHOULD sign using rsa-sha256. For this reason, dkim will *not* use rsa-sha1 as a fallback if the openssl library does not support sha256.
71
+ If you wish to override this behaviour and use whichever algorithm is available you can use this snippet (**not recommended**).
72
+
73
+ Dkim::signing_algorithm = defined?(OpenSSL::Digest::SHA256) ? 'rsa-sha256' : 'rsa-sha1'
74
+
75
+ Example executable
76
+ ==================
77
+
78
+ The library includes a `dkimsign.rb` executable suitable for testing the library or performing simple signatures.
79
+
80
+ `dkimsign.rb DOMAIN SELECTOR KEYFILE [MAILFILE]`
81
+
82
+ If MAILFILE is not specified `dkimsign.rb` will read the mail message from standard in.
83
+
84
+ Limitations
85
+ ===========
86
+
87
+ * Strictly a DKIM signing library. No support for signature verification. *(none planned)*
88
+ * No support for the older Yahoo! DomainKeys standard ([RFC 4870](http://tools.ietf.org/html/rfc4870)) *(none planned)*
89
+ * No support for specifying DKIM identity `i=` *(planned)*
90
+ * No support for body length `l=` *(planned)*
91
+ * No support for signature expiration `x=` *(planned)*
92
+ * No support for copied header fields `z=` *(not immediately planned)*
93
+
94
+ Resources
95
+ =========
96
+
97
+ * [RFC 4871](http://tools.ietf.org/html/rfc4871)
98
+ * Inspired by perl's [Mail-DKIM](http://dkimproxy.sourceforge.net/)
99
+
100
+ Copyright
101
+ =========
102
+
103
+ (The MIT License)
104
+
105
+ Copyright (c) 2011 [John Hawthorn](http://www.johnhawthorn.com/)
106
+
107
+ Permission is hereby granted, free of charge, to any person obtaining
108
+ a copy of this software and associated documentation files (the
109
+ "Software"), to deal in the Software without restriction, including
110
+ without limitation the rights to use, copy, modify, merge, publish,
111
+ distribute, sublicense, and/or sell copies of the Software, and to
112
+ permit persons to whom the Software is furnished to do so, subject to
113
+ the following conditions:
114
+
115
+ The above copyright notice and this permission notice shall be
116
+ included in all copies or substantial portions of the Software.
117
+
118
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
119
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
120
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
121
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
122
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
123
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
data/Rakefile CHANGED
@@ -1,2 +1,14 @@
1
+ require 'rake/testtask'
1
2
  require 'bundler'
3
+
4
+ task :default => [:test]
5
+
6
+ desc 'Run tests.'
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.libs << 'lib' << 'test'
9
+ t.pattern = 'test/**/*_test.rb'
10
+ t.verbose = true
11
+ end
12
+
2
13
  Bundler::GemHelper.install_tasks
14
+
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
3
  if ARGV.length != 2 && ARGV.length != 3
4
- puts "Usage: dkimsign.rb SELECTOR KEYFILE [MAILFILE]"
4
+ puts "Usage: dkimsign.rb DOMAIN SELECTOR KEYFILE [MAILFILE]"
5
5
  exit 0
6
6
  end
7
7
 
8
8
  require 'dkim'
9
9
 
10
- selector, keyfile,mailfile = ARGV
10
+ domain, selector, keyfile, mailfile = ARGV
11
11
 
12
12
  keyfile = File.open(keyfile)
13
13
  mailfile = mailfile ? File.open(mailfile) : STDIN
@@ -15,7 +15,8 @@ mailfile = mailfile ? File.open(mailfile) : STDIN
15
15
  mail = mailfile.read.gsub(/\r?\n/, "\r\n")
16
16
  key = keyfile.read
17
17
 
18
- Dkim::selector = selector
18
+ Dkim::domain = domain
19
+ Dkim::selector = selector
19
20
  Dkim::private_key = key
20
21
 
21
22
  print Dkim::SignedMail.new(mail).to_s
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
15
15
  s.rubyforge_project = "dkim"
16
16
 
17
17
  s.files = `git ls-files`.split("\n")
18
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.test_files = `git ls-files -- test/*`.split("\n")
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
  end
@@ -1,7 +1,4 @@
1
1
 
2
- require 'dkim/header'
3
- require 'dkim/header_list'
4
- require 'dkim/body'
5
2
  require 'dkim/signed_mail'
6
3
 
7
4
  module Dkim
@@ -16,19 +13,25 @@ module Dkim
16
13
  List-Post List-Owner List-Archive}
17
14
 
18
15
  class << self
19
- attr_accessor :signing_algorithm, :signable_headers, :domain, :selector
16
+ attr_accessor :signing_algorithm, :signable_headers, :domain, :selector, :header_canonicalization, :body_canonicalization
20
17
 
21
18
  attr_reader :private_key
22
19
  def private_key= key
23
20
  key = OpenSSL::PKey::RSA.new(key) if key.is_a?(String)
24
21
  @private_key = key
25
22
  end
23
+
24
+ def sign message, options={}
25
+ SignedMail.new(message, options).to_s
26
+ end
26
27
  end
27
28
  end
28
29
 
29
- Dkim::signable_headers = Dkim::DefaultHeaders
30
- Dkim::domain = nil
31
- Dkim::selector = nil
32
- Dkim::signing_algorithm = 'rsa-sha256'
33
- Dkim::private_key = nil
30
+ Dkim::signable_headers = Dkim::DefaultHeaders
31
+ Dkim::domain = nil
32
+ Dkim::selector = nil
33
+ Dkim::signing_algorithm = 'rsa-sha256'
34
+ Dkim::private_key = nil
35
+ Dkim::header_canonicalization = 'relaxed'
36
+ Dkim::body_canonicalization = 'relaxed'
34
37
 
@@ -1,20 +1,32 @@
1
+ require 'dkim/canonicalizable'
2
+
1
3
  module Dkim
2
4
  class Body < Struct.new(:body)
3
- def to_canonical
4
- body = self.body.dup
5
+ include Canonicalizable
5
6
 
6
- # Ignores all whitespace at the end of lines. Implementations MUST NOT remove the CRLF at the end of the line.
7
- body.gsub!(/[ \t]+(?=\r\n|\z)/, '')
7
+ def canonical_relaxed
8
+ # special case from errata 1377
9
+ return "" if self.body.empty?
10
+
11
+ body = self.body.dup
8
12
 
9
13
  # Reduces all sequences of WSP within a line to a single SP character.
10
14
  body.gsub!(/[ \t]+/, ' ')
11
15
 
16
+ # Ignores all whitespace at the end of lines. Implementations MUST NOT remove the CRLF at the end of the line.
17
+ body.gsub!(/ \r\n/, "\r\n")
18
+
12
19
  # Ignores all empty lines at the end of the message body.
13
- body.gsub!(/(\r?\n)*\z/, '')
20
+ body.gsub!(/[ \r\n]*\z/, '')
21
+
14
22
  body += "\r\n"
15
23
  end
16
- def to_s
17
- self.body.dup
24
+ def canonical_simple
25
+ body = self.body.dup
26
+
27
+ # Ignores all empty lines at the end of the message body.
28
+ body.gsub!(/(\r?\n)*\z/, '')
29
+ body += "\r\n"
18
30
  end
19
31
  end
20
32
  end
@@ -0,0 +1,14 @@
1
+ module Dkim
2
+ module Canonicalizable
3
+ def to_s form='simple'
4
+ case form
5
+ when 'simple'
6
+ canonical_simple
7
+ when 'relaxed'
8
+ canonical_relaxed
9
+ else
10
+ raise "Unknown canonicalization: #{form}"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+
2
+ require 'dkim/header'
3
+
4
+ module Dkim
5
+ class DkimHeader < Header
6
+ def initialize values={}
7
+ self.key = 'DKIM-Signature'
8
+ @values = values.to_a.flatten.each_slice(2).to_a
9
+ end
10
+ def value
11
+ @values.map do |(k, v)|
12
+ " #{k}=#{v}"
13
+ end.join(';')
14
+ end
15
+ def [] k
16
+ value = @values.detect {|(a,b)| a == k }
17
+ value && value[1]
18
+ end
19
+ def []= k, v
20
+ value = @values.detect {|(a,b)| a == k }
21
+ if !value
22
+ value = [k, nil]
23
+ @values << value
24
+ end
25
+ value[1] = v
26
+ end
27
+ end
28
+ end
@@ -1,12 +1,23 @@
1
+ require 'dkim/canonicalizable'
2
+
1
3
  module Dkim
2
4
  class Header < Struct.new(:key, :value)
3
- def to_canonical
4
- key = self.key.dup
5
- value = self.value.dup
5
+ include Canonicalizable
6
+
7
+ def relaxed_key
8
+ key = self.key.dup
6
9
 
7
10
  #Convert all header field names (not the header field values) to lowercase. For example, convert "SUBJect: AbC" to "subject: AbC".
8
11
  key.downcase!
9
12
 
13
+ # Delete any WSP characters remaining before the colon separating the header field name from the header field value.
14
+ key.gsub!(/[ \t]*\z/, '')
15
+
16
+ key
17
+ end
18
+ def relaxed_value
19
+ value = self.value.dup
20
+
10
21
  # Unfold all header field continuation lines as described in [RFC2822]
11
22
  value.gsub!(/\r?\n[ \t]+/, ' ')
12
23
 
@@ -16,13 +27,15 @@ module Dkim
16
27
  # Delete all WSP characters at the end of each unfolded header field value.
17
28
  value.gsub!(/[ \t]*\z/, '')
18
29
 
19
- # Delete any WSP characters remaining before and after the colon separating the header field name from the header field value.
30
+ # Delete any WSP characters remaining after the colon separating the header field name from the header field value.
20
31
  value.gsub!(/\A[ \t]*/, '')
21
- key.gsub!(/[ \t]*\z/, '')
22
32
 
23
- "#{key}:#{value}"
33
+ value
34
+ end
35
+ def canonical_relaxed
36
+ "#{relaxed_key}:#{relaxed_value}"
24
37
  end
25
- def to_s
38
+ def canonical_simple
26
39
  "#{key}:#{value}"
27
40
  end
28
41
  end
@@ -9,7 +9,7 @@ module Dkim
9
9
  end
10
10
  def [](key)
11
11
  @headers.detect do |header|
12
- header.key == key
12
+ header.relaxed_key == key
13
13
  end
14
14
  end
15
15
  def each(&block)
@@ -1,10 +1,12 @@
1
1
  require 'openssl'
2
- require 'base64'
2
+
3
+ require 'dkim/body'
4
+ require 'dkim/dkim_header'
5
+ require 'dkim/header'
6
+ require 'dkim/header_list'
3
7
 
4
8
  module Dkim
5
9
  class SignedMail
6
- EMAIL_REGEX = /[A-Z0-9._%+-]+@([A-Z0-9.-]+\.[A-Z]{2,6})/i
7
-
8
10
  def initialize message, options={}
9
11
  message = message.gsub(/\r?\n/, "\r\n")
10
12
  headers, body = message.split(/\r?\n\r?\n/, 2)
@@ -17,10 +19,12 @@ module Dkim
17
19
  @time = options[:time]
18
20
  @signing_algorithm = options[:signing_algorithm]
19
21
  @private_key = options[:private_key]
22
+ @header_canonicalization = options[:header_canonicalization]
23
+ @body_canonicalization = options[:body_canonicalization]
20
24
  end
21
25
 
22
26
  # options for signatures
23
- attr_writer :signing_algorithm, :signable_headers, :domain, :selector, :time
27
+ attr_writer :signing_algorithm, :signable_headers, :domain, :selector, :time, :header_canonicalization, :body_canonicalization
24
28
 
25
29
  def private_key= key
26
30
  key = OpenSSL::PKey::RSA.new(key) if key.is_a?(String)
@@ -36,57 +40,65 @@ module Dkim
36
40
  @signable_headers || Dkim::signable_headers
37
41
  end
38
42
  def domain
39
- @domain || Dkim::domain || (@headers['From'].value =~ EMAIL_REGEX && $1)
43
+ @domain || Dkim::domain
40
44
  end
41
45
  def selector
42
46
  @selector || Dkim::selector
43
47
  end
44
48
  def time
45
- @time ||= Time.now
49
+ @time
50
+ end
51
+ def header_canonicalization
52
+ @header_canonicalization || Dkim::header_canonicalization
53
+ end
54
+ def body_canonicalization
55
+ @body_canonicalization || Dkim::body_canonicalization
46
56
  end
47
57
 
48
58
  def signed_headers
49
- (@headers.map(&:key) & signable_headers).sort
50
- end
51
- def dkim_header_values(b)
52
- [
53
- 'v', 1,
54
- 'a', signing_algorithm,
55
- 'c', 'relaxed/relaxed',
56
- 'd', domain,
57
- 'q', 'dns/txt',
58
- 's', selector,
59
- 't', time.to_i,
60
- 'bh', body_hash,
61
- 'h', signed_headers.join(':'),
62
- 'b', b
63
- ]
64
- end
65
- def dkim_header(b=nil)
66
- b ||= header_signature
67
- v = dkim_header_values(b).each_slice(2).map do |(key, value)|
68
- "#{key}=#{value}"
69
- end.join('; ')
70
- Header.new('DKIM-Signature', v)
59
+ (@headers.map(&:relaxed_key) & signable_headers.map(&:downcase)).sort
71
60
  end
72
61
  def canonical_header
73
62
  headers = signed_headers.map do |key|
74
- @headers[key]
75
- end
76
- headers << dkim_header('')
77
- headers.map(&:to_canonical).join("\r\n")
63
+ @headers[key].to_s(header_canonicalization) + "\r\n"
64
+ end.join
78
65
  end
79
66
  def canonical_body
80
- @body.to_canonical
67
+ @body.to_s(body_canonicalization)
81
68
  end
82
69
 
83
- def header_signature
84
- base64_encode private_key.sign(digest_alg, canonical_header)
85
- end
86
- def body_hash
87
- base64_encode digest_alg.digest(canonical_body)
70
+ def dkim_header
71
+ dkim_header = DkimHeader.new
72
+
73
+ raise "A private key is required" unless private_key
74
+ raise "A domain is required" unless domain
75
+ raise "A selector is required" unless selector
76
+
77
+ # Add basic DKIM info
78
+ dkim_header['v'] = '1'
79
+ dkim_header['a'] = signing_algorithm
80
+ dkim_header['c'] = "#{header_canonicalization}/#{body_canonicalization}"
81
+ dkim_header['d'] = domain
82
+ dkim_header['q'] = 'dns/txt'
83
+ dkim_header['s'] = selector
84
+ dkim_header['t'] = (time || Time.now).to_i
85
+
86
+ # Add body hash and blank signature
87
+ dkim_header['bh']= base64_encode digest_alg.digest(canonical_body)
88
+ dkim_header['h'] = signed_headers.join(':')
89
+ dkim_header['b'] = ''
90
+
91
+ # Calculate signature based on intermediate signature header
92
+ headers = canonical_header
93
+ headers << dkim_header.to_s(header_canonicalization)
94
+ signature = base64_encode private_key.sign(digest_alg, headers)
95
+ dkim_header['b'] = signature
96
+
97
+ dkim_header
88
98
  end
99
+
89
100
  def to_s
101
+ # Return the original message with the calculated header
90
102
  headers = @headers.to_a + [dkim_header]
91
103
  headers.map(&:to_s).join("\r\n") +
92
104
  "\r\n\r\n" +
@@ -95,7 +107,7 @@ module Dkim
95
107
 
96
108
  private
97
109
  def base64_encode data
98
- Base64.encode64(data).gsub("\n",'')
110
+ [data].pack('m0*').gsub("\n",'')
99
111
  end
100
112
  def digest_alg
101
113
  case signing_algorithm
@@ -1,3 +1,3 @@
1
1
  module Dkim
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,78 @@
1
+
2
+ require 'test_helper'
3
+
4
+ class CanonicalizationTest < Test::Unit::TestCase
5
+ # from section 3.4.6 of rfc4871
6
+ def setup
7
+ @input = <<-eos.rfc_format
8
+ A: <SP> X <CRLF>
9
+ B <SP> : <SP> Y <HTAB><CRLF>
10
+ <HTAB> Z <SP><SP><CRLF>
11
+ <CRLF>
12
+ <SP> C <SP><CRLF>
13
+ D <SP><HTAB><SP> E <CRLF>
14
+ <CRLF>
15
+ <CRLF>
16
+ eos
17
+ @mail = Dkim::SignedMail.new(@input)
18
+ @mail.signable_headers = ['A', 'B']
19
+ end
20
+ def test_relaxed_header
21
+ @mail.header_canonicalization = 'relaxed'
22
+ expected_header = <<-eos.rfc_format
23
+ a:X <CRLF>
24
+ b:Y <SP> Z <CRLF>
25
+ eos
26
+ assert_equal expected_header, @mail.canonical_header
27
+ end
28
+ def test_relaxed_body
29
+ @mail.body_canonicalization = 'relaxed'
30
+ expected_body = <<-eos.rfc_format
31
+ <SP> C <CRLF>
32
+ D <SP> E <CRLF>
33
+ eos
34
+ assert_equal expected_body, @mail.canonical_body
35
+ end
36
+
37
+ def test_simple_header
38
+ @mail.header_canonicalization = 'simple'
39
+ expected_header = <<-eos.rfc_format
40
+ A: <SP> X <CRLF>
41
+ B <SP> : <SP> Y <HTAB><CRLF>
42
+ <HTAB> Z <SP><SP><CRLF>
43
+ eos
44
+ assert_equal expected_header, @mail.canonical_header
45
+ end
46
+ def test_simple_body
47
+ @mail.body_canonicalization = 'simple'
48
+ expected_body = <<-eos.rfc_format
49
+ <SP> C <SP><CRLF>
50
+ D <SP><HTAB><SP> E <CRLF>
51
+ eos
52
+ assert_equal expected_body, @mail.canonical_body
53
+ end
54
+
55
+ # from errata: empty bodies
56
+ def test_simple_empty_body
57
+ @mail = Dkim::SignedMail.new("test: test\r\n\r\n")
58
+ @mail.body_canonicalization = 'simple'
59
+
60
+ assert_equal "\r\n", @mail.canonical_body
61
+ end
62
+
63
+ def test_relaxed_empty_body
64
+ @mail = Dkim::SignedMail.new("test: test\r\n\r\n")
65
+ @mail.body_canonicalization = 'relaxed'
66
+
67
+ assert_equal "", @mail.canonical_body
68
+ end
69
+
70
+ def test_relaxed_errata_1384
71
+ body = "testing<crlf><sp><sp><cr><lf><cr><lf>".rfc_format
72
+ @mail = Dkim::SignedMail.new("test: test\r\n\r\n#{body}")
73
+ @mail.body_canonicalization = 'relaxed'
74
+
75
+ assert_equal "testing\r\n", @mail.canonical_body
76
+ end
77
+ end
78
+
@@ -0,0 +1,29 @@
1
+
2
+ require 'test/unit'
3
+ require 'dkim'
4
+
5
+ class String
6
+ # Parse the format used in rfc4871
7
+ #
8
+ # In the following examples, actual whitespace is used only for
9
+ # clarity. The actual input and output text is designated using
10
+ # bracketed descriptors: "<SP>" for a space character, "<HTAB>" for a
11
+ # tab character, and "<CRLF>" for a carriage-return/line-feed sequence.
12
+ # For example, "X <SP> Y" and "X<SP>Y" represent the same three
13
+ # characters.
14
+ def rfc_format
15
+ str = self.dup
16
+ str.gsub!(/\s/,'')
17
+ str.gsub!(/<SP>/i,' ')
18
+ str.gsub!(/<CR>/i,"\r")
19
+ str.gsub!(/<LF>/i,"\n")
20
+ str.gsub!(/<CRLF>/i,"\r\n")
21
+ str.gsub!(/<HTAB>/i,"\t")
22
+ str
23
+ end
24
+ end
25
+
26
+ # examples used in rfc
27
+ Dkim::domain = 'example.com'
28
+
29
+
metadata CHANGED
@@ -1,8 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dkim
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 27
4
5
  prerelease:
5
- version: 0.0.1
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
6
11
  platform: ruby
7
12
  authors:
8
13
  - John Hawthorn
@@ -10,7 +15,8 @@ autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
17
 
13
- date: 2011-05-10 00:00:00 Z
18
+ date: 2011-05-31 00:00:00 -07:00
19
+ default_executable:
14
20
  dependencies: []
15
21
 
16
22
  description: gem for adding DKIM signatures to email messages
@@ -24,16 +30,23 @@ extra_rdoc_files: []
24
30
 
25
31
  files:
26
32
  - .gitignore
33
+ - CHANGELOG.md
27
34
  - Gemfile
35
+ - README.md
28
36
  - Rakefile
29
37
  - bin/dkimsign.rb
30
38
  - dkim.gemspec
31
39
  - lib/dkim.rb
32
40
  - lib/dkim/body.rb
41
+ - lib/dkim/canonicalizable.rb
42
+ - lib/dkim/dkim_header.rb
33
43
  - lib/dkim/header.rb
34
44
  - lib/dkim/header_list.rb
35
45
  - lib/dkim/signed_mail.rb
36
46
  - lib/dkim/version.rb
47
+ - test/canonicalization_test.rb
48
+ - test/test_helper.rb
49
+ has_rdoc: true
37
50
  homepage: https://github.com/jhawthorn/dkim
38
51
  licenses: []
39
52
 
@@ -47,19 +60,26 @@ required_ruby_version: !ruby/object:Gem::Requirement
47
60
  requirements:
48
61
  - - ">="
49
62
  - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
50
66
  version: "0"
51
67
  required_rubygems_version: !ruby/object:Gem::Requirement
52
68
  none: false
53
69
  requirements:
54
70
  - - ">="
55
71
  - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
56
75
  version: "0"
57
76
  requirements: []
58
77
 
59
78
  rubyforge_project: dkim
60
- rubygems_version: 1.8.1
79
+ rubygems_version: 1.6.2
61
80
  signing_key:
62
81
  specification_version: 3
63
82
  summary: DKIM library in ruby
64
- test_files: []
65
-
83
+ test_files:
84
+ - test/canonicalization_test.rb
85
+ - test/test_helper.rb