dkim 0.0.1

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,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in dkim.gemspec
4
+ gemspec
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/ruby
2
+
3
+ if ARGV.length != 2 && ARGV.length != 3
4
+ puts "Usage: dkimsign.rb SELECTOR KEYFILE [MAILFILE]"
5
+ exit 0
6
+ end
7
+
8
+ require 'dkim'
9
+
10
+ selector, keyfile,mailfile = ARGV
11
+
12
+ keyfile = File.open(keyfile)
13
+ mailfile = mailfile ? File.open(mailfile) : STDIN
14
+
15
+ mail = mailfile.read.gsub(/\r?\n/, "\r\n")
16
+ key = keyfile.read
17
+
18
+ Dkim::selector = selector
19
+ Dkim::private_key = key
20
+
21
+ print Dkim::SignedMail.new(mail).to_s
22
+
23
+
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "dkim/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "dkim"
7
+ s.version = Dkim::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["John Hawthorn"]
10
+ s.email = ["john.hawthorn@gmail.com"]
11
+ s.homepage = "https://github.com/jhawthorn/dkim"
12
+ s.summary = %q{DKIM library in ruby}
13
+ s.description = %q{gem for adding DKIM signatures to email messages}
14
+
15
+ s.rubyforge_project = "dkim"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
@@ -0,0 +1,34 @@
1
+
2
+ require 'dkim/header'
3
+ require 'dkim/header_list'
4
+ require 'dkim/body'
5
+ require 'dkim/signed_mail'
6
+
7
+ module Dkim
8
+ DefaultHeaders = %w{
9
+ From Sender Reply-To Subject Date
10
+ Message-ID To Cc MIME-Version
11
+ Content-Type Content-Transfer-Encoding Content-ID Content-Description
12
+ Resent-Date Resent-From Resent-Sender Resent-To Resent-cc
13
+ Resent-Message-ID
14
+ In-Reply-To References
15
+ List-Id List-Help List-Unsubscribe List-Subscribe
16
+ List-Post List-Owner List-Archive}
17
+
18
+ class << self
19
+ attr_accessor :signing_algorithm, :signable_headers, :domain, :selector
20
+
21
+ attr_reader :private_key
22
+ def private_key= key
23
+ key = OpenSSL::PKey::RSA.new(key) if key.is_a?(String)
24
+ @private_key = key
25
+ end
26
+ end
27
+ end
28
+
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
34
+
@@ -0,0 +1,20 @@
1
+ module Dkim
2
+ class Body < Struct.new(:body)
3
+ def to_canonical
4
+ body = self.body.dup
5
+
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)/, '')
8
+
9
+ # Reduces all sequences of WSP within a line to a single SP character.
10
+ body.gsub!(/[ \t]+/, ' ')
11
+
12
+ # Ignores all empty lines at the end of the message body.
13
+ body.gsub!(/(\r?\n)*\z/, '')
14
+ body += "\r\n"
15
+ end
16
+ def to_s
17
+ self.body.dup
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ module Dkim
2
+ class Header < Struct.new(:key, :value)
3
+ def to_canonical
4
+ key = self.key.dup
5
+ value = self.value.dup
6
+
7
+ #Convert all header field names (not the header field values) to lowercase. For example, convert "SUBJect: AbC" to "subject: AbC".
8
+ key.downcase!
9
+
10
+ # Unfold all header field continuation lines as described in [RFC2822]
11
+ value.gsub!(/\r?\n[ \t]+/, ' ')
12
+
13
+ # Convert all sequences of one or more WSP characters to a single SP character.
14
+ value.gsub!(/[ \t]+/, ' ')
15
+
16
+ # Delete all WSP characters at the end of each unfolded header field value.
17
+ value.gsub!(/[ \t]*\z/, '')
18
+
19
+ # Delete any WSP characters remaining before and after the colon separating the header field name from the header field value.
20
+ value.gsub!(/\A[ \t]*/, '')
21
+ key.gsub!(/[ \t]*\z/, '')
22
+
23
+ "#{key}:#{value}"
24
+ end
25
+ def to_s
26
+ "#{key}:#{value}"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ module Dkim
2
+ class HeaderList
3
+ include Enumerable
4
+ def initialize headers
5
+ @headers = headers.split(/\r?\n(?!([ \t]))/).map do |header|
6
+ key, value = header.split(':', 2)
7
+ Header.new(key, value)
8
+ end
9
+ end
10
+ def [](key)
11
+ @headers.detect do |header|
12
+ header.key == key
13
+ end
14
+ end
15
+ def each(&block)
16
+ @headers.each(&block)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,112 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Dkim
5
+ class SignedMail
6
+ EMAIL_REGEX = /[A-Z0-9._%+-]+@([A-Z0-9.-]+\.[A-Z]{2,6})/i
7
+
8
+ def initialize message, options={}
9
+ message = message.gsub(/\r?\n/, "\r\n")
10
+ headers, body = message.split(/\r?\n\r?\n/, 2)
11
+ @headers = HeaderList.new headers
12
+ @body = Body.new body
13
+
14
+ @signable_headers = options[:signable_headers]
15
+ @domain = options[:domain]
16
+ @selector = options[:selector]
17
+ @time = options[:time]
18
+ @signing_algorithm = options[:signing_algorithm]
19
+ @private_key = options[:private_key]
20
+ end
21
+
22
+ # options for signatures
23
+ attr_writer :signing_algorithm, :signable_headers, :domain, :selector, :time
24
+
25
+ def private_key= key
26
+ key = OpenSSL::PKey::RSA.new(key) if key.is_a?(String)
27
+ @private_key = key
28
+ end
29
+ def private_key
30
+ @private_key || Dkim::private_key
31
+ end
32
+ def signing_algorithm
33
+ @signing_algorithm || Dkim::signing_algorithm
34
+ end
35
+ def signable_headers
36
+ @signable_headers || Dkim::signable_headers
37
+ end
38
+ def domain
39
+ @domain || Dkim::domain || (@headers['From'].value =~ EMAIL_REGEX && $1)
40
+ end
41
+ def selector
42
+ @selector || Dkim::selector
43
+ end
44
+ def time
45
+ @time ||= Time.now
46
+ end
47
+
48
+ 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)
71
+ end
72
+ def canonical_header
73
+ headers = signed_headers.map do |key|
74
+ @headers[key]
75
+ end
76
+ headers << dkim_header('')
77
+ headers.map(&:to_canonical).join("\r\n")
78
+ end
79
+ def canonical_body
80
+ @body.to_canonical
81
+ end
82
+
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)
88
+ end
89
+ def to_s
90
+ headers = @headers.to_a + [dkim_header]
91
+ headers.map(&:to_s).join("\r\n") +
92
+ "\r\n\r\n" +
93
+ @body.to_s
94
+ end
95
+
96
+ private
97
+ def base64_encode data
98
+ Base64.encode64(data).gsub("\n",'')
99
+ end
100
+ def digest_alg
101
+ case signing_algorithm
102
+ when 'rsa-sha1'
103
+ OpenSSL::Digest::SHA1.new
104
+ when 'rsa-sha256'
105
+ OpenSSL::Digest::SHA256.new
106
+ else
107
+ raise "Unknown digest algorithm: '#{signing_algorithm}'"
108
+ end
109
+ end
110
+ end
111
+ end
112
+
@@ -0,0 +1,3 @@
1
+ module Dkim
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dkim
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - John Hawthorn
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-05-10 00:00:00 Z
14
+ dependencies: []
15
+
16
+ description: gem for adding DKIM signatures to email messages
17
+ email:
18
+ - john.hawthorn@gmail.com
19
+ executables:
20
+ - dkimsign.rb
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - .gitignore
27
+ - Gemfile
28
+ - Rakefile
29
+ - bin/dkimsign.rb
30
+ - dkim.gemspec
31
+ - lib/dkim.rb
32
+ - lib/dkim/body.rb
33
+ - lib/dkim/header.rb
34
+ - lib/dkim/header_list.rb
35
+ - lib/dkim/signed_mail.rb
36
+ - lib/dkim/version.rb
37
+ homepage: https://github.com/jhawthorn/dkim
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ requirements: []
58
+
59
+ rubyforge_project: dkim
60
+ rubygems_version: 1.8.1
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: DKIM library in ruby
64
+ test_files: []
65
+