dkimverify 0.0.2 → 0.0.3

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.
@@ -1,141 +0,0 @@
1
- require_relative 'query'
2
- require_relative 'key'
3
-
4
- require 'resolv'
5
-
6
- module DKIM
7
- module Query
8
- #
9
- # Represents the DKIM keys of a domain.
10
- #
11
- class Domain
12
-
13
- include Enumerable
14
-
15
- # Name of the domain
16
- #
17
- # @return [String]
18
- attr_reader :name
19
-
20
- # DKIM Keys of the domain
21
- #
22
- # @return [Hash{String => Key}]
23
- attr_reader :keys
24
-
25
- #
26
- # Initializes the domain.
27
- #
28
- # @param [String] name
29
- # The domain name.
30
- #
31
- # @param [Hash{String => Key}] keys
32
- # The DKIM Keys of the domain.
33
- #
34
- # @api public
35
- #
36
- def initialize(name,keys={})
37
- @name = name
38
- @keys = keys
39
- end
40
-
41
- #
42
- # Parses the DKIM Keys.
43
- #
44
- # @param [String] domain
45
- # The domain the keys belong to.
46
- #
47
- # @param [Hash{String => String}] keys
48
- # The DKIM selectors and keys.
49
- #
50
- # @return [Domain]
51
- # The domain and it's parsed DKIM keys.
52
- #
53
- # @api semipublic
54
- #
55
- def self.parse(domain,keys={})
56
- keys = Hash[keys.map { |selector,record|
57
- [selector, Key.parse(record)]
58
- }]
59
-
60
- return new(domain,keys)
61
- end
62
-
63
- #
64
- # Parses the DKIM Keys.
65
- #
66
- # @param [String] domain
67
- # The domain the keys belong to.
68
- #
69
- # @param [Hash{String => String}] keys
70
- # The DKIM selectors and keys.
71
- #
72
- # @return [Domain]
73
- # The domain and it's parsed DKIM keys.
74
- #
75
- # @raise [Parslet::ParseFailed]
76
- # One of the keys was invalid.
77
- #
78
- # @api semipublic
79
- #
80
- def self.parse!(domain,keys={})
81
- keys = Hash[keys.map { |selector,record|
82
- [selector, Key.parse!(record)]
83
- }]
84
-
85
- return new(domain,keys)
86
- end
87
-
88
- #
89
- # Queries the domain for all DKIM selectors.
90
- #
91
- # @param [String] domain
92
- # The domain to query.
93
- #
94
- # @option options [Array<String>] :selectors
95
- # sub-domain selectors.
96
- #
97
- # @option options [Resolv::DNS] :resolver
98
- #
99
- # @return [Domain]
100
- # The domain and it's DKIM Keys.
101
- #
102
- # @api public
103
- #
104
- def self.query(domain,options={})
105
- parse(domain,Query.query(domain,options))
106
- end
107
-
108
- #
109
- # Enumerates over each individual key.
110
- #
111
- # @yield [key]
112
- # The given block will be passed each key.
113
- #
114
- # @yieldparam [DKIM::Query::Key] key
115
- # A key belonging to the domain.
116
- #
117
- # @return [Enumerator]
118
- # If no block was given, an Enumerator will be returned.
119
- #
120
- # @api public
121
- #
122
- def each(&block)
123
- @keys.each_value(&block)
124
- end
125
-
126
- #
127
- # Selects a key from the domain.
128
- #
129
- # @param [String] selector
130
- # The selector.
131
- #
132
- # @return [Key, nil]
133
- # The key within that selector.
134
- #
135
- def [](selector)
136
- @keys[selector]
137
- end
138
-
139
- end
140
- end
141
- end
@@ -1,8 +0,0 @@
1
- require 'parslet'
2
-
3
- module DKIM
4
- module Query
5
- class InvalidKey < Parslet::ParseFailed
6
- end
7
- end
8
- end
@@ -1,162 +0,0 @@
1
- require_relative 'exceptions'
2
- require_relative 'parser'
3
- require_relative 'malformed_key'
4
-
5
- module DKIM
6
- module Query
7
- #
8
- # Represents an individual DKIM signing key.
9
- #
10
- class Key
11
-
12
- # DKIM version.
13
- #
14
- # @return [:DKIM1]
15
- attr_reader :v
16
- alias version v
17
-
18
- # `g=` tag.
19
- #
20
- # @return [String, nil]
21
- attr_reader :g
22
- alias granularity g
23
-
24
- # `h=` tag.
25
- #
26
- # @return [:sha1, :sha256, Array<:sha1, :sha256, String>, nil]
27
- attr_reader :h
28
- alias hash h
29
-
30
- # `k=` tag.
31
- #
32
- # @return [:rsa, String]
33
- attr_reader :k
34
- alias key k
35
-
36
- # `n=` tag.
37
- #
38
- # @return [String, nil]
39
- attr_reader :n
40
- alias notes n
41
-
42
- # `p=` tag.
43
- #
44
- # @return [String, nil]
45
- attr_reader :p
46
- alias public_key p
47
-
48
- # `s=` tag.
49
- #
50
- # @return [:email, :*, String, Array<:email, :*, String>, nil]
51
- attr_reader :s
52
- alias service_type s
53
-
54
- # `t=` tag.
55
- #
56
- # @return [:y, :s, String, nil]
57
- attr_reader :t
58
- alias flags t
59
-
60
- #
61
- # Initialize the key.
62
- #
63
- # @param [Hash{Symbol => Symbol,String}] tags
64
- # Tags for the key.
65
- #
66
- # @option tags [Symbol] :v
67
- #
68
- # @option tags [Symbol] :g
69
- #
70
- # @option tags [Symbol] :h
71
- #
72
- # @option tags [Symbol] :k
73
- #
74
- # @option tags [Symbol] :n
75
- #
76
- # @option tags [Symbol] :p
77
- #
78
- # @option tags [Symbol] :s
79
- #
80
- # @option tags [Symbol] :t
81
- #
82
- def initialize(tags={})
83
- @v, @g, @h, @k, @n, @p, @s, @t = tags.values_at(:v,:g,:h,:k,:n,:p,:s,:t)
84
- end
85
-
86
- #
87
- # Parses a DKIM Key record.
88
- #
89
- # @param [String] record
90
- # The DKIM key record.
91
- #
92
- # @return [Key]
93
- # The new key.
94
- #
95
- # @raise [InvalidKey]
96
- # Could not parse the DKIM Key record.
97
- #
98
- def self.parse!(record)
99
- new(Parser.parse(record))
100
- rescue Parslet::ParseFailed => error
101
- raise(InvalidKey.new(error.message,error.cause))
102
- end
103
-
104
- #
105
- # Parses a DKIM Key record.
106
- #
107
- # @param [String] record
108
- # The DKIM key record.
109
- #
110
- # @return [Key, MalformedKey]
111
- # The parsed key. If the key could not be parsed, a {MalformedKey}
112
- # will be returned.
113
- #
114
- def self.parse(record)
115
- begin
116
- parse!(record)
117
- rescue Parslet::ParseFailed => error
118
- MalformedKey.new(record,error.cause)
119
- end
120
- end
121
-
122
- #
123
- # Converts the key to a Hash.
124
- #
125
- # @return [Hash{:v,:g,:h,:k,:n,:p,:s,:t => Object}]
126
- #
127
- def to_hash
128
- {
129
- v: @v,
130
- g: @g,
131
- h: @h,
132
- k: @k,
133
- n: @n,
134
- p: @p,
135
- s: @s,
136
- t: @t
137
- }
138
- end
139
-
140
- #
141
- # Converts the key back into a DKIM String.
142
- #
143
- # @return [String]
144
- #
145
- def to_s
146
- tags = []
147
-
148
- tags << "v=#{@v}" if @v
149
- tags << "g=#{@g}" if @g
150
- tags << "h=#{@h}" if @h
151
- tags << "k=#{@k}" if @k
152
- tags << "p=#{@p}" if @p
153
- tags << "s=#{@s}" if @s
154
- tags << "t=#{@t}" if @t
155
- tags << "n=#{@n}" if @n
156
-
157
- return tags.join('; ')
158
- end
159
-
160
- end
161
- end
162
- end
@@ -1,36 +0,0 @@
1
- module DKIM
2
- module Query
3
- #
4
- # Represents a unparsable DKIM key.
5
- #
6
- class MalformedKey
7
-
8
- # Raw value of the DKIM key.
9
- #
10
- # @return [String]
11
- attr_reader :value
12
-
13
- # Cause of the parser failure.
14
- #
15
- # @return [Parslet::Cause]
16
- attr_reader :cause
17
-
18
- #
19
- # Initializes the malformed key.
20
- #
21
- # @param [String] value
22
- # The raw DKIM key.
23
- #
24
- # @param [Parslet::Cause] cause
25
- # The cause of the parser failure.
26
- #
27
- def initialize(value,cause)
28
- @value = value
29
- @cause = cause
30
- end
31
-
32
- alias to_s value
33
-
34
- end
35
- end
36
- end
@@ -1,175 +0,0 @@
1
- require 'parslet'
2
- require 'openssl'
3
-
4
- module DKIM
5
- module Query
6
- #
7
- # DKIM parser.
8
- #
9
- # @see https://tools.ietf.org/html/rfc6376#section-3
10
- #
11
- class Parser < Parslet::Parser
12
-
13
- root :record
14
- rule(:record) do
15
- (
16
- fws? >> key_tag >> fws? >>
17
- (str(';') >> fws? >> key_tag >> fws?).repeat(0) >> str(';').maybe
18
- ).as(:tag_list)
19
- end
20
-
21
- rule(:key_tag) do
22
- (
23
- key_v_tag |
24
- key_g_tag |
25
- key_h_tag |
26
- key_k_tag |
27
- key_n_tag |
28
- key_p_tag |
29
- key_s_tag |
30
- key_t_tag
31
- ).as(:tag)
32
- end
33
-
34
- private
35
-
36
- def self.key_tag_rule(name,&block)
37
- rule(:"key_#{name}_tag") do
38
- str(name).as(:name) >>
39
- fws? >> str('=') >> fws? >>
40
- instance_eval(&block).as(:value)
41
- end
42
- end
43
-
44
- def symbol(name)
45
- str(name).as(:symbol)
46
- end
47
-
48
- public
49
-
50
- key_tag_rule('v') { symbol('DKIM1') }
51
- key_tag_rule('g') { key_g_tag_lpart }
52
- rule(:key_g_tag_lpart) do
53
- (
54
- dot_atom_text.maybe >>
55
- (str('*') >> dot_atom_text.maybe).maybe
56
- ).as(:string)
57
- end
58
- rule(:dot_atom_text) do
59
- atext.repeat(1) >> (str('.') >> atext.repeat(1)).repeat(0)
60
- end
61
-
62
- key_tag_rule('h') do
63
- key_h_tag_alg >> (fws? >> str(':') >> fws? >> key_h_tag_alg).repeat(0)
64
- end
65
- rule(:key_h_tag_alg) { symbol('sha1') | symbol('sha256') | x_key_h_tag_alg }
66
- rule(:x_key_h_tag_alg) { hyphenated_word.as(:string) }
67
-
68
- key_tag_rule('k') { key_k_tag_type }
69
- rule(:key_k_tag_type) { symbol('rsa') | x_key_k_tag_type }
70
- rule(:x_key_k_tag_type) { hyphenated_word.as(:string) }
71
-
72
- key_tag_rule('n') { qp_section.as(:string) }
73
- key_tag_rule('p') { base64string.as(:asn1).maybe }
74
- key_tag_rule('s') do
75
- key_s_tag_type >> (fws? >> str(':') >> fws? >> key_s_tag_type).repeat(0)
76
- end
77
- rule(:key_s_tag_type) { symbol('email') | symbol('*') | x_key_s_tag_type }
78
- rule(:x_key_s_tag_type) { hyphenated_word.as(:string) }
79
-
80
- key_tag_rule('t') do
81
- key_t_tag_flag >> (fws? >> str(':') >> fws? >> key_t_tag_flag).repeat(0)
82
- end
83
- rule(:key_t_tag_flag) { match['ys'].as(:symbol) | x_key_t_tag_flag }
84
- rule(:x_key_t_tag_flag) { hyphenated_word.as(:string) }
85
-
86
- #
87
- # Section 2.6: DKIM-Quoted-Printable
88
- #
89
- rule(:dkim_quoted_printable) do
90
- (fws | hex_octet | dkim_safe_char).repeat(0)
91
- end
92
- rule(:dkim_safe_char) do
93
- match['\x21-\x3a'] | str("\x3c") | match['\x3e-\x7e']
94
- end
95
-
96
- #
97
- # Section 2.4: Common ABNF Tokens
98
- #
99
- rule(:hyphenated_word) do
100
- alpha >> (
101
- (str('-').absnt? >> (alpha | digit)) |
102
- (str('-').repeat(0) >> (alpha | digit))
103
- ).repeat(0)
104
- end
105
- rule(:base64string) do
106
- (alpha | digit | str('+') | str('/') | fws).repeat(1) >>
107
- (str('=') >> fws? >> (str('=') >> fws?)).maybe
108
- end
109
-
110
- #
111
- # Section 2.3: Whitespace
112
- #
113
- rule(:sp) { str(' ') }
114
- rule(:crlf) { str("\r\n") }
115
- rule(:wsp) { match['\t '] }
116
- rule(:wsp?) { wsp.maybe }
117
- rule(:lwsp) { (wsp | crlf >> wsp).repeat(0) }
118
- rule(:fws) { (wsp.repeat(0) >> crlf).maybe >> wsp.repeat(1) }
119
- rule(:fws?) { fws.maybe }
120
-
121
- #
122
- # Character rules
123
- #
124
- rule(:alpha) { match['a-zA-Z'] }
125
- rule(:digit) { match['0-9'] }
126
- rule(:alnum) { match['a-zA-Z0-9'] }
127
- rule(:valchar) { match['\x21-\x3a'] | match['\x3c-\x7e'] }
128
- rule(:alnumpunc) { match['a-zA-Z0-9_'] }
129
- rule(:atext) { alnum | match['!#$%&\'*+\-/=?^ `{|}~'] }
130
-
131
- #
132
- # Quoted printable
133
- #
134
- rule(:qp_section) do
135
- (wsp? >> ptext.repeat(1) >> (wsp >> ptext.repeat(1)).repeat(0)).maybe
136
- end
137
- rule(:ptext) { hex_octet | safe_char }
138
- rule(:safe_char) { match['\x21-\x3c'] | match['\x3e-\x7e'] }
139
- rule(:hex_octet) { str('=') >> match['0-9A-F'].repeat(2,2) }
140
-
141
- class Transform < Parslet::Transform
142
-
143
- rule(:symbol => simple(:name)) { name.to_sym }
144
- rule(:string => simple(:text)) { text.to_s }
145
- # XXX: temporarily disable ASN1 decoding, due to an OpenSSL bug.
146
- # rule(:asn1 => simple(:blob)) { OpenSSL::ASN1.decode(blob) }
147
- rule(:asn1 => simple(:blob)) { blob }
148
-
149
- rule(tag: {name: simple(:name), value: subtree(:value)}) do
150
- {name.to_sym => value}
151
- end
152
-
153
- rule(tag_list: subtree(:hashes)) do
154
- case hashes
155
- when Array then hashes.reduce(&:merge!)
156
- else hashes
157
- end
158
- end
159
-
160
- end
161
-
162
- #
163
- # Parses the text into structured data.
164
- #
165
- # @param [String] text
166
- #
167
- # @return [Hash]
168
- #
169
- def self.parse(text)
170
- Transform.new.apply(new.parse(text))
171
- end
172
-
173
- end
174
- end
175
- end
@@ -1,74 +0,0 @@
1
- require 'resolv'
2
-
3
- module DKIM
4
- module Query
5
- #
6
- # Queries the domain for all DKIM selectors.
7
- #
8
- # @param [String] domain
9
- # The domain to query.
10
- #
11
- # @option options [Array<String>] :selectors
12
- # sub-domain selectors.
13
- #
14
- # @option options [Resolv::DNS] :resolver
15
- # Optional resolver to use.
16
- #
17
- # @return [Hash{String => String}]
18
- # The DKIM keys for the domain.
19
- #
20
- # @api semipublic
21
- #
22
- def self.query(domain,options={})
23
- selectors = options.fetch(:selectors) { selectors_for(domain) }
24
- resolver = options.fetch(:resolver) { Resolv::DNS.new }
25
-
26
- keys = {}
27
-
28
- selectors.each do |selector|
29
- host = "#{selector}._domainkey.#{domain}"
30
-
31
- begin
32
- keys[selector] = resolver.getresource(
33
- host, Resolv::DNS::Resource::IN::TXT
34
- ).strings.join
35
- rescue Resolv::ResolvError
36
- end
37
- end
38
-
39
- return keys
40
- end
41
-
42
- # Default known selectors
43
- SELECTORS = %w[default dkim google s1024 c1211 mandrill]
44
-
45
- #
46
- # DKIM query selectors for the host.
47
- #
48
- # @param [String] host
49
- #
50
- # @return [Array<String>]
51
- #
52
- # @api private
53
- #
54
- def self.selectors_for(host)
55
- SELECTORS + [host_without_tld(host)]
56
- end
57
-
58
- #
59
- # Removes the TLD from the hostname.
60
- #
61
- # @param [String] host
62
- #
63
- # @return [String]
64
- #
65
- # @api private
66
- #
67
- def self.host_without_tld(host)
68
- if host.include?('.') then host[0,host.rindex('.')]
69
- else host
70
- end
71
- end
72
-
73
- end
74
- end
@@ -1,6 +0,0 @@
1
- module DKIM
2
- module Query
3
- # dkim-query version
4
- VERSION = '0.2.6'
5
- end
6
- end
@@ -1,4 +0,0 @@
1
- require_relative 'query/version'
2
- require_relative 'query/query'
3
- require_relative 'query/key'
4
- require_relative 'query/domain'
@@ -1,96 +0,0 @@
1
- require 'spec_helper'
2
- require 'dkim/query/domain'
3
-
4
- describe Domain do
5
- let(:domain) { 'yahoo.com' }
6
- let(:key) do
7
- Key.new(
8
- k: :rsa,
9
- p: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB",
10
- n: "A 1024 bit key;",
11
- )
12
- end
13
- let(:selector) { 's1024' }
14
- let(:keys) { {selector => key} }
15
-
16
- subject { described_class.new(domain,keys) }
17
-
18
- describe "#initialize" do
19
- it "should set name" do
20
- expect(subject.name).to be domain
21
- end
22
-
23
- it "should set keys" do
24
- expect(subject.keys).to be keys
25
- end
26
- end
27
-
28
- describe ".parse" do
29
- let(:key) do
30
- %{k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB; n=A 1024 bit key}
31
- end
32
- let(:keys) { {selector => key} }
33
-
34
- subject { described_class.parse(domain,keys) }
35
-
36
- it "should parse the keys" do
37
- expect(subject.keys[selector]).to be_kind_of(Key)
38
- end
39
- end
40
-
41
- describe ".query" do
42
- subject { described_class.query(domain) }
43
-
44
- it "should find all known keys" do
45
- expect(subject.keys).to have_key('s1024')
46
- end
47
-
48
- context "with custom selectors" do
49
- let(:selectors) { ['google', 's1024'] }
50
-
51
- subject { described_class.query(domain, selectors: selectors) }
52
-
53
- it "should query those selectors only" do
54
- expect(subject.keys).to have_key('s1024')
55
- end
56
- end
57
-
58
- context "with no selectors" do
59
- let(:selectors) { [] }
60
-
61
- subject { described_class.query(domain, selectors: selectors) }
62
-
63
- it "should not find any keys" do
64
- expect(subject.keys).to be_empty
65
- end
66
- end
67
- end
68
-
69
- describe "#each" do
70
- context "when given a block" do
71
- it "should yield each Key" do
72
- expect { |b| subject.each(&b) }.to yield_with_args(key)
73
- end
74
- end
75
-
76
- context "when not given a block" do
77
- it "should return an Enumerator" do
78
- expect(subject.each).to be_kind_of(Enumerator)
79
- end
80
- end
81
- end
82
-
83
- describe "#[]" do
84
- context "when given a valid selector" do
85
- it "should return the key" do
86
- expect(subject[selector]).to be key
87
- end
88
- end
89
-
90
- context "when given an unknown selector" do
91
- it "should return nil" do
92
- expect(subject['foo']).to be_nil
93
- end
94
- end
95
- end
96
- end