dkimverify 0.0.2 → 0.0.3

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