Module: PDKIM
- Defined in:
- lib/pdkim.rb
Constant Summary collapse
- CRLF =
"\r\n"
Instance Method Summary collapse
-
#pdkim_dkim_public_key_lookup(name) ⇒ Object
key = pdkim_dkim_public_key_lookup(name).
-
#pdkim_feed(ctx, data, len) ⇒ Object
ok = pdkim_feed(ctx, data, data_len).
-
#pdkim_feed_finish(ctx) ⇒ Object
ok = pdkim_feed_finish(ctx).
-
#pdkim_free_ctx(ctx) ⇒ Object
pdkim_free_ctx(ctx).
-
#pdkim_init_sign(mode, domain, selector, rsa_privkey) ⇒ Object
ctx = pdkim_init_sign(mode, domain, selector, rsa_privkey).
-
#pdkim_init_verify(state) ⇒ Object
ctx = pdkim_init_verify(mode) { |name| …code to retrieve domain’s public key… }.
-
#pdkim_set_debug_stream(ctx, id) ⇒ Object
pdkim_set_debug_stream(ctx, file_name) pdkim_set_debug_stream(ctx, file_number).
-
#pdkim_set_optional(ctx, sign_headers, identity, canon_headers, canon_body, bodylength, algo, created, expires) ⇒ Object
ok = pdkim_set_optional(ctx, sign_headers, identity, canon_headers, \ canon_body, bodylength, algo, created, expires).
-
#pdkim_sign_an_email(mode, domain, selector, rsa_privkey, canon_headers, canon_body, unsigned_message) ⇒ Object
ok = pdkim_sign_an_email(mode, domain, selector, rsa_privkey, canon_headers, canon_body, unsigned_message).
-
#pdkim_verify_an_email(mode, signed_message, sym_domain_lookup = :pdkim_dkim_public_key_lookup) ⇒ Object
ok = verify_an_email(mode, signed_message, sym_domain_lookup).
Instance Method Details
#pdkim_dkim_public_key_lookup(name) ⇒ Object
key = pdkim_dkim_public_key_lookup(name)
This method retrieves the public key from the domain’s website’s DNS records, if any. If it fails, it will return “nil” which will cause the validation to fail with PDKIM_VERIFY_FAIL.
name
The name to be looked up by the resolver. It has the form:
selector._domainkey.domain.com (org, biz, us, gov, etc.)
the name will be properly formatted if this method is
called from the block in pdkim_init_verify.
Returns: The DKIM public key for the domain in ‘name’ or nil
388 389 390 391 392 |
# File 'lib/pdkim.rb', line 388 def pdkim_dkim_public_key_lookup(name) records = []; Resolv::DNS.open { |dns| records = dns.getresources(name, Resolv::DNS::Resource::IN::TXT) } if records.empty? then nil else records[0].strings.join("") end end |
#pdkim_feed(ctx, data, len) ⇒ Object
ok = pdkim_feed(ctx, data, data_len)
(Repeatedly) feed data to the signing algorithm. The message data MUST use CRLF line endings (like SMTP uses on the wire). The data chunks do not need to be a “line” - you can split chunks at arbitrary locations.
data (Ruby String which is also allowed to contain binary)
Pointer to data to feed. Please note that despite
the example given below, this is not necessarily a
C string, i.e., NULL terminated.
data_len
Length of data being fed, in bytes.
Returns: 0 (PDKIM_OK) for success or a PDKIM_ERR_* constant
175 176 177 |
# File 'lib/pdkim.rb', line 175 def pdkim_feed(ctx, data, len) ruby_pdkim_feed(ctx, data, len) end |
#pdkim_feed_finish(ctx) ⇒ Object
ok = pdkim_feed_finish(ctx)
Signal end-of-message and retrieve the signature block.
Returns:
ok
0 (PDKIM_OK) for success or a PDKIM_ERR_* constant
signatures (An array of hashes of signatures.)
If the function returns PDKIM_OK, it will return
one or more signatures.
Sign only returns 1 signature, but verify will return 1 signature
for each DKIM header in the email being verified.
Returns an array of hashes (only 1 for sign) with the signatures in them
[
{
"error"=>0, # 0 (PDKIM_OK) for success or a PDKIM_ERR_* constant
"signature"=>nil,
"version"=>1,
"algo"=>0,
"canon_headers"=>0,
"canon_body"=>0,
"querymethod"=>0,
"selector"=>"cheezburger",
"domain"=>"duncanthrax.net",
"identity"=>nil,
"created"=>0,
"expires"=>0,
"bodylength"=>-1,
"headernames"=>"Subject:To:From",
"copiedheaders"=>nil,
"sigdata"=>"\xA1\xEDy\x16\xDF\xF1\xF8C\x18\x80\xF8\x1F@\xFCIV&\x0E\xA4\xD5 ...",
"bodyhash"=>"M\x87\xE3_\xE5;T\xD4\x96\x90'I\xEA2\xBF\xCE\x8F\x17\xCD\xEF ...",
"signature_header"=>nil,
"verify_status"=>3,
"verify_ext_status"=>0,
"pubkey"=>{
"version"=>"DKIM1",
"granularity"=>"*",
"hashes"=>nil,
"keytype"=>"rsa",
"srvtype"=>"*",
"notes"=>nil,
"key"=>"0\x81\x9F0\r\x06\t*\x86H\x86\xF7\r\x01\x01\x01\x05\x00\x03\x81\x8D ...",
"testing"=>0,
"no_subdomaining"=>0
}
}
]
231 232 233 |
# File 'lib/pdkim.rb', line 231 def pdkim_feed_finish(ctx) ruby_pdkim_feed_finish(ctx) end |
#pdkim_free_ctx(ctx) ⇒ Object
pdkim_free_ctx(ctx)
Free all allocated memory blocks referenced from
the context, as well as the context itself.
Don't forget to call this or your application will "leak" memory.
242 243 244 |
# File 'lib/pdkim.rb', line 242 def pdkim_free_ctx(ctx) ruby_pdkim_free_ctx(ctx) end |
#pdkim_init_sign(mode, domain, selector, rsa_privkey) ⇒ Object
ctx = pdkim_init_sign(mode, domain, selector, rsa_privkey)
Initialize context for signing.
mode
PDKIM_INPUT_NORMAL or PDKIM_INPUT_SMTP. When SMTP
input is used, the lib will deflate double-dots at
the start of atline to a single dot, and it will
stop processing input when a line with and single
dot is received (Excess input will simply be ignored).
domain
The domain to sign as. This value will land in the
d= tag of the signature. For example, if the MAIL FROM
address is joe@mail.example.com, the domain is
example.com.
selector
The selector string to use. This value will land in
the s= tag of the signature. For example, if the DNS DKIM TXT
record contains 2015may._domainkey.example.com, the selector
is 2015may.
rsa_privkey
The private RSA key, in ASCII armor. It MUST NOT be
encrypted.
Returns: A freshly allocated ctx (context)
37 38 39 |
# File 'lib/pdkim.rb', line 37 def pdkim_init_sign(mode, domain, selector, rsa_privkey) ruby_pdkim_init_sign(mode, domain, selector, rsa_privkey) end |
#pdkim_init_verify(state) ⇒ Object
ctx = pdkim_init_verify(mode) { |name| …code to retrieve domain’s public key… }
Initialize context for verification.
mode
PDKIM_INPUT_NORMAL or PDKIM_INPUT_SMTP. When SMTP
input is used, the lib will deflate double-dots at
the start of atline to a single dot, and it will
stop processing input when a line with and single
dot is received (Excess input will simply be ignored).
block
Tom's pdkim lib does not include a DNS resolver, so one
has been provided in this gem called "pdkim_dkim_public_key_lookup(name)."
You may provide some other mechanism, however. This call, then, would be:
ctx = pdkim_init_verify(mode) do |name|
your_public_key_lookup(name)
end
NOTE: Although the block is on this call to pdkim_init_verify,
the ACTUAL callbacks are made from pdkim_feed_finish as the
DKIM signatures (there can be many) are being validated one by
one. As each signature will have a different domain, a callback
is used to do the lookup.
Returns: A freshly allocated ctx (context)
69 70 71 72 73 74 |
# File 'lib/pdkim.rb', line 69 def pdkim_init_verify(state) raise ArgumentError.new("pdkim_init_verify missing block") if !block_given? ruby_pdkim_init_verify(state) do |name| r = yield(name) end end |
#pdkim_set_debug_stream(ctx, id) ⇒ Object
pdkim_set_debug_stream(ctx, file_name) pdkim_set_debug_stream(ctx, file_number)
Set up debugging stream.
When pdkim.c was compiled with DEBUG defined (which is the recommended default), you can set up a stream where it sends debug output to. If you don’t set a debug stream, no debug output is generated.
file_name
If the first option is called, a file by the name
file_name is opened, and debugging output is APPENDED to it.
Ex: pdkim_set_debug_stream(ctx, "my_debug_log")
file_number
If the second option is choosen, a file by the number
file_number is opened, and debugging output is APPENDED to it.
Ex: pdkim_set_debug_stream(ctx, 2) # STDERR
Returns: nil
97 98 99 |
# File 'lib/pdkim.rb', line 97 def pdkim_set_debug_stream(ctx, id) ruby_pdkim_set_debug_stream(ctx, id) end |
#pdkim_set_optional(ctx, sign_headers, identity, canon_headers, canon_body, bodylength, algo, created, expires) ⇒ Object
ok = pdkim_set_optional(ctx, sign_headers, identity, canon_headers, \
canon_body, bodylength, algo, created, expires)
OPTIONAL: Set additional optional signing options. If you do not use this function, sensible defaults (see below) are used. Any strings you pass in are dup’ed, so you can safely release your copy even before calling pdkim_free() on your context.
sign_headers (default nil)
Colon-separated list of header names. Headers with
a name matching the list will be included in the
signature. When this is NULL, the list of headers
recommended in RFC4781 will be used.
identity (default nil)
An identity string as described in RFC4781. It will
be put into the i= tag of the signature.
canon_headers (default PDKIM_CANON_SIMPLE)
Canonicalization algorithm to use for headers. One
of PDKIM_CANON_SIMPLE or PDKIM_CANON_RELAXED.
canon_body (default PDKIM_CANON_SIMPLE)
Canonicalization algorithm to use for the body. One
of PDKIM_CANON_SIMPLE or PDKIM_CANON_RELAXED.
bodylength (default -1)
Amount of canonicalized body bytes to include in
the body hash calculation. A value of 0 means that
the body is not included in the signature. A value
of -1 (the default) means that there is no limit.
algo (default PDKIM_ALGO_RSA_SHA256)
One of PDKIM_ALGO_RSA_SHA256 or PDKIM_ALGO_RSA_SHA1.
created (default 0)
Seconds since the epoch, describing when the signature
was created. This is copied to the t= tag of the
signature. Setting a value of 0 (the default) omits
the tag from the signature.
expires (default 0)
Seconds since the epoch, describing when the signature
expires. This is copied to the x= tag of the
signature. Setting a value of 0 (the default) omits
the tag from the signature.
Returns: 0 (PDKIM_OK) for success or a PDKIM_ERR_* constant
150 151 152 153 154 155 156 |
# File 'lib/pdkim.rb', line 150 def pdkim_set_optional(ctx, sign_headers, identity, canon_headers, canon_body, bodylength, algo, created, expires) ruby_pdkim_set_optional(ctx, sign_headers, identity, canon_headers, canon_body, bodylength, algo, created, expires) end |
#pdkim_sign_an_email(mode, domain, selector, rsa_privkey, canon_headers, canon_body, unsigned_message) ⇒ Object
ok = pdkim_sign_an_email(mode, domain, selector, rsa_privkey, canon_headers, canon_body, unsigned_message)
Call a single function to sign an email message.
mode
PDKIM_INPUT_NORMAL or PDKIM_INPUT_SMTP. When SMTP
input is used, the lib will deflate double-dots at
the start of atline to a single dot, and it will
stop processing input when a line with and single
dot is received (Excess input will simply be ignored).
domain
The domain to sign as. This value will land in the
d= tag of the signature. For example, if the MAIL FROM
address is joe@mail.example.com, the domain is
example.com.
selector
The selector string to use. This value will land in
the s= tag of the signature. For example, if the DNS DKIM TXT
record contains 2015may._domainkey.example.com, the selector
is 2015may.
rsa_privkey
The private RSA key, in ASCII armor. It MUST NOT be
encrypted. For example, in the sample used for this gem,
the private key is: RSA_PRIVKEY:
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC5+utIbbfbpssvW0TboF73Seos+1ijdPFGwc/z8Yu12cpjBvRb
...
FA0nM8cHuN/VLKjjcrJUK47lZEOsjLv+qTl0i0Lp6giq
-----END RSA PRIVATE KEY-----
canon_headers (default PDKIM_CANON_SIMPLE)
Canonicalization algorithm to use for headers. One
of PDKIM_CANON_SIMPLE or PDKIM_CANON_RELAXED.
canon_body (default PDKIM_CANON_SIMPLE)
Canonicalization algorithm to use for the body. One
of PDKIM_CANON_SIMPLE or PDKIM_CANON_RELAXED.
An array of lines containing the email. The
data array MUST NOT use CRLF line endings, but each line
is assumed to end with a CRLF (like SMTP uses on the
wire). The lines may be of arbitrary length. A line oriented
format was chosen because it's the "natural" way
of formatting the data for Ruby.
Returns: an array of 2 elements: [success_code, message]
if successful, returns 0 (PDKIM_OK) and the
if unsuccessful, returns a PDKIM_ERR_* constant and nil
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/pdkim.rb', line 299 def pdkim_sign_an_email(mode, domain, selector, rsa_privkey, canon_headers, canon_body, ) ctx = pdkim_init_sign(mode, domain, selector, rsa_privkey) return [PDKIM_FAIL, verify_counts] if ctx==0 ok = pdkim_set_optional(ctx, nil, nil, canon_headers, canon_body, -1, PDKIM_ALGO_RSA_SHA256, 0, 0) if ok!=PDKIM_OK pdkim_free_ctx(ctx) return [ok, nil] end .each do |line| ok = pdkim_feed(ctx, line+CRLF, line.length+2) if ok!=PDKIM_OK pdkim_free_ctx(ctx) return [ok, nil] end end signatures = pdkim_feed_finish(ctx) pdkim_free_ctx(ctx) return [PDKIM_ERR_RSA_SIGNING, nil] if signatures.empty? signature = signatures[0][:signature] = [signature] .concat() return [PDKIM_OK, ] end |
#pdkim_verify_an_email(mode, signed_message, sym_domain_lookup = :pdkim_dkim_public_key_lookup) ⇒ Object
ok = verify_an_email(mode, signed_message, sym_domain_lookup)
Call a single function to sign an email message.
mode
PDKIM_INPUT_NORMAL or PDKIM_INPUT_SMTP. When SMTP
input is used, the lib will deflate double-dots at
the start of atline to a single dot, and it will
stop processing input when a line with and single
dot is received (Excess input will simply be ignored).
An array of lines containing the email preceeded by a
DKIM header that was generated by a signing process. The
data array MUST NOT contain CRLF line endings; instead, each line
will have a CRLF added here. The lines may be of arbitrary
length. A line oriented format was chosen because it's
the "natural" way of formatting the data for Ruby.
sym_domain_lookup (OPTIONAL)
A symbolic name of the method to use for private key lookups.
If this parameter is not given, it defaults to :pdkim_dkim_public_key_lookup
which is the built-in lookup function.
Returns:
ok
0 (PDKIM_OK) for success or a PDKIM_ERR_* constant
signatures (An array of hashes of signatures.)
If the function returns PDKIM_OK, it will return
one or more signatures.
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/pdkim.rb', line 355 def pdkim_verify_an_email(mode, , sym_domain_lookup=:pdkim_dkim_public_key_lookup) ctx = pdkim_init_verify(mode) do |name| send(sym_domain_lookup, name) end return [PDKIM_FAIL, verify_counts] if ctx==0 ok = PDKIM_FAIL .each do |line| ok = pdkim_feed(ctx, line+CRLF, line.length+2) if ok!=PDKIM_OK pdkim_free_ctx(ctx) return [ok, verify_counts] end end signatures = pdkim_feed_finish(ctx) pdkim_free_ctx(ctx) return ok, signatures end |