pdkim 0.9
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.
- checksums.yaml +7 -0
- data/bin/1ZsMk0-0iglmo-E9 +98 -0
- data/bin/pdkimgemtest +147 -0
- data/ext/pdkim/pdkimglue.so +0 -0
- data/lib/pdkim.rb +394 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d403271cbc6ebf4df82551821bb5f76915b0fa1e
|
4
|
+
data.tar.gz: 56f3878d604453ff22c7e4b750b78a751f94c468
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c3fe7d5aed35e7d20d2ad6def1caabea3303ebc2a15344fa9a6f303997f5a0b7b9a03a8db7bf966212b4215ee382751c3da0666cdb971db16b472a3fc68cf371
|
7
|
+
data.tar.gz: 9313bc15253e55ca032eb7bf41a379b272aca814eb6c343b3339aeab690ae909e638808053ae37f64ba58c3226edc8c656d89443debbb8c5be8ec85631302b33
|
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/hash
|
2
|
+
:id: 1ZsMk0-0iglmo-E9
|
3
|
+
:time: '2015-10-31 03:22:04 +0000'
|
4
|
+
:local_port: '25'
|
5
|
+
:local_hostname: mail.tsarmail.org
|
6
|
+
:remote_port: 35184
|
7
|
+
:remote_hostname: mail-pa0-x232.google.com
|
8
|
+
:remote_ip: 2607:f8b0:400e:c03::232
|
9
|
+
:authenticated:
|
10
|
+
:encrypted: true
|
11
|
+
:ehlo:
|
12
|
+
:value: " mail-pa0-x232.google.com"
|
13
|
+
:domain: google.com
|
14
|
+
:ip: 173.194.115.32
|
15
|
+
:mailfrom:
|
16
|
+
:value: "<mjwelchphd@gmail.com>"
|
17
|
+
:accepted: true
|
18
|
+
:name:
|
19
|
+
:url: mjwelchphd@gmail.com
|
20
|
+
:local_part: mjwelchphd
|
21
|
+
:domain: gmail.com
|
22
|
+
:czarmailclient: false
|
23
|
+
:owner_id:
|
24
|
+
:mx:
|
25
|
+
- gmail-smtp-in.l.google.com
|
26
|
+
- alt1.gmail-smtp-in.l.google.com
|
27
|
+
- alt2.gmail-smtp-in.l.google.com
|
28
|
+
- alt3.gmail-smtp-in.l.google.com
|
29
|
+
- alt4.gmail-smtp-in.l.google.com
|
30
|
+
:ip: 173.194.199.27
|
31
|
+
:spamhaus: false
|
32
|
+
:barracuda: false
|
33
|
+
:live: true
|
34
|
+
:rcptto:
|
35
|
+
- :value: "<coco@tsarmail.org>"
|
36
|
+
:accepted: true
|
37
|
+
:name:
|
38
|
+
:url: coco@tsarmail.org
|
39
|
+
:local_part: coco
|
40
|
+
:domain: tsarmail.org
|
41
|
+
:czarmailclient: false
|
42
|
+
:owner_id:
|
43
|
+
:senderisfriend: false
|
44
|
+
:mail_type: X
|
45
|
+
:data:
|
46
|
+
:accepted: false
|
47
|
+
:value:
|
48
|
+
:text:
|
49
|
+
- 'Received: by pasz6 with SMTP id z6so90936190pas.2'
|
50
|
+
- " for <coco@tsarmail.org>; Fri, 30 Oct 2015 20:22:04 -0700 (PDT)"
|
51
|
+
- 'DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;'
|
52
|
+
- " d=gmail.com; s=20120113;"
|
53
|
+
- " h=message-id:date:from:user-agent:mime-version:to:subject"
|
54
|
+
- " :content-type:content-transfer-encoding;"
|
55
|
+
- " bh=Z2jCW0avVIbUOQuL6QZlOhhVpMa2bHX2/SOpDUKV+Ec=;"
|
56
|
+
- " b=fdIuma9zlWqC1+tAAtXHXGybhB7Luki6veKE8SimmQL5BfWV5xU9NS7E0QApG2+rgp"
|
57
|
+
- " QQ6VRwy8M9ATjBn3BJvXN462HGsfihPL5DwIr8N1PWhy7wkVJxFvAI/mcbGZlYZE4p99"
|
58
|
+
- " CXVPAYt5lBKzeMFYNF79umSIjhc2HRyE0TFyLxzrH9DlryeQ6ngDJ6VKbPsWRpG0rLS2"
|
59
|
+
- " H8Xs2t6l5wOd22oDcE0k4x3aU6MDMw6/0enU6KuVgmlnjsIemFk0NvpTRVKcWlluDmjI"
|
60
|
+
- " OQ1x6oFNvwtxTpEC6OP89kXM6Fnc0mwn9PJSzzJ/gxmcxpNbmFZ25yHGy48TiA4jTC1y"
|
61
|
+
- " JLeA=="
|
62
|
+
- 'X-Received: by 10.66.147.136 with SMTP id tk8mr12368633pab.37.1446261724525;'
|
63
|
+
- " Fri, 30 Oct 2015 20:22:04 -0700 (PDT)"
|
64
|
+
- 'Return-Path: <mjwelchphd@gmail.com>'
|
65
|
+
- 'Received: from [192.168.1.2] ([96.251.113.83])'
|
66
|
+
- " by smtp.googlemail.com with ESMTPSA id or3sm10805021pbb.56.2015.10.30.20.22.02"
|
67
|
+
- " for <coco@tsarmail.org>"
|
68
|
+
- " (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);"
|
69
|
+
- " Fri, 30 Oct 2015 20:22:03 -0700 (PDT)"
|
70
|
+
- 'Message-ID: <563433D8.9020306@gmail.com>'
|
71
|
+
- 'Date: Fri, 30 Oct 2015 20:22:00 -0700'
|
72
|
+
- 'From: "Michael J. Welch, Ph.D." <mjwelchphd@gmail.com>'
|
73
|
+
- 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.2.0'
|
74
|
+
- 'MIME-Version: 1.0'
|
75
|
+
- 'To: coco@tsarmail.org'
|
76
|
+
- 'Subject: test tsar'
|
77
|
+
- 'Content-Type: text/plain; charset=utf-8; format=flowed'
|
78
|
+
- 'Content-Transfer-Encoding: 7bit'
|
79
|
+
- ''
|
80
|
+
- Coco is Sir Bitealot.
|
81
|
+
:headers:
|
82
|
+
:received: from [192.168.1.2] ([96.251.113.83]) by smtp.googlemail.com
|
83
|
+
with ESMTPSA id or3sm10805021pbb.56.2015.10.30.20.22.02 for <coco@tsarmail.org> (version=TLSv1.2
|
84
|
+
cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 30 Oct 2015 20:22:03
|
85
|
+
-0700 (PDT)
|
86
|
+
:dkim_signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=message-id:date:from:user-agent:mime-version:to:subject :content-type:content-transfer-encoding; bh=Z2jCW0avVIbUOQuL6QZlOhhVpMa2bHX2/SOpDUKV+Ec=; b=fdIuma9zlWqC1+tAAtXHXGybhB7Luki6veKE8SimmQL5BfWV5xU9NS7E0QApG2+rgp QQ6VRwy8M9ATjBn3BJvXN462HGsfihPL5DwIr8N1PWhy7wkVJxFvAI/mcbGZlYZE4p99 CXVPAYt5lBKzeMFYNF79umSIjhc2HRyE0TFyLxzrH9DlryeQ6ngDJ6VKbPsWRpG0rLS2 H8Xs2t6l5wOd22oDcE0k4x3aU6MDMw6/0enU6KuVgmlnjsIemFk0NvpTRVKcWlluDmjI OQ1x6oFNvwtxTpEC6OP89kXM6Fnc0mwn9PJSzzJ/gxmcxpNbmFZ25yHGy48TiA4jTC1y JLeA==
|
87
|
+
:x_received: by 10.66.147.136 with SMTP id tk8mr12368633pab.37.1446261724525; Fri,
|
88
|
+
30 Oct 2015 20:22:04 -0700 (PDT)
|
89
|
+
:return_path: "<mjwelchphd@gmail.com>"
|
90
|
+
:message_id: "<563433D8.9020306@gmail.com>"
|
91
|
+
:date: Fri, 30 Oct 2015 20:22:00 -0700
|
92
|
+
:from: '"Michael J. Welch, Ph.D." <mjwelchphd@gmail.com>'
|
93
|
+
:user_agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.2.0
|
94
|
+
:mime_version: '1.0'
|
95
|
+
:to: coco@tsarmail.org
|
96
|
+
:subject: test tsar
|
97
|
+
:content_type: text/plain; charset=utf-8; format=flowed
|
98
|
+
:content_transfer_encoding: 7bit
|
data/bin/pdkimgemtest
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
require 'pdkim'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
RSA_PRIVATE_KEY = <<EOT
|
6
|
+
-----BEGIN RSA PRIVATE KEY-----
|
7
|
+
MIICXQIBAAKBgQC5+utIbbfbpssvW0TboF73Seos+1ijdPFGwc/z8Yu12cpjBvRb
|
8
|
+
5/qRJd83XCySRs0QkK1zWx4soPffbtyJ9TU5mO76M23lIuI5slJ4QLA0UznGxfHd
|
9
|
+
fXpK9qRnmG6A4HRHC9B93pjTo6iBksRhIeSsTL94EbUJ625i0Lqg4i6NVQIDAQAB
|
10
|
+
AoGBAIDGqJH/Pta9+GTzGovE0N0D9j1tUKPl/ocS/m4Ya7fgdQ36q8rTpyFICvan
|
11
|
+
QUmL8sQsmZ2Nkygt0VSJy/VOr6niQmoi97PY0egxvvK5mtc/nxePCGwYLOMpB6ql
|
12
|
+
0UptotmvJU3tjyHZbksOf6LlzvpAgk7GnxLF1Cg/RJhH9ubBAkEA6b32mr52u3Uz
|
13
|
+
BjbVWez1XBcxgwHk8A4NF9yCpHtVRY3751FZbrhy7oa+ZvYokxS4dzrepZlB1dqX
|
14
|
+
IXaq7CgakQJBAMuwpG4N5x1/PfLODD7PYaJ5GSIx6BZoJObnx/42PrIm2gQdfs6W
|
15
|
+
1aClscqMyj5vSBF+cWWqu1i7j6+qugSswIUCQA3T3BPZcqKyUztp4QM53mX9RUOP
|
16
|
+
yCBfZGzl8aCTXz8HIEDV8il3pezwcbEbnNjen+8Fv4giYd+p18j2ATSJRtECQGaE
|
17
|
+
lG3Tz4PYG/zN2fnu9KwKmSzNw4srhY82D0GSWcHerhIuKjmeTw0Y+EAC1nPQHIy5
|
18
|
+
gCd0Y/DIDgyTOCbML+UCQQClbgAoYnIpbTUklWH00Z1xr+Y95BOjb4lyjyIF98B2
|
19
|
+
FA0nM8cHuN/VLKjjcrJUK47lZEOsjLv+qTl0i0Lp6giq
|
20
|
+
-----END RSA PRIVATE KEY-----
|
21
|
+
EOT
|
22
|
+
|
23
|
+
RSA_PUBLIC_KEY_DKIM = "v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5+utIbbfbpssvW0TboF73Seos+1ijdPFGwc/z8Yu12cpjBvRb5/qRJd83XCySRs0QkK1zWx4soPffbtyJ9TU5mO76M23lIuI5slJ4QLA0UznGxfHdfXpK9qRnmG6A4HRHC9B93pjTo6iBksRhIeSsTL94EbUJ625i0Lqg4i6NVQIDAQAB;"
|
24
|
+
|
25
|
+
DOMAIN = "duncanthrax.net"
|
26
|
+
SELECTOR = "cheezburger"
|
27
|
+
|
28
|
+
PDKIM_RETURN_CODES = ["PDKIM_VERIFY_NONE", "PDKIM_VERIFY_INVALID", "PDKIM_VERIFY_FAIL", "PDKIM_VERIFY_PASS"]
|
29
|
+
|
30
|
+
TEST_MESSAGE = [
|
31
|
+
"From: Tom Kistner <tom@duncanthrax.net>",
|
32
|
+
"X-Folded-Header: line one",
|
33
|
+
"\tline two",
|
34
|
+
"To: PDKIM User",
|
35
|
+
"Subject: PDKIM Test",
|
36
|
+
"",
|
37
|
+
"Test 1, 2, 3, 4",
|
38
|
+
"This is a simple test of Ruby PDKIM."
|
39
|
+
]
|
40
|
+
|
41
|
+
# the dkim header expected from this message is:
|
42
|
+
#DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=simple/simple; d=duncanthrax.net; s=cheezburger;
|
43
|
+
# h=Subject:To:From; bh=+oeSNE7b9Ka6Gdh9ItFGX3J6Wacjc/JxAUaId7ON0T0=;
|
44
|
+
# b=Ap6DcX3x2MEoj1E3KBow7NF/2g5CnUoqkt0hgqJ0DufuOsFAPLl0tYA+yYIoCp8Acn/BJkjVYY+WQ7mlSUJfrZEZYIq1P+BZgBdP+Z+g3vrK2zEJchvwpnP0+xKniIktxT2WRQOoH3HBb/5Z1AhtuNfPEoE+kZN22Gksto4bqdg=;
|
45
|
+
|
46
|
+
class PdkimTest
|
47
|
+
|
48
|
+
include PDKIM
|
49
|
+
|
50
|
+
def main
|
51
|
+
puts
|
52
|
+
puts
|
53
|
+
test1
|
54
|
+
puts
|
55
|
+
puts
|
56
|
+
test2
|
57
|
+
puts
|
58
|
+
puts
|
59
|
+
test3
|
60
|
+
puts
|
61
|
+
end
|
62
|
+
|
63
|
+
def display(name, unsigned_message, signed_message, signatures)
|
64
|
+
puts name.upcase
|
65
|
+
if !unsigned_message.nil?
|
66
|
+
puts "------------------------------------------------------------"
|
67
|
+
puts "---- The test email before signing. -"
|
68
|
+
puts "------------------------------------------------------------"
|
69
|
+
puts unsigned_message.join(CRLF)+CRLF
|
70
|
+
end
|
71
|
+
puts
|
72
|
+
puts "------------------------------------------------------------"
|
73
|
+
puts "---- The test email after signing. -"
|
74
|
+
puts "------------------------------------------------------------"
|
75
|
+
puts signed_message.join(CRLF)+CRLF
|
76
|
+
puts
|
77
|
+
case
|
78
|
+
when signatures.size==0
|
79
|
+
puts "------------------------------------------------------------"
|
80
|
+
puts "---- The no DKIM headers were found: -"
|
81
|
+
puts "------------------------------------------------------------"
|
82
|
+
when signatures.size==1
|
83
|
+
puts "------------------------------------------------------------"
|
84
|
+
puts "---- The signature of the DKIM header: -"
|
85
|
+
puts "------------------------------------------------------------"
|
86
|
+
signatures.each { |signature| puts "- %-36s%-20s -"%[signature[:domain], PDKIM_RETURN_CODES[signature[:verify_status]]] }
|
87
|
+
puts "------------------------------------------------------------"
|
88
|
+
else
|
89
|
+
puts "------------------------------------------------------------"
|
90
|
+
puts "---- The signatures of the DKIM headers: -"
|
91
|
+
puts "------------------------------------------------------------"
|
92
|
+
signatures.each { |signature| puts "%-38s%-20s -"%[signature[:domain], PDKIM_RETURN_CODES[signature[:verify_status]]] }
|
93
|
+
puts "------------------------------------------------------------"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def test1
|
98
|
+
# ---------- SIGN ----------
|
99
|
+
ctx = pdkim_init_sign(PDKIM_INPUT_NORMAL, DOMAIN, SELECTOR, RSA_PRIVATE_KEY)
|
100
|
+
# ok = pdkim_set_debug_stream(ctx, 2) # 2 --> STDERR
|
101
|
+
ok = pdkim_set_optional(ctx, nil, nil, PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE, -1, PDKIM_ALGO_RSA_SHA256, 0, 0)
|
102
|
+
TEST_MESSAGE.each do |line|
|
103
|
+
ok = pdkim_feed(ctx, line+CRLF, line.length+2)
|
104
|
+
end
|
105
|
+
signatures = pdkim_feed_finish(ctx)
|
106
|
+
signed_message = [signatures[0][:signature]]
|
107
|
+
signed_message.concat(TEST_MESSAGE)
|
108
|
+
pdkim_free_ctx(ctx)
|
109
|
+
|
110
|
+
# --------- VERIFY ---------
|
111
|
+
# the block on this call is mandatory, and handles the DNS lookup
|
112
|
+
# to get the domain's public key
|
113
|
+
ctx = pdkim_init_verify(PDKIM_INPUT_NORMAL) do |name|
|
114
|
+
RSA_PUBLIC_KEY_DKIM
|
115
|
+
end
|
116
|
+
signed_message.each do |line|
|
117
|
+
ok = pdkim_feed(ctx, line+CRLF, line.length+2)
|
118
|
+
end
|
119
|
+
signatures = pdkim_feed_finish(ctx)
|
120
|
+
pdkim_free_ctx(ctx)
|
121
|
+
display("TEST1 -- SIGNING/VERIFYING THE LONG WAY", TEST_MESSAGE, signed_message, signatures)
|
122
|
+
end
|
123
|
+
|
124
|
+
def test2
|
125
|
+
ok, signed_message = pdkim_sign_an_email(PDKIM_INPUT_NORMAL, DOMAIN, SELECTOR, RSA_PRIVATE_KEY, PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE, TEST_MESSAGE)
|
126
|
+
ok, signatures = pdkim_verify_an_email(PDKIM_INPUT_NORMAL, signed_message, :fake_domain_lookup)
|
127
|
+
display("TEST2 -- SIGNING/VERIFYING THE SHORT WAY", TEST_MESSAGE, signed_message, signatures)
|
128
|
+
end
|
129
|
+
|
130
|
+
def fake_domain_lookup(name)
|
131
|
+
RSA_PUBLIC_KEY_DKIM
|
132
|
+
end
|
133
|
+
|
134
|
+
# If Google ever removes the selector in this test email, this will cease to function
|
135
|
+
def test3
|
136
|
+
mail = YAML::load_file("bin/1ZsMk0-0iglmo-E9")
|
137
|
+
gmail = mail[:data][:text]
|
138
|
+
ok, signatures = pdkim_verify_an_email(PDKIM_INPUT_NORMAL, gmail)
|
139
|
+
display("TEST3 -- VERIFYING A GMAIL SIGNATURE", nil, gmail, signatures)
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
PdkimTest.new.main
|
145
|
+
|
146
|
+
|
147
|
+
|
Binary file
|
data/lib/pdkim.rb
ADDED
@@ -0,0 +1,394 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
require_relative '../ext/pdkim/pdkimglue'
|
3
|
+
|
4
|
+
module PDKIM
|
5
|
+
|
6
|
+
CRLF = "\r\n"
|
7
|
+
|
8
|
+
# ctx = pdkim_init_sign(mode, domain, selector, rsa_privkey)
|
9
|
+
#
|
10
|
+
# Initialize context for signing.
|
11
|
+
#
|
12
|
+
# mode
|
13
|
+
# PDKIM_INPUT_NORMAL or PDKIM_INPUT_SMTP. When SMTP
|
14
|
+
# input is used, the lib will deflate double-dots at
|
15
|
+
# the start of atline to a single dot, and it will
|
16
|
+
# stop processing input when a line with and single
|
17
|
+
# dot is received (Excess input will simply be ignored).
|
18
|
+
#
|
19
|
+
# domain
|
20
|
+
# The domain to sign as. This value will land in the
|
21
|
+
# d= tag of the signature. For example, if the MAIL FROM
|
22
|
+
# address is joe@mail.example.com, the domain is
|
23
|
+
# example.com.
|
24
|
+
#
|
25
|
+
# selector
|
26
|
+
# The selector string to use. This value will land in
|
27
|
+
# the s= tag of the signature. For example, if the DNS DKIM TXT
|
28
|
+
# record contains 2015may._domainkey.example.com, the selector
|
29
|
+
# is 2015may.
|
30
|
+
#
|
31
|
+
# rsa_privkey
|
32
|
+
# The private RSA key, in ASCII armor. It MUST NOT be
|
33
|
+
# encrypted.
|
34
|
+
#
|
35
|
+
# Returns: A freshly allocated ctx (context)
|
36
|
+
#
|
37
|
+
def pdkim_init_sign(mode, domain, selector, rsa_privkey)
|
38
|
+
ruby_pdkim_init_sign(mode, domain, selector, rsa_privkey)
|
39
|
+
end
|
40
|
+
|
41
|
+
# ctx = pdkim_init_verify(mode) { |name| ...code to retrieve domain's public key... }
|
42
|
+
#
|
43
|
+
# Initialize context for verification.
|
44
|
+
#
|
45
|
+
# mode
|
46
|
+
# PDKIM_INPUT_NORMAL or PDKIM_INPUT_SMTP. When SMTP
|
47
|
+
# input is used, the lib will deflate double-dots at
|
48
|
+
# the start of atline to a single dot, and it will
|
49
|
+
# stop processing input when a line with and single
|
50
|
+
# dot is received (Excess input will simply be ignored).
|
51
|
+
#
|
52
|
+
# block
|
53
|
+
# Tom's pdkim lib does not include a DNS resolver, so one
|
54
|
+
# has been provided in this gem called "pdkim_dkim_public_key_lookup(name)."
|
55
|
+
# You may provide some other mechanism, however. This call, then, would be:
|
56
|
+
#
|
57
|
+
# ctx = pdkim_init_verify(mode) do |name|
|
58
|
+
# your_public_key_lookup(name)
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# NOTE: Although the block is on this call to pdkim_init_verify,
|
62
|
+
# the ACTUAL callbacks are made from pdkim_feed_finish as the
|
63
|
+
# DKIM signatures (there can be many) are being validated one by
|
64
|
+
# one. As each signature will have a different domain, a callback
|
65
|
+
# is used to do the lookup.
|
66
|
+
#
|
67
|
+
# Returns: A freshly allocated ctx (context)
|
68
|
+
#
|
69
|
+
def pdkim_init_verify(state)
|
70
|
+
raise ArgumentError.new("pdkim_init_verify missing block") if !block_given?
|
71
|
+
ruby_pdkim_init_verify(state) do |name|
|
72
|
+
r = yield(name)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# pdkim_set_debug_stream(ctx, file_name)
|
77
|
+
# pdkim_set_debug_stream(ctx, file_number)
|
78
|
+
#
|
79
|
+
# Set up debugging stream.
|
80
|
+
#
|
81
|
+
# When pdkim.c was compiled with DEBUG defined (which is the
|
82
|
+
# recommended default), you can set up a stream where it
|
83
|
+
# sends debug output to. If you don't set a debug
|
84
|
+
# stream, no debug output is generated.
|
85
|
+
# file_name
|
86
|
+
# If the first option is called, a file by the name
|
87
|
+
# file_name is opened, and debugging output is APPENDED to it.
|
88
|
+
# Ex: pdkim_set_debug_stream(ctx, "my_debug_log")
|
89
|
+
#
|
90
|
+
# file_number
|
91
|
+
# If the second option is choosen, a file by the number
|
92
|
+
# file_number is opened, and debugging output is APPENDED to it.
|
93
|
+
# Ex: pdkim_set_debug_stream(ctx, 2) # STDERR
|
94
|
+
#
|
95
|
+
# Returns: nil
|
96
|
+
#
|
97
|
+
def pdkim_set_debug_stream(ctx, id)
|
98
|
+
ruby_pdkim_set_debug_stream(ctx, id)
|
99
|
+
end
|
100
|
+
|
101
|
+
# ok = pdkim_set_optional(ctx, sign_headers, identity, canon_headers, \
|
102
|
+
# canon_body, bodylength, algo, created, expires)
|
103
|
+
#
|
104
|
+
# OPTIONAL: Set additional optional signing options. If you do
|
105
|
+
# not use this function, sensible defaults (see below) are used.
|
106
|
+
# Any strings you pass in are dup'ed, so you can safely release
|
107
|
+
# your copy even before calling pdkim_free() on your context.
|
108
|
+
#
|
109
|
+
# sign_headers (default nil)
|
110
|
+
# Colon-separated list of header names. Headers with
|
111
|
+
# a name matching the list will be included in the
|
112
|
+
# signature. When this is NULL, the list of headers
|
113
|
+
# recommended in RFC4781 will be used.
|
114
|
+
#
|
115
|
+
# identity (default nil)
|
116
|
+
# An identity string as described in RFC4781. It will
|
117
|
+
# be put into the i= tag of the signature.
|
118
|
+
#
|
119
|
+
# canon_headers (default PDKIM_CANON_SIMPLE)
|
120
|
+
# Canonicalization algorithm to use for headers. One
|
121
|
+
# of PDKIM_CANON_SIMPLE or PDKIM_CANON_RELAXED.
|
122
|
+
#
|
123
|
+
# canon_body (default PDKIM_CANON_SIMPLE)
|
124
|
+
# Canonicalization algorithm to use for the body. One
|
125
|
+
# of PDKIM_CANON_SIMPLE or PDKIM_CANON_RELAXED.
|
126
|
+
#
|
127
|
+
# bodylength (default -1)
|
128
|
+
# Amount of canonicalized body bytes to include in
|
129
|
+
# the body hash calculation. A value of 0 means that
|
130
|
+
# the body is not included in the signature. A value
|
131
|
+
# of -1 (the default) means that there is no limit.
|
132
|
+
#
|
133
|
+
# algo (default PDKIM_ALGO_RSA_SHA256)
|
134
|
+
# One of PDKIM_ALGO_RSA_SHA256 or PDKIM_ALGO_RSA_SHA1.
|
135
|
+
#
|
136
|
+
# created (default 0)
|
137
|
+
# Seconds since the epoch, describing when the signature
|
138
|
+
# was created. This is copied to the t= tag of the
|
139
|
+
# signature. Setting a value of 0 (the default) omits
|
140
|
+
# the tag from the signature.
|
141
|
+
#
|
142
|
+
# expires (default 0)
|
143
|
+
# Seconds since the epoch, describing when the signature
|
144
|
+
# expires. This is copied to the x= tag of the
|
145
|
+
# signature. Setting a value of 0 (the default) omits
|
146
|
+
# the tag from the signature.
|
147
|
+
#
|
148
|
+
# Returns: 0 (PDKIM_OK) for success or a PDKIM_ERR_* constant
|
149
|
+
#
|
150
|
+
def pdkim_set_optional(ctx, sign_headers,
|
151
|
+
identity, canon_headers, canon_body, bodylength,
|
152
|
+
algo, created, expires)
|
153
|
+
ruby_pdkim_set_optional(ctx, sign_headers,
|
154
|
+
identity, canon_headers, canon_body, bodylength,
|
155
|
+
algo, created, expires)
|
156
|
+
end
|
157
|
+
|
158
|
+
# ok = pdkim_feed(ctx, data, data_len)
|
159
|
+
#
|
160
|
+
# (Repeatedly) feed data to the signing algorithm. The message
|
161
|
+
# data MUST use CRLF line endings (like SMTP uses on the
|
162
|
+
# wire). The data chunks do not need to be a "line" - you
|
163
|
+
# can split chunks at arbitrary locations.
|
164
|
+
#
|
165
|
+
# data (Ruby String which is also allowed to contain binary)
|
166
|
+
# Pointer to data to feed. Please note that despite
|
167
|
+
# the example given below, this is not necessarily a
|
168
|
+
# C string, i.e., NULL terminated.
|
169
|
+
#
|
170
|
+
# data_len
|
171
|
+
# Length of data being fed, in bytes.
|
172
|
+
#
|
173
|
+
# Returns: 0 (PDKIM_OK) for success or a PDKIM_ERR_* constant
|
174
|
+
#
|
175
|
+
def pdkim_feed(ctx, data, len)
|
176
|
+
ruby_pdkim_feed(ctx, data, len)
|
177
|
+
end
|
178
|
+
|
179
|
+
# ok = pdkim_feed_finish(ctx)
|
180
|
+
#
|
181
|
+
# Signal end-of-message and retrieve the signature block.
|
182
|
+
#
|
183
|
+
# Returns:
|
184
|
+
# ok
|
185
|
+
# 0 (PDKIM_OK) for success or a PDKIM_ERR_* constant
|
186
|
+
#
|
187
|
+
# signatures (An array of hashes of signatures.)
|
188
|
+
# If the function returns PDKIM_OK, it will return
|
189
|
+
# one or more signatures.
|
190
|
+
#
|
191
|
+
# Sign only returns 1 signature, but verify will return 1 signature
|
192
|
+
# for each DKIM header in the email being verified.
|
193
|
+
#
|
194
|
+
# Returns an array of hashes (only 1 for sign) with the signatures in them
|
195
|
+
# [
|
196
|
+
# {
|
197
|
+
# "error"=>0, # 0 (PDKIM_OK) for success or a PDKIM_ERR_* constant
|
198
|
+
# "signature"=>nil,
|
199
|
+
# "version"=>1,
|
200
|
+
# "algo"=>0,
|
201
|
+
# "canon_headers"=>0,
|
202
|
+
# "canon_body"=>0,
|
203
|
+
# "querymethod"=>0,
|
204
|
+
# "selector"=>"cheezburger",
|
205
|
+
# "domain"=>"duncanthrax.net",
|
206
|
+
# "identity"=>nil,
|
207
|
+
# "created"=>0,
|
208
|
+
# "expires"=>0,
|
209
|
+
# "bodylength"=>-1,
|
210
|
+
# "headernames"=>"Subject:To:From",
|
211
|
+
# "copiedheaders"=>nil,
|
212
|
+
# "sigdata"=>"\xA1\xEDy\x16\xDF\xF1\xF8C\x18\x80\xF8\x1F@\xFCIV&\x0E\xA4\xD5 ...",
|
213
|
+
# "bodyhash"=>"M\x87\xE3_\xE5;T\xD4\x96\x90'I\xEA2\xBF\xCE\x8F\x17\xCD\xEF ...",
|
214
|
+
# "signature_header"=>nil,
|
215
|
+
# "verify_status"=>3,
|
216
|
+
# "verify_ext_status"=>0,
|
217
|
+
# "pubkey"=>{
|
218
|
+
# "version"=>"DKIM1",
|
219
|
+
# "granularity"=>"*",
|
220
|
+
# "hashes"=>nil,
|
221
|
+
# "keytype"=>"rsa",
|
222
|
+
# "srvtype"=>"*",
|
223
|
+
# "notes"=>nil,
|
224
|
+
# "key"=>"0\x81\x9F0\r\x06\t*\x86H\x86\xF7\r\x01\x01\x01\x05\x00\x03\x81\x8D ...",
|
225
|
+
# "testing"=>0,
|
226
|
+
# "no_subdomaining"=>0
|
227
|
+
# }
|
228
|
+
# }
|
229
|
+
# ]
|
230
|
+
#
|
231
|
+
def pdkim_feed_finish(ctx)
|
232
|
+
ruby_pdkim_feed_finish(ctx)
|
233
|
+
end
|
234
|
+
|
235
|
+
# pdkim_free_ctx(ctx)
|
236
|
+
#
|
237
|
+
# Free all allocated memory blocks referenced from
|
238
|
+
# the context, as well as the context itself.
|
239
|
+
#
|
240
|
+
# Don't forget to call this or your application will "leak" memory.
|
241
|
+
#
|
242
|
+
def pdkim_free_ctx(ctx)
|
243
|
+
ruby_pdkim_free_ctx(ctx)
|
244
|
+
end
|
245
|
+
|
246
|
+
# ok = pdkim_sign_an_email(mode, domain, selector, rsa_privkey, canon_headers, canon_body, unsigned_message)
|
247
|
+
#
|
248
|
+
# Call a single function to sign an email message.
|
249
|
+
#
|
250
|
+
# mode
|
251
|
+
# PDKIM_INPUT_NORMAL or PDKIM_INPUT_SMTP. When SMTP
|
252
|
+
# input is used, the lib will deflate double-dots at
|
253
|
+
# the start of atline to a single dot, and it will
|
254
|
+
# stop processing input when a line with and single
|
255
|
+
# dot is received (Excess input will simply be ignored).
|
256
|
+
#
|
257
|
+
# domain
|
258
|
+
# The domain to sign as. This value will land in the
|
259
|
+
# d= tag of the signature. For example, if the MAIL FROM
|
260
|
+
# address is joe@mail.example.com, the domain is
|
261
|
+
# example.com.
|
262
|
+
#
|
263
|
+
# selector
|
264
|
+
# The selector string to use. This value will land in
|
265
|
+
# the s= tag of the signature. For example, if the DNS DKIM TXT
|
266
|
+
# record contains 2015may._domainkey.example.com, the selector
|
267
|
+
# is 2015may.
|
268
|
+
#
|
269
|
+
# rsa_privkey
|
270
|
+
# The private RSA key, in ASCII armor. It MUST NOT be
|
271
|
+
# encrypted. For example, in the sample used for this gem,
|
272
|
+
# the private key is: RSA_PRIVKEY:
|
273
|
+
# -----BEGIN RSA PRIVATE KEY-----
|
274
|
+
# MIICXQIBAAKBgQC5+utIbbfbpssvW0TboF73Seos+1ijdPFGwc/z8Yu12cpjBvRb
|
275
|
+
# ...
|
276
|
+
# FA0nM8cHuN/VLKjjcrJUK47lZEOsjLv+qTl0i0Lp6giq
|
277
|
+
# -----END RSA PRIVATE KEY-----
|
278
|
+
#
|
279
|
+
# canon_headers (default PDKIM_CANON_SIMPLE)
|
280
|
+
# Canonicalization algorithm to use for headers. One
|
281
|
+
# of PDKIM_CANON_SIMPLE or PDKIM_CANON_RELAXED.
|
282
|
+
#
|
283
|
+
# canon_body (default PDKIM_CANON_SIMPLE)
|
284
|
+
# Canonicalization algorithm to use for the body. One
|
285
|
+
# of PDKIM_CANON_SIMPLE or PDKIM_CANON_RELAXED.
|
286
|
+
#
|
287
|
+
# unsigned_message
|
288
|
+
# An array of lines containing the email. The message
|
289
|
+
# data array MUST NOT use CRLF line endings, but each line
|
290
|
+
# is assumed to end with a CRLF (like SMTP uses on the
|
291
|
+
# wire). The lines may be of arbitrary length. A line oriented
|
292
|
+
# format was chosen because it's the "natural" way
|
293
|
+
# of formatting the data for Ruby.
|
294
|
+
#
|
295
|
+
# Returns: an array of 2 elements: [success_code, message]
|
296
|
+
# if successful, returns 0 (PDKIM_OK) and the signed_message
|
297
|
+
# if unsuccessful, returns a PDKIM_ERR_* constant and nil
|
298
|
+
#
|
299
|
+
def pdkim_sign_an_email(mode, domain, selector, rsa_privkey, canon_headers, canon_body, unsigned_message)
|
300
|
+
ctx = pdkim_init_sign(mode, domain, selector, rsa_privkey)
|
301
|
+
return [PDKIM_FAIL, verify_counts] if ctx==0
|
302
|
+
ok = pdkim_set_optional(ctx, nil, nil, canon_headers, canon_body, -1, PDKIM_ALGO_RSA_SHA256, 0, 0)
|
303
|
+
if ok!=PDKIM_OK
|
304
|
+
pdkim_free_ctx(ctx)
|
305
|
+
return [ok, nil]
|
306
|
+
end
|
307
|
+
unsigned_message.each do |line|
|
308
|
+
ok = pdkim_feed(ctx, line+CRLF, line.length+2)
|
309
|
+
if ok!=PDKIM_OK
|
310
|
+
pdkim_free_ctx(ctx)
|
311
|
+
return [ok, nil]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
signatures = pdkim_feed_finish(ctx)
|
315
|
+
pdkim_free_ctx(ctx)
|
316
|
+
return [PDKIM_ERR_RSA_SIGNING, nil] if signatures.empty?
|
317
|
+
signature = signatures[0][:signature]
|
318
|
+
signed_message = [signature]
|
319
|
+
signed_message.concat(unsigned_message)
|
320
|
+
return [PDKIM_OK, signed_message]
|
321
|
+
end
|
322
|
+
|
323
|
+
# ok = verify_an_email(mode, signed_message, sym_domain_lookup)
|
324
|
+
#
|
325
|
+
# Call a single function to sign an email message.
|
326
|
+
#
|
327
|
+
# mode
|
328
|
+
# PDKIM_INPUT_NORMAL or PDKIM_INPUT_SMTP. When SMTP
|
329
|
+
# input is used, the lib will deflate double-dots at
|
330
|
+
# the start of atline to a single dot, and it will
|
331
|
+
# stop processing input when a line with and single
|
332
|
+
# dot is received (Excess input will simply be ignored).
|
333
|
+
#
|
334
|
+
# signed_message
|
335
|
+
# An array of lines containing the email preceeded by a
|
336
|
+
# DKIM header that was generated by a signing process. The message
|
337
|
+
# data array MUST NOT contain CRLF line endings; instead, each line
|
338
|
+
# will have a CRLF added here. The lines may be of arbitrary
|
339
|
+
# length. A line oriented format was chosen because it's
|
340
|
+
# the "natural" way of formatting the data for Ruby.
|
341
|
+
#
|
342
|
+
# sym_domain_lookup (OPTIONAL)
|
343
|
+
# A symbolic name of the method to use for private key lookups.
|
344
|
+
# If this parameter is not given, it defaults to :pdkim_dkim_public_key_lookup
|
345
|
+
# which is the built-in lookup function.
|
346
|
+
#
|
347
|
+
# Returns:
|
348
|
+
# ok
|
349
|
+
# 0 (PDKIM_OK) for success or a PDKIM_ERR_* constant
|
350
|
+
#
|
351
|
+
# signatures (An array of hashes of signatures.)
|
352
|
+
# If the function returns PDKIM_OK, it will return
|
353
|
+
# one or more signatures.
|
354
|
+
#
|
355
|
+
def pdkim_verify_an_email(mode, signed_message, sym_domain_lookup=:pdkim_dkim_public_key_lookup)
|
356
|
+
ctx = pdkim_init_verify(mode) do |name|
|
357
|
+
send(sym_domain_lookup, name)
|
358
|
+
end
|
359
|
+
return [PDKIM_FAIL, verify_counts] if ctx==0
|
360
|
+
ok = PDKIM_FAIL
|
361
|
+
signed_message.each do |line|
|
362
|
+
ok = pdkim_feed(ctx, line+CRLF, line.length+2)
|
363
|
+
if ok!=PDKIM_OK
|
364
|
+
pdkim_free_ctx(ctx)
|
365
|
+
return [ok, verify_counts]
|
366
|
+
end
|
367
|
+
end
|
368
|
+
signatures = pdkim_feed_finish(ctx)
|
369
|
+
pdkim_free_ctx(ctx)
|
370
|
+
return ok, signatures
|
371
|
+
end
|
372
|
+
|
373
|
+
# key = pdkim_dkim_public_key_lookup(name)
|
374
|
+
#
|
375
|
+
# This method retrieves the public key from the domain's
|
376
|
+
# website's DNS records, if any. If it fails, it will return
|
377
|
+
# "nil" which will cause the validation to fail with
|
378
|
+
# PDKIM_VERIFY_FAIL.
|
379
|
+
#
|
380
|
+
# name
|
381
|
+
# The name to be looked up by the resolver. It has the form:
|
382
|
+
# selector._domainkey.domain.com (org, biz, us, gov, etc.)
|
383
|
+
# the name will be properly formatted if this method is
|
384
|
+
# called from the block in pdkim_init_verify.
|
385
|
+
#
|
386
|
+
# Returns: The DKIM public key for the domain in 'name' or nil
|
387
|
+
#
|
388
|
+
def pdkim_dkim_public_key_lookup(name)
|
389
|
+
records = [];
|
390
|
+
Resolv::DNS.open { |dns| records = dns.getresources(name, Resolv::DNS::Resource::IN::TXT) }
|
391
|
+
if records.empty? then nil else records[0].strings.join("") end
|
392
|
+
end
|
393
|
+
|
394
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pdkim
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.9'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Kistner
|
8
|
+
- Michael J. Welch, Ph.D.
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-07-20 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: |-
|
15
|
+
PDKIM - a RFC4871 (DKIM) implementation
|
16
|
+
http://duncanthrax.net/pdkim/
|
17
|
+
Copyright (C) 2009 Tom Kistner <tom@duncanthrax.net>
|
18
|
+
|
19
|
+
Includes code from the PolarSSL project.
|
20
|
+
http://polarssl.org
|
21
|
+
Copyright (C) 2009 Paul Bakker <polarssl_maintainer@polarssl.org>
|
22
|
+
Copyright (C) 2006-2008 Christophe Devine
|
23
|
+
|
24
|
+
This gem (C) 2015-2017 Michael J. Welch, Ph.D. <mjwelchphd@gmail.com>
|
25
|
+
Source code can be found on GitHub: https://github.com/mjwelchphd/pdkim
|
26
|
+
|
27
|
+
Test by running pdkimgemtest after installing.
|
28
|
+
|
29
|
+
PDKIM is the pacakge that Exim4 uses for DKIM support.
|
30
|
+
email: mjwelchphd@gmail.com
|
31
|
+
executables:
|
32
|
+
- pdkimgemtest
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- bin/1ZsMk0-0iglmo-E9
|
37
|
+
- bin/pdkimgemtest
|
38
|
+
- ext/pdkim/pdkimglue.so
|
39
|
+
- lib/pdkim.rb
|
40
|
+
homepage: http://www.czarmail.com
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
metadata: {}
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
- ext
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 2.4.6
|
62
|
+
signing_key:
|
63
|
+
specification_version: 4
|
64
|
+
summary: A Ruby Gem wrapping Tom Kistner's PDKIM package.
|
65
|
+
test_files: []
|