Mail-DKIM-0.40/0000755030404400001440000000000012105005167012070 5ustar jlongusersMail-DKIM-0.40/TODO0000644030404400001440000001031212055417140012557 0ustar jlongusersCommon: - don't "die" on PRINT or CLOSE, find better ways to report the error DKIM-Signature: - allow version tag (DONE) - accept q=dns/txt (DONE) - method to set/get "z" tag DKIM Public Key Records: - enforce t=s option (if present) - provide method for caller to get (to check the "testing" flag) Verifier: - verify multiple signatures (ietf05 6.1) (DONE) - check that From header is signed (ietf05 6.1.1) - check public key "granularity" (DONE) - handle no response from first DNS server listed in resolv.conf (currently it goes to the second server after 5 seconds, but it does this for EVERY signature, so this will badly affect overall throughput) - **minor bug**- when Debug_Canonicalization=1 on a message with multiple signatures, the canonicalized output is recorded multiple times. Probably only the first valid signature should receive the Debug_Canonicalization option - provide semi-standard mechanism to report results of verification (including what, if any, of header.from and header.sender can be trusted) - provide mechanism in the API to run the DNS lookups in parallel with other processing (e.g. the SpamAssassin plugin would want to start the DNS queries as early as possible, but continue processing other aspects of the message while waiting for the DNS queries to complete). Net::DNS::Async may be useful here. Policy: - make it possible to determine explicit vs implicit default policy (DONE) - lookup BOTH draft-allman-dkim-ssp policy AND rfc4870(historical) policy (DONE) - this will probably be: lookup allman policy, and if not found, then try rfc4870(historical) policy (REJECTED) Signer: - allow DomainKeys signatures without using a policy object - allow adding chained signatures in one pass (e.g. allow adding a DomainKeys signature, and a DKIM signature, with the new DKIM signature signing the new DomainKeys signature) (REJECTED) - allow creation of i= and x= tags (DONE) - allow creation of l=, t=, and z= tags - do header-wrapping to signature before signing (DONE) - allow signer policy to change which private key is used Testing (some of this may already be implemented): - test public key errors: - DNS timeout - SERVFAIL - syntax error in public key record - test DNS timeout for signing policy - test key records composed of fragmented TXT records - test signature options: - unspecified query type - query type of "dns/" - bad query type (DONE) - bad algorithm (DONE) - unspecified algorithm - bad canonicalization - unspecified canonicalization - test presence of version tag in signature - IMPORTANT- allow `make test' to work when DNS is not available - test various components of verifying, so better diagnostics can be made when the verify.t script reports a bunch of unexplained failures - test absense of h= tag in DKIM signature - test use of non-ASCII characters in header names and h= tag Possible issues in base-10 draft: - 6.1.2 - check g= tag of public key against i= tag of signature (DONE) - 6.1.2 - check h= tag of public key against a= tag of signature (DONE) - 3.5 - t= tag, create it when signing messages, check it when verifying - 3.5 - x= tag, create it when signing messages - check it when verifying (DONE) - 5.4 - allow better control of which headers to sign - 5.5 - recommended headers to sign and NOT to sign (DONE) - 3.3.1 - what's an RSA exponent? - 6.1.1 - configurable list of unacceptable signing domains, e.g. "com" and "co.uk" Possible issues in RFC 4871: - 3.6.1 - g= should be case-sensitive (see 3.2 "tag values must be processed as case sensitive unless...", and 3.6.1, "g=", which does NOT mention case-sensitivity) - 3.6.1 - g= tag using irregular characters - 3.5 - i= tag, should allow quoted-printable encoding - 3.5 - i= tag, internationalized domains? - 3.5 - l= tag, what happens if the number is REALLY big, or doesn't contain a number? - 3.5 - q= tag, should skip signature if subtype is not "txt" (I think I do this, but do other verifiers?) - rationale- if a dns/foo type comes out, then it will be WRONG to lookup the txt record - 3.2 - "if a tag name does occur more than once, the entire tag-list is invalid" Mail-DKIM-0.40/t/0000755030404400001440000000000012105005167012333 5ustar jlongusersMail-DKIM-0.40/t/adsp.t0000755030404400001440000000201012055417140013445 0ustar jlongusers#!/usr/bin/perl -I../blib/lib use strict; use warnings; use Test::More tests => 6; use Mail::DKIM::Verifier; use Mail::DKIM::AuthorDomainPolicy; my $message = <<'END'; From: Jason Long Sender: George Subject: test message This message has no signature. END $message =~ s/\n/\015\012/gs; my $dkim = Mail::DKIM::Verifier->new(); $dkim->PRINT($message); $dkim->CLOSE; ok($dkim, "created verifier"); my $policy; $policy = Mail::DKIM::AuthorDomainPolicy->new(); ok($policy, "new() works"); $policy = Mail::DKIM::AuthorDomainPolicy->parse( String => "dkim=all", Domain => "messiah.edu", ); ok($policy, "parse() works"); my $result; $result = $policy->apply($dkim); print "# $result\n"; ok($result eq "neutral", "got expected result"); $policy = Mail::DKIM::AuthorDomainPolicy->parse( String => "dkim=discardable", Domain => "messiah.edu", ); ok($policy, "parse() works"); $result = $policy->apply($dkim); print "# $result\n"; ok($result eq "reject", "got expected result"); Mail-DKIM-0.40/t/external_signer.t0000755030404400001440000000235412055417140015722 0ustar jlongusers#!/usr/bin/perl -I../lib use strict; use warnings; use Test::Simple tests => 3; use Mail::DKIM::Signer; # in this test, instead of specifying a private key file, # or a Mail::DKIM::PrivateKey object, we specify a custom class # instead, one which performs the RSA-sign operation itself. # In our case, we simply return a dummy value, so this test # is to ensure that Mail::DKIM itself does not care about the # format that is returned. package MyCustomSigner; sub sign_digest { my $self = shift; my ($digest_type, $digest_binary) = @_; return "\0\0\0\0\0\0"; } package main; my $custom_signer = bless { }, "MyCustomSigner"; my $dkim = Mail::DKIM::Signer->new( Algorithm => "rsa-sha1", Method => "relaxed", Domain => "example.org", Selector => "test", Key => $custom_signer, ); ok($dkim, "new() works"); my $sample_email = < Subject: hi there Comment: what is a comment this is a sample message END_OF_SAMPLE $sample_email =~ s/\n/\015\012/gs; $dkim->PRINT($sample_email); $dkim->CLOSE; my $signature = $dkim->signature; ok($signature, "signature() works"); print "# signature=" . $signature->as_string . "\n"; ok($signature->as_string =~ /b=AAAAAAAA/, "got expected signature value"); Mail-DKIM-0.40/t/public_key.t0000755030404400001440000000232312055417140014653 0ustar jlongusers#!/usr/bin/perl -I../lib use strict; use warnings; use Test::More tests => 5; use Mail::DKIM::Verifier; $Mail::DKIM::DNS::TIMEOUT = 3; # # this public key exists # my $pubkey = Mail::DKIM::PublicKey->fetch( Protocol => "dns", Selector => "test1", Domain => "messiah.edu", ); ok($pubkey, "public key exists"); # # this public key is "NXDOMAIN" # $pubkey = Mail::DKIM::PublicKey->fetch( Protocol => "dns", Selector => "nonexistent", Domain => "messiah.edu", ); ok(!$pubkey, "public key should not exist"); ok($@ =~ /^NXDOMAIN$/, "reason given is NXDOMAIN"); SKIP: { skip "these tests fail when run on the other side of my firewall", 2 unless ($ENV{DNS_TESTS} && $ENV{DNS_TESTS} > 1); $pubkey = eval { Mail::DKIM::PublicKey->fetch( Protocol => "dns", Selector => "foo", Domain => "blackhole.messiah.edu", ) }; my $E = $@; print "# got error: $E" if $E; ok(!$pubkey && $E && $E =~ /(timeout|timed? out)/, "timeout error fetching public key"); $pubkey = eval { Mail::DKIM::PublicKey->fetch( Protocol => "dns", Selector => "foo", Domain => "blackhole2.messiah.edu", ) }; $E = $@; print "# got error: $E" if $E; ok(!$pubkey && $E && $E =~ /SERVFAIL/, "SERVFAIL dns error fetching public key"); } Mail-DKIM-0.40/t/corpus/0000755030404400001440000000000012105005167013646 5ustar jlongusersMail-DKIM-0.40/t/corpus/badkey_15.txt0000644030404400001440000000133212055417140016154 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=blackhole2.messiah.edu; h=from:to :subject:date; s=test3; bh=F84VyLrUbda4ryQ+R8 +e+8UgVxM=; b=g4rCx46HdrfJu2tIlNpNsBW5IDJZMbMOaxPYzZ3qOfxm7lw0VN nzjx3Or5SlaeKSlhGqQwDmCqHzZ9caarICdQ== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (dns failure) Date: Wed, 9 Apr 2008 09:11:00 -0500 Should cause a verification error, the DNS public key cannot be checked. This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/dk_headers_1.txt0000644030404400001440000000314312055417140016723 0ustar jlongusersReceived: from ug-out-1314.google.com (ug-out-1314.google.com [66.249.92.174]) by mx2.messiah.edu (Postfix) with ESMTP id 79F442ADB4D for ; Tue, 10 Oct 2006 15:27:14 -0400 (EDT) Received: by ug-out-1314.google.com with SMTP id 29so952130ugc for ; Tue, 10 Oct 2006 12:27:13 -0700 (PDT) DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=beta; d=gmail.com; h=received:message-id:date:from:to:subject:mime-version:content-type; b=JOYJLw6miPAjUXx+gIQm3NsWuFInq05TuJppyoxqTopYPe3bSKzRBPLw5X+OMO36re/FDEfZnOjJ4cNYvVPuld6JEikNjk2RK98unQvcdkgBPZODuE7g/vXgLZo005nQkwvfd+4dbpHiINsOTT/0ASxQ+65OcgBu0CRR/DO7z/M= Received: by 10.66.240.12 with SMTP id n12mr8614290ugh; Tue, 10 Oct 2006 12:27:12 -0700 (PDT) Received: by 10.67.96.17 with HTTP; Tue, 10 Oct 2006 12:27:12 -0700 (PDT) Date: Tue, 10 Oct 2006 15:27:12 -0400 From: "Jason Long" To: jlong@messiah.edu Subject: Test from gmail Message-ID: MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_Part_20675_4029655.1160508432731" ------=_Part_20675_4029655.1160508432731 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-Disposition: inline This message sent from Gmail. ------=_Part_20675_4029655.1160508432731 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline This message sent from Gmail.
------=_Part_20675_4029655.1160508432731-- Mail-DKIM-0.40/t/corpus/ignore_1.txt0000644030404400001440000000142712055417140016120 0ustar jlongusersDKIM-Signature: v=5; a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns; s=selector1; bh=rYdLRBGGXK4PCDh+3AbwGuV2OEU=; b=NjJBlNqLnQcz8zwF899l4JHO0blD3rRSlR3mPiOLa137fG5RSKOHeYle0U5sBcRNwIUQub+kpXhi9snZqf0zbRfO+TBaZ8dfdVRzX0d4n0PoQCX453fAAnmZcsgD3jevR+R0J/oa1Ao1A0Oj5X7mwut35QcwjIi42bDIjsFP9xk= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This should be ignored because of a v=5 signature. Mail-DKIM-0.40/t/corpus/badkey_12.txt0000644030404400001440000000125412055417140016154 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to :subject:date; s=test3; i=JLONG@messiah.edu; bh=N9OQp3Fydw5h5SJ1 UDyZrgBPEDU=; b=NC/Z6Cxe5zrXRfaHn+GXPUZtJKq4NqkbJUKPyTd98gqgb3Np tlp94hM9wVIdBfIN1e5sgrAyixdRcWs5vRKKGA== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (g= is case-sensitive compare) Date: Wed, 7 Dec 2005 09:11:00 -0500 This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/bad_dk_2.txt0000644030404400001440000000072312055417140016040 0ustar jlongusersDomainKey-Signature: a=rsa-sha1; c=simple; d=messiah.edu; q=dns; s=test1; b=DmkekrvBPH2D/VSZU9vSOk/xWrllr QUy9KbJf/0IiMiILDXUIdQsf0J3DgGu3I/mez54kh/1bUA9r7b0plE2CA== From: Jason Long Sender: George To: Nobody Subject: domainkeys test - no h= tag Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/bad_ietf01_1.txt0000644030404400001440000000240512055417140016530 0ustar jlongusersReturn-Path: X-Original-To: test@dkimtest.jason.long.name Delivered-To: dkimtest@mx2.messiah.edu Received: from voicemail.cis.att.net (unknown [12.34.200.188]) by mx2.messiah.edu (Postfix) with ESMTP id 02F12E15D8 for ; Wed, 3 May 2006 15:06:32 -0400 (EDT) Received: from (localhost[127.0.0.1]) by voicemail.cis.att.net (vm2) with SMTP id <2006050319071918800spa0re>; Wed, 3 May 2006 19:07:19 +0000 DKIM-Signature: a=rsa-sha256; c=relaxed; d=vmt2.cis.att.net; t=1146680862; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; bh=HryPFX2R6r7JPsX1Z7+yReZddQR2PjvCvdXgaxW5QYU=; s=shan; b=QXd8h2UbBO7fIPz/Iy3wNwbVU6dih6ozokPXqAvI6p9iG5SqFahyTXwqZeltC4az3Sjay7Vx+b5e 1s2rQuhT4SKD47gJYs4kw0JgV2WLanF3oR1hWD0tL0vuDeUgH6kr Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Tony Hansen MIME-Version: 1.0 To: dkim-test@altn.org, sa-test@sendmail.net, autorespond+dkim@dk.elandsys.com Subject: this is a test message minimum.ietf-01.sha256-relaxed Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Message-Id: <20060503190632.02F12E15D8@mx2.messiah.edu> The quick brown fox jumped over the lazy dog. This body has been altered. Mail-DKIM-0.40/t/corpus/ignore_3.txt0000644030404400001440000000143412055417140016120 0ustar jlongusersDKIM-Signature: v=0.5; a=dsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns; s=selector1; bh=rYdLRBGGXK4PCDh+3AbwGuV2OEU=; b=NjJBlNqLnQcz8zwF899l4JHO0blD3rRSlR3mPiOLa137fG5RSKOHeYle0U5sBcRNwIUQub+kpXhi9snZqf0zbRfO+TBaZ8dfdVRzX0d4n0PoQCX453fAAnmZcsgD3jevR+R0J/oa1Ao1A0Oj5X7mwut35QcwjIi42bDIjsFP9xk= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> Ignore this signature; it has an unsupported key algorithm. Mail-DKIM-0.40/t/corpus/badkey_7.txt0000644030404400001440000000145212055417140016100 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns/txt; s=testbad7; bh=rYdLRBGGXK4PCDh+3AbwGuV2OEU=; b=fTmnR2WeabrW0qAlIDm25QOI8pEVfKayiu56bNQeIg7Qbcg7SEeq9Y71MU5WK11DWUwg2+nruwq+y22VQq/Wtw+014kTkfT073wMlTBjeZqNl63T+whlwgUAIjgR6XuR+BP6bc1/nRBbVB7Wjfctj5yNUgGCZqxVEh07wBtgFg0= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (badkey_7) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_rfc4871_4.txt0000644030404400001440000000103212055417140016736 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to: subject:date:message-id; q=dns/txt; s=test1; i=JLong@Messiah.Edu; bh= a1tsxn+Nkk390KsMynntsb/bycM=; b=CZ+EhwbclRQIvdhanykJLhkpMANaS/+c GTEQ/0Ziy6t4SqM9TIeRv3qfzm+4TRNxvfiEzfr5KcTyVOnIVuQW3A== From: Jason Long To: Nobody Subject: dkim test - case-differing domain name Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_dk_7.txt0000644030404400001440000000074412055417140016252 0ustar jlongusersDomainKey-Signature: a=rsa-sha1; c=simple; d=messiah.edu; h=from:to:subject: date:message-id; q=dns; s=test1; b=rpgP51ZIZ69J8F1XOImeXrROOXPZj NNkJ2GcB0wxm43/Uq0E1Mg6jyEFr/S0Y6gvDrhnjJAAxM4RQ76xPC2Odw== From: Jason Long To: Nobody Subject: domainkeys test - case-different domain names Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/ignore_4.txt0000644030404400001440000000145212055417140016121 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-sha1; c=future; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns; s=selector1; bh=rYdLRBGGXK4PCDh+3AbwGuV2OEU=; b=NjJBlNqLnQcz8zwF899l4JHO0blD3rRSlR3mPiOLa137fG5RSKOHeYle0U5sBcRNwIUQub+kpXhi9snZqf0zbRfO+TBaZ8dfdVRzX0d4n0PoQCX453fAAnmZcsgD3jevR+R0J/oa1Ao1A0Oj5X7mwut35QcwjIi42bDIjsFP9xk= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> Ignore this signature; it has an unsupported canonicalization method. Mail-DKIM-0.40/t/corpus/badkey_1.txt0000644030404400001440000000145512055417140016075 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns/txt; s=nonexistent; bh=rYdLRBGGXK4PCDh+3AbwGuV2OEU=; b=fTmnR2WeabrW0qAlIDm25QOI8pEVfKayiu56bNQeIg7Qbcg7SEeq9Y71MU5WK11DWUwg2+nruwq+y22VQq/Wtw+014kTkfT073wMlTBjeZqNl63T+whlwgUAIjgR6XuR+BP6bc1/nRBbVB7Wjfctj5yNUgGCZqxVEh07wBtgFg0= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (badkey_1) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/ignore_2.txt0000644030404400001440000000144112055417140016115 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-md5; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns; s=selector1; bh=rYdLRBGGXK4PCDh+3AbwGuV2OEU=; b=NjJBlNqLnQcz8zwF899l4JHO0blD3rRSlR3mPiOLa137fG5RSKOHeYle0U5sBcRNwIUQub+kpXhi9snZqf0zbRfO+TBaZ8dfdVRzX0d4n0PoQCX453fAAnmZcsgD3jevR+R0J/oa1Ao1A0Oj5X7mwut35QcwjIi42bDIjsFP9xk= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> Ignore this signature; it has an unsupported digesting algorithm. Mail-DKIM-0.40/t/corpus/badkey_11.txt0000644030404400001440000000140312055417140016147 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha256; c=simple; d=messiah.edu; h=from:to: subject:date:message-id; q=dns/txt; s=test2; bh=yWVxGwA0isgTMZQY PnTyKv2gX8+hj4nSW9BGUYd/EbI=; b=IBgb6pvA1+zfgPfBUPf53yvBlA+czl1f vMVOd31CJ9QvMe2yp4AjoTTJKvZ93gisUV3uigJZryLeJV1SpfMBTw== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (h= requires sha1, not sha256, so it should fail) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/mine_ietf01_2.txt0000644030404400001440000000142612055417140016735 0ustar jlongusersDKIM-Signature: a=rsa-sha1; c=relaxed; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns; s=selector1; bh=N9OQp3Fydw5h5SJ1UDyZrgBPEDU=; b=1yiqw+Fz+aI1abGvAB6GR3UmMtgqiRRjWBk5CKXZKDHM4jTjMO0rY1dg9g+kFfbtotOEtgB0J3UtJzJyyLI1+LqeQsjTDEGprzXS21dqhkQnr/l/eZEP5/uqFm8YRrfarbWHkHj8oCsuE1/xi2rfCwyWkw1jO8N2/SzMkVQC/9M= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/multiple_2.txt0000644030404400001440000000242512055417140016470 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=foobar; d=messiah.edu; h=date:from:subject; q=dns/txt; s=selector1; bh=q6DWKdHUzNbVPt6YBbD1KOai/b8=; b=keocS8z7y+utmOuSEa9Q3jpty3bD7ggJgVzh1As9IxLe1xdsLg6tsTgbSntF1Eo2OBwR3EEb1IfTnJ6YvranPVoau5xVF5ydSBjk65HDK/vl1jNrNR4JSkyxxsSFF1npfPZTA9pCEMskdU7mUPqHzC/AxOtm/npzviD83+VUPfA= DKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=date:from:subject; q=dns/txt; s=selector1; bh=q6DWKdHUzNbVPt6YBbD1KOai/b8=; b=keocS8z7y+utmOuSEa9Q3jpty3bD7ggJgVzh1As9IxLe1xdsLg6tsTgbSntF1Eo2OBwR3EEb1IfTnJ6YvranPVoau5xVF5ydSBjk65HDK/vl1jNrNR4JSkyxxsSFF1npfPZTA9pCEMskdU7mUPqHzC/AxOtm/npzviD83+VUPfA= DKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=date:from:subject; q=dns/txt; s=selector1; bh=q6DWKdHUzNbVPt6YBbD1KOai/b8=; b=shouldfailutmOuSEa9Q3jpty3bD7ggJgVzh1As9IxLe1xdsLg6tsTgbSntF1Eo2OBwR3EEb1IfTnJ6YvranPVoau5xVF5ydSBjk65HDK/vl1jNrNR4JSkyxxsSFF1npfPZTA9pCEMskdU7mUPqHzC/AxOtm/npzviD83+VUPfA= DKIM-Signature: synerr Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Jason Long Subject: dkim test (multiple_2) The quick brown fox jumped over the lazy dog. The first signature should be invalid (i.e. bad canonicalization) The second signature should pass The third signature should fail The fourth signature is unparseable. Mail-DKIM-0.40/t/corpus/ignore_5.txt0000644030404400001440000000127012055417140016120 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to:subject:date:message-id; q=http; s=test1; bh=gG+cRFxnpu7ApVgxzeYxYHywjBA=; b=SqBRGTdPJyZZUbgRrzZlOxXeHvFXmUvPTLyR7FqkFfezM7SH1YMoFqmL1a5Yteej8CzNBJifhmJ9a3SoyuLn8w== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (ignore_5) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> Ignore this signature, the query method is unsupported. Mail-DKIM-0.40/t/corpus/good_ietf00_2.txt0000644030404400001440000000253312055417140016734 0ustar jlongusersReturn-Path: X-Original-To: test@dkimtest.jason.long.name Delivered-To: dkimtest@mx2.messiah.edu Received: from voicemail.cis.att.net (unknown [12.34.200.188]) by mx2.messiah.edu (Postfix) with ESMTP id 5C2EB733A7 for ; Fri, 3 Mar 2006 10:39:09 -0500 (EST) Date: Fri, 3 Mar 2006 15:34:38 +0000 (GMT) X-Comment: Sending client does not conform to RFC822 minimum requirements X-Comment: Date has been added by Maillennium Received: from (localhost[127.0.0.1]) by voicemail.cis.att.net (vm2) with SMTP id <2006030315343818800sp98ne>; Fri, 3 Mar 2006 15:34:38 +0000 DKIM-Signature: a=rsa-sha1; c=relaxed/relaxed; d=vmt2.cis.att.net; t=1141353706; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; s=foo; b=MAI8z5Tnc7kzh4ffqKgfbGtbRQtqSO51sYw0FHWDX62GELvDNqHRaJ+y/Std+qDATrcHXkBKqj2N 98d6i6KkM07pavFVlD3WOa83SeRk5Izma32WncKyG3nfFX4lYUwz Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Tony Hansen MIME-Version: 1.0 To: dkim-test@altn.org, sa-test@sendmail.net, autorespond+dkim@dk.elandsys.com Subject: prefixed blanks test message relaxed/relaxed Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Message-Id: <20060303153909.5C2EB733A7@mx2.messiah.edu> The quick brown fox jumped over the lazy dog. Mail-DKIM-0.40/t/corpus/badkey_10.txt0000644030404400001440000000136712055417140016157 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to:subject:date:message-id; q=dns/txt; s=test5; i=jlong@subdomain.messiah.edu; bh=a1tsxn+Nkk390KsMynntsb/bycM=; b=OJLajmX/ndyDwjAAC6e1fElkVLoKBJivp5cFoOT2UrW2Pqs5zRZLjsE6+QkxcnBySL9g4l2/EYVMidhg1iXo9g== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (i= using subdomain, should fail) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/badkey_6.txt0000644030404400001440000000145212055417140016077 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns/txt; s=testbad4; bh=rYdLRBGGXK4PCDh+3AbwGuV2OEU=; b=fTmnR2WeabrW0qAlIDm25QOI8pEVfKayiu56bNQeIg7Qbcg7SEeq9Y71MU5WK11DWUwg2+nruwq+y22VQq/Wtw+014kTkfT073wMlTBjeZqNl63T+whlwgUAIjgR6XuR+BP6bc1/nRBbVB7Wjfctj5yNUgGCZqxVEh07wBtgFg0= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (badkey_6) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_dk_yahoo.txt0000644030404400001440000000446212055417140017224 0ustar jlongusersReturn-Path: X-Original-To: dktest@mx2.messiah.edu Delivered-To: dktest@mx2.messiah.edu Received: from mx2.messiah.edu (localhost [127.0.0.1]) by mx2.messiah.edu (Postfix) with ESMTP id 4D4312A7FA9 for ; Tue, 10 Oct 2006 15:30:29 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 3.1.5 (2006-08-29) on mymail1a.messiah.edu X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=HTML_MESSAGE autolearn=ham version=3.1.5 Received: from web34101.mail.mud.yahoo.com (web34101.mail.mud.yahoo.com [66.163.178.99]) by mx2.messiah.edu (Postfix) with SMTP id AAFAB2A9535 for ; Tue, 10 Oct 2006 15:30:28 -0400 (EDT) Received: (qmail 29991 invoked by uid 60001); 10 Oct 2006 19:30:27 -0000 DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=s1024; d=yahoo.com; h=Message-ID:Received:Date:From:Subject:To:MIME-Version:Content-Type:Content-Transfer-Encoding; b=AEpft01eHCTIX08AdBaACL0/1Qs+/xDO8CuOJI+MJWJyJX4elu9ciIUDee6SrnF0+RmDAYDKKfU7ImKhLZMJ/iB+aYC4d2HMp0YOUQfHyPUIc5IyurGpp9Rz7AY+9X+kj2eC3w7NrzRldLmnbrWRy+/XOpjF550uq/nwbMhmxLU= ; Message-ID: <20061010193027.29989.qmail@web34101.mail.mud.yahoo.com> Received: from [153.42.34.240] by web34101.mail.mud.yahoo.com via HTTP; Tue, 10 Oct 2006 12:30:27 PDT Date: Tue, 10 Oct 2006 12:30:27 -0700 (PDT) From: Jason Long Subject: test from Yahoo To: test@dktest.jason.long.name MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="0-237517700-1160508627=:29354" Content-Transfer-Encoding: 8bit --0-237517700-1160508627=:29354 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit This is testing DomainKeys from Yahoo. --------------------------------- Get your own web address for just $1.99/1st yr. We'll help. Yahoo! Small Business. --0-237517700-1160508627=:29354 Content-Type: text/html; charset=iso-8859-1 Content-Transfer-Encoding: 8bit This is testing DomainKeys from Yahoo.


Get your own web address for just $1.99/1st yr. We'll help. Yahoo! Small Business. --0-237517700-1160508627=:29354-- Mail-DKIM-0.40/t/corpus/good_dk_4.txt0000644030404400001440000000136112055417140016243 0ustar jlongusersDomainKey-Signature: a=rsa-sha1; c=nofws; d=messiah.edu; h=from:sender:to: subject:date:message-id; q=dns; s=test3; b=H1Q37m3r5/SkjZa3Mik97 kZ28YjnE3u0h/ANUr/WnVNqnCoGS1BtY6a2soWtgaiv8pj70HR1BWh0B1GBjTvS1 g== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: George Sender: Jason Long To: Nobody Subject: domainkeys test (key has g= requirement, should pass) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_dk_2.txt0000644030404400001440000000302312055417140016236 0ustar jlongusersDomainKey-Signature: c=nofws; s=s1024; d=yahoo.com; h=Message-ID:Received:Date:From:Subject:To:MIME-Version:Content-Type:Content-Transfer-Encoding; b=AEpft01eHCTIX08AdBaACL0/1Qs+/xDO8CuOJI+MJWJyJX4elu9ciIUDee6SrnF0+RmDAYDKKfU7ImKhLZMJ/iB+aYC4d2HMp0YOUQfHyPUIc5IyurGpp9Rz7AY+9X+kj2eC3w7NrzRldLmnbrWRy+/XOpjF550uq/nwbMhmxLU= ; Message-ID: <20061010193027.29989.qmail@web34101.mail.mud.yahoo.com> Received: from [153.42.34.240] by web34101.mail.mud.yahoo.com via HTTP; Tue, 10 Oct 2006 12:30:27 PDT Date: Tue, 10 Oct 2006 12:30:27 -0700 (PDT) From: Jason Long Subject: test from Yahoo To: test@dktest.jason.long.name MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="0-237517700-1160508627=:29354" Content-Transfer-Encoding: 8bit --0-237517700-1160508627=:29354 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit This is testing DomainKeys from Yahoo. --------------------------------- Get your own web address for just $1.99/1st yr. We'll help. Yahoo! Small Business. --0-237517700-1160508627=:29354 Content-Type: text/html; charset=iso-8859-1 Content-Transfer-Encoding: 8bit This is testing DomainKeys from Yahoo.


Get your own web address for just $1.99/1st yr. We'll help. Yahoo! Small Business. --0-237517700-1160508627=:29354-- Mail-DKIM-0.40/t/corpus/mine_ietf01_4.txt0000644030404400001440000000145012055417140016734 0ustar jlongusersDKIM-Signature: a=rsa-sha256; c=relaxed; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns; s=selector1; bh=wFR5pSNOmhbMZz1RLNBFllRfc72sMxK5gTMq/iO1mec=; b=P74upWoriEJAAWayBhBb+I2uYuF328LBQ1vvzIDmufKrBPed1Jdpz0pmGTOe7TOdOCs75mMZ/vHDI24d07u8HS2CAT6DNVcxGGMdTB1cdOU8zPZA0dMkf8E8cz/d1BP7TgIINvS9CdB13XeddvYuiw3jATCoMFxLxJmQ7E44Tgg= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_rfc4871_3.txt0000644030404400001440000000132512055417140016742 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to:subject:date:message-id; q=dns/txt; s=test1; foo=bar; bh=a1tsxn+Nkk390KsMynntsb/bycM=; b=U0zAE8NPDILfQP9A5fpD35v5SVa/9e+Vrh7hDJpFzram1eVYZTkJmNqHdSF5nBPaCcRt6XBWR9InJkSl/UttZQ== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (sig. has extra tags) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_dk_gmail.txt0000644030404400001440000000415712055417140017177 0ustar jlongusersReturn-Path: X-Original-To: dktest@mx2.messiah.edu Delivered-To: dktest@mx2.messiah.edu Received: from mx2.messiah.edu (localhost [127.0.0.1]) by mx2.messiah.edu (Postfix) with ESMTP id 05B98272FBA for ; Tue, 10 Oct 2006 15:27:15 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 3.1.5 (2006-08-29) on mymail1a.messiah.edu X-Spam-Level: * X-Spam-Status: No, score=1.6 required=5.0 tests=HTML_10_20,HTML_MESSAGE, HTML_SHORT_LENGTH autolearn=no version=3.1.5 Received: from ug-out-1314.google.com (ug-out-1314.google.com [66.249.92.174]) by mx2.messiah.edu (Postfix) with ESMTP id 79F442ADB4D for ; Tue, 10 Oct 2006 15:27:14 -0400 (EDT) Received: by ug-out-1314.google.com with SMTP id 29so952130ugc for ; Tue, 10 Oct 2006 12:27:13 -0700 (PDT) DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=beta; d=gmail.com; h=received:message-id:date:from:to:subject:mime-version:content-type; b=JOYJLw6miPAjUXx+gIQm3NsWuFInq05TuJppyoxqTopYPe3bSKzRBPLw5X+OMO36re/FDEfZnOjJ4cNYvVPuld6JEikNjk2RK98unQvcdkgBPZODuE7g/vXgLZo005nQkwvfd+4dbpHiINsOTT/0ASxQ+65OcgBu0CRR/DO7z/M= Received: by 10.66.240.12 with SMTP id n12mr8614290ugh; Tue, 10 Oct 2006 12:27:12 -0700 (PDT) Received: by 10.67.96.17 with HTTP; Tue, 10 Oct 2006 12:27:12 -0700 (PDT) Message-ID: Date: Tue, 10 Oct 2006 15:27:12 -0400 From: "Jason Long" To: jlong@messiah.edu Subject: Test from gmail MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_Part_20675_4029655.1160508432731" ------=_Part_20675_4029655.1160508432731 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-Disposition: inline This message sent from Gmail. ------=_Part_20675_4029655.1160508432731 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline This message sent from Gmail.
------=_Part_20675_4029655.1160508432731-- Mail-DKIM-0.40/t/corpus/badkey_3.txt0000644030404400001440000000145212055417140016074 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns/txt; s=testbad1; bh=rYdLRBGGXK4PCDh+3AbwGuV2OEU=; b=fTmnR2WeabrW0qAlIDm25QOI8pEVfKayiu56bNQeIg7Qbcg7SEeq9Y71MU5WK11DWUwg2+nruwq+y22VQq/Wtw+014kTkfT073wMlTBjeZqNl63T+whlwgUAIjgR6XuR+BP6bc1/nRBbVB7Wjfctj5yNUgGCZqxVEh07wBtgFg0= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (badkey_3) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/mine_ietf05_1.txt0000644030404400001440000000150712055417140016740 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns/txt; s=selector1; bh=rYdLRBGGXK4PCDh+3AbwGuV2OEU=; b=fTmnR2WeabrW0qAlIDm25QOI8pEVfKayiu56bNQeIg7Qbcg7SEeq9Y71MU5WK11DWUwg2+nruwq+y22VQq/Wtw+014kTkfT073wMlTBjeZqNl63T+whlwgUAIjgR6XuR+BP6bc1/nRBbVB7Wjfctj5yNUgGCZqxVEh07wBtgFg0= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here This message has a v=0.5 signature. Blah blah blah Mail-DKIM-0.40/t/corpus/bad_dk_3.txt0000644030404400001440000000064712055417140016046 0ustar jlongusersDomainKey-Signature: a=rsa-sha1; c=simple; d=messiah.edu; s=test1; b=DmkekrvBPH2D/VSZU9vSOk/xWrllr QUy9KbJf/0IiMiILDXUIdQsf0J3DgGu3I/mez54kh/1bUA9r7b0plE2CA== From: Jason Long To: Nobody Subject: domainkeys test - no h= tag Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/no_body_2.txt0000644030404400001440000000136112055417140016264 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns/txt; s=selector1; bh=uoq1oCgLlTqpdDX/iUbLy7J1Wic=; b=akH0wLCX2J61xzcSfd8vo9wH9BYTddfmyUQPxIwZTVLjCwCyUtE88owD4zKDa8RZ9lqbkpa0z+oRiTvTT/defySlWs9fT3HIcQ+GcrhV2DF7+4BY+VHf9Qk0ML4ajwq3s+r8zfM/+hv2D1K/BuNgf+AIKyC7axz0b4CsfdBSZI4= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test - empty body Date: Wed, 22 Feb 2007 09:11:00 -0500 Message-ID: <20070222091100@test.messiah.edu> Mail-DKIM-0.40/t/corpus/bad_1.txt0000644030404400001440000000234012055417140015356 0ustar jlongusersReturn-Path: X-Original-To: test@dkimtest.jason.long.name Delivered-To: dkimtest@mx2.messiah.edu Received: from voicemail.cis.att.net (unknown [12.34.200.188]) by mx2.messiah.edu (Postfix) with ESMTP id 02F12E15D8 for ; Wed, 3 May 2006 15:06:32 -0400 (EDT) Received: from (localhost[127.0.0.1]) by voicemail.cis.att.net (vm2) with SMTP id <2006050319071918800spa0re>; Wed, 3 May 2006 19:07:19 +0000 DKIM-Signature: a=rsa-sha256; c=relaxed; d=vmt2.cis.att.net; t=1146680862; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; bh=HryPFX2R6r7JPsX1Z7+yReZddQR2PjvCvdXgaxW5QYU=; s=shan; b=QXd8h2UbBO7fIPzVU6dih6ozokPXqAvI6p9iG5SqFahyTXwqZeltC4az3Sjay7Vx+b5e 1s2rQuhT4SKD47gJYs4kw0JgV2WLanF3oR1hWD0tL0vuDeUgH6kr Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Tony Hansen MIME-Version: 1.0 To: dkim-test@altn.org, sa-test@sendmail.net, autorespond+dkim@dk.elandsys.com Subject: this is a test message minimum.ietf-01.sha256-relaxed Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Message-Id: <20060503190632.02F12E15D8@mx2.messiah.edu> The quick brown fox jumped over the lazy dog. Mail-DKIM-0.40/t/corpus/good_ietf00_4.txt0000644030404400001440000000226712055417140016742 0ustar jlongusersReturn-Path: X-Original-To: test@dkimtest.jason.long.name Delivered-To: dkimtest@mx2.messiah.edu Received: from voicemail.cis.att.net (unknown [12.34.200.188]) by mx2.messiah.edu (Postfix) with ESMTP id 581BB7323C for ; Wed, 1 Mar 2006 16:27:14 -0500 (EST) Received: from (localhost[127.0.0.1]) by voicemail.cis.att.net (vm2) with SMTP id <2006030121275918800sp93ne>; Wed, 1 Mar 2006 21:27:59 +0000 DKIM-Signature: a=rsa-sha1; c=simple/simple; d=vmt2.cis.att.net; t=1141248471; h=DaTe : FrOm : MiMe-vErSiOn : To : SuBjEcT : CoNtEnT-TyPe : CoNtEnT-TrAnSfEr-eNcOdInG; s=foo; b=lvE5sFj5AGe0A9f7px9ZrnTyLoxBdwzePVI8JXT4rlKg2KP1TuoNW/KNjoezhIcnLlchxw8Dp0w4 /0W1Mp/Z9N3spa7EYTAvfL4BxDOPhnT+q5aIfcAXSufReQijhI4M Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Tony Hansen MIME-Version: 1.0 To: dkim-test@altn.org, sa-test@sendmail.net, autorespond+dkim@dk.elandsys.com Subject: test with mixed case h=hdr names simple/simple Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Message-Id: <20060301212714.581BB7323C@mx2.messiah.edu> The quick brown fox jumped over the lazy dog. Mail-DKIM-0.40/t/corpus/good_qp_2.txt0000644030404400001440000000127212055417140016264 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to :subject:date; s=test3; i=jlong@messiah=2Eedu; bh=OW2otvzd7V2TO8 w056SjbYRFCa0=; b=TuQa6fkzR09SXKTV2LOWHmzVawRieFypY3x9ZhXsEul6+H /odHbD71uDwBEFdkWwNh7n/WKWjy7gqyftQ0/ung== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (i= uses quoted-printable) Date: Wed, 9 Apr 2008 09:11:00 -0500 Should pass. This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/goodkey_1.txt0000644030404400001440000000120612055417140016271 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to:subject:date; q=dns/txt; s=test1; bh=N9OQp3Fydw5h5SJ1UDyZrgBPEDU=; b=ZiYNuPr43CrzT9wUwgAapNx0NUVrBAV3nXj8AlOTkCh3OqVaR13LVxlUKA9hbznugXETCKSENDr4hGBPvuMwcA== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (goodkey_1) Date: Wed, 7 Dec 2005 09:11:00 -0500 This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/ignore_6.txt0000644030404400001440000000131712055417140016123 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to:subject:date:message-id; q=dns/special; s=test1; bh=d+8ftYRNRA3Rl7UsO0gP3zT0XE0=; b=C5L1RpN/Nwer3JHq6dcDIilbAfNMynbCdPyGi1fGSsIIuSzBM2mmR3E9p13XqPeoz4rkeOP9PrwUTn3lpdH2uw== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (ignore_6) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> Ignore this signature, it has an option other than txt for dns query. Mail-DKIM-0.40/t/corpus/bad_ietf01_3.txt0000644030404400001440000000235012055417140016531 0ustar jlongusersReturn-Path: X-Original-To: test@dkimtest.jason.long.name Delivered-To: dkimtest@mx2.messiah.edu Received: from voicemail.cis.att.net (unknown [12.34.200.188]) by mx2.messiah.edu (Postfix) with ESMTP id 02F12E15D8 for ; Wed, 3 May 2006 15:06:32 -0400 (EDT) Received: from (localhost[127.0.0.1]) by voicemail.cis.att.net (vm2) with SMTP id <2006050319071918800spa0re>; Wed, 3 May 2006 19:07:19 +0000 DKIM-Signature: a=rsa-sha256; c=relaxed; d=vmt2.cis.att.net; t=1146680862; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; bh=HryPFX2R6r7JPsX1Z7+yReZddQR2PjvCvdXgaxW5QYU=; s=shan; b=QXd8h2UbBO7fIPz/I+this+signature+has+been+altered+hyTXwqZeltC4az3Sjay7Vx+b5e 1s2rQuhT4SKD47gJYs4kw0JgV2WLanF3oR1hWD0tL0vuDeUgH6kr Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Tony Hansen MIME-Version: 1.0 To: dkim-test@altn.org, sa-test@sendmail.net, autorespond+dkim@dk.elandsys.com Subject: this is a test message minimum.ietf-01.sha256-relaxed Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Message-Id: <20060503190632.02F12E15D8@mx2.messiah.edu> The quick brown fox jumped over the lazy dog. Mail-DKIM-0.40/t/corpus/mine_ietf01_3.txt0000644030404400001440000000144712055417140016741 0ustar jlongusersDKIM-Signature: a=rsa-sha256; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns; s=selector1; bh=wFR5pSNOmhbMZz1RLNBFllRfc72sMxK5gTMq/iO1mec=; b=DeszbIPSQpibaxLRk4BoOmdRVulYKZCtHLVIQ0gVMJ0WGz9AQgyc7Oi7l8cwYFK7lGmpCAaie8GcbX9IZrJI4xCcinMImVcmPafM4GtJI1V5mWb+f3lltvzowyMU4TQPzy/IiABKmf5OHW0ydrMEbS0brKX6Lc4s9qerpKwYd1M= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/badkey_4.txt0000644030404400001440000000145212055417140016075 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns/txt; s=testbad2; bh=rYdLRBGGXK4PCDh+3AbwGuV2OEU=; b=fTmnR2WeabrW0qAlIDm25QOI8pEVfKayiu56bNQeIg7Qbcg7SEeq9Y71MU5WK11DWUwg2+nruwq+y22VQq/Wtw+014kTkfT073wMlTBjeZqNl63T+whlwgUAIjgR6XuR+BP6bc1/nRBbVB7Wjfctj5yNUgGCZqxVEh07wBtgFg0= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (badkey_4) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_dk_5.txt0000644030404400001440000000135312055417140016245 0ustar jlongusersDomainKey-Signature: a=rsa-sha1; c=nofws; d=messiah.edu; h=from:sender:to: subject:date:message-id; q=dns; s=test4; b=i5J01ofFaB5XDFC9+O3nb mcThOO+QW75bwDxNWVt+PsZOuTqB8qu1v9wu9mC6VF3I56lpwQIQEgZoecCQT4eb Q== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: George Sender: Jason Long To: Nobody Subject: domainkeys test (key has empty g=, should pass) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_ietf01_1.txt0000644030404400001440000000235012055417140016731 0ustar jlongusersReturn-Path: X-Original-To: test@dkimtest.jason.long.name Delivered-To: dkimtest@mx2.messiah.edu Received: from voicemail.cis.att.net (unknown [12.34.200.188]) by mx2.messiah.edu (Postfix) with ESMTP id 02F12E15D8 for ; Wed, 3 May 2006 15:06:32 -0400 (EDT) Received: from (localhost[127.0.0.1]) by voicemail.cis.att.net (vm2) with SMTP id <2006050319071918800spa0re>; Wed, 3 May 2006 19:07:19 +0000 DKIM-Signature: a=rsa-sha256; c=relaxed; d=vmt2.cis.att.net; t=1146680862; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; bh=HryPFX2R6r7JPsX1Z7+yReZddQR2PjvCvdXgaxW5QYU=; s=shan; b=QXd8h2UbBO7fIPz/Iy3wNwbVU6dih6ozokPXqAvI6p9iG5SqFahyTXwqZeltC4az3Sjay7Vx+b5e 1s2rQuhT4SKD47gJYs4kw0JgV2WLanF3oR1hWD0tL0vuDeUgH6kr Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Tony Hansen MIME-Version: 1.0 To: dkim-test@altn.org, sa-test@sendmail.net, autorespond+dkim@dk.elandsys.com Subject: this is a test message minimum.ietf-01.sha256-relaxed Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Message-Id: <20060503190632.02F12E15D8@mx2.messiah.edu> The quick brown fox jumped over the lazy dog. Mail-DKIM-0.40/t/corpus/good_ietf00_1.txt0000644030404400001440000000253112055417140016731 0ustar jlongusersReturn-Path: X-Original-To: test@dkimtest.jason.long.name Delivered-To: dkimtest@mx2.messiah.edu Received: from voicemail.cis.att.net (unknown [12.34.200.188]) by mx2.messiah.edu (Postfix) with ESMTP id AF5B673483 for ; Fri, 3 Mar 2006 10:39:09 -0500 (EST) Date: Fri, 3 Mar 2006 15:34:41 +0000 (GMT) X-Comment: Sending client does not conform to RFC822 minimum requirements X-Comment: Date has been added by Maillennium Received: from (localhost[127.0.0.1]) by voicemail.cis.att.net (vm2) with SMTP id <2006030315344118800sp98oe>; Fri, 3 Mar 2006 15:34:41 +0000 DKIM-Signature: a=rsa-sha1; c=relaxed/simple; d=vmt2.cis.att.net; t=1141353705; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; s=foo; b=WECs6qf6NR7HdlerchZEyOXRiyvYZgeoZyIWvaL4A6wtz9R8xCVV9oKrjMO7/l9QYBju/UPlGiZK guscixD2rQvw+zGxkdsFsarocO+aSXLPU3O1HYLWRH98PPpr5LEP Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Tony Hansen MIME-Version: 1.0 To: dkim-test@altn.org, sa-test@sendmail.net, autorespond+dkim@dk.elandsys.com Subject: prefixed blanks test message relaxed/simple Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Message-Id: <20060303153909.AF5B673483@mx2.messiah.edu> The quick brown fox jumped over the lazy dog. Mail-DKIM-0.40/t/corpus/bad_dk_5.txt0000644030404400001440000000066012055417140016043 0ustar jlongusersDomainKey-Signature: a=rsa-sha1; c=simple; d=messiah.edu; q=bogus; s=test1; b=DmkekrvBPH2D/VSZU9vSOk/xWrllr QUy9KbJf/0IiMiILDXUIdQsf0J3DgGu3I/mez54kh/1bUA9r7b0plE2CA== From: Jason Long To: Nobody Subject: domainkeys test - no h= tag Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_ietf00_3.txt0000644030404400001440000000251312055417140016733 0ustar jlongusersReturn-Path: X-Original-To: test@dkimtest.jason.long.name Delivered-To: dkimtest@mx2.messiah.edu Received: from voicemail.cis.att.net (unknown [12.34.200.188]) by mx2.messiah.edu (Postfix) with ESMTP id D393973174 for ; Fri, 3 Mar 2006 10:39:08 -0500 (EST) Date: Fri, 3 Mar 2006 15:34:36 +0000 (GMT) X-Comment: Sending client does not conform to RFC822 minimum requirements X-Comment: Date has been added by Maillennium Received: from (localhost[127.0.0.1]) by voicemail.cis.att.net (vm2) with SMTP id <2006030315343618800sp98me>; Fri, 3 Mar 2006 15:34:36 +0000 DKIM-Signature: a=rsa-sha1; c=relaxed; d=vmt2.cis.att.net; t=1141353705; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; s=foo; b=Xg6KO55vExLhrTCaJExpU+YOnP6cxxsdPs7TzpmbLWImD/p1nR/VoBZh+mEzpyJmAs6m+ETqjo3l J8nvtgJmvMdRF3g5RJa+DnStZVydMOlfRasQDT6wnjLG0vNa72eN Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Tony Hansen MIME-Version: 1.0 To: dkim-test@altn.org, sa-test@sendmail.net, autorespond+dkim@dk.elandsys.com Subject: prefixed blanks test message relaxed Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Message-Id: <20060303153908.D393973174@mx2.messiah.edu> The quick brown fox jumped over the lazy dog. Mail-DKIM-0.40/t/corpus/badkey_8.txt0000644030404400001440000000132712055417140016102 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to:subject:date:message-id; q=dns/txt; s=testbad8; i=jlong@messiah.edu; bh=N9OQp3Fydw5h5SJ1UDyZrgBPEDU=; b=A+2Cc4OXC/PhT/5zcWDPKh6JSidy2TC+7Ejg4vgXpIvsFU6nXFPxpT2jEpXuBvwcpXI7tAcFOFwwIwOwxIywuA== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (badkey_8) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/bad_dk_1.txt0000644030404400001440000000130412055417140016033 0ustar jlongusersDomainKey-Signature: a=rsa-sha1; c=nofws; d=messiah.edu; h=from:to:subject: date:message-id; q=dns; s=test1; b=RKd+t+/rSjCZ6vuvpXaSK/kH6NtFd eyCfC0jXN2LigEU7RVb5iW33aW8UN6gEKmSAmfd4/8xIFVtOq+fAQg4gw== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: George To: Nobody Subject: domainkeys test (should fail since sender domain is wrong) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/goodkey_4.txt0000644030404400001440000000136612055417140016303 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to: subject:date:message-id; q=dns/txt; s=test1; i=jlong@messiah.edu; bh= a1tsxn+Nkk390KsMynntsb/bycM=; b=FE5JP1m+QoWKRybcAWo/Uae/WcbDkMFh 0JQYRN0PdhHqH7CS4izjiIj7t6CXIbF6PD7S5FUbQ5Swt2ffR6F0/Q== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (i= using subdomain, should pass) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/goodkey_3.txt0000644030404400001440000000123312055417140016273 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to:subject:date; q=dns/txt; s=test3; i=jlong@messiah.edu; bh=N9OQp3Fydw5h5SJ1UDyZrgBPEDU=; b=RwH23zxIJNWSjpzNY3SnADAsHf4sjDqxPwcDC+uEUsnlqjeKG9cS3aX1ID+d6vVz+uuRjFKh0wxvOFeQVF4wcA== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (goodkey_3) Date: Wed, 7 Dec 2005 09:11:00 -0500 This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/ignore_7.txt0000644030404400001440000000142112055417140016120 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to:subject:date:message-id; q=dns/txt; s=selector1; x=1193229946; bh=ige3XiTAsWMkmHv+s6ZBSIbLNNc=; b=mRpAeHLMjhsluDZZpF+snljReZUX/MSFv9XShzGVo4wryMSvc5QEh/YtYwWmvm1gEwTgXbmhMgTmQuIIOggcQwlPurJSkz8Abx4HaHlhtFBIzAhc/R5Dfvh+Ni/tCPCqkOMiyE7yQjXBraxgZ9DpyFGf8JYZLDu1o6pz7hqeZcE= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (ignore_7) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> Ignore this signature, it is expired. Mail-DKIM-0.40/t/corpus/badkey_9.txt0000644030404400001440000000131412055417140016077 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to:subject:date:message-id; q=dns/txt; s=test4; bh=a1tsxn+Nkk390KsMynntsb/bycM=; b=h2JxFpS6tGwIhc80mS5fuzRwlIr3jdycXDAuFtUOvH4AdbfWtoixEvLpHl8yhqlbXJJWmlyqJLmWkULjE3enOA== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (empty i= local part) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_dk_1.txt0000644030404400001440000000304612055417140016242 0ustar jlongusersDomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=s1024; d=yahoo.com; h=Message-ID:Received:Date:From:Subject:To:MIME-Version:Content-Type:Content-Transfer-Encoding; b=AEpft01eHCTIX08AdBaACL0/1Qs+/xDO8CuOJI+MJWJyJX4elu9ciIUDee6SrnF0+RmDAYDKKfU7ImKhLZMJ/iB+aYC4d2HMp0YOUQfHyPUIc5IyurGpp9Rz7AY+9X+kj2eC3w7NrzRldLmnbrWRy+/XOpjF550uq/nwbMhmxLU= ; Message-ID: <20061010193027.29989.qmail@web34101.mail.mud.yahoo.com> Received: from [153.42.34.240] by web34101.mail.mud.yahoo.com via HTTP; Tue, 10 Oct 2006 12:30:27 PDT Date: Tue, 10 Oct 2006 12:30:27 -0700 (PDT) From: Jason Long Subject: test from Yahoo To: test@dktest.jason.long.name MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="0-237517700-1160508627=:29354" Content-Transfer-Encoding: 8bit --0-237517700-1160508627=:29354 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit This is testing DomainKeys from Yahoo. --------------------------------- Get your own web address for just $1.99/1st yr. We'll help. Yahoo! Small Business. --0-237517700-1160508627=:29354 Content-Type: text/html; charset=iso-8859-1 Content-Transfer-Encoding: 8bit This is testing DomainKeys from Yahoo.


Get your own web address for just $1.99/1st yr. We'll help. Yahoo! Small Business. --0-237517700-1160508627=:29354-- Mail-DKIM-0.40/t/corpus/badkey_14.txt0000644030404400001440000000133112055417140016152 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=blackhole.messiah.edu; h=from:to :subject:date; s=test3; bh=F84VyLrUbda4ryQ+R8 +e+8UgVxM=; b=g4rCx46HdrfJu2tIlNpNsBW5IDJZMbMOaxPYzZ3qOfxm7lw0VN nzjx3Or5SlaeKSlhGqQwDmCqHzZ9caarICdQ== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (dns failure) Date: Wed, 9 Apr 2008 09:11:00 -0500 Should cause a verification error, the DNS public key cannot be checked. This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/bad_dk_4.txt0000644030404400001440000000065312055417140016044 0ustar jlongusersDomainKey-Signature: a=rsa-sha1; c=simple; d=messiah.edu; q=; s=test1; b=DmkekrvBPH2D/VSZU9vSOk/xWrllr QUy9KbJf/0IiMiILDXUIdQsf0J3DgGu3I/mez54kh/1bUA9r7b0plE2CA== From: Jason Long To: Nobody Subject: domainkeys test - no h= tag Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/ignore_8.txt0000644030404400001440000000133712055417140016127 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to:subject:date:message-id; q=dns/txt; s=test1; i=jon@example.org; bh=a1tsxn+Nkk390KsMynntsb/bycM=; b=geBkkvsxno7gZuGYSZmebXgM4G2V35hojxZduBbI5gFLBSUIF0D4NIbzrjbZnuoz7wOjZCXueHxtRidSAHanOw== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (i= has wrong domain) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_dk_3.txt0000644030404400001440000000130212055417140016235 0ustar jlongusersDomainKey-Signature: a=rsa-sha1; c=nofws; d=messiah.edu; h=from:to:subject: date:message-id; q=dns; s=test3; b=Y/BiKnnQFSFJs46ZZw1Qh0hovxT/L +Db3izq4PAKhe3BjfhtxSNcqsnTj7QNjX/4duotVj5FWvKifkz3AVFP1A== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: domainkeys test (key has g= requirement, should pass) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/badkey_13.txt0000644030404400001440000000126212055417140016154 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to :subject:date; s=test3; i=foo@messiah.edu; bh=F84VyLrUbda4ryQ+R8 +e+8UgVxM=; b=g4rCx46HdrfJu2tIlNpNsBW5IDJZMbMOaxPYzZ3qOfxm7lw0VN nzjx3Or5SlaeKSlhGqQwDmCqHzZ9caarICdQ== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (g= is compared to i= tag) Date: Wed, 9 Apr 2008 09:11:00 -0500 Should fail. This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/no_body_3.txt0000644030404400001440000000136312055417140016267 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns/txt; s=selector1; bh=uoq1oCgLlTqpdDX/iUbLy7J1Wic=; b=akH0wLCX2J61xzcSfd8vo9wH9BYTddfmyUQPxIwZTVLjCwCyUtE88owD4zKDa8RZ9lqbkpa0z+oRiTvTT/defySlWs9fT3HIcQ+GcrhV2DF7+4BY+VHf9Qk0ML4ajwq3s+r8zfM/+hv2D1K/BuNgf+AIKyC7axz0b4CsfdBSZI4= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test - empty body Date: Wed, 22 Feb 2007 09:11:00 -0500 Message-ID: <20070222091100@test.messiah.edu> Mail-DKIM-0.40/t/corpus/good_ietf01_2.txt0000644030404400001440000000232712055417140016736 0ustar jlongusersReturn-Path: X-Original-To: test@dkimtest.jason.long.name Delivered-To: dkimtest@mx2.messiah.edu Received: from voicemail.cis.att.net (unknown [12.34.200.188]) by mx2.messiah.edu (Postfix) with ESMTP id 2E373ACA56 for ; Wed, 3 May 2006 15:06:33 -0400 (EDT) Received: from (localhost[127.0.0.1]) by voicemail.cis.att.net (vm2) with SMTP id <2006050319072018800spa0te>; Wed, 3 May 2006 19:07:20 +0000 DKIM-Signature: a=rsa-sha1; c=relaxed; d=vmt2.cis.att.net; t=1146680822; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; bh=XsjhzIollWedOVMzExq3LQiemo8=; s=shan; b=dfA9w7PexfOn1CVB53OD5JHnEs5SA9CzqPKewF/P7RK+fm9xE5PxCoQ21RdxjVAjky6RssDnlxP/ UHDW78TkK5jxSYc93xFI0WoFyWkiVQ5cpi/rhwKy8kQRIdovh3HX Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Tony Hansen MIME-Version: 1.0 To: dkim-test@altn.org, sa-test@sendmail.net, autorespond+dkim@dk.elandsys.com Subject: this is a test message minimum.domainkeys.sha1-relaxed Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Message-Id: <20060503190633.2E373ACA56@mx2.messiah.edu> The quick brown fox jumped over the lazy dog. Mail-DKIM-0.40/t/corpus/good_1878523.txt0000644030404400001440000000130212055417140016256 0ustar jlongusersReceived: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long DKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to: subject:date; q=dns/txt; s=test1; bh=NKeLfwHtKnDOE3FQbS1TqhxDYN0 =; b=VFNuRhCNOYPIMewC73aSqICVdmI8N3QgyDBYN0/suweIrjiGF+A6MWIF3Kc GKr3mcfDKsmNoTcaSCP47cB6wAA== To: Nobody Subject: dkim test (signature moved) Date: Wed, 7 Dec 2005 09:11:00 -0500 This tests whether issue #1878523 is fixed. This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/bad_1878954.txt0000644030404400001440000000101112055417140016061 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=relaxed/simple; d=ijs.si; h=from: message-id:date; q=dns/txt; s=jakla2; bh=/edzoYuyn17WXm8KeqcX/R+ khdQ=; b=S7zv7fa8ju7VDq20iD+0OcAE/7k3P1LvYKyIOJxeaEbXh8C06q1+Q4l KUASCgesOs2M/3E7lkJdmRQvnYdX41BxkoCrI31suRpLyykIQYjM/pUoKHruEZaQ 3K5Ud6SVKaI2abyzNFT5Yn3QdjdKAQMfMEFC/MAfdooQml/X6SIo= this message certainly doesn't verify, since I modified it, but in certain versions of Mail::DKIM (e.g. 0.30.1), it crashes with this error: "Can't use an undefined value as an ARRAY reference" Mail-DKIM-0.40/t/corpus/mine_ietf01_1.txt0000644030404400001440000000142512055417140016733 0ustar jlongusersDKIM-Signature: a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns; s=selector1; bh=N9OQp3Fydw5h5SJ1UDyZrgBPEDU=; b=oBuc3TDZNcm5z+Q64oXEz5jEpCaW60qJ8Nm3bN8sjOXdHSRF3RAQoZ9KgBMTJhrxfukvxoSmwn9l/LtgEfuSk0ozMfxdWvroEdVwANv3XeJK7q5ASSq5qtxhpEUMoArRqmos6lCzaCYpS+nWltNFDQN/rbAnjQNcjihikrkLRTI= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/badkey_5.txt0000644030404400001440000000145212055417140016076 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns/txt; s=testbad3; bh=rYdLRBGGXK4PCDh+3AbwGuV2OEU=; b=fTmnR2WeabrW0qAlIDm25QOI8pEVfKayiu56bNQeIg7Qbcg7SEeq9Y71MU5WK11DWUwg2+nruwq+y22VQq/Wtw+014kTkfT073wMlTBjeZqNl63T+whlwgUAIjgR6XuR+BP6bc1/nRBbVB7Wjfctj5yNUgGCZqxVEh07wBtgFg0= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (badkey_5) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/badkey_2.txt0000644030404400001440000000145512055417140016076 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns/txt; s=testrevoked; bh=rYdLRBGGXK4PCDh+3AbwGuV2OEU=; b=fTmnR2WeabrW0qAlIDm25QOI8pEVfKayiu56bNQeIg7Qbcg7SEeq9Y71MU5WK11DWUwg2+nruwq+y22VQq/Wtw+014kTkfT073wMlTBjeZqNl63T+whlwgUAIjgR6XuR+BP6bc1/nRBbVB7Wjfctj5yNUgGCZqxVEh07wBtgFg0= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (badkey_2) Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_qp_1.txt0000644030404400001440000000127212055417140016263 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to :subject:date; s=test3; i==6Along@messiah.edu; bh=OW2otvzd7V2TO8 w056SjbYRFCa0=; b=Vfr9HgUlyVf1ZaRVMV8VJNSDXn7f1j2N/rFM4PPmYIC2GD pSelCRrdA979Buuu/Mmx9FTWoZJBL+s5tafFM8bw== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (i= uses quoted-printable) Date: Wed, 9 Apr 2008 09:11:00 -0500 Should pass. This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/good_ietf00_5.txt0000644030404400001440000000225112055417140016734 0ustar jlongusersReturn-Path: X-Original-To: test@dkimtest.jason.long.name Delivered-To: dkimtest@mx2.messiah.edu Received: from voicemail.cis.att.net (unknown [12.34.200.188]) by mx2.messiah.edu (Postfix) with ESMTP id B3646731A1 for ; Wed, 1 Mar 2006 16:27:13 -0500 (EST) Received: from (localhost[127.0.0.1]) by voicemail.cis.att.net (vm2) with SMTP id <2006030121275918800sp93le>; Wed, 1 Mar 2006 21:27:59 +0000 DKIM-Signature: a=rsa-sha1; c=simple; d=vmt2.cis.att.net; t=1141248470; h=DaTe : FrOm : MiMe-vErSiOn : To : SuBjEcT : CoNtEnT-TyPe : CoNtEnT-TrAnSfEr-eNcOdInG; s=foo; b=N6Jpj0Mneee9OsJhnqZzOAk7gqmVRvgFX8/jV1evzep969M29rdRpsk8LPXKXiSNElYtpUaDfdwE x7QGFVbY9vAouC1uoCWN0E08WgKuwRbFWSOUraXUuzdgh0/j4nxp Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Tony Hansen MIME-Version: 1.0 To: dkim-test@altn.org, sa-test@sendmail.net, autorespond+dkim@dk.elandsys.com Subject: test with mixed case h=hdr names simple Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Message-Id: <20060301212713.B3646731A1@mx2.messiah.edu> The quick brown fox jumped over the lazy dog. Mail-DKIM-0.40/t/corpus/good_qp_3.txt0000644030404400001440000000127212055417140016265 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to :subject:date; s=test3; i=jlong=40messiah.edu; bh=OW2otvzd7V2TO8 w056SjbYRFCa0=; b=DqfCOAEklcd1hgviRVra6RtufRW8JjdJ/h6tQmCOi3ATVD /HakPtw/OqNsMPGBAwUkIYpi1mbt+t09sFj74unw== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (i= uses quoted-printable) Date: Wed, 9 Apr 2008 09:11:00 -0500 Should pass. This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/dk_headers_2.txt0000644030404400001440000000320512055417140016723 0ustar jlongusersReceived: from ug-out-1314.google.com (ug-out-1314.google.com [66.249.92.174]) by mx2.messiah.edu (Postfix) with ESMTP id 79F442ADB4D for ; Tue, 10 Oct 2006 15:27:14 -0400 (EDT) Received: by ug-out-1314.google.com with SMTP id 29so952130ugc for ; Tue, 10 Oct 2006 12:27:13 -0700 (PDT) DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=beta; d=gmail.com; h=foobar:received:message-id:date:from:to:barnone:subject:mime-version:content-type; b=JOYJLw6miPAjUXx+gIQm3NsWuFInq05TuJppyoxqTopYPe3bSKzRBPLw5X+OMO36re/FDEfZnOjJ4cNYvVPuld6JEikNjk2RK98unQvcdkgBPZODuE7g/vXgLZo005nQkwvfd+4dbpHiINsOTT/0ASxQ+65OcgBu0CRR/DO7z/M= Received: by 10.66.240.12 with SMTP id n12mr8614290ugh; Tue, 10 Oct 2006 12:27:12 -0700 (PDT) Received: by 10.67.96.17 with HTTP; Tue, 10 Oct 2006 12:27:12 -0700 (PDT) Date: Tue, 10 Oct 2006 15:27:12 -0400 From: "Jason Long" To: jlong@messiah.edu Subject: Test from gmail Sender: evil doer Message-ID: MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_Part_20675_4029655.1160508432731" ------=_Part_20675_4029655.1160508432731 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-Disposition: inline This message sent from Gmail. ------=_Part_20675_4029655.1160508432731 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline This message sent from Gmail.
------=_Part_20675_4029655.1160508432731-- Mail-DKIM-0.40/t/corpus/dk_multiple_1.txt0000644030404400001440000000361412055417140017146 0ustar jlongusersDomainKey-Signature: a=rsa-sha1; c=simple; d=messiah.edu; h=date:from:to: subject:sender:message-id:mime-version:content-type; q=dns; s= test1; b=LtVZqGbwYWMXJVIEDEMJGKY6CxYOGPxlNVskEWTdOENGSjfuCe+sqrp e4K4pq6gh0J35O3qZZXrNC21g/gezyg== Received: from ug-out-1314.google.com (ug-out-1314.google.com [66.249.92.174]) by mx2.messiah.edu (Postfix) with ESMTP id 79F442ADB4D for ; Tue, 10 Oct 2006 15:27:14 -0400 (EDT) Received: by ug-out-1314.google.com with SMTP id 29so952130ugc for ; Tue, 10 Oct 2006 12:27:13 -0700 (PDT) DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=beta; d=gmail.com; h=foobar:received:message-id:date:from:to:barnone:subject:mime-version:content-type; b=JOYJLw6miPAjUXx+gIQm3NsWuFInq05TuJppyoxqTopYPe3bSKzRBPLw5X+OMO36re/FDEfZnOjJ4cNYvVPuld6JEikNjk2RK98unQvcdkgBPZODuE7g/vXgLZo005nQkwvfd+4dbpHiINsOTT/0ASxQ+65OcgBu0CRR/DO7z/M= Received: by 10.66.240.12 with SMTP id n12mr8614290ugh; Tue, 10 Oct 2006 12:27:12 -0700 (PDT) Received: by 10.67.96.17 with HTTP; Tue, 10 Oct 2006 12:27:12 -0700 (PDT) Date: Tue, 10 Oct 2006 15:27:12 -0400 From: "Jason Long" To: jlong@messiah.edu Subject: Test from gmail Sender: "George" Message-ID: MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_Part_20675_4029655.1160508432731" ------=_Part_20675_4029655.1160508432731 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-Disposition: inline This message sent from Gmail. ------=_Part_20675_4029655.1160508432731 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline This message sent from Gmail.
------=_Part_20675_4029655.1160508432731-- Mail-DKIM-0.40/t/corpus/good_dk_6.txt0000644030404400001440000000065612055417140016253 0ustar jlongusersDomainKey-Signature: a=rsa-sha1; c=simple; d=messiah.edu; q=dns; s=test1; b=DmkekrvBPH2D/VSZU9vSOk/xWrllr QUy9KbJf/0IiMiILDXUIdQsf0J3DgGu3I/mez54kh/1bUA9r7b0plE2CA== From: Jason Long To: Nobody Subject: domainkeys test - no h= tag Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/no_body_1.txt0000644030404400001440000000135712055417140016270 0ustar jlongusersDKIM-Signature: v=0.5; a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns/txt; s=selector1; bh=uoq1oCgLlTqpdDX/iUbLy7J1Wic=; b=akH0wLCX2J61xzcSfd8vo9wH9BYTddfmyUQPxIwZTVLjCwCyUtE88owD4zKDa8RZ9lqbkpa0z+oRiTvTT/defySlWs9fT3HIcQ+GcrhV2DF7+4BY+VHf9Qk0ML4ajwq3s+r8zfM/+hv2D1K/BuNgf+AIKyC7axz0b4CsfdBSZI4= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test - empty body Date: Wed, 22 Feb 2007 09:11:00 -0500 Message-ID: <20070222091100@test.messiah.edu> Mail-DKIM-0.40/t/corpus/goodkey_2.txt0000644030404400001440000000120612055417140016272 0ustar jlongusersDKIM-Signature: v=1; a=rsa-sha1; c=simple; d=messiah.edu; h=from:to:subject:date; q=dns/txt; s=test2; bh=N9OQp3Fydw5h5SJ1UDyZrgBPEDU=; b=sROAwTBt1swNXbyYeo2ZNgjk0fBh8oohypYpT5WxUSK5nOwex4/FQNJ0r+m8Y2L3bez96rFCwd+E0Nq/YLv+yQ== Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 From: Jason Long To: Nobody Subject: dkim test (goodkey_2) Date: Wed, 7 Dec 2005 09:11:00 -0500 This is a test More lines here Blah blah blah Mail-DKIM-0.40/t/corpus/multiple_1.txt0000644030404400001440000000442712055417140016473 0ustar jlongusersReturn-Path: X-Original-To: test@dkimtest.jason.long.name Delivered-To: dkimtest@mx2.messiah.edu Received: from voicemail.cis.att.net (unknown [12.34.200.188]) by mx2.messiah.edu (Postfix) with ESMTP id 02F12E15D8 for ; Wed, 3 May 2006 15:06:32 -0400 (EDT) Received: from (localhost[127.0.0.1]) by voicemail.cis.att.net (vm2) with SMTP id <2006050319071918800spa0re>; Wed, 3 May 2006 19:07:19 +0000 DKIM-Signature: a=rsa-sha256; c=simple; d=vmt2.cis.att.net; t=1146680862; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; bh=HryPFX2R6r7JPsX1Z7+yReZddQR2PjvCvdXgaxW5QYU=; s=shan; b=QXd8h2UbBO7fIPz/Iy3wNwbVU6dih6ozokPXqAvI6p9iG5SqFahyTXwqZeltC4az3Sjay7Vx+b5e 1s2rQuhT4SKD47gJYs4kw0JgV2WLanF3oR1hWD0tL0vuDeUgH6kr DKIM-Signature: a=rsa-sha256; c=relaxed; d=vmt2.cis.att.net; t=1146680862; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; bh=HryPFX2R6r7JPsX1Z7+yReZddQR2PjvCvdXgaxW5QYU=; s=shan; b=QXd8h2UbBO7fIPz/Iy3wNwbVU6dih6ozokPXqAvI6p9iG5SqFahyTXwqZeltC4az3Sjay7Vx+b5e 1s2rQuhT4SKD47gJYs4kw0JgV2WLanF3oR1hWD0tL0vuDeUgH6kr DKIM-Signature: a=rsa-sha256; c=relaxed; d=vmt2.cis.att.net; t=1146680862; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; bh=HryPFX2R6r7JPsX1Z7+QR2PjvCvdXgaxW5QYU=; s=shan; b=QXd8h2UbBO7fIPz/Iy3wNwbVU6dih6ozokPXqAvI6p9iG5SqFahyTXwqZeltC4az3Sjay7Vx+b5e 1s2rQuhT4SKD47gJYs4kw0JgV2WLanF3oR1hWD0tL0vuDeUgH6kr DKIM-Signature: a=rsa-sha256; c=relaxed; d=vmt2.cis.att.net; t=1146680862; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; bh=HryPFX2R6r7JPsX1Z7+yReZddQR2PjvCvdXgaxW5QYU=; s=shan; b=QXd8h2UbBO7fIPzVU6dih6ozokPXqAvI6p9iG5SqFahyTXwqZeltC4az3Sjay7Vx+b5e 1s2rQuhT4SKD47gJYs4kw0JgV2WLanF3oR1hWD0tL0vuDeUgH6kr Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Tony Hansen MIME-Version: 1.0 To: dkim-test@altn.org, sa-test@sendmail.net, autorespond+dkim@dk.elandsys.com Subject: this is a test message minimum.ietf-01.sha256-relaxed Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Message-Id: <20060503190632.02F12E15D8@mx2.messiah.edu> The quick brown fox jumped over the lazy dog. Mail-DKIM-0.40/t/corpus/bad_ietf01_2.txt0000644030404400001440000000232012055417140016525 0ustar jlongusersReturn-Path: X-Original-To: test@dkimtest.jason.long.name Delivered-To: dkimtest@mx2.messiah.edu Received: from voicemail.cis.att.net (unknown [12.34.200.188]) by mx2.messiah.edu (Postfix) with ESMTP id 02F12E15D8 for ; Wed, 3 May 2006 15:06:32 -0400 (EDT) Received: from (localhost[127.0.0.1]) by voicemail.cis.att.net (vm2) with SMTP id <2006050319071918800spa0re>; Wed, 3 May 2006 19:07:19 +0000 DKIM-Signature: a=rsa-sha256; c=relaxed; d=vmt2.cis.att.net; t=1146680862; h=Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; bh=HryPFX2R6r7JPsX1Z7+yReZddQR2PjvCvdXgaxW5QYU=; s=shan; b=QXd8h2UbBO7fIPz/Iy3wNwbVU6dih6ozokPXqAvI6p9iG5SqFahyTXwqZeltC4az3Sjay7Vx+b5e 1s2rQuhT4SKD47gJYs4kw0JgV2WLanF3oR1hWD0tL0vuDeUgH6kr Date: Wed, 15 Feb 2006 17:32:54 -0500 From: Tony Hansen MIME-Version: 1.0 To: dkim-test@altn.org, sa-test@sendmail.net, autorespond+dkim@dk.elandsys.com Subject: this subject has been altered Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Message-Id: <20060503190632.02F12E15D8@mx2.messiah.edu> The quick brown fox jumped over the lazy dog. Mail-DKIM-0.40/t/policy.t0000755030404400001440000000660312055417140014031 0ustar jlongusers#!/usr/bin/perl -I../blib/lib use strict; use warnings; use Test::More tests => 19; use Mail::DKIM::DkPolicy; use Mail::DKIM::DkimPolicy; use Mail::DKIM::AuthorDomainPolicy; my $policy; $policy = Mail::DKIM::DkPolicy->new(); ok($policy, "new() works"); $policy = Mail::DKIM::DkPolicy->parse(String => "o=~; t=y"); ok($policy, "parse() works"); $policy = Mail::DKIM::DkPolicy->fetch( Protocol => "dns", Domain => "messiah.edu"); ok($policy, "fetch() works (requires DNS)"); ok(!$policy->is_implied_default_policy, "not the default policy"); $policy = Mail::DKIM::DkPolicy->parse(String => ""); ok($policy, "parse() works (no tags)"); ok(!defined($policy->note), "note tag has default value"); $policy->note("hi there"); ok($policy->note eq "hi there", "note tag has been changed"); ok($policy->policy eq "~", "policy tag has default value"); $policy->policy("-"); ok($policy->policy eq "-", "policy tag has been changed"); ok(!$policy->testing, "testing flag has default value"); #$policy->testing(1); #ok($policy->testing, "testing flag has been changed"); ok($policy->as_string, "as_string() method is implemented"); $policy = Mail::DKIM::DkPolicy->fetch( Protocol => "dns", Sender => 'alfred@nobody.messiah.edu', ); ok($policy, "fetch() returns policy for nonexistent domain"); ok($policy->is_implied_default_policy, "yep, it's the default policy"); $policy = Mail::DKIM::AuthorDomainPolicy->fetch( Protocol => "dns", Domain => "nonexistent-subdomain.messiah.edu", ); ok($policy, "fetch() returns policy for nonexistent domain"); ok(!$policy->is_implied_default_policy, "shouldn't be the default policy"); ok($policy->policy eq "NXDOMAIN", "got policy of NXDOMAIN"); SKIP: { skip "these tests fail when run on the other side of my firewall", 3 unless ($ENV{DNS_TESTS} && $ENV{DNS_TESTS} > 1); $policy = eval { Mail::DKIM::AuthorDomainPolicy->fetch( Protocol => "dns", Domain => "blackhole.messiah.edu", ) }; my $E = $@; print "# got error: $E" if $E; ok(!$policy && $E && $E =~ /(timeout|timed? out)/, "timeout error fetching policy"); $policy = eval { Mail::DKIM::AuthorDomainPolicy->fetch( Protocol => "dns", Domain => "blackhole2.messiah.edu", ) }; $E = $@; print "# got error: $E" if $E; ok(!$policy && $E && $E =~ /SERVFAIL/, "SERVFAIL dns error fetching policy"); # test a policy record where _domainkey.DOMAIN gives a # DNS error, but DOMAIN itself is valid $policy = eval { Mail::DKIM::AuthorDomainPolicy->fetch( Protocol => "dns", Domain => "blackhole3.messiah.edu", ) }; $E = $@; print "# got error: $E" if $E; ok(!$policy && $E && $E =~ /SERVFAIL/, "SERVFAIL dns error fetching policy"); } #debug_policies(qw(yahoo.com hotmail.com gmail.com)); #debug_policies(qw(paypal.com ebay.com)); #debug_policies(qw(cisco.com sendmail.com)); sub debug_policies { foreach my $domain (@_) { print "# $domain:\n"; print "# DomainKeys: "; my $policy = Mail::DKIM::DkPolicy->fetch( Protocol => "dns", Domain => $domain); if ($policy->is_implied_default_policy) { print "no policy\n"; } else { print $policy->policy . " ("; print $policy->as_string . ")\n"; } print "# DKIM: "; $policy = Mail::DKIM::DkimPolicy->fetch( Protocol => "dns", Domain => $domain); if ($policy->is_implied_default_policy) { print "no policy\n"; } else { print $policy->policy . " ("; print $policy->as_string . ")\n"; } } } Mail-DKIM-0.40/t/Mail-DKIM.t0000644030404400001440000000072512055417140014132 0ustar jlongusers# Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl Mail-DKIM.t' ######################### # change 'tests => 1' to 'tests => last_test_to_print'; use Test::More tests => 1; BEGIN { use_ok('Mail::DKIM') }; ######################### # Insert your test code below, the Test::More module is use()ed here so read # its man page ( perldoc Test::More ) for help writing this test script. Mail-DKIM-0.40/t/signature.t0000755030404400001440000000475712055417140014543 0ustar jlongusers#!/usr/bin/perl -I../lib use strict; use warnings; use Test::Simple tests => 12; use Mail::DKIM::Signature; use Mail::DKIM::TextWrap; my $signature = Mail::DKIM::Signature->new(); ok($signature, "new() works"); $signature->algorithm("rsa-sha1"); ok($signature->algorithm eq "rsa-sha1", "algorithm() works"); $signature->canonicalization("relaxed", "simple"); my ($header_can, $body_can) = $signature->canonicalization; ok($header_can eq "relaxed", "canonicalization() works (I)"); ok($body_can eq "simple", "canonicalization() works (II)"); my $combined = $signature->canonicalization; ok($combined eq "relaxed/simple", "canonicalization() works (III)"); $signature->canonicalization("simple/relaxed"); ok($signature->canonicalization eq "simple/relaxed", "canonicalization() works (IV)"); my $unparsed = "DKIM-Signature: a=rsa-sha1; c=relaxed"; $signature = Mail::DKIM::Signature->parse($unparsed); ok($signature, "parse() works (I)"); $unparsed = "DKIM-Signature: a = rsa-sha1; c = simple/simple; d = example.org ; h = Date : From : MIME-Version : To : Subject : Content-Type : Content-Transfer-Encoding; s = foo; b=aqanVhX/f1gmXSdVeX3KdmeKTZb1mkj1y111tZRp/8tXWX/srpGu2SJ/+O06fQv8YtgP0BrSRpEC WEtFgMHcDf0ZFLQgtm0f7vPBO98vDtB7dpDExzHyTsK9rxm8Cf18"; $signature = Mail::DKIM::Signature->parse($unparsed); ok($signature, "parse() works (II)"); ok($signature->domain eq "example.org", "parse() correctly handles spaces"); print "#BEFORE->\n" . $signature->as_string . "\n"; $signature->prettify_safe; print "#SAFE--->\n" . $signature->as_string . "\n"; $signature->prettify; print "#PRETTY->\n" . $signature->as_string . "\n"; check_pretty($signature->as_string); $unparsed = "DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ijs.si; s=jakla2;\n\tt=1225813757; bh=g3zLYH4xKxcPrHOD18z9YfpQcnk/GaJedfustWU5uGs=; b="; $signature = Mail::DKIM::Signature->parse($unparsed); ok($signature, "parse() works (III)"); print "#BEFORE->\n" . $signature->as_string . "\n"; $signature->data("blah"); print "#AFTER-->\n" . $signature->as_string . "\n"; my $first_part_1 = ($signature->as_string =~ /^(.*?b=)/s)[0]; $signature->prettify_safe; print "#PRETTY->\n" . $signature->as_string . "\n"; my $first_part_2 = ($signature->as_string =~ /^(.*?b=)/s)[0]; ok($first_part_1 eq $first_part_2, "signature preserved with prettify_safe"); sub check_pretty { my $str = shift; my @lines = split /\n/s, $str; my $any_long_lines = grep { length($_) > 72 } @lines; ok(!$any_long_lines, "any lines exceed 72 characters"); } Mail-DKIM-0.40/t/simple_canonicalization.t0000755030404400001440000000226112055417140017424 0ustar jlongusers#!/usr/bin/perl use strict; use warnings; use Test::Simple tests => 4; use Mail::DKIM::Canonicalization::simple; use Mail::DKIM::Signature; my $dkim_signature = "DKIM-Signature: h=from:subject; s=test; d=example.org; b="; my $signature = Mail::DKIM::Signature->parse($dkim_signature); ok($signature, "create signature works"); my $method = Mail::DKIM::Canonicalization::simple->new( Signature => $signature); ok($method, "new() works"); my @tmp_headers = ( "from :\tJason\015\012", "Subject: this is the\015\012 subject\015\012", ); $method->add_header($tmp_headers[0]); $method->add_header($tmp_headers[1]); $method->finish_header(Headers => \@tmp_headers); $method->add_body("This is the body.\015\012"); $method->add_body("Another line of the body.\015\12"); $method->finish_body; $method->finish_message; ok(1, "finish_message() works"); my $expected = "from : Jason Subject: this is the subject This is the body. Another line of the body. $dkim_signature"; $expected =~ s/\n/\015\012/gs; ok($method->result eq $expected, "got expected result"); # uncomment this if you're not getting the expected result #print ">" . $method->result . "<\n"; #print ">" . $expected . "<\n"; Mail-DKIM-0.40/t/signer_dk.t0000755030404400001440000000436112055417140014476 0ustar jlongusers#!/usr/bin/perl -I../lib use strict; use warnings; use Test::Simple tests => 8; use Mail::DKIM::Signer; use Mail::DKIM::DkSignature; # The main purpose of this set of tests is to ensure that I am aware # whenever a change in my code causes the generated signature to change # for DomainKeys signatures. Generally this should never occur for a # change in DomainKeys code. (In contrast to DKIM, where the generated # signature gets included in the signature, so any changes to the format # of the signature will cause the hash to change.) # { my %sig_args; my $policyfn = sub { my $signer = shift; if ($sig_args{Headers} && $sig_args{Headers} eq "*") { $sig_args{Headers} = $signer->headers; } $signer->add_signature(Mail::DKIM::DkSignature->new(%sig_args)); return; }; my $tdir = -f "t/test.key" ? "t" : "."; my $keyfile = "$tdir/test.key"; my $sample_email = < Subject: hi there Comment: what is a comment this is a sample message END_OF_SAMPLE $sample_email =~ s/\n/\015\012/gs; sub sign_sample_using_args { %sig_args = ( Algorithm => "rsa-sha1", Selector => "test8", Domain => "messiah.edu", @_, ); my $dkim = Mail::DKIM::Signer->new( Policy => $policyfn, KeyFile => $keyfile, ); $dkim->PRINT($sample_email); $dkim->CLOSE; my $signature = $dkim->signature; return $signature; } } my $signature; $signature = sign_sample_using_args( Method => "simple", ); ok($signature, "signature() works"); print "# " . $signature->as_string . "\n"; ok($signature->data =~ /^TL93PzPvedAijHChCAt/, "got expected signature"); $signature = sign_sample_using_args( Method => "simple", Headers => "*", ); ok($signature, "signature() works"); print "# " . $signature->as_string . "\n"; ok($signature->data =~ /^n\+qfVkhQPch80atOC7/, "got expected signature"); $signature = sign_sample_using_args( Method => "nofws", ); ok($signature, "signature() works"); print "# " . $signature->as_string . "\n"; ok($signature->data =~ /^JWzIzvCZBIYnoMebzKU/, "got expected signature"); $signature = sign_sample_using_args( Method => "nofws", Headers => "*", ); ok($signature, "signature() works"); print "# " . $signature->as_string . "\n"; ok($signature->data =~ /^fkDC8iF/, "got expected signature"); Mail-DKIM-0.40/t/test.key0000644030404400001440000000156712055417140014037 0ustar jlongusers-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQC848PMQXuR6V4Tbj3Wb2TN/4Qon0v8ch1WoS0v5wC4/2FXHaH5 g7xNwtIwgfE7kxkSqv1LnS4tUrpHjWcXJhtCkT9wkI0FOVFzKHRc7Gy7vYSFMUCn ojiVVQjxY6bnyPLIIP2l/D++0awe+XCWQsjFZ+CHW5Lo/lQ/bdrUcYEU/QIDAQAB AoGAFpfr0J+4hyBx10MldGPIm/dHOJCtRxnjNnx66jLoXDh3zTLHMBA40thNye2R DD4xDpGTUJsRbexqSaq5PQSa1+vQuJDsbDu24HYoczevvdWBOBq3aR4OZOBnfueY bqxiY1TfKTHaC+2lNyKWn/wb+FKDPogrymPDSQ9A26UgjGUCQQDp9jv7V1Bd5rGm ZTMUnh4A1tqTF2Ba65/qZHRHW0TDvjdmjvOnRwzjSTGljVZ/g+KKbrjNuh4vu3w2 PxvwAsZbAkEAzq6osN7uXKu0kxvz6+SDHQP1qo3ePNVnGlvhsRp6mL+paJwnPo+L w9RR83Vvz2tG6Tcez96FheEzvIagokZhhwJAY6lVYbqO7IER2cywFq9IHplnYFML 448Nft4tvhu9zhggJ1rrpa0Za1RJhrkPSKBYhxmlNVV7+F9ICR+W+gVkkwJBAJA7 tP9RY9iJ/wcWX7/EBXevNraiMHAVoND1Q+gbCWklf6zAJGb1N90eg146GyANxjuk Or1nlZibU0aCUQFNymsCQDb7Wwq+ajK6WhLN5zAYYcyh2xKaJ7kylBMQqeB0eQ5Z ugJSnXpSRrz11qMO/ClWOoipHz+iH0SU5njjSdER1x8= -----END RSA PRIVATE KEY----- Mail-DKIM-0.40/t/signer.t0000755030404400001440000001052412055417140014016 0ustar jlongusers#!/usr/bin/perl -I../lib use strict; use warnings; use Test::Simple tests => 20; use Mail::DKIM::Signer; my $EXPECTED_RE = qr/CIDMVc94VWhLZ4Ktq2Q05011qBXSO/; my $tdir = -f "t/test.key" ? "t" : "."; my $keyfile = "$tdir/test.key"; my $dkim = Mail::DKIM::Signer->new( Algorithm => "rsa-sha1", Method => "relaxed", Domain => "example.org", Selector => "test", KeyFile => $keyfile); ok($dkim, "new() works"); my $sample_email = < Subject: hi there Comment: what is a comment this is a sample message END_OF_SAMPLE $sample_email =~ s/\n/\015\012/gs; $dkim->PRINT($sample_email); $dkim->CLOSE; my $signature = $dkim->signature; ok($signature, "signature() works"); print "# signature=" . $signature->as_string . "\n"; ok($signature->as_string =~ /$EXPECTED_RE/, "got expected signature value"); # now try a SHA256 signature $dkim = Mail::DKIM::Signer->new( Algorithm => "rsa-sha256", Method => "relaxed", Domain => "example.org", Selector => "test", KeyFile => $keyfile); ok($dkim, "new() works"); $dkim->PRINT($sample_email); $dkim->CLOSE; ok($dkim->signature, "signature() works"); # add some headers to the first email $sample_email = "Received: from x\015\012" . "Received: from y\015\012" . $sample_email; $sample_email =~ s/^Comments:.*?$/comments: this can be changed/m; $dkim = Mail::DKIM::Signer->new( Algorithm => "rsa-sha1", Method => "relaxed", Domain => "example.org", Selector => "test", Identity => "bob\@example.org", Timestamp => time(), KeyFile => $keyfile); ok($dkim, "new() works"); $dkim->PRINT($sample_email); $dkim->CLOSE; ok($dkim->signature, "signature() works"); print "# signature=" . $dkim->signature->as_string . "\n"; # check whether the signature includes/excludes certain header fields my $sigstr = $dkim->signature->as_string; ok($sigstr =~ /subject/i, "subject was signed"); ok($sigstr =~ /from/i, "from was signed"); ok($sigstr !~ /received/i, "received was excluded"); ok($sigstr !~ /comments/i, "comments was excluded"); # check if the identity got included ok($sigstr =~ /i=bob\@/, "got expected identity value"); # check if timestamp got included ok($sigstr =~ /t=\d+/, "found timestamp value"); eval { $dkim = Mail::DKIM::Signer->new( Algorithm => "rsa-sha1", Method => "relaxed", Domain => "example.org", Selector => "test", KeyFile => "$tdir/non_existent_file_!!"); }; { my $E = $@; print "# $E" if $E; ok($E, "new() with bogus key file dies as expected"); } eval { $dkim = Mail::DKIM::Signer->new( Algorithm => "rsa-sha1", Method => "relaxed", Domain => "example.org", Selector => "test", KeyFile => "$tdir/unreadable_file"); }; { my $E = $@; print "# $E" if $E; ok($E, "new() with bogus key file dies as expected"); } { # TEST signing a message with no header my $dkim = Mail::DKIM::Signer->new( Algorithm => "rsa-sha1", Method => "relaxed", Domain => "example.org", Selector => "test", KeyFile => $keyfile); my $sample_email = <PRINT($sample_email); $dkim->CLOSE; ok($dkim->signature, "signature() works"); } { # TEST signing a message with LOTS OF blank lines my $dkim = Mail::DKIM::Signer->new( Algorithm => "rsa-sha1", Method => "relaxed", Domain => "example.org", Selector => "test", KeyFile => $keyfile); my $sample_email = < Subject: hi there Comment: what is a comment this is a sample message END_OF_SAMPLE $sample_email .= ("\n" x 50000); $sample_email =~ s/\n/\015\012/gs; # older, broken, versions of Mail::DKIM will hang here $dkim->PRINT($sample_email); $dkim->CLOSE; ok($dkim->signature, "signature() works"); } { # TEST signing a message with obsolete header syntax my $dkim = Mail::DKIM::Signer->new( Algorithm => "rsa-sha1", Method => "relaxed", Domain => "example.org", Selector => "test", KeyFile => $keyfile); my $sample_email = < Subject: hi there Comment: what is a comment this is a sample message END_OF_SAMPLE $sample_email =~ s/\n/\015\012/gs; $dkim->PRINT($sample_email); $dkim->CLOSE; ok($dkim->signature, "signature() works"); my $sigstr = $dkim->signature->as_string; ok($sigstr =~ /subject/i, "subject was signed"); ok($sigstr =~ /from/i, "from was signed"); } Mail-DKIM-0.40/t/verifier.t0000755030404400001440000002216212104756576014361 0ustar jlongusers#!/usr/bin/perl -I../lib use strict; use warnings; use Test::More tests => 104; use Mail::DKIM::Verifier; my $homedir = (-d "t") ? "t" : "."; my $dkim = Mail::DKIM::Verifier->new(); ok($dkim, "new() works"); $dkim = Mail::DKIM::Verifier->new_object(); ok($dkim, "new_object() works"); my $sample_email = read_file("$homedir/test5.txt"); ok($sample_email, "able to read sample email"); ok($sample_email =~ /\015\012/, "sample has proper line endings"); $dkim->PRINT($sample_email); $dkim->CLOSE; my $result = $dkim->result; ok($result, "result() works"); SKIP: { skip "older-prestandard DKIM signatures", 5; test_email("good_ietf00_1.txt", "pass"); test_email("good_ietf00_2.txt", "pass"); test_email("good_ietf00_3.txt", "pass"); test_email("good_ietf00_4.txt", "pass"); test_email("good_ietf00_5.txt", "pass"); } test_email("mine_ietf01_1.txt", "pass"); test_email("mine_ietf01_2.txt", "pass"); test_email("mine_ietf01_3.txt", "pass"); test_email("mine_ietf01_4.txt", "pass"); test_email("mine_ietf05_1.txt", "pass"); test_email("good_ietf01_1.txt", "pass"); test_email("good_ietf01_2.txt", "pass"); test_email("good_rfc4871_3.txt", "pass"); # tests extra tags in signature test_email("good_rfc4871_4.txt", "pass"); # case-differing domain name test_email("good_1878523.txt", "pass"); # test issue #1878523 test_email("multiple_1.txt", "pass"); test_email("multiple_2.txt", "pass"); my @sigs = $dkim->signatures; ok($sigs[0]->result eq "invalid", "first signature is 'invalid'"); ok($sigs[1]->result eq "pass", "second signature is 'pass'"); ok($sigs[2]->result eq "fail", "third signature is 'fail'"); test_email("good_qp_1.txt", "pass"); # tests i= quoted-printable value test_email("good_qp_2.txt", "pass"); # tests i= quoted-printable value test_email("good_qp_3.txt", "pass"); # tests i= quoted-printable value test_email("bad_ietf01_1.txt", "fail"); ok($dkim->result_detail =~ /body/, "determined body had been altered"); test_email("bad_ietf01_2.txt", "fail"); ok($dkim->result_detail =~ /message/, "determined message had been altered"); test_email("bad_ietf01_3.txt", "fail"); ok($dkim->result_detail =~ /RSA/, "determined RSA failure"); test_email("bad_1.txt", "fail"); #openssl error print "# " . $dkim->result_detail . "\n"; SKIP: { skip "did not recognize OpenSSL error", 1 unless ($dkim->result_detail =~ /OpenSSL/i); like($dkim->result_detail, qr/OpenSSL/i, "determined OpenSSL error"); } test_email("bad_1878954.txt", "fail"); # shouldn't die # test older DomainKeys messages, from Gmail and Yahoo! test_email("good_dk_gmail.txt", "pass"); test_email("good_dk_yahoo.txt", "pass"); test_email("good_dk_1.txt", "pass"); test_email("good_dk_2.txt", "pass"); test_email("good_dk_3.txt", "pass"); # key with g= tag (ident in From header) test_email("good_dk_4.txt", "pass"); # key with g= tag (ident in Sender head) test_email("good_dk_5.txt", "pass"); # key with empty g= test_email("good_dk_6.txt", "pass"); # no h= tag test_email("good_dk_7.txt", "pass"); # case-differing domain names test_email("dk_headers_1.txt", "pass"); test_email("dk_headers_2.txt", "pass"); test_email("bad_dk_1.txt", "invalid"); # sig. domain != From header (no Sender) test_email("bad_dk_2.txt", "invalid"); # added Sender header, no h= tag SKIP: { skip "missing q= tag on DomainKey signature accepted", 1; test_email("bad_dk_3.txt", "invalid"); # no q= tag } test_email("bad_dk_4.txt", "invalid"); # empty q= tag test_email("bad_dk_5.txt", "invalid"); # unrecognized q= tag test_email("dk_multiple_1.txt", "pass"); my @dksigs = $dkim->signatures; ok(@dksigs == 2, "found two signatures"); ok($dksigs[0]->result eq "pass", "first signature is 'pass'"); ok($dksigs[1]->result eq "pass", "second signature is 'pass'"); # test empty/missing body - simple canonicalization test_email("no_body_1.txt", "pass"); test_email("no_body_2.txt", "pass"); test_email("no_body_3.txt", "pass"); # # test various problems with the signature itself # test_email("ignore_1.txt", "invalid"); # unsupported v= tag (v=5) test_email("ignore_2.txt", "invalid"); # unsupported a= tag (a=rsa-md5) test_email("ignore_3.txt", "invalid"); # unsupported a= tag (a=dsa-sha1) test_email("ignore_4.txt", "invalid"); # unsupported c= tag (c=future) test_email("ignore_5.txt", "invalid"); # unsupported q= tag (q=http) test_email("ignore_6.txt", "invalid"); # unsupported q= tag (q=dns/special) test_email("ignore_7.txt", "invalid"); # expired signature test_email("ignore_8.txt", "invalid"); # bad i= value # # test variants on the public key # test_email("goodkey_1.txt", "pass"); # public key with s=email test_email("goodkey_2.txt", "pass"); # public key with extra tags, h=, s=, etc. test_email("goodkey_3.txt", "pass"); # public key with g=jl*g test_email("goodkey_4.txt", "pass"); # public key with implied g # # test problems with the public key # test_email("badkey_1.txt", "invalid"); # public key NXDOMAIN ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_2.txt", "invalid"); # public key REVOKED ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_3.txt", "invalid"); # public key unsupported v= tag ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_4.txt", "invalid"); # public key syntax error ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_5.txt", "invalid"); # public key unsupported k= tag ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_6.txt", "invalid"); # public key unsupported s= tag ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_7.txt", "invalid"); # public key unsupported h= tag ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_8.txt", "invalid"); # public key unmatched g= tag ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_9.txt", "invalid"); # public key empty g= tag ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_10.txt", "invalid"); # public key requires i == d ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_11.txt", "invalid"); # public key unmatched h= tag ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_12.txt", "invalid"); # public key g= != i= by case ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_13.txt", "invalid"); # public key g= matches From but not i= ok($dkim->result_detail =~ /public key/, "detail mentions public key"); test_email("badkey_14.txt", "invalid"); # dns error (timeout) ok($dkim->result_detail =~ /public key/, "detail mentions public key"); ok($dkim->result_detail =~ /dns.*timed? ?out/i, "type of dns failure"); test_email("badkey_15.txt", "invalid"); # dns error (SERVFAIL) ok($dkim->result_detail =~ /public key/, "detail mentions public key"); ok($dkim->result_detail =~ /dns.*SERVFAIL/i, "type of dns failure"); sub read_file { my $srcfile = shift; open my $fh, "<", $srcfile or die "Error: can't open $srcfile: $!\n"; binmode $fh; local $/; my $content = <$fh>; close $fh; return $content; } sub test_email { my ($file, $expected_result) = @_; print "# verifying message '$file'\n"; $dkim = Mail::DKIM::Verifier->new(); my $path = "$homedir/corpus/$file"; my $email = read_file($path); $dkim->PRINT($email); $dkim->CLOSE; my $result = $dkim->result; print "# result: " . $dkim->result_detail . "\n"; ok($result eq $expected_result, "'$file' should '$expected_result'"); } # override the DNS implementation, so that these tests do not # rely on DNS servers I have no control over my $CACHE; sub Mail::DKIM::DNS::fake_query { my ($domain, $type) = @_; die "can't lookup $type record" if $type ne "TXT"; unless ($CACHE) { open my $fh, "<", "$homedir/FAKE_DNS.dat" or die "Error: cannot read $homedir/FAKE_DNS.dat: $!\n"; $CACHE = {}; while (<$fh>) { chomp; next if /^\s*[#;]/ || /^\s*$/; my ($k, $v) = split /\s+/, $_, 2; $CACHE->{$k} = ($v =~ /^~~(.*)~~$/) ? "$1" : $v eq "NXDOMAIN" ? [] : [ bless \$v, "FakeDNS::Record" ]; } close $fh; } if (not exists $CACHE->{$domain}) { warn "did not cache that DNS entry: $domain\n"; print STDERR ">>>\n"; my @result = Mail::DKIM::DNS::orig_query($domain, $type); if (!@result) { print STDERR "No results: $@\n"; } else { foreach my $rr (@result) { # join with no intervening spaces, RFC 6376 if (Net::DNS->VERSION >= 0.69) { # must call txtdata() in a list context printf STDERR ("%s\n", join("", $rr->txtdata)); } else { # char_str_list method is 'historical' printf STDERR ("%s\n", join("", $rr->char_str_list)); } } } print STDERR "<<<\n"; die; } if (ref $CACHE->{$domain}) { return @{$CACHE->{$domain}}; } else { die "DNS error: $CACHE->{$domain}\n"; } } BEGIN { unless ($ENV{use_real_dns}) { *Mail::DKIM::DNS::orig_query = *Mail::DKIM::DNS::query; *Mail::DKIM::DNS::query = *Mail::DKIM::DNS::fake_query; } } package FakeDNS::Record; sub type { return "TXT"; } sub char_str_list { return ${$_[0]}; } sub txtdata { return ${$_[0]}; } Mail-DKIM-0.40/t/FAKE_DNS.dat0000644030404400001440000000746412104756576014272 0ustar jlongusers# this file contains DNS records used by verifier.t # selector1._domainkey.messiah.edu k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDhMlYqwtUA9UrrDcNp/IMtdFnytggDl5oIAzJ55oWzPILZE7eX4hLdP6WperHm1WJ9M32XsiKrr4TDbWfp4WjGWBnXf8QMi+WlDuEFOvwVRC/uWy+sAiEf3VcBR5KjGvDovPnGSnW8uDntSOY4HlkTJF/BTWnk29zKmlGyGnw9mQIDAQAB test1._domainkey.messiah.edu v=DKIM1; t=y; s=email; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALgSoqXVSEmfcIsOzw7oRuCCOwsmtX/SJnTWxYyj2leFxfS/AVJ+dYfY+hXqMsT7l+MZvvh/R1WzN4MO/kI/7XsCAwEAAQ== test2._domainkey.messiah.edu v=DKIM1; s=email:web:fine; x1 = extra ; t = y:n:extra; h=md5:sha1; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALgSoqXVSEmfcIsOzw7oRuCCOwsmtX/SJnTWxYyj2leFxfS/AVJ+dYfY+hXqMsT7l+MZvvh/R1WzN4MO/kI/7XsCAwEAAQ== ; test3._domainkey.messiah.edu v=DKIM1; g=jl*g; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALgSoqXVSEmfcIsOzw7oRuCCOwsmtX/SJnTWxYyj2leFxfS/AVJ+dYfY+hXqMsT7l+MZvvh/R1WzN4MO/kI/7XsCAwEAAQ== test4._domainkey.messiah.edu v=DKIM1; g=; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALgSoqXVSEmfcIsOzw7oRuCCOwsmtX/SJnTWxYyj2leFxfS/AVJ+dYfY+hXqMsT7l+MZvvh/R1WzN4MO/kI/7XsCAwEAAQ== test5._domainkey.messiah.edu v=DKIM1; t=s; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALgSoqXVSEmfcIsOzw7oRuCCOwsmtX/SJnTWxYyj2leFxfS/AVJ+dYfY+hXqMsT7l+MZvvh/R1WzN4MO/kI/7XsCAwEAAQ== testbad1._domainkey.messiah.edu v=DKIM3; t=y; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALgSoqXVSEmfcIsOzw7oRuCCOwsmtX/SJnTWxYyj2leFxfS/AVJ+dYfY+hXqMsT7l+MZvvh/R1WzN4MO/kI/7XsCAwEAAQ== testbad2._domainkey.messiah.edu k=rsa; t; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALgSoqXVSEmfcIsOzw7oRuCCOwsmtX/SJnTWxYyj2leFxfS/AVJ+dYfY+hXqMsT7l+MZvvh/R1WzN4MO/kI/7XsCAwEAAQ== testbad3._domainkey.messiah.edu k=foobar; t=y; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALgSoqXVSEmfcIsOzw7oRuCCOwsmtX/SJnTWxYyj2leFxfS/AVJ+dYfY+hXqMsT7l+MZvvh/R1WzN4MO/kI/7XsCAwEAAQ== testbad4._domainkey.messiah.edu v=DKIM1; s=chat; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALgSoqXVSEmfcIsOzw7oRuCCOwsmtX/SJnTWxYyj2leFxfS/AVJ+dYfY+hXqMsT7l+MZvvh/R1WzN4MO/kI/7XsCAwEAAQ== testbad7._domainkey.messiah.edu v=DKIM1; h=bad; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALgSoqXVSEmfcIsOzw7oRuCCOwsmtX/SJnTWxYyj2leFxfS/AVJ+dYfY+hXqMsT7l+MZvvh/R1WzN4MO/kI/7XsCAwEAAQ== testbad8._domainkey.messiah.edu v=DKIM1; g=*poe; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALgSoqXVSEmfcIsOzw7oRuCCOwsmtX/SJnTWxYyj2leFxfS/AVJ+dYfY+hXqMsT7l+MZvvh/R1WzN4MO/kI/7XsCAwEAAQ== testrevoked._domainkey.messiah.edu k=rsa; t=y; p= s1024._domainkey.yahoo.com k=rsa; t=y; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB; n=A 1024 bit key; beta._domainkey.gmail.com t=y; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC69TURXN3oNfz+G/m3g5rt4P6nsKmVgU1D6cw2X6BnxKJNlQKm10f8tMx6P6bN7juTR1BeD8ubaGqtzm2rWK4LiMJqhoQcwQziGbK1zp/MkdXZEWMCflLY6oUITrivK7JNOLXtZbdxJG2y/RAHGswKKyVhSP9niRsZF/IBr5p8uQIDAQAB jakla2._domainkey.ijs.si v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvWSehCSmnxlhzM+P1Ai+7CgzeAcvkL3RdoHFq8JwtpKN3iLnp/s1yRwE/heAi4QQXxDRdlB0bJm5NxZOsckzK7tJM8EdkebMjyXeKOzBKoJaOIlsx4WC2qHqORB0RLqm4lqJFYZJpUypEpskeAGy7WBG7a+1hOlir9+Tf9xtOkwIDAQAB shan._domainkey.vmt2.cis.att.net v=DKIM1; k=rsa; h=sha1:sha256:sha512;n=send%20comments%20to%20tony%40att%2Ecom; g=*; s=*;p=MHwwDQYJKoZIhvcNAQEBBQADawAwaAJhALSQ1y/+tHT1d9XvpiVap4Z+GFaydEmDgfC48m3wLLmDqfKBADWYIqrCnfKPvZPzGYzo+aJMEiAOTtiNxPWYToiTfJlTpn2YyEEz6OUIXw0uc+NfEQviN4QQr0jVX9yRjwIDAQAB foo._domainkey.vmt2.cis.att.net v=DKIM1; k=rsa; n=send%20comments%20to%20tony%40att%2Ecom; g=*; s=*;p=MHwwDQYJKoZIhvcNAQEBBQADawAwaAJhALSQ1y/+tHT1d9XvpiVap4Z+GFaydEmDgfC48m3wLLmDqfKBADWYIqrCnfKPvZPzGYzo+aJMEiAOTtiNxPWYToiTfJlTpn2YyEEz6OUIXw0uc+NfEQviN4QQr0jVX9yRjwIDAQAB nonexistent._domainkey.messiah.edu NXDOMAIN test3._domainkey.blackhole.messiah.edu ~~Query timed out~~ test3._domainkey.blackhole2.messiah.edu ~~SERVFAIL~~ Mail-DKIM-0.40/t/test5.txt0000644030404400001440000000132712055417140014145 0ustar jlongusersDKIM-Signature: a=rsa-sha1; c=simple; d=messiah.edu; h=received:received:from:to:subject:date:message-id; q=dns; s=selector1; b=zOgUMbToWrXAfWb1U0Ybe9SvD18ydan8niTN4r9YMuKnxzzRCg+uhqBYLp2NXZaLuajYfaBCgDTiJ5rCY3nqmxAqyNnvWnMBehuM05/oLvwkXFiZfYl5JgYY0ZyuBc0lhj1XaELAfHt3LAgCT/xAZyWiVWpfMUrE5cZTrVT7zUs= Received: from x.y.test by example.net via TCP with ESMTP id ABC12345 for ; 21 Nov 1997 10:05:43 -0600 Received: from machine.example by x.y.test; 21 Nov 1997 10:01:22 -0600 Invalid header From: Jason Long To: Nobody Subject: dkim test Date: Wed, 7 Dec 2005 09:11:00 -0500 Message-ID: <20051207091100@test.messiah.edu> This is a test Mail-DKIM-0.40/t/signer_policy.t0000755030404400001440000001060112055417140015371 0ustar jlongusers#!/usr/bin/perl -I../lib use strict; use warnings; use Test::Simple tests => 24; use Mail::DKIM::Signer; my $keyfile = -f "t/test.key" ? "t/test.key" : "test.key"; my $policy; my $dkim; # test specification of a policy "class" $policy = "MySignerPolicy"; $dkim = sign_sample_using_args( Policy => $policy, KeyFile => $keyfile); ok($dkim, "processed message"); my $signature = $dkim->signature; ok($signature, "signature() works"); print "# signature=" . $signature->as_string . "\n"; ok($signature->as_string =~ /d=different-domain/, "got expected domain in signature"); ok($signature->as_string =~ /c=relaxed/, "got expected canonicalization method in signature"); ok($signature->as_string =~ /a=rsa-sha256/, "got expected algorithm in signature"); # try using a policy "object" $policy = bless {}, "MySignerPolicy"; $dkim = sign_sample_using_args( Policy => $policy, KeyFile => $keyfile); ok($dkim, "processed message"); $signature = $dkim->signature; ok($signature, "signature() works"); print "# signature=" . $signature->as_string . "\n"; ok($signature->as_string =~ /d=different-domain/, "got expected domain in signature"); # now a policy as an anonymous subroutine $policy = sub { my $signer = shift; $signer->domain("different-domain.example"); $signer->method("relaxed"); $signer->algorithm("rsa-sha256"); $signer->selector("beta"); $signer->key_file($keyfile); return 1; }; $dkim = sign_sample_using_args( Policy => $policy); ok($dkim, "processed message"); $signature = $dkim->signature; ok($signature, "got signature"); # this policy should not produce any signature $policy = sub { my $signer = shift; return 0; }; $dkim = sign_sample_using_args( Policy => $policy, KeyFile => $keyfile); ok($dkim, "processed message"); $signature = $dkim->signature; ok(!$signature, "no signature"); # this policy should produce a DomainKeys signature use Mail::DKIM::DkSignature; $policy = sub { my $signer = shift; $signer->add_signature( new Mail::DKIM::DkSignature( Algorithm => "rsa-sha1", Method => "nofws", Headers => $dkim->headers, Domain => "different-domain.example", Selector => "beta", )); return; }; $dkim = sign_sample_using_args( Policy => $policy, KeyFile => $keyfile); ok($dkim, "processed message"); $signature = $dkim->signature; ok($signature, "got signature"); print "# signature=" . $signature->as_string . "\n"; ok($signature->as_string =~ /DomainKey-Signature/, "got DomainKeys signature"); ok($signature->as_string =~ /d=different-domain/, "got expected domain in signature"); ok($signature->as_string =~ /c=nofws/, "got expected canonicalization method in signature"); ok($signature->as_string !~ /bh=/, "no bh= tag in signature"); # this policy should produce two signature (one DKIM and one DomainKeys) $policy = sub { my $signer = shift; $signer->add_signature( new Mail::DKIM::DkSignature( Algorithm => "rsa-sha1", Method => "nofws", Headers => $dkim->headers, Domain => "different-domain.example", Selector => "beta", )); $signer->add_signature( new Mail::DKIM::Signature( Algorithm => "rsa-sha256", Method => "relaxed", Headers => $dkim->headers, Domain => "different-domain.example", Selector => "beta", )); }; $dkim = sign_sample_using_args( Policy => $policy, KeyFile => $keyfile); ok($dkim, "processed message"); $signature = $dkim->signature; ok($signature, "got signature"); print "# signature=" . $signature->as_string . "\n"; ok($signature->as_string =~ /^DKIM-Signature/, "got DKIM signature"); my @multiple = $dkim->signatures; ok(@multiple == 2, "got 2 signatures"); ok($multiple[0]->as_string =~ /^DomainKey-Signature/, "first is DomainKeys signature"); ok($multiple[1]->as_string =~ /^DKIM-Signature/, "second is DKIM signature"); sub sign_sample_using_args { my %args = @_; my $dkim = Mail::DKIM::Signer->new(%args) or die "couldn't create signer object"; my $sample_email = < Subject: hi there this is a sample message END_OF_SAMPLE $sample_email =~ s/\n/\015\012/gs; $dkim->PRINT($sample_email); $dkim->CLOSE; return $dkim; } package MySignerPolicy; use Mail::DKIM::SignerPolicy; use base "Mail::DKIM::SignerPolicy"; sub apply { my ($self, $signer) = @_; $signer->domain("different-domain.example"); $signer->method("relaxed"); $signer->algorithm("rsa-sha256"); $signer->selector("beta"); return 1; } Mail-DKIM-0.40/t/textwrap.t0000755030404400001440000000622012055417140014403 0ustar jlongusers#!/usr/bin/perl -I../lib use strict; use warnings; use Test::Simple tests => 16; use Mail::DKIM::TextWrap; my $tw; $tw = Mail::DKIM::TextWrap->new; ok($tw, "new() works"); my $output = ""; my @lines; $tw = Mail::DKIM::TextWrap->new( Margin => 10, Output => \$output, ); $tw->add("Mary had a little lamb, whose fleece was white as snow.\n"); $tw->finish; my $saved1 = $output; check_output("basic wrapping"); ok(@lines == 7, "basic wrapping got expected number of lines"); foreach ("Mary ", "had ", "a ", "little ", "lamb, ", "whose ", "fleece ", "was ", "white ", "as ", "snow.\n") { $tw->add($_); } $tw->finish; my $saved2 = $output; check_output("basic wrapping- words added separately, space following each"); ok($saved1 eq $saved2, "same result when words added separately, space following each"); foreach ("Mary", " had", " a", " little", " lamb,", " whose", " fleece", " was", " white", " as", " snow.\n") { $tw->add($_); } $tw->finish; my $saved3 = $output; check_output("basic wrapping- words added separately, space preceding each"); ok($saved1 eq $saved3, "same result when words added separately, space preceding each"); $tw->{Separator} = "\n "; $tw->add("Mary had a little lamb, whose fleece was white as snow.\n"); $tw->finish; check_output("with second-line indent"); ok($lines[0] =~ /^Mary had a/, "first line looks ok"); $tw = Mail::DKIM::TextWrap->new( Margin => 10, Output => \$output, Break => qr/[\s:]/, ); $tw->add("apple:orange:banana:apricot:blueberry:strawberry-kiwi\n"); $tw->finish; check_output("colon-separated list"); ok($lines[0] eq "apple:", "first line looks ok"); ok($lines[1] eq "orange:", "second line looks ok"); ok($lines[$#lines] =~ "strawberry-kiwi", "over-long word did not get split"); $tw->add(" apple : orange : apricot : kiwi \n"); $tw->finish; check_output("colon-separated list with spaces"); ok($lines[0] =~ /^\s/, "first line begins with space"); ok($lines[$#lines] =~ /\s$/, "last line ends with space"); ok(grep(!/(^\s|\s$)/, @lines[1 .. ($#lines - 1)]), "middle lines neither begin nor end with space"); $tw = Mail::DKIM::TextWrap->new( Margin => 10, Output => \$output, Break => qr/[\s:]/, BreakBefore => qr/[:]/, ); $tw->add("apple:orange:banana:apricot:lime:kiwi\n"); $tw->finish; check_output("colon-separated list, split before colons"); ok($lines[0] eq "apple", "first line looks ok"); ok($lines[1] eq ":orange", "second line looks ok"); ok($lines[$#lines] =~ /:kiwi$/, "last line looks ok"); $tw = Mail::DKIM::TextWrap->new( Margin => 10, Output => \$output, ); $tw->add("apple"); $tw->add("orange"); $tw->add("banana"); $tw->add("apricot"); $tw->finish; check_output(""); ok(@lines == 1, "no wrapping took place"); $tw = Mail::DKIM::TextWrap->new( Margin => 10, Output => \$output, ); foreach (qw(apple orange banana apricot)) { $tw->add($_); $tw->flush; } $tw->finish; check_output(""); ok(!(grep { length($_) > 10 } @lines), "no long lines"); sub check_output { my ($test_name) = @_; @lines = split /\n/, $output; $output = ""; print "# $test_name\n"; print "# " . ('-' x $tw->{Margin}) . "\n"; foreach my $l (@lines) { print "# $l\n"; } print "# " . ('-' x $tw->{Margin}) . "\n"; } Mail-DKIM-0.40/Makefile.PL0000644030404400001440000000104312105004733014036 0ustar jlongusersuse 5.006_001; use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( NAME => 'Mail::DKIM', VERSION => "0.40", PREREQ_PM => { Crypt::OpenSSL::RSA => 0.24, Digest::SHA => 0, Mail::Address => 0, MIME::Base64 => 0, Net::DNS => 0, Test::Simple => 0, # only needed for `make test' }, ABSTRACT_FROM => 'lib/Mail/DKIM.pm', # retrieve abstract from module AUTHOR => 'Jason Long ', ); Mail-DKIM-0.40/scripts/0000755030404400001440000000000012105005167013557 5ustar jlongusersMail-DKIM-0.40/scripts/dkimverify.pl0000755030404400001440000000440712104543131016272 0ustar jlongusers#!/usr/bin/perl -I../lib # # Copyright (c) 2005-2007 Messiah College. This program is free software. # You can redistribute it and/or modify it under the terms of the # GNU Public License as found at http://www.fsf.org/copyleft/gpl.html. # # Written by Jason Long, jlong@messiah.edu. use strict; use warnings; use Mail::DKIM::Verifier; use Getopt::Long; my $debug_canonicalization; GetOptions( "debug-canonicalization=s" => \$debug_canonicalization, ) or die "Error: invalid argument(s)\n"; my $debugfh; if (defined $debug_canonicalization) { open $debugfh, ">", $debug_canonicalization or die "Error: cannot write to $debug_canonicalization: $!\n"; } # recommended, but may cause compatibility problems with old firewalls Mail::DKIM::DNS::enable_EDNS0; my $dkim = new Mail::DKIM::Verifier( Debug_Canonicalization => $debugfh, ); while () { chomp; s/\015$//; $dkim->PRINT("$_\015\012"); } $dkim->CLOSE; if ($debugfh) { close $debugfh; print STDERR "wrong canonicalized message to $debug_canonicalization\n"; } print "originator address: " . $dkim->message_originator->address . "\n"; foreach my $signature ($dkim->signatures) { print "signature identity: " . $signature->identity . "\n"; print "verify result: " . $signature->result_detail . "\n"; } foreach my $policy ($dkim->policies) { my $policy_name = $policy->name; print "$policy_name policy result: "; my $policy_result = $policy->apply($dkim); print "$policy_result\n"; } __END__ =head1 NAME dkimverify.pl - verifies DKIM signatures on an email message =head1 SYNOPSIS dkimverify.pl [options] < signed_email.txt options: --debug-canonicalization=FILE dkimverify.pl --help to see a full description of the various options =head1 OPTIONS =over =item B<--debug-canonicalization> Outputs the canonicalized message to the specified file, in addition to computing the DKIM signature. This is helpful for debugging canonicalization methods. =back =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2007 by Messiah College This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut Mail-DKIM-0.40/scripts/test_canonicalization.pl0000755030404400001440000000271412055417140020511 0ustar jlongusers#!/usr/bin/perl -I../../.. use strict; use warnings; use Getopt::Long; my $headers; GetOptions( "headers=s" => \$headers ) or die "Error: invalid argument(s)\n"; my $canon_method = $ARGV[0] or die "Error: no canonicalization method specified\n"; use Mail::DKIM::Canonicalization::nowsp; use Mail::DKIM::Canonicalization::relaxed; use Mail::DKIM::Canonicalization::simple; use Mail::DKIM::Signature; my $crlf = "\015\012"; # read in headers my @headers; my @header_names; while () { # standardize line terminators chomp; $_ .= $crlf; last if ($_ eq $crlf); if (/^\s/ && @headers) { # continues last header $headers[@headers - 1] .= $_; } else { # starts a new header push @headers, $_; if (/^(\S[^:\s]*)\s*:/) { push @header_names, $1; } } } # determine value of h= tag unless (defined $headers) { $headers = join(":", @header_names); } # create a dummy signature my $signature = new Mail::DKIM::Signature( Algorithm => "rsa-sha1", Method => $canon_method, Domain => "example.org", Selector => "selector"); $signature->headerlist($headers); # create a canonicalization object my $canon_class = "Mail::DKIM::Canonicalization::$canon_method"; my $can = $canon_class->new( Signature => $signature, output_fh => *STDOUT); # repeat the headers foreach my $header (@headers) { $can->add_header($header); } $can->finish_header; # read the body while () { chomp; $can->add_body("$_\015\012"); } $can->finish_body; Mail-DKIM-0.40/scripts/dkimsign.pl0000755030404400001440000000755312104756576015756 0ustar jlongusers#!/usr/bin/perl -I../lib # # Copyright (c) 2005-2007 Messiah College. This program is free software. # You can redistribute it and/or modify it under the terms of the # GNU Public License as found at http://www.fsf.org/copyleft/gpl.html. # # Written by Jason Long, jlong@messiah.edu. use strict; use warnings; use Mail::DKIM::Signer; use Mail::DKIM::TextWrap; use Getopt::Long; use Pod::Usage; my $type = "dkim"; my $selector = "selector1"; my $algorithm = "rsa-sha1"; my $method = "simple"; my $domain; # undef => auto-select domain my $expiration; my $identity; my $key_protocol; my @extra_tag; my $debug_canonicalization; my $binary; my $help; GetOptions( "type=s" => \$type, "algorithm=s" => \$algorithm, "method=s" => \$method, "selector=s" => \$selector, "domain=s" => \$domain, "expiration=i" => \$expiration, "identity=s" => \$identity, "key-protocol=s" => \$key_protocol, "debug-canonicalization=s" => \$debug_canonicalization, "extra-tag=s" => \@extra_tag, "binary" => \$binary, "help|?" => \$help, ) or pod2usage(2); pod2usage(1) if $help; pod2usage("Error: unrecognized argument(s)") unless (@ARGV == 0); my $debugfh; if (defined $debug_canonicalization) { open $debugfh, ">", $debug_canonicalization or die "Error: cannot write $debug_canonicalization: $!\n"; } if ($binary) { binmode STDIN; } my $dkim = new Mail::DKIM::Signer( Policy => \&signer_policy, Algorithm => $algorithm, Method => $method, Selector => $selector, KeyFile => "private.key", Debug_Canonicalization => $debugfh, ); while () { unless ($binary) { chomp $_; s/\015?$/\015\012/s; } $dkim->PRINT($_); } $dkim->CLOSE; if ($debugfh) { close $debugfh; print STDERR "wrote canonicalized message to $debug_canonicalization\n"; } print $dkim->signature->as_string . "\n"; sub signer_policy { my $dkim = shift; use Mail::DKIM::DkSignature; $dkim->domain($domain || $dkim->message_sender->host); my $class = $type eq "domainkeys" ? "Mail::DKIM::DkSignature" : $type eq "dkim" ? "Mail::DKIM::Signature" : die "unknown signature type '$type'\n"; my $sig = $class->new( Algorithm => $dkim->algorithm, Method => $dkim->method, Headers => $dkim->headers, Domain => $dkim->domain, Selector => $dkim->selector, defined($expiration) ? (Expiration => time() + $expiration) : (), defined($identity) ? (Identity => $identity) : (), ); $sig->protocol($key_protocol) if defined $key_protocol; foreach my $extra (@extra_tag) { my ($n, $v) = split /=/, $extra, 2; $sig->set_tag($n, $v); } $dkim->add_signature($sig); return; } __END__ =head1 NAME dkimsign.pl - computes a DKIM signature for an email message =head1 SYNOPSIS dkimsign.pl [options] < original_email.txt options: --type=TYPE --method=METHOD --selector=SELECTOR --expiration=INTEGER --debug-canonicalization=FILE dkimsign.pl --help to see a full description of the various options =head1 OPTIONS =over =item B<--expiration> Optional. Specify the desired signature expiration, as a delta from the signature timestamp. =item B<--type> Determines the desired signature. Use dkim for a DKIM-Signature, or domainkeys for a DomainKey-Signature. =item B<--method> Determines the desired canonicalization method. Possible values are simple, simple/simple, simple/relaxed, relaxed, relaxed/relaxed, relaxed/simple. =item B<--debug-canonicalization> Outputs the canonicalized message to the specified file, in addition to computing the DKIM signature. This is helpful for debugging canonicalization methods. =back =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2007 by Messiah College This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut Mail-DKIM-0.40/scripts/test_bare_rsa_sha1.pl0000755030404400001440000000053412055417140017654 0ustar jlongusers#!/usr/bin/perl -I../../.. use strict; use warnings; use Mail::DKIM::Algorithm::rsa_sha1; unless (-f "private.key") { die "File not found: private.key\n"; } my $rsa_sha1 = new Mail::DKIM::Algorithm::rsa_sha1( KeyFile => "private.key"); while () { chomp; $rsa_sha1->PRINT("$_\015\012"); } $rsa_sha1->CLOSE; print $rsa_sha1->sign; Mail-DKIM-0.40/scripts/test_nowsp_rsa_sha1.pl0000755030404400001440000000077412055417140020117 0ustar jlongusers#!/usr/bin/perl -I../../.. use strict; use warnings; use Mail::DKIM::Algorithm::rsa_sha1; use Mail::DKIM::Canonicalization::nowsp; unless (-f "private.key") { die "File not found: private.key\n"; } tie *rsa_sha1, "Mail::DKIM::Algorithm::rsa_sha1", "KeyFile" => "private.key"; my $nowsp = new Mail::DKIM::Canonicalization::nowsp( output_fh => *rsa_sha1); while () { chomp; $nowsp->PRINT("$_\015\012"); } $nowsp->CLOSE; my $rsa_sha1 = tied *rsa_sha1; print $rsa_sha1->sign; print "\n"; Mail-DKIM-0.40/HACKING.DKIM0000644030404400001440000001116612055417140013611 0ustar jlongusersHere is a list of components of Mail::DKIM, and the parts of the DKIM spec. they implement: http://mipassoc.org/dkim/specs/draft-allman-dkim-base-01.txt Canonicalization/DkimCommon.pm -- 5.4 Canonicalization/simple.pm -- 3.4.1 and 3.4.3 Canonicalization/relaxed.pm -- 3.4.2 and 3.4.4 http://mipassoc.org/mass/specs/draft-allman-dkim-base-00-10dc.html Algorithm/rsa_sha1.pm -- 3.3.1 Canonicalization/nowsp.pm -- 3.4.2 Signature.pm -- 3.5 Signer.pm -- 5 Verifier.pm -- 6 -- New version - update version numbers in these files: lib/Mail/DKIM.pm lib/Mail/DKIM/Verifier.pm lib/Mail/DKIM/Signer.pm lib/Mail/DKIM/Common.pm README -- New algorithm: create new algorithm class by copying and editing lib/Mail/DKIM/Algorithm/rsa_sha1.pm edit lib/Mail/DKIM/Common.pm: get_algorithm_class() - add a check for your new algorithm and return the name of your new algorithm class add a "use" line at the top of this file so that your algorithm class gets imported -- How the Verifier Works: First, the message headers are fed into the verifier object, where they are stored into a buffer until all headers have been seen. message +----------+ ------> | Verifier | +----------+ When the blank line separating the header from the body has been seen, the verifier looks at the headers, picking out and parsing each DKIM and DomainKey signature for verification. The signature specifies which algorithm and canonicalization method the verifier should use. The verifier creates an "algorithm object" corresponding to each signature. Each algorithm object will perform the verification for one signature. Now the verifier feeds the message headers from its buffer into each newly-constructed algorithm object, which in turn feeds the headers into the header canonicalizer, which canonicalizes the headers and feeds the result into a Digest object, which will compute the SHA-1 or SHA-256 digest. +----------+ +-----------+ +---------+ can. +--------+ | Verifier | headers | Algorithm | | Canoni- | headers | Header | | | ------> | | --> | calizer | ------> | Digest | +----------+ +-----------+ +---------+ +--------+ Now the verifier accepts the rest of the message (i.e. the body). The body is not buffered in memory; it gets piped through the algorithm, the body canonicalizer, and into the body digest. message +----------+ +-----------+ +---------+ can. +--------+ body | Verifier | | Algorithm | | Canoni- | body | Body | ------> | | > | | > | calizer | ---> | Digest | +----------+ +-----------+ +---------+ +--------+ Now the whole message has been read. The DKIM signature, minus the contents of the b= tag, is fed into the header canonicalizer, which gets fed into the header digest. modified DKIM +---------+ can. +--------+ signature | Canoni- | header | Header | ------------> | calizer | -----> | Digest | +---------+ +--------+ The header digest is computed, and the algorithm verifies it against the value of the b= tag in the signature. If it fails to match, the signature has "failed". Next, the body digest is computed, and compared with that in the signature. If it fails to match, the signature has "failed". Otherwise, the signature has "passed". -- Asynchronous DNS lookups In the dkimproxy case, as the message is received it is being "fed" into the DKIM verifier. The DKIM verifier can emit the DNS queries as soon as the header is read and parsed. Each signature is assumed to have a valid public key. When the entire message is finished, the DKIM verifier will wait for any DNS queries that haven't finished, then verify the signatures. In the SpamAssassin case, the message is already in memory. SpamAssassin will want to create the DKIM verifier and give it the message header early so that the DNS queries can be emitted. Then when the DNS responses are received, it can come back and give the verifier the rest of the message. Considering this, it might be useful to provide a slightly different API for SpamAssassin. One that explicitly specifies the header boundaries (when it expects DNS queries to be emitted), and the end of message (when the DNS responses are ready, or it's ok to block until they are). E.g. $dkim = Verifier->new(Resolver => $my_custom_resolver); $dkim->process_header($entire_header); $dkim->process_body($entire_message_body); $my_custom_resolver->wait_for_responses(); $dkim->do_verification(); my $result = $dkim->result; Mail-DKIM-0.40/README0000644030404400001440000000301112055417140012745 0ustar jlongusersMail-DKIM version 0.40 ====================== This module implements the various components of the DKIM and DomainKeys message-signing and verifying standards for Internet mail. It currently tries to implement these specifications: * RFC4871, for DKIM * RFC4870, for DomainKeys With each release, this module is getting bigger, but don't worry, most of the growth is from having more things to test with `make test'. INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES This module requires these other modules and libraries: Crypt::OpenSSL::RSA Digest::SHA Mail::Address (part of the MailTools package) MIME::Base64 Net::DNS USAGE Decide whether you want to "sign" or "verify" messages. To sign, see the Mail::DKIM::Signer module. To verify, see the Mail::DKIM::Verifier module. BUGS Some details of the specification are not completely implemented. See the TODO file for a list of things I know about. Report bugs to Jason Long . If `make test' fails, please include the versions of your installed Crypt::OpenSSL::RSA module and OpenSSL libraries. COPYRIGHT AND LICENCE Copyright (C) 2010 by Jason Long Copyright (C) 2006-2009 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. SEE ALSO The DKIM proxy home page, http://dkimproxy.sourceforge.net/. Mail-DKIM-0.40/lib/0000755030404400001440000000000012105005167012636 5ustar jlongusersMail-DKIM-0.40/lib/Mail/0000755030404400001440000000000012105005167013520 5ustar jlongusersMail-DKIM-0.40/lib/Mail/DKIM.pm0000644030404400001440000000401212055417140014601 0ustar jlongusers#!/usr/bin/perl use strict; use warnings; package Mail::DKIM; our $VERSION = 0.40; 1; __END__ =head1 NAME Mail::DKIM - Signs/verifies Internet mail with DKIM/DomainKey signatures =head1 SYNOPSIS # verify a message use Mail::DKIM::Verifier; # create a verifier object my $dkim = Mail::DKIM::Verifier->new(); # read an email from stdin, pass it into the verifier while () { # remove local line terminators chomp; s/\015$//; # use SMTP line terminators $dkim->PRINT("$_\015\012"); } $dkim->CLOSE; # what is the result of the verify? my $result = $dkim->result; =head1 DESCRIPTION This module implements the various components of the DKIM and DomainKeys message-signing and verifying standards for Internet mail. It currently tries to implement these specifications: =over =item RFC4871, for DKIM =item RFC4870, for DomainKeys =back The module uses an object-oriented interface. You use one of two different classes, depending on whether you are signing or verifying a message. To sign, use the L class. To verify, use the L class. Simple, eh? =head1 SEE ALSO L, L http://dkimproxy.sourceforge.net/ =head1 KNOWN BUGS Problems passing `make test' seem to usually point at a faulty DNS configuration on your machine, or something weird about your OpenSSL libraries. The "author signing policy" component is still under construction. The author signing policy is supposed to identify the practice of the message author, so you could for example reject a message from an author who claims they always sign their messages. See L. =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2007, 2009 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut Mail-DKIM-0.40/lib/Mail/DKIM/0000755030404400001440000000000012105005167014244 5ustar jlongusersMail-DKIM-0.40/lib/Mail/DKIM/AuthorDomainPolicy.pm0000644030404400001440000001736712055450054020375 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2009 Messiah College. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::AuthorDomainPolicy; use base "Mail::DKIM::Policy"; # base class is used for parse(), as_string() use Mail::DKIM::DNS; =head1 NAME Mail::DKIM::AuthorDomainPolicy - represents an Author Domain Signing Practices (ADSP) record =head1 DESCRIPTION The Author Domain Signing Policies (ADSP) record can be published by any domain to help a receiver know what to do when it encounters an unsigned message claiming to originate from that domain. The record is published as a DNS TXT record at _adsp._domainkey.DOMAIN where DOMAIN is the domain of the message's "From" address. More details about this record can be found by reading the specification itself at L. =head1 CONSTRUCTORS =head2 fetch() Lookup an ADSP record in DNS. my $policy = Mail::DKIM::AuthorDomainPolicy->fetch( Protocol => "dns", Author => 'jsmith@example.org', ); If the ADSP record is found and appears to be valid, an object containing that record's information will be constructed and returned. If the ADSP record is blank or simply does not exist, an object representing the default policy will be returned instead. (See also L.) If a DNS error occurs (e.g. SERVFAIL or time-out), this method will "die". =cut sub fetch { my $class = shift; my %prms = @_; my $self = eval { $class->SUPER::fetch(%prms) }; my $E = $@; if ($self && !$self->is_implied_default_policy) { return $self; } # didn't find a policy; check the domain itself { #FIXME- not good to have this code duplicated between #here and get_lookup_name() # if ($prms{Author} && !$prms{Domain}) { $prms{Domain} = ($prms{Author} =~ /\@([^@]*)$/ and $1); } unless ($prms{Domain}) { die "no domain to fetch policy for\n"; } my @resp = Mail::DKIM::DNS::query($prms{Domain}, "MX"); if (!@resp && $@ eq "NXDOMAIN") { return $class->nxdomain_policy; } } die $E if $E; return $self; } # get_lookup_name() - determine name of record to fetch # sub get_lookup_name { my $self = shift; my ($prms) = @_; # in ADSP, the record to fetch is determined based on the From header if ($prms->{Author} && !$prms->{Domain}) { $prms->{Domain} = ($prms->{Author} =~ /\@([^@]*)$/ and $1); } unless ($prms->{Domain}) { die "no domain to fetch policy for\n"; } # IETF seems poised to create policy records this way return "_adsp._domainkey." . $prms->{Domain}; } =head2 new() Construct a default policy object. my $policy = Mail::DKIM::AuthorDomainPolicy->new; =cut sub new { my $class = shift; return $class->parse(String => ""); } =head2 parse() Construct an ADSP record from a string. my $policy = Mail::DKIM::AuthorDomainPolicy->parse( String => "dkim=all", Domain => "aaa.example", ); =cut #undocumented private class method our $DEFAULT_POLICY; sub default { my $class = shift; $DEFAULT_POLICY ||= $class->new; return $DEFAULT_POLICY; } #undocumented private class method our $NXDOMAIN_POLICY; sub nxdomain_policy { my $class = shift; if (!$NXDOMAIN_POLICY) { $NXDOMAIN_POLICY = $class->new; $NXDOMAIN_POLICY->policy("NXDOMAIN"); } return $NXDOMAIN_POLICY; } =head1 METHODS =head2 apply() Apply the policy to the results of a DKIM verifier. my $result = $policy->apply($dkim_verifier); The caller must provide an instance of L, one which has already been fed the message being verified. Possible results are: =over =item accept The message is approved by the sender signing policy. =item reject The message is rejected by the sender signing policy. It can be considered very suspicious. =item neutral The message is neither approved nor rejected by the sender signing policy. It can be considered somewhat suspicious. =back Note: in the future, these values may become: none - no ADSP record is published pass - a passing signature is present fail - ADSP record is "all" and no passing signature is found discard - ADSP record is "discardable" and no passing signature is found nxdomain - the DNS domain does not exist temperror - transient error occurred permerror - non-transient error occurred =cut sub apply { my $self = shift; my ($dkim) = @_; # first_party indicates whether there is a DKIM signature with # a d= tag matching the address in the From: header my $first_party; my @passing_signatures = grep { $_->result && $_->result eq "pass" } $dkim->signatures; foreach my $signature (@passing_signatures) { my $author_domain = $dkim->message_originator->host; if (lc $author_domain eq lc $signature->domain) { # found a first party signature $first_party = 1; last; } } return "accept" if $first_party; return "reject" if ($self->signall_strict); return "neutral"; } =head2 is_implied_default_policy() Tells whether this policy implied. my $is_implied = $policy->is_implied_default_policy; If you fetch the policy for a particular domain, but that domain does not have a policy published, then the "default policy" is in effect. Use this method to detect when that happens. =cut sub is_implied_default_policy { my $self = shift; my $default_policy = ref($self)->default; return ($self == $default_policy); } =head2 location() Tells where the policy was fetched from. If the policy is domain-wide, this will be domain where the policy was published. If the policy is user-specific, TBD. If nothing is published for the domain, and the default policy was returned instead, the location will be C. =cut sub location { my $self = shift; return $self->{Domain}; } sub name { return "ADSP"; } =head2 policy() Get or set the outbound signing policy (dkim=) tag. my $sp = $policy->policy; Outbound signing policy for the entity. Possible values are: =over =item C The default. The entity may sign some or all email. =item C All mail from the domain is expected to be signed, using a valid Author signature, but the author does not suggest discarding mail without a valid signature. =item C All mail from the domain is expected to be signed, using a valid Author signature, and the author is so confident that non-signed mail claiming to be from this domain can be automatically discarded by the recipient's mail server. =item C<"NXDOMAIN"> The domain is out of scope, i.e., the domain does not exist in the DNS. =back =cut sub policy { my $self = shift; (@_) and $self->{tags}->{dkim} = shift; if (defined $self->{tags}->{dkim}) { return $self->{tags}->{dkim}; } else { return "unknown"; } } =head2 signall() True if policy is "all". =cut sub signall { my $self = shift; return $self->policy && ($self->policy =~ /all/i); } =head2 signall_discardable() True if policy is "strict". =cut sub signall_strict { my $self = shift; return $self->policy && ($self->policy =~ /discardable/i); } 1; =head1 BUGS =over =item * Section 4.3 of the specification says to perform a query on the domain itself just to see if it exists. This class is not currently doing that, i.e. it might report NXDOMAIN because _adsp._domainkey.example.org is nonexistent, but it should not be treated the same as example.org being nonexistent. =back =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2009 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut Mail-DKIM-0.40/lib/Mail/DKIM/Canonicalization/0000755030404400001440000000000012105005167017531 5ustar jlongusersMail-DKIM-0.40/lib/Mail/DKIM/Canonicalization/relaxed.pm0000644030404400001440000000456612055417140021530 0ustar jlongusers#!/usr/bin/perl # Copyright 2005 Messiah College. All rights reserved. # Jason Long # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::Canonicalization::relaxed; use base "Mail::DKIM::Canonicalization::DkimCommon"; use Carp; sub init { my $self = shift; $self->SUPER::init; $self->{canonicalize_body_empty_lines} = 0; } sub canonicalize_header { my $self = shift; croak "wrong number of parameters" unless (@_ == 1); my ($line) = @_; # # step 1: convert all header field names (not the header field values) # to lower case # if ($line =~ /^([^:]+):(.*)/s) { # lowercase field name $line = lc($1) . ":$2"; } # # step 2: unwrap all header field continuation lines... i.e. # remove any CRLF sequences that are followed by WSP # $line =~ s/\015\012(\s)/$1/g; # # step 3: convert all sequences of one or more WSP characters to # a single SP character # $line =~ s/[ \t]+/ /g; # # step 4: delete all WSP characters at the end of the header field value # $line =~ s/ \z//s; # # step 5: delete any WSP character remaining before and after the colon # separating the header field name from the header field value # $line =~ s/^([^:\s]+)\s*:\s*/$1:/; return $line; } sub canonicalize_body { my $self = shift; my ($multiline) = @_; $multiline =~ s/\015\012\z//s; # # step 1: ignore all white space at the end of lines # $multiline =~ s/[ \t]+(?=\015\012|\z)//g; # # step 2: reduce all sequences of WSP within a line to a single # SP character # $multiline =~ s/[ \t]+/ /g; $multiline .= "\015\012"; # # step 3: ignore empty lines at the end of the message body # (i.e. do not emit empty lines until a following nonempty line # is found) # my $empty_lines = $self->{canonicalize_body_empty_lines}; if ( $multiline =~ s/^((?:\015\012)+)// ) { # count & strip leading empty lines $empty_lines += length($1)/2; } if ($empty_lines > 0 && length($multiline) > 0) { # re-insert leading white if any nonempty lines exist $multiline = ("\015\012" x $empty_lines) . $multiline; $empty_lines = 0; } while ($multiline =~ /\015\012\015\012\z/) { # count & strip trailing empty lines chop $multiline; chop $multiline; $empty_lines++; } $self->{canonicalize_body_empty_lines} = $empty_lines; return $multiline; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/Canonicalization/dk_simple.pm0000644030404400001440000000255312055417140022045 0ustar jlongusers#!/usr/bin/perl # Copyright 2005 Messiah College. All rights reserved. # Jason Long # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::Canonicalization::dk_simple; use base "Mail::DKIM::Canonicalization::DkCommon"; use Carp; sub init { my $self = shift; $self->SUPER::init; $self->{canonicalize_body_empty_lines} = 0; } sub canonicalize_header { my $self = shift; croak "wrong number of parameters" unless (@_ == 1); my ($line) = @_; return $line; } sub canonicalize_body { my $self = shift; my ($multiline) = @_; # ignore empty lines at the end of the message body # # (i.e. do not emit empty lines until a following nonempty line # is found) # my $empty_lines = $self->{canonicalize_body_empty_lines}; if ( $multiline =~ s/^((?:\015\012)+)// ) { # count & strip leading empty lines $empty_lines += length($1)/2; } if ($empty_lines > 0 && length($multiline) > 0) { # re-insert leading white if any nonempty lines exist $multiline = ("\015\012" x $empty_lines) . $multiline; $empty_lines = 0; } while ($multiline =~ /\015\012\015\012\z/) { # count & strip trailing empty lines chop $multiline; chop $multiline; $empty_lines++; } $self->{canonicalize_body_empty_lines} = $empty_lines; return $multiline; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/Canonicalization/nowsp.pm0000644030404400001440000000150212055417140021235 0ustar jlongusers#!/usr/bin/perl # Copyright 2005 Messiah College. All rights reserved. # Jason Long # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::Canonicalization::nowsp; use base "Mail::DKIM::Canonicalization::DkimCommon"; use Carp; sub canonicalize_header { my $self = shift; croak "wrong number of parameters" unless (@_ == 1); my ($line) = @_; # remove all whitespace $line =~ s/[ \t\015\012]//g; if ($line =~ /^([^:]+):(.*)$/) { # lowercase field name $line = lc($1) . ":$2"; } return $line; } sub canonicalize_body { my $self = shift; my ($multiline) = @_; $multiline =~ s/[ \t\015\012]//g; return $multiline; } sub finish_body { my $self = shift; $self->SUPER::finish_body; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/Canonicalization/simple.pm0000644030404400001440000000347612055417140021374 0ustar jlongusers#!/usr/bin/perl # Copyright 2005 Messiah College. All rights reserved. # Jason Long # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::Canonicalization::simple; use base "Mail::DKIM::Canonicalization::DkimCommon"; use Carp; sub init { my $self = shift; $self->SUPER::init; $self->{canonicalize_body_empty_lines} = 0; } sub canonicalize_header { my $self = shift; croak "wrong number of parameters" unless (@_ == 1); my ($line) = @_; # # draft-allman-dkim-base-01.txt, section 3.4.1: # the "simple" header field canonicalization algorithm does not # change the header field in any way # return $line; } sub canonicalize_body { my $self = shift; my ($multiline) = @_; # # draft-allman-dkim-base-01.txt, section 3.4.3: # the "simple" body canonicalization algorithm ignores all # empty lines at the end of the message body # # # (i.e. do not emit empty lines until a following nonempty line # is found) # my $empty_lines = $self->{canonicalize_body_empty_lines}; if ( $multiline =~ s/^((?:\015\012)+)// ) { # count & strip leading empty lines $empty_lines += length($1)/2; } if (length($multiline) > 0) { $self->{canonicalize_body_started} = 1; if ($empty_lines > 0) { # re-insert leading white if any nonempty lines exist $multiline = ("\015\012" x $empty_lines) . $multiline; $empty_lines = 0; } } while ($multiline =~ /\015\012\015\012\z/) { # count & strip trailing empty lines chop $multiline; chop $multiline; $empty_lines++; } $self->{canonicalize_body_empty_lines} = $empty_lines; return $multiline; } sub finish_body { my $self = shift; $self->{canonicalize_body_started} or $self->output("\015\012"); $self->SUPER::finish_body; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/Canonicalization/dk_nofws.pm0000644030404400001440000000142012055417140021700 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2006 Messiah College. All rights reserved. # Jason Long # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::Canonicalization::dk_nofws; use base "Mail::DKIM::Canonicalization::dk_simple"; use Carp; sub canonicalize_header { my $self = shift; my ($line) = @_; $line =~ s/[ \t\015\012]//g; return $self->SUPER::canonicalize_header($line . "\015\012"); } sub canonicalize_body { my $self = shift; my ($multiline) = @_; $multiline =~ s/[ \t]//g; $multiline =~ s/\015(?!\012)//g; # standalone CR $multiline =~ s/([^\015])\012/$1/g; # standalone LF return $self->SUPER::canonicalize_body($multiline); } 1; Mail-DKIM-0.40/lib/Mail/DKIM/Canonicalization/DkCommon.pm0000644030404400001440000000752012055417140021604 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2006 Messiah College. All rights reserved. # Jason Long # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::Canonicalization::DkCommon; use base "Mail::DKIM::Canonicalization::Base"; use Carp; sub init { my $self = shift; $self->SUPER::init; $self->{header_count} = 0; } # similar to code in DkimCommon.pm sub add_header { #Note: canonicalization of headers is performed #in finish_header() my $self = shift; $self->{header_count}++; } sub finish_header { my $self = shift; my %args = @_; # RFC4870, 3.3: # h = A colon-separated list of header field names that identify the # headers presented to the signing algorithm. If present, the # value MUST contain the complete list of headers in the order # presented to the signing algorithm. # # In the presence of duplicate headers, a signer may include # duplicate entries in the list of headers in this tag. If a # header is included in this list, a verifier must include all # occurrences of that header, subsequent to the "DomainKey- # Signature:" header in the verification. # # RFC4870, 3.4.2.1: # * Each line of the email is presented to the signing algorithm in # the order it occurs in the complete email, from the first line of # the headers to the last line of the body. # * If the "h" tag is used, only those header lines (and their # continuation lines if any) added to the "h" tag list are included. # only consider headers AFTER my signature my @sig_headers; { my $s0 = @{$args{Headers}} - $self->{header_count}; my $s1 = @{$args{Headers}} - 1; @sig_headers = (@{$args{Headers}})[$s0 .. $s1]; } # check if signature specifies a list of headers my @sig_header_names = $self->{Signature}->headerlist; if (@sig_header_names) { # - first, group all header fields with the same name together # (using a hash of arrays) my %heads; foreach my $line (@sig_headers) { next unless $line =~ /^([^\s:]+)\s*:/; my $field_name = lc $1; $heads{$field_name} ||= []; push @{$heads{$field_name}}, $line; } # - second, count how many times each header field name appears # in the h= tag my %counts; foreach my $field_name (@sig_header_names) { $heads{lc $field_name} ||= []; $counts{lc $field_name}++; } # - finally, working backwards through the h= tag, # collect the headers we will be signing (last to first). # Normally, one occurrence of a name in the h= tag # correlates to one occurrence of that header being presented # to canonicalization, but if (working backwards) we are # at the first occurrence of that name, and there are # multiple headers of that name, then put them all in. # @sig_headers = (); while (my $field_name = pop @sig_header_names) { $counts{lc $field_name}--; if ($counts{lc $field_name} > 0) { # this field is named more than once in the h= tag, # so only take the last occuring of that header my $line = pop @{$heads{lc $field_name}}; unshift @sig_headers, $line if defined $line; } else { unshift @sig_headers, @{$heads{lc $field_name}}; $heads{lc $field_name} = []; } } } # iterate through each header, in the order determined above foreach my $line (@sig_headers) { if ($line =~ /^(from|sender)\s*:(.*)$/i) { my $field = $1; my $content = $2; $self->{interesting_header}->{lc $field} = $content; } $line =~ s/\015\012\z//s; $self->output($self->canonicalize_header($line . "\015\012")); } $self->output($self->canonicalize_body("\015\012")); } sub add_body { my $self = shift; my ($multiline) = @_; $self->output($self->canonicalize_body($multiline)); } sub finish_body { } sub finish_message { } 1; Mail-DKIM-0.40/lib/Mail/DKIM/Canonicalization/Base.pm0000644030404400001440000001143212055417140020744 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2007 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::Canonicalization::Base; use base "Mail::DKIM::MessageParser"; use Carp; sub new { my $class = shift; return $class->new_object(@_); } sub init { my $self = shift; $self->SUPER::init; unless ($self->{output} || $self->{output_fh} || $self->{output_digest} || $self->{buffer}) { $self->{result} = ""; $self->{buffer} = \$self->{result}; } } sub output { my $self = shift; # my ($output) = @_; # optimized away for speed my $out_fh = $self->{output_fh}; if ($out_fh) { print $out_fh @_; } if (my $digest = $self->{output_digest}) { $digest->add(@_); } if (my $out_obj = $self->{output}) { $out_obj->PRINT(@_); } if (my $buffer = $self->{buffer}) { ${$self->{buffer}} .= $_[0]; } # this supports Debug_Canonicalization if (my $debug = $self->{Debug_Canonicalization}) { if (UNIVERSAL::isa($debug, "SCALAR")) { $$debug .= $_[0]; } elsif (UNIVERSAL::isa($debug, "GLOB")) { print $debug @_; } elsif (UNIVERSAL::isa($debug, "IO::Handle")) { $debug->print(@_); } } } sub result { my $self = shift; return $self->{result}; } 1; __END__ =head1 NAME Mail::DKIM::Canonicalization::Base - base class for canonicalization methods =head1 SYNOPSIS # canonicalization results get output to STDOUT my $method = new Mail::DKIM::Canonicalization::relaxed( output_fh => *STDOUT, Signature => $dkim_signature); # add headers $method->add_header("Subject: this is the subject\015\012"); $method->finish_header(Headers => \@all_headers); # add body $method->add_body("This is the body.\015\012"); $method->add_body("Another two lines\015\012of the body.\015\012"); $method->finish_body; # this adds the signature to the end $method->finish_message; =head1 CONSTRUCTOR Use the new() method of the desired canonicalization implementation class to construct a canonicalization object. E.g. my $method = new Mail::DKIM::Canonicalization::relaxed( output_fh => *STDOUT, Signature => $dkim_signature); The constructors accept these arguments: =over =item Signature (Required) Provide the DKIM signature being constructed (if the message is being signed), or the DKIM signature being verified (if the message is being verified). The canonicalization method either writes parameters to the signature, or reads parameters from the signature (e.g. the h= tag). =item output If specified, the canonicalized message will be passed to this object with the PRINT method. =item output_digest If specified, the canonicalized message will be added to this digest. (Uses the add() method.) =item output_fh If specified, the canonicalized message will be written to this file handle. =back If none of the output parameters are specified, then the canonicalized message is appended to an internal buffer. The contents of this buffer can be accessed using the result() method. =head1 METHODS =head2 add_body() - feeds part of the body into the canonicalization $method->add_body("This is the body.\015\012"); $method->add_body("Another two lines\015\012of the body.\015\012"); The body should be fed one or more "lines" at a time. I.e. do not feed part of a line. =head2 finish_header() - called when the header has been completely parsed $method->finish_header(Headers => \@all_headers); Formerly the canonicalization object would only get the header data through successive invocations of add_header(). However, that required the canonicalization object to store a copy of the entire header so that it could choose the order in which headers were fed to the digest object. This is inefficient use of memory, since a message with many signatures may use many canonicalization objects and each canonicalization object has its own copy of the header. The headers array is an array of one element per header field, with the headers not processed/canonicalized in any way. =head2 result() my $result = $method->result; If you did not specify an object or handle to send the output to, the result of the canonicalization is stored in the canonicalization method itself, and can be accessed using this method. =head1 SEE ALSO L =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2007 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut Mail-DKIM-0.40/lib/Mail/DKIM/Canonicalization/DkimCommon.pm0000644030404400001440000000715412055417140022135 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2007 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::Canonicalization::DkimCommon; use base "Mail::DKIM::Canonicalization::Base"; use Carp; sub init { my $self = shift; $self->SUPER::init; $self->{body_count} = 0; $self->{body_truncated} = 0; # these canonicalization methods require signature to use $self->{Signature} or croak "no signature specified"; } # similar to code in DkCommon.pm sub add_header { #Note: canonicalization of headers is performed #in finish_header() } sub finish_header { my $self = shift; my %args = @_; # Headers are canonicalized in the order specified by the h= tag. # However, in the case of multiple instances of the same header name, # the headers will be canonicalized in reverse order (i.e. "from # the bottom of the header field block to the top"). # # This is described in 5.4 of RFC4871. # Since the bottom-most headers are to get precedence, we reverse # the headers here... (now the first header matching a particular # name is the header to insert) my @mess_headers = reverse @{$args{Headers}}; # presence of a h= tag is mandatory... unless (defined $self->{Signature}->headerlist) { die "Error: h= tag is required for this canonicalization\n"; } # iterate through the header field names specified in the signature my @sig_headers = $self->{Signature}->headerlist; foreach my $hdr_name (@sig_headers) { $hdr_name = lc $hdr_name; # find the specified header in the message inner_loop: for (my $i = 0; $i < @mess_headers; $i++) { my $hdr = $mess_headers[$i]; if ($hdr =~ /^([^\s:]+)\s*:/) { my $key = lc $1; if ($key eq $hdr_name) { # found it # remove it from our list, so if it occurs more than # once, we'll get the next header in line splice @mess_headers, $i, 1; $hdr =~ s/\015\012\z//s; $self->output($self->canonicalize_header($hdr) . "\015\012"); last inner_loop; } } } } } sub add_body { my $self = shift; my ($multiline) = @_; $multiline = $self->canonicalize_body($multiline); if ($self->{Signature}) { if (my $limit = $self->{Signature}->body_count) { my $remaining = $limit - $self->{body_count}; if (length($multiline) > $remaining) { $self->{body_truncated} += length($multiline) - $remaining; $multiline = substr($multiline, 0, $remaining); } } } $self->{body_count} += length($multiline); $self->output($multiline); } sub finish_body { } sub finish_message { my $self = shift; if ($self->{Signature}) { $self->output("\015\012"); # append the DKIM-Signature (without data) my $line = $self->{Signature}->as_string_without_data; # signature is subject to same canonicalization as headers $self->output($self->canonicalize_header($line)); } } sub body_count { my $self = shift; return $self->{body_count}; } sub body_truncated { my $self = shift; return $self->{body_truncated}; } 1; __END__ =head1 NAME Mail::DKIM::Canonicalization::DkimCommon - common canonicalization methods =head1 DESCRIPTION This class implements functionality that is common to all the currently-defined DKIM canonicalization methods, but not necessarily common with future canonicalization methods. For functionality that is common to all canonicalization methods (including future methods), see Mail::DKIM::Canonicalization::Base. =head1 SEE ALSO Mail::DKIM::Canonicalization::Base =cut Mail-DKIM-0.40/lib/Mail/DKIM/Key.pm0000644030404400001440000000322412055417140015335 0ustar jlongusers#!/usr/bin/perl # # Copyright 2006 Jason Long. All rights reserved. # # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. package Mail::DKIM::Key; use strict; sub cork { my $self = shift; (@_) and $self->{'CORK'} = shift; $self->{'CORK'} or $self->convert; $self->{'CORK'}; } sub data { my $self = shift; (@_) and $self->{'DATA'} = shift; $self->{'DATA'}; } sub errorstr { my $self = shift; (@_) and $self->{'ESTR'} = shift; $self->{'ESTR'}; } sub size { my $self = shift; return $self->cork->size * 8; } sub type { my $self = shift; (@_) and $self->{'TYPE'} = shift; $self->{'TYPE'}; } sub calculate_EM { my ($digest_algorithm, $digest, $emLen) = @_; # this function performs DER encoding of the algorithm ID for the # hash function and the hash value itself # It has this syntax: # DigestInfo ::= SEQUENCE { # digestAlgorithm AlgorithmIdentifier, # digest OCTET STRING # } # RFC 3447, page 42, provides the following octet values: my %digest_encoding = ( "SHA-1" => pack("H*", "3021300906052B0E03021A05000414"), "SHA-256" => pack("H*", "3031300d060960864801650304020105000420"), ); defined $digest_encoding{$digest_algorithm} or die "Unsupported digest algorithm '$digest_algorithm'"; my $T = $digest_encoding{$digest_algorithm} . $digest; my $tLen = length($T); if ($emLen < $tLen + 11) { die "Intended encoded message length too short."; } my $PS = chr(0xff) x ($emLen - $tLen - 3); my $EM = chr(0) . chr(1) . $PS . chr(0) . $T; return $EM; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/Algorithm/0000755030404400001440000000000012105005167016172 5ustar jlongusersMail-DKIM-0.40/lib/Mail/DKIM/Algorithm/rsa_sha1.pm0000644030404400001440000000237012055417140020235 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2006 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::Algorithm::rsa_sha1; use base "Mail::DKIM::Algorithm::Base"; use Carp; use MIME::Base64; use Digest::SHA; sub init_digests { my $self = shift; # initialize a SHA-1 Digest $self->{header_digest} = Digest::SHA->new(1); $self->{body_digest} = Digest::SHA->new(1); } sub sign { my $self = shift; croak "wrong number of arguments" unless (@_ == 1); my ($private_key) = @_; my $digest = $self->{header_digest}->digest; my $signature = $private_key->sign_digest("SHA-1", $digest); return encode_base64($signature, ""); } sub verify { my $self = shift; croak "wrong number of arguments" unless (@_ == 0); my $base64 = $self->signature->data; my $public_key = $self->signature->get_public_key; my $sig = decode_base64($base64); my $digest = $self->{header_digest}->digest; return unless $public_key->verify_digest("SHA-1", $digest, $sig); return $self->check_body_hash; } sub wants_pre_signature_headers { return 1; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/Algorithm/Base.pm0000644030404400001440000001555712055417140017421 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2007 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; use Mail::DKIM::Canonicalization::nowsp; use Mail::DKIM::Canonicalization::relaxed; use Mail::DKIM::Canonicalization::simple; package Mail::DKIM::Algorithm::Base; use Carp; use MIME::Base64; sub new { my $class = shift; my %args = @_; my $self = bless \%args, $class; $self->init; return $self; } sub init { my $self = shift; croak "no signature" unless $self->{Signature}; $self->{mode} = $self->{Signature}->signature ? "verify" : "sign"; # allows subclasses to set the header_digest and body_digest # properties $self->init_digests; my ($header_method, $body_method) = $self->{Signature}->canonicalization; my $header_class = $self->get_canonicalization_class($header_method); my $body_class = $self->get_canonicalization_class($body_method); $self->{canon} = $header_class->new( output_digest => $self->{header_digest}, Signature => $self->{Signature}, Debug_Canonicalization => $self->{Debug_Canonicalization}); $self->{body_canon} = $body_class->new( output_digest => $self->{body_digest}, Signature => $self->{Signature}, Debug_Canonicalization => $self->{Debug_Canonicalization}); } # override this method, please... # this method should set the "header_digest" and "body_digest" properties sub init_digests { die "not implemented"; } # private method - DKIM-specific sub get_canonicalization_class { my $self = shift; croak "wrong number of arguments" unless (@_ == 1); my ($method) = @_; my $class = $method eq "nowsp" ? "Mail::DKIM::Canonicalization::nowsp" : $method eq "relaxed" ? "Mail::DKIM::Canonicalization::relaxed" : $method eq "simple" ? "Mail::DKIM::Canonicalization::simple" : die "unknown method $method\n"; return $class; } =head1 NAME Mail::DKIM::Algorithm::Base - base class for DKIM "algorithms" =head1 SYNOPSIS my $algorithm = new Mail::DKIM::Algorithm::rsa_sha1( Signature => $dkim_signature ); # add headers $algorithm->add_header("Subject: this is the subject\015\012"); $algorithm->finish_header; # add body $algorithm->add_body("This is the body.\015\012"); $algorithm->add_body("Another line of the body.\015\012"); $algorithm->finish_body; # now sign or verify... # TODO... =head1 CONSTRUCTOR You should not create an object of this class directly. Instead, use one of the DKIM algorithm implementation classes, such as rsa_sha1: my $algorithm = new Mail::DKIM::Algorithm::rsa_sha1( Signature => $dkim_signature ); =head1 METHODS =head2 add_body() - feeds part of the body into the algorithm/canonicalization $algorithm->add_body("This is the body.\015\012"); $algorithm->add_body("Another line of the body.\015\012"); The body should be fed one "line" at a time. =cut sub add_body { my $self = shift; my $canon = $self->{body_canon} || $self->{canon}; $canon->add_body(@_); } =head2 add_header() - feeds a header field into the algorithm/canonicalization $algorithm->add_header("Subject: this is the subject\015\012"); The header must start with the header field name and continue through any folded lines (including the embedded sequences). It terminates with the at the end of the header field. =cut sub add_header { my $self = shift; $self->{canon}->add_header(@_); } =head2 finish_body() - signals the end of the message body $algorithm->finish_body Call this method when all lines from the body have been submitted. After calling this method, use sign() or verify() to get the results from the algorithm. =cut sub finish_body { my $self = shift; my $body_canon = $self->{body_canon} || $self->{canon}; $body_canon->finish_body; $self->finish_message; } =head2 finish_header() - signals the end of the header field block $algorithm->finish_header; Call this method when all the headers have been submitted. =cut sub finish_header { my $self = shift; $self->{canon}->finish_header(@_); } # checks the bh= tag of the signature to see if it has the same body # hash as computed by canonicalizing/digesting the actual message body. # If it doesn't match, a false value is returned, and the # verification_details property is set to "body has been altered" sub check_body_hash { my $self = shift; # The body_hash value is set in finish_message(), if we're operating # from a version of the DKIM spec that uses the bh= tag. Otherwise, # the signature shouldn't have a bh= tag to check. if ($self->{body_hash}) { my $body_hash = $self->{body_hash}; my $expected = decode_base64($self->{Signature}->body_hash); if ($body_hash ne $expected) { $self->{verification_details} = "body has been altered"; # print STDERR "I calculated " # . encode_base64($body_hash, "") . "\n"; # print STDERR "signature has " # . encode_base64($expected, "") . "\n"; return; } } return 1; } sub finish_message { my $self = shift; # DKIM requires the signature itself to be committed into the digest. # But first, we need to set the bh= tag on the signature, then # "prettify" it. $self->{body_hash} = $self->{body_digest}->digest; if ($self->{mode} eq "sign") { $self->{Signature}->body_hash( encode_base64($self->{body_hash}, "")); } if ($self->{mode} eq "sign") { $self->{Signature}->prettify; } my $sig_line = $self->{Signature}->as_string_without_data; my $canonicalized = $self->{canon}->canonicalize_header($sig_line); $self->{canon}->output($canonicalized); } =head2 sign() - generates a signature using a private key $base64 = $algorithm->sign($private_key); =cut # override this method, please... sub sign { die "Not implemented"; } =head2 signature() - get/set the signature worked on by this algorithm my $old_signature = $algorithm->signature; $algorithm->signature($new_signature); =cut sub signature { my $self = shift; @_ and $self->{Signature} = shift; return $self->{Signature}; } =head2 verify() - verifies a signature $result = $algorithm->verify(); Must be called after finish_body(). The result is a true/false value: true indicates the signature data is valid, false indicates it is invalid. For an invalid signature, details may be obtained from $algorithm->{verification_details} or $@. =cut # override this method, please... sub verify { die "Not implemented"; } 1; __END__ =head1 SEE ALSO L =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2005-2007 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut Mail-DKIM-0.40/lib/Mail/DKIM/Algorithm/rsa_sha256.pm0000644030404400001440000000240212055417140020405 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2006 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::Algorithm::rsa_sha256; use base "Mail::DKIM::Algorithm::Base"; use Carp; use MIME::Base64; use Digest::SHA; sub init_digests { my $self = shift; # initialize a SHA-256 Digest $self->{header_digest} = new Digest::SHA(256); $self->{body_digest} = new Digest::SHA(256); } sub sign { my $self = shift; croak "wrong number of arguments" unless (@_ == 1); my ($private_key) = @_; my $digest = $self->{header_digest}->digest; my $signature = $private_key->sign_digest("SHA-256", $digest); return encode_base64($signature, ""); } sub verify { my $self = shift; croak "wrong number of arguments" unless (@_ == 0); my $base64 = $self->signature->data; my $public_key = $self->signature->get_public_key; my $digest = $self->{header_digest}->digest; my $sig = decode_base64($base64); return unless $public_key->verify_digest("SHA-256", $digest, $sig); return $self->check_body_hash; } sub wants_pre_signature_headers { return 1; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/Algorithm/dk_rsa_sha1.pm0000644030404400001440000000567612055417140020727 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2006 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; use Mail::DKIM::Canonicalization::dk_simple; use Mail::DKIM::Canonicalization::dk_nofws; package Mail::DKIM::Algorithm::dk_rsa_sha1; use base "Mail::DKIM::Algorithm::Base"; use Carp; use MIME::Base64; use Digest::SHA; sub finish_header { my $self = shift; $self->SUPER::finish_header(@_); if ((my $s = $self->signature) && $self->{canon}->{interesting_header}) { my $sender = $self->{canon}->{interesting_header}->{sender}; $sender = defined($sender) && (Mail::Address->parse($sender))[0]; my $author = $self->{canon}->{interesting_header}->{from}; $author = defined($author) && (Mail::Address->parse($author))[0]; if ($sender) { $s->init_identity($sender->address, "header.sender"); } elsif ($author) { $s->init_identity($author->address, "header.from"); } } return; } sub get_canonicalization_class { my $self = shift; croak "wrong number of arguments" unless (@_ == 1); my ($method) = @_; my $class = $method eq "nofws" ? "Mail::DKIM::Canonicalization::dk_nofws" : $method eq "simple" ? "Mail::DKIM::Canonicalization::dk_simple" : die "unknown method $method\n"; return $class; } sub init { my $self = shift; die "no signature" unless $self->{Signature}; $self->{mode} = $self->{Signature}->signature ? "verify" : "sign"; # allows subclasses to set the header_digest and body_digest # properties $self->init_digests; my $method = $self->{Signature}->canonicalization; my $canon_class = $self->get_canonicalization_class($method); $self->{canon} = $canon_class->new( output_digest => $self->{header_digest}, Signature => $self->{Signature}, Debug_Canonicalization => $self->{Debug_Canonicalization}); } sub init_digests { my $self = shift; # initialize a SHA-1 Digest $self->{header_digest} = Digest::SHA->new(1); $self->{body_digest} = $self->{header_digest}; } sub sign { my $self = shift; croak "wrong number of arguments" unless (@_ == 1); my ($private_key) = @_; my $digest = $self->{header_digest}->digest; my $signature = $private_key->sign_digest("SHA-1", $digest); return encode_base64($signature, ""); } sub verify { my $self = shift; croak "wrong number of arguments" unless (@_ == 0); my $base64 = $self->signature->data; my $public_key = $self->signature->get_public_key; my $digest = $self->{header_digest}->digest; my $sig = decode_base64($base64); return $public_key->verify_digest("SHA-1", $digest, $sig); } sub finish_message { my $self = shift; # DomainKeys doesn't include the signature in the digest, # but we still want it to look "pretty" :). if ($self->{mode} eq "sign") { $self->{Signature}->prettify; } } sub wants_pre_signature_headers { return 0; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/DkimPolicy.pm0000644030404400001440000001622212055417140016653 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2007 Messiah College. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::DkimPolicy; use base "Mail::DKIM::Policy"; # base class is used for parse(), as_string() use Mail::DKIM::DNS; =head1 NAME Mail::DKIM::DkimPolicy - represents a DKIM Sender Signing Practices record =head1 DESCRIPTION The Sender Signing Practices (SSP) record can be published by any domain to help a receiver know what to do when it encounters an unsigned message claiming to originate from that domain. The record is published as a DNS TXT record at _policy._domainkey.DOMAIN where DOMAIN is the domain of the message's "From" address. This record format has been superceded by ADSP. See L for information about ADSP. It is implemented here because at one time it appeared this is what would be standardized by the IETF. It will be removed from Mail::DKIM at some point in the future. The last version of the SSP specification can be found at L. =head1 CONSTRUCTORS =head2 fetch() Lookup a DKIM signing practices record. my $policy = Mail::DKIM::DkimPolicy->fetch( Protocol => "dns", Author => 'jsmith@example.org', ); =cut # get_lookup_name() - determine name of record to fetch # sub get_lookup_name { my $self = shift; my ($prms) = @_; # in DKIM, the record to fetch is determined based on the From header if ($prms->{Author} && !$prms->{Domain}) { $prms->{Domain} = ($prms->{Author} =~ /\@([^@]*)$/ and $1); } unless ($prms->{Domain}) { die "no domain to fetch policy for\n"; } # IETF seems poised to create policy records this way return "_policy._domainkey." . $prms->{Domain}; } =head2 new() Construct a default policy object. my $policy = Mail::DKIM::DkimPolicy->new; =cut sub new { my $class = shift; return $class->parse(String => "o=~"); } #undocumented private class method our $DEFAULT_POLICY; sub default { my $class = shift; $DEFAULT_POLICY ||= $class->new; return $DEFAULT_POLICY; } =head1 METHODS =head2 apply() Apply the policy to the results of a DKIM verifier. my $result = $policy->apply($dkim_verifier); The caller must provide an instance of L, one which has already been fed the message being verified. Possible results are: =over =item accept The message is approved by the sender signing policy. =item reject The message is rejected by the sender signing policy. It can be considered very suspicious. =item neutral The message is neither approved nor rejected by the sender signing policy. It can be considered somewhat suspicious. =back =cut sub apply { my $self = shift; my ($dkim) = @_; # first_party indicates whether there is a DKIM signature with # an i= tag matching the address in the From: header my $first_party; #FIXME - if there are multiple verified signatures, each one # should be checked foreach my $signature ($dkim->signatures) { # only valid/verified signatures are considered next unless ($signature->result && $signature->result eq "pass"); my $oa = $dkim->message_originator->address; if ($signature->identity_matches($oa)) { # found a first party signature $first_party = 1; last; } } #TODO - consider testing flag return "accept" if $first_party; return "reject" if ($self->signall_strict && !$self->testing); if ($self->signall) { # is there ANY valid signature? my $verify_result = $dkim->result; return "accept" if $verify_result eq "pass"; } return "reject" if ($self->signall && !$self->testing); return "neutral"; } =head2 flags() Get or set the flags (t=) tag. A colon-separated list of flags. Flag values are: =over =item y The entity is testing signing practices, and the Verifier SHOULD NOT consider a message suspicious based on the record. =item s The signing practices apply only to the named domain, and not to subdomains. =back =cut sub flags { my $self = shift; (@_) and $self->{tags}->{t} = shift; $self->{tags}->{t}; } =head2 is_implied_default_policy() Is this policy implied? my $is_implied = $policy->is_implied_default_policy; If you fetch the policy for a particular domain, but that domain does not have a policy published, then the "default policy" is in effect. Use this method to detect when that happens. =cut sub is_implied_default_policy { my $self = shift; my $default_policy = ref($self)->default; return ($self == $default_policy); } =head2 location() Where the policy was fetched from. If the policy is domain-wide, this will be domain where the policy was published. If the policy is user-specific, TBD. If nothing is published for the domain, and the default policy was returned instead, the location will be C. =cut sub location { my $self = shift; return $self->{Domain}; } sub name { return "author"; } =head2 policy() Get or set the outbound signing policy (dkim=) tag. my $sp = $policy->policy; Outbound signing policy for the entity. Possible values are: =over =item C The default. The entity may sign some or all email. =item C All mail from the entity is signed. (The DKIM signature can use any domain, not necessarily matching the From: address.) =item C All mail from the entity is signed with Originator signatures. (The DKIM signature uses a domain matching the From: address.) =back =cut sub policy { my $self = shift; (@_) and $self->{tags}->{dkim} = shift; if (defined $self->{tags}->{dkim}) { return $self->{tags}->{dkim}; } elsif (defined $self->{tags}->{o}) { return $self->{tags}->{o}; } else { return "unknown"; } } =head2 signall() True if policy is "all". =cut sub signall { my $self = shift; return $self->policy && ($self->policy =~ /all/i || $self->policy eq "-"); # an older symbol for "all" } =head2 signall_strict() True if policy is "strict". =cut sub signall_strict { my $self = shift; return $self->policy && ($self->policy =~ /strict/i || $self->policy eq "!"); # "!" is an older symbol for "strict" } sub signsome { my $self = shift; $self->policy or return 1; $self->policy eq "~" and return 1; return; } =head2 testing() Checks the testing flag. my $testing = $policy->testing; If nonzero, the testing flag is set on the signing policy, and the verify should not consider a message suspicious based on this policy. =cut sub testing { my $self = shift; my $t = $self->flags; ($t && $t =~ /y/i) and return 1; return; } 1; =head1 BUGS =over =item * If a sender signing policy is not found for a given domain, the fetch() method should search the parent domains, according to section 4 of the dkim-ssp Internet Draft. =back =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2007 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut Mail-DKIM-0.40/lib/Mail/DKIM/DkPolicy.pm0000644030404400001440000001523712055417140016332 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2009 Messiah College. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::DkPolicy; use base "Mail::DKIM::Policy"; use Mail::DKIM::DNS; =head1 NAME Mail::DKIM::DkPolicy - represents a DomainKeys Sender Signing Policy record =head1 DESCRIPTION DomainKeys sender signing policies are described in RFC4870(historical). It is a record published in the message sender's (i.e. the person who transmitted the message) DNS that describes how they sign messages. =head1 CONSTRUCTORS =head2 fetch() - fetch a sender signing policy from DNS my $policy = Mail::DKIM::DkPolicy->fetch( Protocol => "dns", Sender => 'joe@example.org', ); The following named arguments are accepted: =over =item Protocol always specify "dns" =item Author the "author" of the message for which policy is being checked. This is the first email address in the "From" header. According to RFC 2822, section 3.6.2, the "From" header lists who is responsible for writing the message. =item Sender the "sender" of the message for which policy is being checked. This is the first email address in the "Sender" header, or if there is not a "Sender" header, the "From" header. According to RFC 2822, section 3.6.2, the "Sender" header lists who is responsible for transmitting the message. =back Depending on what type of policy is being checked, both the Sender and Author fields may need to be specified. If a DNS error or timeout occurs, an exception is thrown. Otherwise, a policy object of some sort will be returned. If no policy is actually published, then the "default policy" will be returned. To check when this happens, use my $is_default = $policy->is_implied_default_policy; =cut # get_lookup_name() - determine name of record to fetch # sub get_lookup_name { my $self = shift; my ($prms) = @_; # in DomainKeys, the record to fetch is determined based on the # Sender header, then the From header if ($prms->{Author} && !$prms->{Sender}) { $prms->{Sender} = $prms->{Author}; } if ($prms->{Sender} && !$prms->{Domain}) { # pick domain from email address $prms->{Domain} = ($prms->{Sender} =~ /\@([^@]*)$/ and $1); } unless ($prms->{Domain}) { die "no domain to fetch policy for\n"; } # IETF seems poised to create policy records this way #my $host = "_policy._domainkey." . $prms{Domain}; # but Yahoo! policy records are still much more common # see historic RFC4870, section 3.6 return "_domainkey." . $prms->{Domain}; } =head2 new() - construct a default policy object my $policy = Mail::DKIM::DkPolicy->new; =cut sub new { my $class = shift; return $class->parse(String => "o=~"); } =head2 parse() - gets a policy object by parsing a string my $policy = Mail::DKIM::DkPolicy->parse( String => "o=~; t=y" ); =cut #undocumented private class method our $DEFAULT_POLICY; sub default { my $class = shift; $DEFAULT_POLICY ||= $class->new; return $DEFAULT_POLICY; } =head1 METHODS =head2 apply() - apply the policy to the results of a DKIM verifier my $result = $policy->apply($dkim_verifier); The caller must provide an instance of L, one which has already been fed the message being verified. Possible results are: =over =item accept The message is approved by the sender signing policy. =item reject The message is rejected by the sender signing policy. =item neutral The message is neither approved nor rejected by the sender signing policy. It can be considered suspicious. =back =cut sub apply { my $self = shift; my ($dkim) = @_; my $first_party; foreach my $signature ($dkim->signatures) { next if $signature->result ne "pass"; my $oa = $dkim->message_sender->address; if ($signature->identity_matches($oa)) { # found a first party signature $first_party = 1; last; } } return "accept" if $first_party; return "reject" if ($self->signall && !$self->testing); return "neutral"; } =head2 flags() - get or set the flags (t=) tag A vertical-bar separated list of flags. =cut sub flags { my $self = shift; (@_) and $self->{tags}->{t} = shift; $self->{tags}->{t}; } =head2 is_implied_default_policy() - is this policy implied? my $is_implied = $policy->is_implied_default_policy; If you fetch the policy for a particular domain, but that domain does not have a policy published, then the "default policy" is in effect. Use this method to detect when that happens. =cut sub is_implied_default_policy { my $self = shift; my $default_policy = ref($self)->default; return ($self == $default_policy); } =head2 location() - where the policy was fetched from DomainKeys policies only have per-domain policies, so this will be the domain where the policy was published. If nothing is published for the domain, and the default policy was returned instead, the location will be C. =cut sub name { return "sender"; } =head2 note() - get or set the human readable notes (n=) tag Human readable notes regarding the record. Undef if no notes specified. =cut sub note { my $self = shift; (@_) and $self->{tags}->{n} = shift; $self->{tags}->{n}; } =head2 policy() - get or set the outbound signing policy (o=) tag my $sp = $policy->policy; Outbound signing policy for the entity. Possible values are: =over =item C<~> The default. The domain may sign some (but not all) email. =item C<-> The domain signs all email. =back =cut sub policy { my $self = shift; (@_) and $self->{tags}->{o} = shift; if (defined $self->{tags}->{o}) { return $self->{tags}->{o}; } else { return "~"; # the default } } =head2 signall() - true if policy is "-" =cut sub signall { my $self = shift; return ($self->policy && $self->policy eq "-"); } sub signsome { my $self = shift; $self->policy or return 1; $self->policy eq "~" and return 1; return; } =head2 testing() - checks the testing flag my $testing = $policy->testing; If nonzero, the testing flag is set on the signing policy, and the verify should not consider a message suspicious based on this policy. =cut sub testing { my $self = shift; my $t = $self->flags; ($t && $t =~ /y/i) and return 1; return; } 1; =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2009 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut Mail-DKIM-0.40/lib/Mail/DKIM/Signer.pm0000644030404400001440000004053012055417311016035 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2007 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; use Mail::DKIM::PrivateKey; use Mail::DKIM::Signature; =head1 NAME Mail::DKIM::Signer - generates a DKIM signature for a message =head1 SYNOPSIS use Mail::DKIM::Signer; use Mail::DKIM::TextWrap; #recommended # create a signer object my $dkim = Mail::DKIM::Signer->new( Algorithm => "rsa-sha1", Method => "relaxed", Domain => "example.org", Selector => "selector1", KeyFile => "private.key", ); # read an email from a file handle $dkim->load(*STDIN); # or read an email and pass it into the signer, one line at a time while () { # remove local line terminators chomp; s/\015$//; # use SMTP line terminators $dkim->PRINT("$_\015\012"); } $dkim->CLOSE; # what is the signature result? my $signature = $dkim->signature; print $signature->as_string; =head1 DESCRIPTION This class is the part of L responsible for generating signatures for a given message. You create an object of this class, specifying the parameters of the signature you wish to create, or specifying a callback function so that the signature parameters can be determined later. Next, you feed it the entire message using L, completing with L. Finally, use the L method to access the generated signatures. =head2 Pretty Signatures L includes a signature-wrapping module (which inserts linebreaks into the generated signature so that it looks nicer in the resulting message. To enable this module, simply call use Mail::DKIM::TextWrap; in your program before generating the signature. =head1 CONSTRUCTOR =head2 new() Construct an object-oriented signer. # create a signer using the default policy my $dkim = Mail::DKIM::Signer->new( Algorithm => "rsa-sha1", Method => "relaxed", Domain => "example.org", Selector => "selector1", KeyFile => "private.key", ); # create a signer using a custom policy my $dkim = Mail::DKIM::Signer->new( Policy => $policyfn, ); The "default policy" is to create a DKIM signature using the specified parameters, but only if the message's sender matches the domain. The following parameters can be passed to this new() method to influence the resulting signature: Algorithm, Method, Domain, Selector, KeyFile, Identity, Timestamp. If you want different behavior, you can provide a "signer policy" instead. A signer policy is a subroutine or class that determines signature parameters after the message's headers have been parsed. See the section L below for more information. See L for more information about policy objects. In addition to the parameters demonstrated above, the following are recognized: =over =item Key rather than using C, use C to use an already-loaded L object. =back =cut package Mail::DKIM::Signer; use base "Mail::DKIM::Common"; use Carp; our $VERSION = 0.40; # PROPERTIES # # public: # # $dkim->{Algorithm} # identifies what algorithm to use when signing the message # default is "rsa-sha1" # # $dkim->{Domain} # identifies what domain the message is signed for # # $dkim->{KeyFile} # name of the file containing the private key used to sign # # $dkim->{Method} # identifies what canonicalization method to use when signing # the message. default is "relaxed" # # $dkim->{Policy} # a signing policy (of type Mail::DKIM::SigningPolicy) # # $dkim->{Selector} # identifies name of the selector identifying the key # # $dkim->{Key} # the loaded private key # # private: # # $dkim->{algorithms} = [] # an array of algorithm objects... an algorithm object is created for # each signature being added to the message # # $dkim->{result} # result of the signing policy: "signed" or "skipped" # # $dkim->{signature} # the created signature (of type Mail::DKIM::Signature) sub init { my $self = shift; $self->SUPER::init; if (defined $self->{KeyFile}) { $self->{Key} ||= Mail::DKIM::PrivateKey->load( File => $self->{KeyFile}); } unless ($self->{"Algorithm"}) { # use default algorithm $self->{"Algorithm"} = "rsa-sha1"; } unless ($self->{"Method"}) { # use default canonicalization method $self->{"Method"} = "relaxed"; } unless ($self->{"Domain"}) { # use default domain $self->{"Domain"} = "example.org"; } unless ($self->{"Selector"}) { # use default selector $self->{"Selector"} = "unknown"; } } sub finish_header { my $self = shift; $self->{algorithms} = []; my $policy = $self->{Policy}; if (UNIVERSAL::isa($policy, "CODE")) { # policy is a subroutine ref my $default_sig = $policy->($self); unless (@{$self->{algorithms}} || $default_sig) { $self->{"result"} = "skipped"; return; } } elsif ($policy && $policy->can("apply")) { # policy is a Perl object or class my $default_sig = $policy->apply($self); unless (@{$self->{algorithms}} || $default_sig) { $self->{"result"} = "skipped"; return; } } unless (@{$self->{algorithms}}) { # no algorithms were created yet, so construct a signature # using the current signature properties # check properties unless ($self->{"Algorithm"}) { die "invalid algorithm property"; } unless ($self->{"Method"}) { die "invalid method property"; } unless ($self->{"Domain"}) { die "invalid header property"; } unless ($self->{"Selector"}) { die "invalid selector property"; } $self->add_signature( Mail::DKIM::Signature->new( Algorithm => $self->{"Algorithm"}, Method => $self->{"Method"}, Headers => $self->headers, Domain => $self->{"Domain"}, Selector => $self->{"Selector"}, Key => $self->{"Key"}, KeyFile => $self->{"KeyFile"}, ($self->{"Identity"} ? (Identity => $self->{"Identity"}) : ()), ($self->{"Timestamp"} ? (Timestamp => $self->{"Timestamp"}) : ()), )); } foreach my $algorithm (@{$self->{algorithms}}) { # output header as received so far into canonicalization foreach my $header (@{$self->{headers}}) { $algorithm->add_header($header); } $algorithm->finish_header(Headers => $self->{headers}); } } sub finish_body { my $self = shift; foreach my $algorithm (@{$self->{algorithms}}) { # finished canonicalizing $algorithm->finish_body; # load the private key file if necessary my $signature = $algorithm->signature; my $key = $signature->{Key} || $signature->{KeyFile} || $self->{Key} || $self->{KeyFile}; if (defined($key) && !ref($key)) { $key = Mail::DKIM::PrivateKey->load( File => $key); } $key or die "no key available to sign with\n"; # compute signature value my $signb64 = $algorithm->sign($key); $signature->data($signb64); # insert linebreaks in signature data, if desired $signature->prettify_safe(); $self->{signature} = $signature; $self->{result} = "signed"; } } =head1 METHODS =head2 PRINT() Feed part of the message to the signer. $dkim->PRINT("a line of the message\015\012"); Feeds content of the message being signed into the signer. The API is designed this way so that the entire message does NOT need to be read into memory at once. Please note that although the PRINT() method expects you to use SMTP-style line termination characters, you should NOT use the SMTP-style dot-stuffing technique described in RFC 2821 section 4.5.2. Nor should you use a . sequence to terminate the message. =head2 CLOSE() Call this when finished feeding in the message. $dkim->CLOSE; This method finishes the canonicalization process, computes a hash, and generates a signature. =head2 add_signature() Used by signer policy to create a new signature. $dkim->add_signature(new Mail::DKIM::Signature(...)); Signer policies can use this method to specify complete parameters for the signature to add, including what type of signature. For more information, see L. =cut sub add_signature { my $self = shift; my $signature = shift; # create a canonicalization filter and algorithm my $algorithm_class = $signature->get_algorithm_class( $signature->algorithm) or die "unsupported algorithm " . ($signature->algorithm || "") . "\n"; my $algorithm = $algorithm_class->new( Signature => $signature, Debug_Canonicalization => $self->{Debug_Canonicalization}, ); push @{$self->{algorithms}}, $algorithm; return; } =head2 algorithm() Get or set the selected algorithm. $alg = $dkim->algorithm; $dkim->algorithm("rsa-sha1"); =cut sub algorithm { my $self = shift; if (@_ == 1) { $self->{Algorithm} = shift; } return $self->{Algorithm}; } =head2 domain() Get or set the selected domain. $alg = $dkim->domain; $dkim->domain("example.org"); =cut sub domain { my $self = shift; if (@_ == 1) { $self->{Domain} = shift; } return $self->{Domain}; } =head2 load() Load the entire message from a file handle. $dkim->load($file_handle); Reads a complete message from the designated file handle, feeding it into the signer. The message must use line terminators (same as the SMTP protocol). =cut =head2 headers() Determine which headers to put in signature. my $headers = $dkim->headers; This is a string containing the names of the header fields that will be signed, separated by colons. =cut # these are headers that "should" be included in the signature, # according to the DKIM spec. my @DEFAULT_HEADERS = qw(From Sender Reply-To Subject Date Message-ID To Cc MIME-Version Content-Type Content-Transfer-Encoding Content-ID Content-Description Resent-Date Resent-From Resent-Sender Resent-To Resent-cc Resent-Message-ID In-Reply-To References List-Id List-Help List-Unsubscribe List-Subscribe List-Post List-Owner List-Archive); sub headers { my $self = shift; croak "unexpected argument" if @_; # these are the header fields we found in the message we're signing my @found_headers = @{$self->{header_field_names}}; # these are the headers we actually want to sign my @wanted_headers = @DEFAULT_HEADERS; if ($self->{Headers}) { push @wanted_headers, split /:/, $self->{Headers}; } my @headers = grep { my $a = $_; scalar grep { lc($a) eq lc($_) } @wanted_headers } @found_headers; return join(":", @headers); } # return nonzero if this is header we should sign sub want_header { my $self = shift; my ($header_name) = @_; #TODO- provide a way for user to specify which headers to sign return scalar grep { lc($_) eq lc($header_name) } @DEFAULT_HEADERS; } =head2 key() Get or set the private key object. my $key = $dkim->key; $dkim->key(Mail::DKIM::PrivateKey->load(File => "private.key")); The key object can be any object that implements the L. (Providing your own object can be useful if your actual keys are stored out-of-process.) If you use this method to specify a private key, do not use L. =cut sub key { my $self = shift; if (@_) { $self->{Key} = shift; $self->{KeyFile} = undef; } return $self->{Key}; } =head2 key_file() Get or set the filename containing the private key. my $filename = $dkim->key_file; $dkim->key_file("private.key"); If you use this method to specify a private key file, do not use L. =cut sub key_file { my $self = shift; if (@_) { $self->{Key} = undef; $self->{KeyFile} = shift; } return $self->{KeyFile}; } =head2 method() Get or set the selected canonicalization method. $alg = $dkim->method; $dkim->method("relaxed"); =cut sub method { my $self = shift; if (@_ == 1) { $self->{Method} = shift; } return $self->{Method}; } =head2 message_originator() Access the "From" header. my $address = $dkim->message_originator; Returns the "originator address" found in the message, as a L object. This is typically the (first) name and email address found in the From: header. If there is no From: header, then an empty L object is returned. To get just the email address part, do: my $email = $dkim->message_originator->address; See also L. =head2 message_sender() Access the "From" or "Sender" header. my $address = $dkim->message_sender; Returns the "sender" found in the message, as a L object. This is typically the (first) name and email address found in the Sender: header. If there is no Sender: header, it is the first name and email address in the From: header. If neither header is present, then an empty L object is returned. To get just the email address part, do: my $email = $dkim->message_sender->address; The "sender" is the mailbox of the agent responsible for the actual transmission of the message. For example, if a secretary were to send a message for another person, the "sender" would be the secretary and the "originator" would be the actual author. =cut =head2 selector() Get or set the current key selector. $alg = $dkim->selector; $dkim->selector("alpha"); =cut sub selector { my $self = shift; if (@_ == 1) { $self->{Selector} = shift; } return $self->{Selector}; } =head2 signature() Access the generated signature object. my $signature = $dkim->signature; Returns the generated signature. The signature is an object of type L. If multiple signatures were generated, this method returns the last one. The signature (as text) should be B to the message to make the resulting message. At the very least, it should precede any headers that were signed. =head2 signatures() Access list of generated signature objects. my @signatures = $dkim->signatures; Returns all generated signatures, as a list. =cut sub signatures { my $self = shift; croak "no arguments allowed" if @_; return map { $_->signature } @{$self->{algorithms}}; } =head1 SIGNER POLICIES The new() constructor takes an optional Policy argument. This can be a Perl object or class with an apply() method, or just a simple subroutine reference. The method/subroutine will be called with the signer object as an argument. The policy is responsible for checking the message and specifying signature parameters. The policy must return a nonzero value to create the signature, otherwise no signature will be created. E.g., my $policyfn = sub { my $dkim = shift; # specify signature parameters $dkim->algorithm("rsa-sha1"); $dkim->method("relaxed"); $dkim->domain("example.org"); $dkim->selector("mx1"); # return true value to create the signature return 1; }; Or the policy object can actually create the signature, using the add_signature method within the policy object. If you add a signature, you do not need to return a nonzero value. This mechanism can be utilized to create multiple signatures, or to create the older DomainKey-style signatures. my $policyfn = sub { my $dkim = shift; $dkim->add_signature( new Mail::DKIM::Signature( Algorithm => "rsa-sha1", Method => "relaxed", Headers => $dkim->headers, Domain => "example.org", Selector => "mx1", )); $dkim->add_signature( new Mail::DKIM::DkSignature( Algorithm => "rsa-sha1", Method => "nofws", Headers => $dkim->headers, Domain => "example.org", Selector => "mx1", )); return; }; If no policy is specified, the default policy is used. The default policy signs every message using the domain, algorithm, method, and selector specified in the new() constructor. =head1 SEE ALSO L =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2007 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut 1; Mail-DKIM-0.40/lib/Mail/DKIM/DkSignature.pm0000644030404400001440000002140012104756576017037 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2006 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; use Mail::DKIM::PublicKey; use Mail::DKIM::Algorithm::dk_rsa_sha1; package Mail::DKIM::DkSignature; use base "Mail::DKIM::Signature"; use Carp; =head1 NAME Mail::DKIM::DkSignature - represents a DomainKeys-Signature header =head1 CONSTRUCTORS =head2 new() Create a new DomainKey signature from parameters my $signature = Mail::DKIM::DkSignature->new( [ Algorithm => "rsa-sha1", ] [ Signature => $base64, ] [ Method => "simple", ] [ Domain => "example.org", ] [ Headers => "from:subject:date:message-id", ] [ Query => "dns", ] [ Selector => "alpha", ] [ Key => $private_key, ] ); =cut sub new { my $type = shift; my %prms = @_; my $self = {}; bless $self, $type; $self->algorithm($prms{'Algorithm'} || "rsa-sha1"); $self->signature($prms{'Signature'}); $self->canonicalization($prms{'Method'} || "simple"); $self->domain($prms{'Domain'}); $self->headerlist($prms{'Headers'}); $self->protocol($prms{'Query'} || "dns"); $self->selector($prms{'Selector'}); $self->key($prms{'Key'}) if defined $prms{'Key'}; return $self; } =head2 parse() Create a new signature from a DomainKey-Signature header my $sig = Mail::DKIM::DkSignature->parse( "DomainKey-Signature: a=rsa-sha1; b=yluiJ7+0=; c=nofws" ); Constructs a signature by parsing the provided DomainKey-Signature header content. You do not have to include the header name (i.e. "DomainKey-Signature:") but it is recommended, so the header name can be preserved and returned the same way in L. Note: The input to this constructor is in the same format as the output of the as_string method. =cut sub parse { my $class = shift; croak "wrong number of arguments" unless (@_ == 1); my ($string) = @_; # remove line terminator, if present $string =~ s/\015\012\z//; # remove field name, if present my $prefix; if ($string =~ /^(domainkey-signature:)(.*)/si) { # save the field name (capitalization), so that it can be # restored later $prefix = $1; $string = $2; } my $self = $class->Mail::DKIM::KeyValueList::parse($string); $self->{prefix} = $prefix; return $self; } =head1 METHODS =cut =head2 as_string() Convert the signature header as a string. print $signature->as_string . "\n"; outputs DomainKey-Signature: a=rsa-sha1; b=yluiJ7+0=; c=nofws As shown in the example, the as_string method can be used to generate the DomainKey-Signature that gets prepended to a signed message. =cut sub as_string_without_data { croak "as_string_without_data not implemented"; } sub body_count { croak "body_count not implemented"; } sub body_hash { croak "body_hash not implemented"; } =head2 algorithm() Get or set the algorithm (a=) field The algorithm used to generate the signature. Defaults to "rsa-sha1", an RSA-signed SHA-1 digest. =cut sub algorithm { my $self = shift; if (@_) { $self->set_tag("a", shift); } my $a = $self->get_tag("a"); return defined $a && $a ne '' ? lc $a : 'rsa-sha1'; } =head2 canonicalization() Get or set the canonicalization (c=) field. $signature->canonicalization("nofws"); $signature->canonicalization("simple"); $method = $signature->canonicalization; Message canonicalization (default is "simple"). This informs the verifier of the type of canonicalization used to prepare the message for signing. =cut sub canonicalization { my $self = shift; croak "too many arguments" if (@_ > 1); if (@_) { $self->set_tag("c", shift); } return lc($self->get_tag("c")) || "simple"; } sub DEFAULT_PREFIX { return "DomainKey-Signature:"; } =head2 domain() Get or set the domain (d=) field. my $d = $signature->domain; # gets the domain value $signature->domain("example.org"); # sets the domain value The domain of the signing entity, as specified in the signature. This is the domain that will be queried for the public key. =cut sub domain { my $self = shift; if (@_) { $self->set_tag("d", shift); } my $d = $self->get_tag("d"); return defined $d ? lc $d : undef; } sub expiration { my $self = shift; croak "cannot change expiration on " . ref($self) if @_; return undef; } use MIME::Base64; sub check_canonicalization { my $self = shift; my $c = $self->canonicalization; my @known = ("nofws", "simple"); return unless (grep { $_ eq $c } @known); return 1; } # checks whether the protocol found on this subject is valid for # fetching the public key # returns a true value if protocol is "dns", false otherwise # sub check_protocol { my $self = shift; my $protocol = $self->protocol; return unless $protocol; return ($protocol eq "dns"); } sub check_version { #DomainKeys doesn't have a v= tag return 1; } sub get_algorithm_class { my $self = shift; croak "wrong number of arguments" unless (@_ == 1); my ($algorithm) = @_; my $class = $algorithm eq "rsa-sha1" ? "Mail::DKIM::Algorithm::dk_rsa_sha1" : undef; return $class; } # get_public_key - same as parent class sub hash_algorithm { my $self = shift; my $algorithm = $self->algorithm; return $algorithm eq "rsa-sha1" ? "sha1" : undef; } =head2 headerlist() Get or set the signed header fields (h=) field. $signature->headerlist("a:b:c"); my $headerlist = $signature->headerlist; my @headers = $signature->headerlist; Signed header fields. A colon-separated list of header field names that identify the header fields presented to the signing algorithm. In scalar context, the list of header field names will be returned as a single string, with the names joined together with colons. In list context, the header field names will be returned as a list. =cut #sub headerlist # is in Signature.pm =head2 identity() Get the signing identity. my $i = $signature->identity; In DomainKey signatures, the signing identity is the first address found in the Sender header or the From header. This field is populated by the L when processing a DomainKey signature. =cut sub identity { my $self = shift; croak "cannot change identity on " . ref($self) if @_; return $self->{dk_identity}; } =head2 identity_source() Determine which header had the identity. my $source = $signature->identity_source; If the message is being verified, this method will tell you which of the message headers was used to determine the signature identity. Possible values are "header.sender" and "header.from". =cut sub identity_source { my $self = shift; croak "unexpected argument" if @_; return $self->{dk_identity_source}; } # init_identity() - initialize the DomainKeys concept of identity # # The signing identity of a DomainKeys signature is the sender # of the message itself, i.e. the address in the Sender/From header. # The sender may not be known when the signature object is # constructed (since the signature usually precedes the From/Sender # header), so use this method when you have the From/Sender value. # See also finish_header() in Mail::DKIM::Verifier. # sub init_identity { my $self = shift; $self->{dk_identity} = shift; $self->{dk_identity_source} = shift; } sub method { croak "method not implemented (use canonicalization instead)"; } =head2 protocol() Get or set the query methods (q=) field. A colon-separated list of query methods used to retrieve the public key (default is "dns"). =cut sub protocol { my $self = shift; (@_) and $self->set_tag("q", shift); # although draft-delany-domainkeys-base-06 does mandate presence of a # q=dns tag, it is quote common that q tag is missing - be merciful return !defined($self->get_tag("q")) ? 'dns' : $self->get_tag("q"); } =head2 selector() Get or set the selector (s=) field. The selector subdivides the namespace for the "d=" (domain) tag. =cut # same as parent class =head2 signature() Get or set the signature data (b=) field. The signature data. Whitespace is automatically stripped from the returned value. =cut # same as parent class sub timestamp { croak "timestamp not implemented"; } sub version { croak "version not implemented"; } =head1 SEE ALSO L for DKIM-Signature headers =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2007,2010 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut 1; Mail-DKIM-0.40/lib/Mail/DKIM/PublicKey.pm0000644030404400001440000002444612104756576016523 0ustar jlongusers#!/usr/bin/perl # Copyright 2005 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::PublicKey; use base ("Mail::DKIM::KeyValueList", "Mail::DKIM::Key"); *calculate_EM = \&Mail::DKIM::Key::calculate_EM; use Mail::DKIM::DNS; sub new { my $type = shift; my %prms = @_; my $self = {}; $self->{'GRAN'} = $prms{'Granularity'}; $self->{'NOTE'} = $prms{'Note'}; $self->{'TEST'} = $prms{'Testing'}; $self->{'TYPE'} = ($prms{'Type'} or "rsa"); $self->{'DATA'} = $prms{'Data'}; bless $self, $type; } =head1 CONSTRUCTOR =head2 fetch() - retrieve a public key record from DNS my $public_key = Mail::DKIM::PublicKey->fetch( Protocol => "dns", Selector => "brisbane", Domain => "example.com", ); If the public key is found, a L object is returned, representing the information found in DNS. If the public key does not exist in DNS, then C is returned. If a DNS error occurs while fetching the key, then this method will C. If the public key was found, but is not valid (e.g. it is "revoked"), then this method will C. =cut sub fetch { my $class = shift; my $waiter = $class->fetch_async(@_); my $self = $waiter->(); return $self; } #EXPERIMENTAL1 # my $x = Mail::DKIM::PublicKey->fetch_async( # Protocol => "dns", # etc., # ); # my $public_key = $x->(); #EXPERIMENTAL2 # my ($pubk, $E); # my $x = Mail::DKIM::PublicKey->fetch_async( # Protocol => "dns", # etc., # Callbacks => { }, # ); # return $x->(); sub fetch_async { my $class = shift; my %prms = @_; my ($query_type, $query_options) = split(/\//, $prms{Protocol}, 2); if (lc($query_type || "") ne "dns") { die "unknown query type '".($query_type||"")."'\n"; } defined($prms{Selector}) && length($prms{Selector}) or die "invalid/missing Selector\n"; defined($prms{Domain}) && length($prms{Domain}) or die "invalid/missing Domain\n"; my $host = $prms{Selector} . "._domainkey." . $prms{Domain}; my %callbacks = %{$prms{Callbacks} || {}}; my $on_success = $callbacks{Success} || sub { $_[0] }; $callbacks{Success} = sub { my @resp = @_; unless (@resp) { # no requested resource records or NXDOMAIN, return $on_success->(); } my $strn; foreach my $rr (@resp) { next unless $rr->type eq "TXT"; # join with no intervening spaces, RFC 6376 if (Net::DNS->VERSION >= 0.69) { # must call txtdata() in a list context $strn = join "", $rr->txtdata; } else { # char_str_list method is 'historical' $strn = join "", $rr->char_str_list; } last; } $strn or return $on_success->(); my $self = $class->parse($strn); $self->{Selector} = $prms{'Selector'}; $self->{Domain} = $prms{'Domain'}; $self->check; return $on_success->($self); }; # # perform DNS query for public key... # my $waiter = Mail::DKIM::DNS::query_async( $host, "TXT", Callbacks => \%callbacks, ); return $waiter; } =head1 METHODS =cut # check syntax of the public key # throw an error if any errors are detected sub check { my $self = shift; # check public key version tag if (my $v = $self->get_tag("v")) { unless ($v eq "DKIM1") { die "unsupported version\n"; } } # check public key granularity my $g = $self->granularity; # check key type if (my $k = $self->get_tag("k")) { unless ($k eq "rsa") { die "unsupported key type\n"; } } # check public-key data my $p = $self->data; if (not defined $p) { die "missing p= tag\n"; } if ($p eq "") { die "revoked\n"; } unless ($p =~ /^[A-Za-z0-9\+\/\=]+$/) { die "invalid data\n"; } # have OpenSSL load the key eval { $self->cork; }; if ($@) { # see also finish_body chomp (my $E = $@); if ($E =~ /(OpenSSL error: .*?) at /) { $E = "$1"; } elsif ($E =~ /^(panic:.*?) at /) { $E = "OpenSSL $1"; } die "$E\n"; } # check service type if (my $s = $self->get_tag("s")) { my @list = split(/:/, $s); unless (grep { $_ eq "*" || $_ eq "email" } @list) { die "does not support email\n"; } } return 1; } # check_granularity() - check whether this key matches signature identity # # a public key record can restrict what identities it may sign with, # g=, granularity, restricts the local part of the identity # t=s, restricts whether subdomains can be used # # This method returns true if the given identity is allowed by this # public key; it returns false otherwise. # If false is returned, you can check C<$@> for an explanation of # why. # sub check_granularity { my $self = shift; my ($identity, $empty_g_means_wildcard) = @_; # check granularity my $g = $self->granularity; # yuck- what is this $empty_g_means_wildcard parameter? # well, it turns out that with DomainKeys signatures, # an empty g= is the same as g=* if ($g eq "" && $empty_g_means_wildcard) { $g = "*"; } # split i= value into a "local part" and a "domain part" my ($local_part, $domain_part); if ($identity =~ /^(.*)\@([^@]*)$/) { $local_part = $1; $domain_part = $2; } else { $local_part = ""; $domain_part = $identity; } my ($begins, $ends) = split /\*/, $g, 2; if (defined $ends) { # the g= tag contains an asterisk # the local part must be at least as long as the pattern if (length($local_part) < length($begins) + length($ends) or # the local part must begin with $begins substr($local_part, 0, length($begins)) ne $begins or # the local part must end with $ends (length($ends) && substr($local_part, -length($ends)) ne $ends)) { $@ = "granularity mismatch\n"; return; } } else { if ($g eq "") { $@ = "granularity is empty\n"; return; } unless ($local_part eq $begins) { $@ = "granularity mismatch\n"; return; } } # check subdomains if ($self->subdomain_flag) { unless ($domain_part eq lc($self->{'Domain'})) { $@ = "does not support signing subdomains\n"; return; } } return 1; } # returns true if the actual hash algorithm used is listed by this # public key; dies otherwise # sub check_hash_algorithm { my $self = shift; my ($hash_algorithm) = @_; # check hash algorithm if (my $h = $self->get_tag("h")) { my @list = split(/:/, $h); unless (grep { $_ eq $hash_algorithm } @list) { die "does not support hash algorithm '$hash_algorithm'\n"; } } return 1; } # Create an OpenSSL public key object from the Base64-encoded data # found in this public key's DNS record. The OpenSSL object is saved # in the "cork" property. sub convert { use Crypt::OpenSSL::RSA; my $self = shift; $self->data or return; # have to PKCS1ify the pubkey because openssl is too finicky... my $cert = "-----BEGIN PUBLIC KEY-----\n"; for (my $i = 0; $i < length $self->data; $i += 64) { $cert .= substr $self->data, $i, 64; $cert .= "\n"; } $cert .= "-----END PUBLIC KEY-----\n"; my $cork = Crypt::OpenSSL::RSA->new_public_key($cert) or die "unable to generate public key object"; # segfaults on my machine # $cork->check_key or # return; $self->cork($cork); return 1; } sub verify { my $self = shift; my %prms = @_; my $rtrn; eval { $rtrn = $self->cork->verify($prms{'Text'}, $prms{'Signature'}); }; $@ and $self->errorstr($@), return; return $rtrn; } =head2 granularity() - get or set the granularity (g=) field my $g = $public_key->granularity; $public_key->granularity("*"); Granularity of the key. The value must match the Local-part of the effective "i=" tag of the DKIM-Signature header field. The granularity is a literal value, or a pattern with a single '*' wildcard character that matches zero or more characters. If no granularity is defined, then the default value, '*', will be returned. =cut sub granularity { my $self = shift; # set new granularity if provided (@_) and $self->set_tag("g", shift); my $g = $self->get_tag("g"); if (defined $g) { return $g; } else { return '*'; } } sub notes { my $self = shift; (@_) and $self->set_tag("n", shift); return $self->get_tag("n"); } sub data { my $self = shift; (@_) and $self->set_tag("p", shift); my $p = $self->get_tag("p"); # remove whitespace (actually only LWSP is allowed) $p =~ tr/\015\012 \t//d if defined $p; return $p; } sub flags { my $self = shift; (@_) and $self->set_tag("t", shift); return $self->get_tag("t") || ""; } # subdomain_flag() - checks whether "s" is specified in flags # # returns true if "s" is found, false otherwise # sub subdomain_flag { my $self = shift; my @flags = split /:/, $self->flags; return grep { $_ eq "s" } @flags; } sub revoked { my $self = shift; $self->data or return 1; return; } sub testing { my $self = shift; my $flags = $self->flags; my @flaglist = split(/:/, $flags); if (grep { $_ eq "y" } @flaglist) { return 1; } return undef; } sub verify_sha1_digest { my $self = shift; my ($digest, $signature) = @_; return $self->verify_digest("SHA-1", $digest, $signature); } # verify_digest() - returns true if the digest verifies, false otherwise # # if false, $@ is set to a description of the problem # sub verify_digest { my $self = shift; my ($digest_algorithm, $digest, $signature) = @_; my $rsa_pub = $self->cork; if (!$rsa_pub) { $@ = $@ ne '' ? "RSA failed: $@" : "RSA unknown problem"; $@ .= ", s=$self->{Selector} d=$self->{Domain}"; return; } $rsa_pub->use_no_padding; my $verify_result = $rsa_pub->encrypt($signature); my $k = $rsa_pub->size; my $expected = calculate_EM($digest_algorithm, $digest, $k); return 1 if ($verify_result eq $expected); # well, the RSA verification failed; I wonder if the RSA signing # was performed on a different digest value? I think we can check... # basically, if the $verify_result has the same prefix as $expected, # then only the digest was different my $digest_len = length $digest; my $prefix_len = length($expected) - $digest_len; if (substr($verify_result, 0, $prefix_len) eq substr($expected, 0, $prefix_len)) { $@ = "message has been altered"; return; } $@ = "bad RSA signature"; return; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/KeyValueList.pm0000644030404400001440000001027712055417140017174 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2007 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::KeyValueList; use Carp; sub new { my $class = shift; my %args = @_; my $self = bless \%args, $class; return $self; } sub parse { my $self_or_class = shift; croak "wrong number of arguments" unless (@_ == 1); my ($string) = @_; my $self = ref($self_or_class) ? $self_or_class : $self_or_class->new; $self->{tags} = []; $self->{tags_by_name} = {}; foreach my $raw_tag (split /;/, $string, -1) { my $tag = { raw => $raw_tag }; push @{$self->{tags}}, $tag; # strip preceding and trailing whitespace $raw_tag =~ s/^\s+|\s*$//g; next if ($raw_tag eq ""); my ($tagname, $value) = split(/\s*=\s*/, $raw_tag, 2); unless (defined $value) { die "syntax error\n"; } $tag->{name} = $tagname; $tag->{value} = $value; $self->{tags_by_name}->{$tagname} = $tag; } return $self; } sub clone { my $self = shift; my $str = $self->as_string; return ref($self)->parse($str); } sub get_tag { my $self = shift; my ($tagname) = @_; if ($self->{tags_by_name}->{$tagname}) { return $self->{tags_by_name}->{$tagname}->{value}; } return undef; } sub set_tag { my $self = shift; my ($tagname, $value) = @_; if ($tagname =~ /[;=\015\012\t ]/) { croak "invalid tag name"; } if (defined $value) { if ($value =~ /;/) { croak "invalid tag value"; } if ($value =~ /\015\012[^\t ]/) { croak "invalid tag value"; } if ($self->{tags_by_name}->{$tagname}) { $self->{tags_by_name}->{$tagname}->{value} = $value; my ($rawname, $rawvalue) = split(/=/, $self->{tags_by_name}->{$tagname}->{raw}, 2); $self->{tags_by_name}->{$tagname}->{raw} = "$rawname=$value"; } else { my $tag = { name => $tagname, value => $value, raw => " $tagname=$value" }; push @{$self->{tags}}, $tag; $self->{tags_by_name}->{$tagname} = $tag; } } else { if ($self->{tags_by_name}->{$tagname}) { delete $self->{tags_by_name}->{$tagname}; } @{$self->{tags}} = grep { $_->{name} ne $tagname } @{$self->{tags}}; } } sub as_string { my $self = shift; return join(";", map { $_->{raw} } @{$self->{tags}}); } # Start - length of the signature's prefix # Margin - how far to the right the text can go # Insert - characters to insert when wrapping a line # Tags - special processing for tags # Default - how to handle unspecified tags # PreserveNames - if set, the name= part of the tag will be preserved sub wrap { my $self = shift; my %args = @_; my $TEXTWRAP_CLASS = "Mail::DKIM::TextWrap"; return unless (UNIVERSAL::can($TEXTWRAP_CLASS, "new")); my $result = ""; my $wrap = $TEXTWRAP_CLASS->new( Output => \$result, Separator => $args{Insert} || "\015\012\t", Margin => $args{Margin} || 72, cur => $args{Start} || 0, ); my $did_first; foreach my $tag (@{$self->{tags}}) { my $tagname = $tag->{name}; my $tagtype = $args{Tags}->{$tagname} || $args{Default} || ""; $wrap->{Break} = undef; $wrap->{BreakBefore} = undef; $did_first ? $wrap->add(";") : ($did_first = 1); my ($raw_name, $raw_value) = split(/=/, $tag->{raw}, 2); unless ($args{PreserveNames}) { $wrap->flush; #allow a break before the tag name $raw_name =~ s/^\s*/ /; $raw_name =~ s/\s+$//; } $wrap->add($raw_name . "="); if ($tagtype eq "b64") { $raw_value =~ s/\s+//gs; #removes all whitespace $wrap->flush; $wrap->{Break} = qr/./; } elsif ($tagtype eq "list") { $raw_value =~ s/\s+/ /gs; #reduces any whitespace to single space $raw_value =~ s/^\s|\s$//g; #trims preceding/trailing spaces $raw_value =~ s/\s*:\s*/:/g; $wrap->flush; $wrap->{Break} = qr/[\s]/; $wrap->{BreakBefore} = qr/[:]/; } elsif ($tagtype eq "") { $raw_value =~ s/\s+/ /gs; #reduces any whitespace to single space $raw_value =~ s/^\s|\s$//g; #trims preceding/trailing spaces $wrap->flush; $wrap->{Break} = qr/\s/; } $wrap->add($raw_value); } $wrap->finish; parse($self, $result); return; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/MessageParser.pm0000644030404400001440000000475512055422171017360 0ustar jlongusers#!/usr/bin/perl # Copyright 2005 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::MessageParser; use Carp; sub new_object { my $class = shift; return $class->TIEHANDLE(@_); } sub new_handle { my $class = shift; local *TMP; tie *TMP, $class, @_; return *TMP; } sub TIEHANDLE { my $class = shift; my %args = @_; my $self = bless \%args, $class; $self->init; return $self; } sub init { my $self = shift; my $buf = ''; $self->{buf_ref} = \$buf; $self->{in_header} = 1; } sub PRINT { my $self = shift; my $buf_ref = $self->{buf_ref}; $$buf_ref .= @_ == 1 ? $_[0] : join("", @_) if @_; if ($self->{in_header}) { local $1; # avoid polluting a global $1 while ($$buf_ref ne '') { if (substr($$buf_ref,0,2) eq "\015\012") { substr($$buf_ref, 0, 2) = ''; $self->finish_header(); $self->{in_header} = 0; last; } if ($$buf_ref !~ /^(.+?\015\012)[^\ \t]/s) { last; } my $header = $1; $self->add_header($header); substr($$buf_ref, 0, length($header)) = ''; } } if (!$self->{in_header}) { my $j = rindex($$buf_ref,"\015\012"); if ($j >= 0) { # avoid copying a large buffer: the unterminated # last line is typically short compared to the rest my $carry = substr($$buf_ref, $j+2); substr($$buf_ref, $j+2) = ''; # shrink to last CRLF $self->add_body($$buf_ref); # must end on CRLF $$buf_ref = $carry; # restore unterminated last line } } return 1; } sub CLOSE { my $self = shift; my $buf_ref = $self->{buf_ref}; if ($self->{in_header}) { if ($$buf_ref ne '') { # A line of header text ending CRLF would not have been # processed yet since before we couldn't tell if it was # the complete header. Now that we're in CLOSE, we can # finish the header... $$buf_ref =~ s/\015\012\z//s; $self->add_header("$$buf_ref\015\012"); } $self->finish_header; $self->{in_header} = 0; } else { if ($$buf_ref ne '') { $self->add_body($$buf_ref); } } $$buf_ref = ''; $self->finish_body; return 1; } sub add_header { die "add_header not implemented"; } sub finish_header { die "finish_header not implemented"; } sub add_body { die "add_body not implemented"; } sub finish_body { # do nothing by default } sub reset { carp "reset not implemented"; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/Verifier.pm0000644030404400001440000005346312104756576016410 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2009 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; use Mail::DKIM::Signature; use Mail::DKIM::DkSignature; use Mail::Address; =head1 NAME Mail::DKIM::Verifier - verifies a DKIM-signed message =head1 SYNOPSIS use Mail::DKIM::Verifier; # create a verifier object my $dkim = Mail::DKIM::Verifier->new(); # read an email from a file handle $dkim->load(*STDIN); # or read an email and pass it into the verifier, incrementally while () { # remove local line terminators chomp; s/\015$//; # use SMTP line terminators $dkim->PRINT("$_\015\012"); } $dkim->CLOSE; # what is the result of the verify? my $result = $dkim->result; # there might be multiple signatures, what is the result per signature? foreach my $signature ($dkim->signatures) { print "signature identity: " . $signature->identity . "\n"; print "verify result: " . $signature->result_detail . "\n"; } # the alleged author of the email may specify how to handle email foreach my $policy ($dkim->policies) { die "fraudulent message" if ($policy->apply($dkim) eq "reject"); } =cut =head1 DESCRIPTION The verifier object allows an email message to be scanned for DKIM and DomainKeys signatures and those signatures to be verified. The verifier tracks the state of the message as it is read into memory. When the message has been completely read, the signatures are verified and the results of the verification can be accessed. To use the verifier, first create the verifier object. Then start "feeding" it the email message to be verified. When all the _headers_ have been read, the verifier: 1. checks whether any DomainKeys/DKIM signatures were found 2. queries for the public keys needed to verify the signatures 3. sets up the appropriate algorithms and canonicalization objects 4. canonicalizes the headers and computes the header hash Then, when the _body_ of the message has been completely fed into the verifier, the body hash is computed and the signatures are verified. The results of the verification can be checked with L or L. Messages that do not verify may be checked against the alleged sender's published signing policy with L and L. =head1 CONSTRUCTOR =head2 new() Constructs an object-oriented verifier. my $dkim = Mail::DKIM::Verifier->new(); my $dkim = Mail::DKIM::Verifier->new(%options); The only option supported at this time is: =over =item Debug_Canonicalization if specified, the canonicalized message for the first signature is written to the referenced string or file handle. =back =cut package Mail::DKIM::Verifier; use base "Mail::DKIM::Common"; use Carp; our $VERSION = 0.40; our $MAX_SIGNATURES_TO_PROCESS = 50; sub init { my $self = shift; $self->SUPER::init; $self->{signatures} = []; } # @{$dkim->{signatures}} # array of L objects, representing all # parseable signatures found in the header, # ordered from the top of the header to the bottom. # # $dkim->{signature_reject_reason} # simple string listing a reason, if any, for not using a signature. # This may be a helpful diagnostic if there is a signature in the header, # but was found not to be valid. It will be ambiguous if there are more # than one signatures that could not be used. # # $dkim->{signature} # the L selected as the "best" signature. # # @{$dkim->{headers}} # array of strings, each member is one header, in its original format. # # $dkim->{algorithms} # array of algorithms, one for each signature being verified. # # $dkim->{result} # string; the result of the verification (see the result() method) # sub handle_header { my $self = shift; my ($field_name, $contents, $line) = @_; $self->SUPER::handle_header($field_name, $contents); if (lc($field_name) eq "dkim-signature") { eval { my $signature = Mail::DKIM::Signature->parse($line); $self->add_signature($signature); $signature->fetch_public_key; }; if ($@) { # the only reason an error should be thrown is if the # signature really is unparse-able # otherwise, invalid signatures are caught in finish_header() chomp (my $E = $@); $self->{signature_reject_reason} = $E; } } if (lc($field_name) eq "domainkey-signature") { eval { my $signature = Mail::DKIM::DkSignature->parse($line); $self->add_signature($signature); $signature->fetch_public_key; }; if ($@) { # the only reason an error should be thrown is if the # signature really is unparse-able # otherwise, invalid signatures are caught in finish_header() chomp (my $E = $@); $self->{signature_reject_reason} = $E; } } } sub add_signature { my $self = shift; croak "wrong number of arguments" unless (@_ == 1); my ($signature) = @_; # ignore signature headers once we've seen 50 or so # this protects against abuse. return if (@{$self->{signatures}} > $MAX_SIGNATURES_TO_PROCESS); push @{$self->{signatures}}, $signature; unless ($self->check_signature($signature)) { $signature->result("invalid", $self->{signature_reject_reason}); return; } # create a canonicalization filter and algorithm my $algorithm_class = $signature->get_algorithm_class( $signature->algorithm); my $algorithm = $algorithm_class->new( Signature => $signature, Debug_Canonicalization => $self->{Debug_Canonicalization}, ); # push through the headers parsed prior to the signature header if ($algorithm->wants_pre_signature_headers) { # Note: this will include the signature header that led to this # "algorithm"... foreach my $head (@{$self->{headers}}) { $algorithm->add_header($head); } } # save the algorithm $self->{algorithms} ||= []; push @{$self->{algorithms}}, $algorithm; } sub check_signature { my $self = shift; croak "wrong number of arguments" unless (@_ == 1); my ($signature) = @_; unless ($signature->check_version) { # unsupported version if (defined $signature->version) { $self->{signature_reject_reason} = "unsupported version " . $signature->version; } else { $self->{signature_reject_reason} = "missing v tag"; } return 0; } unless ($signature->algorithm && $signature->get_algorithm_class($signature->algorithm)) { # unsupported algorithm $self->{signature_reject_reason} = "unsupported algorithm"; if (defined $signature->algorithm) { $self->{signature_reject_reason} .= " " . $signature->algorithm; } return 0; } unless ($signature->check_canonicalization) { # unsupported canonicalization method $self->{signature_reject_reason} = "unsupported canonicalization"; if (defined $signature->canonicalization) { $self->{signature_reject_reason} .= " " . $signature->canonicalization; } return 0; } unless ($signature->check_protocol) { # unsupported query protocol $self->{signature_reject_reason} = !defined($signature->protocol) ? "missing q tag" : "unsupported query protocol, q=" . $signature->protocol; return 0; } unless ($signature->check_expiration) { # signature has expired $self->{signature_reject_reason} = "signature is expired"; return 0; } unless (defined $signature->domain) { # no domain specified $self->{signature_reject_reason} = "missing d tag"; return 0; } if ($signature->domain eq '') { # blank domain $self->{signature_reject_reason} = "invalid domain in d tag"; return 0; } unless (defined $signature->selector) { # no selector specified $self->{signature_reject_reason} = "missing s tag"; return 0; } return 1; } sub check_public_key { my $self = shift; croak "wrong number of arguments" unless (@_ == 2); my ($signature, $public_key) = @_; my $result = 0; eval { $@ = undef; # HACK- I'm indecisive here about whether I want the # check_foo functions to return false or to "die" # on failure # check public key's allowed hash algorithms $result = $public_key->check_hash_algorithm( $signature->hash_algorithm); # HACK- DomainKeys signatures are allowed to have an empty g= # tag in the public key my $empty_g_means_wildcard = $signature->isa("Mail::DKIM::DkSignature"); # check public key's granularity $result &&= $public_key->check_granularity( $signature->identity, $empty_g_means_wildcard); die $@ if $@; }; if ($@) { my $E = $@; chomp $E; $self->{signature_reject_reason} = "public key: $E"; } return $result; } # returns true if the i= tag is an address with a domain matching or # a subdomain of the d= tag # sub check_signature_identity { my ($signature) = @_; my $d = $signature->domain; my $i = $signature->identity; if (defined($i) && $i =~ /\@([^@]*)$/) { return match_subdomain($1, $d); } return 0; } sub match_subdomain { croak "wrong number of arguments" unless (@_ == 2); my ($subdomain, $superdomain) = @_; my $tmp = substr(".$subdomain", -1 - length($superdomain)); return (lc ".$superdomain" eq lc $tmp); } # # called when the verifier has received the last of the message headers # (body is still to come) # sub finish_header { my $self = shift; # Signatures we found and were successfully parsed are stored in # $self->{signatures}. If none were found, our result is "none". if (@{$self->{signatures}} == 0 && !defined($self->{signature_reject_reason})) { $self->{result} = "none"; return; } foreach my $algorithm (@{$self->{algorithms}}) { $algorithm->finish_header(Headers => $self->{headers}); } # stop processing signatures that are already known to be invalid @{$self->{algorithms}} = grep { my $sig = $_->signature; !($sig->result && $sig->result eq "invalid"); } @{$self->{algorithms}}; if (@{$self->{algorithms}} == 0 && @{$self->{signatures}} > 0) { $self->{result} = $self->{signatures}->[0]->result; $self->{details} = $self->{signatures}->[0]->{verify_details}; return; } } sub _check_and_verify_signature { my $self = shift; my ($algorithm) = @_; # check signature my $signature = $algorithm->signature; unless (check_signature_identity($signature)) { $self->{signature_reject_reason} = "bad identity"; return ("invalid", $self->{signature_reject_reason}); } # get public key my $pkey; eval { $pkey = $signature->get_public_key; }; if ($@) { my $E = $@; chomp $E; $self->{signature_reject_reason} = "public key: $E"; return ("invalid", $self->{signature_reject_reason}); } unless ($self->check_public_key($signature, $pkey)) { return ("invalid", $self->{signature_reject_reason}); } # verify signature my $result; my $details; local $@ = undef; eval { $result = $algorithm->verify() ? "pass" : "fail"; $details = $algorithm->{verification_details} || $@; }; if ($@) { # see also add_signature chomp (my $E = $@); if ($E =~ /(OpenSSL error: .*?) at /) { $E = $1; } elsif ($E =~ /^(panic:.*?) at /) { $E = "OpenSSL $1"; } $result = "fail"; $details = $E; } return ($result, $details); } sub finish_body { my $self = shift; foreach my $algorithm (@{$self->{algorithms}}) { # finish canonicalizing $algorithm->finish_body; my ($result, $details) = $self->_check_and_verify_signature($algorithm); # save the results of this signature verification $algorithm->{result} = $result; $algorithm->{details} = $details; $algorithm->signature->result($result, $details); # collate results ... ignore failed signatures if we already got # one to pass if (!$self->{result} || $result eq "pass") { $self->{signature} = $algorithm->signature; $self->{result} = $result; $self->{details} = $details; } } } =head1 METHODS =head2 PRINT() Feeds part of the message to the verifier. $dkim->PRINT("a line of the message\015\012"); $dkim->PRINT("more of"); $dkim->PRINT(" the message\015\012bye\015\012"); Feeds content of the message being verified into the verifier. The API is designed this way so that the entire message does NOT need to be read into memory at once. Please note that although the PRINT() method expects you to use SMTP-style line termination characters, you should NOT use the SMTP-style dot-stuffing technique described in RFC 2821 section 4.5.2. Nor should you use a . sequence to terminate the message. =head2 CLOSE() Call this when finished feeding in the message. $dkim->CLOSE; This method finishes the canonicalization process, computes a hash, and verifies the signature. =head2 fetch_author_domain_policies() Retrieves ADSP records from DNS. my @policies = $dkim->fetch_author_domain_policies; foreach my $policy (@policies) { my $policy_result = $policy->apply($dkim); } This method will retrieve all applicable "author-domain-signing-practices" published in DNS for this message. Author policies are keyed to the email address(es) in the From: header, i.e. the claimed author of the message. This method returns a *list* of policy records, since there is allowed to be zero or multiple email addresses in the From: header. The result of the apply() method is one of: "accept", "reject", "neutral". See also: L. =cut sub fetch_author_domain_policies { my $self = shift; use Mail::DKIM::AuthorDomainPolicy; return () unless $self->{headers_by_name}->{from}; my @list = Mail::Address->parse( $self->{headers_by_name}->{from} ); my @authors = map { $_->address } @list; # fetch the policies return map { Mail::DKIM::AuthorDomainPolicy->fetch( Protocol => "dns", Author => $_, ) } @authors; } =head2 fetch_author_policy() Retrieves a signing policy from DNS. my $policy = $dkim->fetch_author_policy; my $policy_result = $policy->apply($dkim); This method retrieves the DKIM Sender Signing Practices record as described in Internet Draft draft-ietf-dkim-ssp-00-01dc. This Internet Draft is now obsolete; this method is only kept for backward-compatibility purposes. Please use the L method instead. =cut sub fetch_author_policy { my $self = shift; my ($author) = @_; use Mail::DKIM::DkimPolicy; # determine address found in the "From" $author ||= $self->message_originator->address; # fetch the policy return Mail::DKIM::DkimPolicy->fetch( Protocol => "dns", Author => $author, ); } =head2 fetch_sender_policy() Retrieves a signing policy from DNS. my $policy = $dkim->fetch_sender_policy; my $policy_result = $policy->apply($dkim); The "sender" policy is the sender signing policy as described by the DomainKeys specification, now available in RFC4870(historical). I call it the "sender" policy because it is keyed to the email address in the Sender: header, or the From: header if there is no Sender header. This is the person whom the message claims as the "transmitter" of the message (not necessarily the author). If the email being verified has no From or Sender header from which to get an email address (which violates email standards), then this method will C. The result of the apply() method is one of: "accept", "reject", "neutral". See also: L. =cut sub fetch_sender_policy { my $self = shift; use Mail::DKIM::DkPolicy; # determine addresses found in the "From" and "Sender" headers my $author = $self->message_originator->address; my $sender = $self->message_sender->address; # fetch the policy return Mail::DKIM::DkPolicy->fetch( Protocol => "dns", Author => $author, Sender => $sender, ); } =head2 load() Load the entire message from a file handle. $dkim->load($file_handle); Reads a complete message from the designated file handle, feeding it into the verifier. The message must use line terminators (same as the SMTP protocol). =head2 message_originator() Access the "From" header. my $address = $dkim->message_originator; Returns the "originator address" found in the message, as a L object. This is typically the (first) name and email address found in the From: header. If there is no From: header, then an empty L object is returned. To get just the email address part, do: my $email = $dkim->message_originator->address; See also L. =head2 message_sender() Access the "From" or "Sender" header. my $address = $dkim->message_sender; Returns the "sender" found in the message, as a L object. This is typically the (first) name and email address found in the Sender: header. If there is no Sender: header, it is the first name and email address in the From: header. If neither header is present, then an empty L object is returned. To get just the email address part, do: my $email = $dkim->message_sender->address; The "sender" is the mailbox of the agent responsible for the actual transmission of the message. For example, if a secretary were to send a message for another person, the "sender" would be the secretary and the "originator" would be the actual author. =head2 policies() Retrieves applicable signing policies from DNS. my @policies = $dkim->policies; foreach my $policy (@policies) { $policy_result = $policy->apply($dkim); # $policy_result is one of "accept", "reject", "neutral" } This method searches for and returns any signing policies that would apply to this message. Signing policies are selected based on the domain that the message *claims* to be from. So, for example, if a message claims to be from security@bank, and forwarded by trusted@listserv, when in reality the message came from foe@evilcorp, this method would check for signing policies for security@bank and trusted@listserv. The signing policies might tell whether foe@evilcorp (the real sender) is allowed to send mail claiming to be from your bank or your listserv. I say "might tell", because in reality this is still really hard to specify with any accuracy. In addition, most senders do not publish useful policies. =cut sub policies { my $self = shift; my $sender_policy = eval { $self->fetch_sender_policy() }; my $author_policy = eval { $self->fetch_author_policy() }; return ( $sender_policy ? $sender_policy : (), $author_policy ? $author_policy : (), $self->fetch_author_domain_policies(), ); } =head2 result() Access the result of the verification. my $result = $dkim->result; Gives the result of the verification. The following values are possible: =over =item pass Returned if a valid DKIM-Signature header was found, and the signature contains a correct value for the message. =item fail Returned if a valid DKIM-Signature header was found, but the signature does not contain a correct value for the message. =item invalid Returned if a DKIM-Signature could not be checked because of a problem in the signature itself or the public key record. I.e. the signature could not be processed. =item temperror Returned if a DKIM-Signature could not be checked due to some error which is likely transient in nature, such as a temporary inability to retrieve a public key. A later attempt may produce a better result. =item none Returned if no DKIM-Signature headers (valid or invalid) were found. =back In case of multiple signatures, the "best" result will be returned. Best is defined as "pass", followed by "fail", "invalid", and "none". To examine the results of individual signatures, use the L method to retrieve the signature objects. See L. =cut =head2 result_detail() Access the result, plus details if available. my $detail = $dkim->result_detail; The detail is constructed by taking the result (e.g. "pass", "fail", "invalid" or "none") and appending any details provided by the verification process in parenthesis. The following are possible results from the result_detail() method: pass fail (bad RSA signature) fail (OpenSSL error: ...) fail (message has been altered) fail (body has been altered) invalid (bad identity) invalid (invalid domain in d tag) invalid (missing q tag) invalid (missing d tag) invalid (missing s tag) invalid (unsupported version 0.1) invalid (unsupported algorithm ...) invalid (unsupported canonicalization ...) invalid (unsupported query protocol ...) invalid (signature is expired) invalid (public key: not available) invalid (public key: unknown query type ...) invalid (public key: syntax error) invalid (public key: unsupported version) invalid (public key: unsupported key type) invalid (public key: missing p= tag) invalid (public key: invalid data) invalid (public key: does not support email) invalid (public key: does not support hash algorithm 'sha1') invalid (public key: does not support signing subdomains) invalid (public key: revoked) invalid (public key: granularity mismatch) invalid (public key: granularity is empty) invalid (public key: OpenSSL error: ...) none =head2 signature() Access the message's DKIM signature. my $sig = $dkim->signature; Accesses the signature found and verified in this message. The returned object is of type L. In case of multiple signatures, the signature with the "best" result will be returned. Best is defined as "pass", followed by "fail", "invalid", and "none". =cut =head2 signatures() Access all of this message's signatures. my @all_signatures = $dkim->signatures; Use $signature->result or $signature->result_detail to access the verification results of each signature. =cut sub signatures { my $self = shift; croak "unexpected argument" if @_; return @{$self->{signatures}}; } =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2009 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut 1; Mail-DKIM-0.40/lib/Mail/DKIM/Common.pm0000644030404400001440000000702312055417140016036 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2007 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; use Mail::Address; package Mail::DKIM::Common; use base "Mail::DKIM::MessageParser"; use Carp; our $VERSION = 0.40; sub new { my $class = shift; return $class->new_object(@_); } sub add_header { my $self = shift; my ($line) = @_; foreach my $algorithm (@{$self->{algorithms}}) { $algorithm->add_header($line); } if ($line =~ /^([^:]+?)\s*:(.*)/s) { my $field_name = lc $1; my $contents = $2; $self->handle_header($field_name, $contents, $line); } push @{$self->{headers}}, $line; } sub add_body { my $self = shift; if ($self->{algorithm}) { $self->{algorithm}->add_body(@_); } foreach my $algorithm (@{$self->{algorithms}}) { $algorithm->add_body(@_); } } sub handle_header { my $self = shift; my ($field_name, $contents, $line) = @_; push @{$self->{header_field_names}}, $field_name; # TODO - detect multiple occurrences of From: or Sender: # header and reject them $self->{headers_by_name}->{$field_name} = $contents; } sub init { my $self = shift; $self->SUPER::init(@_); #initialize variables $self->{headers} = []; $self->{headers_by_name} = {}; $self->{header_field_names} = []; } sub load { my $self = shift; my ($fh) = @_; while (<$fh>) { $self->PRINT($_); } $self->CLOSE; } sub message_attributes { my $self = shift; my @attributes; if (my $message_id = $self->message_id) { push @attributes, "message-id=<$message_id>"; } if (my $sig = $self->signature) { push @attributes, "signer=<" . $sig->identity . ">"; } if ($self->{headers_by_name}->{sender}) { my @list = Mail::Address->parse($self->{headers_by_name}->{sender}); if ($list[0]) { push @attributes, "sender=<" . $list[0]->address . ">"; } } elsif ($self->{headers_by_name}->{from}) { my @list = Mail::Address->parse($self->{headers_by_name}->{from}); if ($list[0]) { push @attributes, "from=<" . $list[0]->address . ">"; } } return @attributes; } sub message_id { my $self = shift; croak "wrong number of arguments" unless (@_ == 0); if (my $message_id = $self->{headers_by_name}->{"message-id"}) { if ($message_id =~ /^\s*<(.*)>\s*$/) { return $1; } } return undef; } sub message_originator { my $self = shift; croak "wrong number of arguments" unless (@_ == 0); if ($self->{headers_by_name}->{from}) { my @list = Mail::Address->parse($self->{headers_by_name}->{from}); return $list[0] if @list; } return Mail::Address->new; } sub message_sender { my $self = shift; croak "wrong number of arguments" unless (@_ == 0); if ($self->{headers_by_name}->{sender}) { my @list = Mail::Address->parse($self->{headers_by_name}->{sender}); return $list[0] if @list; } if ($self->{headers_by_name}->{from}) { my @list = Mail::Address->parse($self->{headers_by_name}->{from}); return $list[0] if @list; } return Mail::Address->new; } sub result { my $self = shift; croak "wrong number of arguments" unless (@_ == 0); return $self->{result}; } sub result_detail { my $self = shift; croak "wrong number of arguments" unless (@_ == 0); if ($self->{details}) { return $self->{result} . " (" . $self->{details} . ")"; } return $self->{result}; } sub signature { my $self = shift; croak "wrong number of arguments" unless (@_ == 0); return $self->{signature}; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/SignerPolicy.pm0000644030404400001440000000550212055417140017215 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2006 Messiah College. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # # Written by Jason Long use strict; use warnings; package Mail::DKIM::SignerPolicy; 1; __END__ =head1 NAME Mail::DKIM::SignerPolicy - determines signing parameters for a message =head1 DESCRIPTION A "signer policy" is an object, class, or function used by L to determine what signatures to add to the current message. To take advantage of signer policies, create your own Perl class that extends the L class. The only method you need to implement is the apply() method. The apply() method takes as a parameter the L object. Using this object, it can determine some properties of the message (e.g. what the From: address or Sender: address is). Then it sets various signer properties as desired. The apply() method should return a nonzero value if the message should be signed. If a false value is returned, then the message is "skipped" (i.e. not signed). Here is an example of a policy that always returns the same values: package MySignerPolicy; use base "Mail::DKIM::SignerPolicy"; sub apply { my $self = shift; my $signer = shift; $signer->algorithm("rsa-sha1"); $signer->method("relaxed"); $signer->domain("example.org"); $signer->selector("selector1"); $signer->key_file("private.key"); return 1; } To use this policy, simply specify the name of the class as the Policy parameter... my $dkim = Mail::DKIM::Signer->new( Policy => "MySignerPolicy", ); =head1 ADVANCED You can also have the policy actually build the signature for the Signer to use. To do this, call the signer's add_signature() method from within your apply() callback. E.g., sub apply { my $self = shift; my $signer = shift; $signer->add_signature( new Mail::DKIM::Signature( Algorithm => $signer->algorithm, Method => $signer->method, Headers => $signer->headers, Domain => $signer->domain, Selector => $signer->selector, )); return; } Again, if you do not want any signatures, return zero or undef. If you use add_signature() to create a signature, the default signature will not be created, even if you return nonzero. =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2007 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut Mail-DKIM-0.40/lib/Mail/DKIM/DNS.pm0000644030404400001440000001472012105004777015240 0ustar jlongusers#!/usr/bin/perl # Copyright 2007, 2012 Messiah College. All rights reserved. # Jason Long use strict; use warnings; =head1 NAME Mail::DKIM::DNS - performs DNS queries for Mail::DKIM =head1 DESCRIPTION This is the module that performs DNS queries for L. =head1 CONFIGURATION This module has a couple configuration settings that the caller may want to use to customize the behavior of this module. =head2 $Mail::DKIM::DNS::TIMEOUT This global variable specifies the maximum amount of time (in seconds) to wait for a single DNS query to complete. The default is 10. =head2 Mail::DKIM::DNS::resolver() Use this global subroutine to get or replace the instance of L that Mail::DKIM uses. If set to undef (the default), then a brand new default instance of L will be created the first time a DNS query is needed. You will call this subroutine if you want to specify non-default options to L, such as different timeouts, or to enable use of a persistent socket. For example: # first, construct a custom DNS resolver my $res = Net::DNS::Resolver->new( udp_timeout => 3, tcp_timeout => 3, retry => 2, ); $res->udppacketsize(1240); $res->persistent_udp(1); # then, tell Mail::DKIM to use this resolver Mail::DKIM::DNS::resolver($res); =head2 Mail::DKIM::DNS::enable_EDNS0() This is a convenience subroutine that will construct an appropriate DNS resolver that uses EDNS0 (Extension mechanisms for DNS) to support large DNS replies, and configure Mail::DKIM to use it. (As such, it should NOT be used in conjunction with the resolver() subroutine described above.) Mail::DKIM::DNS::enable_EDNS0(); Use of EDNS0 is recommended, since it reduces the need for falling back to TCP when dealing with large DNS packets. However, it is not enabled by default because some Internet firewalls which do deep inspection of packets are not able to process EDNS0-enabled packets. When there is a firewall on a path to a DNS resolver, the EDNS0 feature should be specifically tested before enabling. =cut # This class contains a method to perform synchronous DNS queries. # Hopefully some day it will have a method to perform # asynchronous DNS queries. package Mail::DKIM::DNS; use Net::DNS; our $TIMEOUT = 10; our $RESOLVER; sub resolver { if (@_) { $RESOLVER = $_[0]; } return $RESOLVER; } sub enable_EDNS0 { # enable EDNS0, set acceptable UDP packet size to a # conservative payload size that should fit into a single # packet (MTU less the IP header size) in most cases; # See also draft-andrews-dnsext-udp-fragmentation # and RFC 3542 section 11.3. my $res = Net::DNS::Resolver->new(); $res->udppacketsize(1280-40); resolver($res); } # query- returns a list of RR objects # or an empty list if the domain record does not exist # (e.g. in the case of NXDOMAIN or NODATA) # or throws an error on a DNS query time-out or other transient error # (e.g. SERVFAIL) # # if an empty list is returned, $@ is also set to a string explaining # why no records were returned (e.g. "NXDOMAIN"). # sub query { my ($domain, $type) = @_; if (! $RESOLVER) { $RESOLVER = Net::DNS::Resolver->new() or die "Internal error: can't create DNS resolver"; } my $rslv = $RESOLVER; # # perform the DNS query # if the query takes too long, we should generate an error # my $resp; my $remaining_time = alarm(0); # check time left, stop the timer my $deadline = time + $remaining_time; my $E; eval { # set a timeout, 10 seconds by default local $SIG{ALRM} = sub { die "DNS query timeout for $domain\n" }; alarm $TIMEOUT; # the query itself could cause an exception, which would prevent # us from resetting the alarm before leaving the eval {} block # so we wrap the query in a nested eval {} block my $E2; eval { $resp = $rslv->send($domain, $type); 1; } or do { $E2 = $@; }; alarm 0; if ($E2) { chomp $E2; die "$E2\n" } # no line number here 1; } or do { $E = $@; # the $@ only makes sense if eval returns a false }; alarm 0; # restart the timer if it was active if ($remaining_time > 0) { my $dt = $deadline - time; # make sure the timer expiration will trigger a signal, # even at the expense of stretching the interval by one second alarm($dt < 1 ? 1 : $dt); } if ($E) { chomp $E; die $E } # ensure a line number # RFC 2308: NODATA is indicated by an answer with the RCODE set to NOERROR # and no relevant answers in the answer section. The authority section # will contain an SOA record, or there will be no NS records there. # NODATA responses have to be algorithmically determined from the # response's contents as there is no RCODE value to indicate NODATA. # In some cases to determine with certainty that NODATA is the correct # response it can be necessary to send another query. if ($resp) { my $header = $resp->header; if ($header) { # NOERROR, NXDOMAIN, SERVFAIL, FORMERR, REFUSED, ... my $rcode = $header->rcode; $@ = $rcode; if ($rcode eq 'NOERROR') { # may or may not contain RRs in the answer sect my @result = grep { lc $_->type eq lc $type } $resp->answer; $@ = 'NODATA' if !@result; return @result; # possibly empty } elsif ($rcode eq 'NXDOMAIN') { return; # empty list, rcode in $@ } } } die "DNS error: " . $rslv->errorstring . "\n"; } # query_async() - perform a DNS query asynchronously # # my $waiter = query_async("example.org", "TXT", # Callbacks => { # Success => \&on_success, # Error => \&on_error, # }, # ); # my $result = $waiter->(); # sub query_async { my ($domain, $type, %prms) = @_; my $callbacks = $prms{Callbacks} || {}; my $on_success = $callbacks->{Success} || sub { $_[0] }; my $on_error = $callbacks->{Error} || sub { die $_[0] }; my $waiter = sub { my @resp; my $rcode; eval { @resp = query($domain, $type); $rcode = $@; 1; } or do { return $on_error->($@); }; $@ = $rcode; return $on_success->(@resp); }; return $waiter; } 1; =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2007, 2012-2013 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut Mail-DKIM-0.40/lib/Mail/DKIM/Signature.pm0000644030404400001440000005101412104756576016564 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2007 Messiah College. All rights reserved. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; use Mail::DKIM::PublicKey; use Mail::DKIM::Algorithm::rsa_sha1; use Mail::DKIM::Algorithm::rsa_sha256; package Mail::DKIM::Signature; use base "Mail::DKIM::KeyValueList"; use Carp; =head1 NAME Mail::DKIM::Signature - represents a DKIM-Signature header =head1 CONSTRUCTORS =head2 new() - create a new signature from parameters my $signature = Mail::DKIM::Signature->new( [ Algorithm => "rsa-sha1", ] [ Signature => $base64, ] [ Method => "relaxed", ] [ Domain => "example.org", ] [ Identity => 'user@example.org', ] [ Headers => "from:subject:date:message-id", ] [ Query => "dns", ] [ Selector => "alpha", ] [ Timestamp => time(), ] [ Expiration => time() + 86400, ] ); =cut sub new { my $class = shift; my %prms = @_; my $self = {}; bless $self, $class; $self->version("1"); $self->algorithm($prms{'Algorithm'} || "rsa-sha1"); $self->signature($prms{'Signature'}); $self->canonicalization($prms{'Method'}) if exists $prms{'Method'}; $self->domain($prms{'Domain'}); $self->headerlist($prms{'Headers'}); $self->protocol($prms{'Query'}) if exists $prms{'Query'}; $self->selector($prms{'Selector'}); $self->identity($prms{'Identity'}) if exists $prms{'Identity'}; $self->timestamp($prms{'Timestamp'}) if defined $prms{'Timestamp'}; $self->expiration($prms{'Expiration'}) if defined $prms{'Expiration'}; $self->key($prms{'Key'}) if defined $prms{'Key'}; return $self; } =head2 parse() - create a new signature from a DKIM-Signature header my $sig = Mail::DKIM::Signature->parse( "DKIM-Signature: a=rsa-sha1; b=yluiJ7+0=; c=relaxed" ); Constructs a signature by parsing the provided DKIM-Signature header content. You do not have to include the header name (i.e. "DKIM-Signature:") but it is recommended, so the header name can be preserved and returned the same way in as_string(). Note: The input to this constructor is in the same format as the output of the as_string method. =cut sub parse { my $class = shift; croak "wrong number of arguments" unless (@_ == 1); my ($string) = @_; # remove line terminator, if present $string =~ s/\015\012\z//; # remove field name, if present my $prefix; if ($string =~ /^(dkim-signature:)(.*)/si) { # save the field name (capitalization), so that it can be # restored later $prefix = $1; $string = $2; } my $self = $class->SUPER::parse($string); $self->{prefix} = $prefix; return $self; } =head1 METHODS =cut # deprecated sub wantheader { my $self = shift; my $attr = shift; $self->headerlist or return 1; foreach my $key ($self->headerlist) { lc $attr eq lc $key and return 1; } return; } =head2 algorithm() - get or set the algorithm (a=) field The algorithm used to generate the signature. Should be either "rsa-sha1", an RSA-signed SHA-1 digest, or "rsa-sha256", an RSA-signed SHA-256 digest. See also hash_algorithm(). =cut sub algorithm { my $self = shift; if (@_) { $self->set_tag("a", shift); } my $a = $self->get_tag("a"); return defined $a ? lc $a : undef; } =head2 as_string() - the signature header as a string print $signature->as_string . "\n"; outputs DKIM-Signature: a=rsa-sha1; b=yluiJ7+0=; c=relaxed As shown in the example, the as_string method can be used to generate the DKIM-Signature that gets prepended to a signed message. =cut sub as_string { my $self = shift; my $prefix = $self->{prefix} || $self->DEFAULT_PREFIX; return $prefix . $self->SUPER::as_string; } # undocumented method sub as_string_debug { my $self = shift; my $prefix = $self->{prefix} || $self->DEFAULT_PREFIX; return $prefix . join(";", map { ">" . $_->{raw} . "<" } @{$self->{tags}}); } =head2 as_string_without_data() - signature without the signature data print $signature->as_string_without_data . "\n"; outputs DKIM-Signature: a=rsa-sha1; b=; c=relaxed This is similar to the as_string() method, but it always excludes the "data" part. This is used by the DKIM canonicalization methods, which require incorporating this part of the signature into the signed message. =cut sub as_string_without_data { my $self = shift; croak "wrong number of arguments" unless (@_ == 0); my $alt = $self->clone; $alt->signature(""); return $alt->as_string; } =head2 body_count() - get or set the body count (l=) field my $i = $signature->body_count; Informs the verifier of the number of bytes in the body of the email included in the cryptographic hash, starting from 0 immediately following the CRLF preceding the body. Also known as the l= tag. When creating a signature, this tag may be either omitted, or set after the selected canonicalization system has received the entire message body (but before it canonicalizes the DKIM-Signature). =cut sub body_count { my $self = shift; # set new body count if provided (@_) and $self->set_tag("l", shift); return $self->get_tag("l"); } =head2 body_hash() - get or set the body hash (bh=) field my $bh = $signature->body_hash; The hash of the body part of the message. Whitespace is ignored in this value. This tag is required. When accessing this value, whitespace is stripped from the tag for you. =cut sub body_hash { my $self = shift; # set new body hash if provided (@_) and $self->set_tag("bh", shift); my $result = $self->get_tag("bh"); if (defined $result) { $result =~ s/\s+//gs; } return $result; } =head2 canonicalization() - get or set the canonicalization (c=) field $signature->canonicalization("relaxed", "simple"); ($header, $body) = $signature->canonicalization; Message canonicalization (default is "simple/simple"). This informs the verifier of the type of canonicalization used to prepare the message for signing. In scalar context, this returns header/body canonicalization as a single string separated by /. In list context, it returns a two element array, containing first the header canonicalization, then the body. =cut sub canonicalization { my $self = shift; if (@_) { $self->set_tag("c", join("/", @_)); } my $c = $self->get_tag("c"); $c = lc $c if defined $c; if (not $c) { $c = "simple/simple"; } my ($c1, $c2) = split(/\//, $c, 2); if (not defined $c2) { # default body canonicalization is "simple" $c2 = "simple"; } if (wantarray) { return ($c1, $c2); } else { return "$c1/$c2"; } } use MIME::Base64; # checks whether this signature specifies a legal canonicalization method # returns true if the canonicalization is acceptable, false otherwise # sub check_canonicalization { my $self = shift; my ($c1, $c2) = $self->canonicalization; my @known = ("nowsp", "simple", "relaxed"); return undef unless (grep { $_ eq $c1 } @known); return undef unless (grep { $_ eq $c2 } @known); return 1; } # checks whether the expiration time on this signature is acceptable # returns a true value if acceptable, false otherwise # sub check_expiration { my $self = shift; my $x = $self->expiration; return 1 if not defined $x; $self->{_verify_time} ||= time(); return ($self->{_verify_time} <= $x); } # checks whether the protocol found on this signature is valid for # fetching the public key # returns a true value if protocol is "dns/txt", false otherwise # sub check_protocol { my $self = shift; my ($type, $options) = split(/\//, $self->protocol, 2); return unless ($type eq "dns"); return if ($options && $options ne "txt"); my $v = $self->version; if ($v) { # in v=1 signatures, the /txt option is REQUIRED return unless ($options && $options eq "txt"); } return 1; } # checks whether the version tag has an acceptable value # returns true if so, otherwise false # sub check_version { my $self = shift; # check version if (my $version = $self->version) { my @ALLOWED_VERSIONS = ("0.5", "1"); return (grep {$_ eq $version} @ALLOWED_VERSIONS); } # we still consider a missing v= tag acceptable, # for backwards-compatibility return 1; } =head2 data() - get or set the signature data (b=) field my $base64 = $signature->data; $signature->data($base64); The signature data. Whitespace is automatically stripped from the returned value. The data is Base64-encoded. =cut sub data { my $self = shift; if (@_) { $self->set_tag("b", shift); } my $b = $self->get_tag("b"); $b =~ tr/\015\012 \t//d if defined $b; return $b; } *signature = \*data; #undocumented, private function #derived from MIME::Base64::Perl (allowed, thanks to the Perl license) # sub decode_qp { my $res = shift; #TODO- should I worry about non-ASCII systems here? $res =~ s/=([\da-fA-F]{2})/pack("C", hex($1))/ge if defined $res; return $res; } #undocumented, private function #derived from MIME::Base64::Perl (allowed, thanks to the Perl license) # sub encode_qp { my $res = shift; # note- unlike MIME quoted-printable, we don't allow whitespace chars my $DISALLOWED = qr/[^!"#\$%&'()*+,\-.\/0-9:;<>?\@A-Z[\\\]^_`a-z{|}~]/; #TODO- should I worry about non-ASCII systems here? $res =~ s/($DISALLOWED)/sprintf('=%02X', ord($1))/eg if defined $res; return $res; } sub DEFAULT_PREFIX { return "DKIM-Signature:"; } =head2 domain() - get or set the domain (d=) field my $d = $signature->domain; # gets the domain value $signature->domain("example.org"); # sets the domain value The domain of the signing entity, as specified in the signature. This is the domain that will be queried for the public key. If using an "internationalized domain name", the domain name must be converted to ASCII (following section 4.1 of RFC 3490) before passing it to this method. =cut sub domain { my $self = shift; if (@_) { $self->set_tag("d", shift); } my $d = $self->get_tag("d"); return defined $d ? lc $d : undef; } =head2 expiration() - get or set the signature expiration (x=) field Signature expiration (default is undef, meaning no expiration). The signature expiration, if defined, is an unsigned integer identifying the standard Unix seconds-since-1970 time when the signature will expire. =cut sub expiration { my $self = shift; (@_) and $self->set_tag("x", shift); return $self->get_tag("x"); } # allows the type of signature to determine what "algorithm" gets used sub get_algorithm_class { my $self = shift; croak "wrong number of arguments" unless (@_ == 1); my ($algorithm) = @_; my $class = $algorithm eq "rsa-sha1" ? "Mail::DKIM::Algorithm::rsa_sha1" : $algorithm eq "rsa-sha256" ? "Mail::DKIM::Algorithm::rsa_sha256" : undef; return $class; } # [private method] # fetch_public_key() - initiate a DNS query for fetching the key # # This method does NOT return the public key. # Use get_public_key() for that. # sub fetch_public_key { my $self = shift; return if exists $self->{public_key_query}; my $on_success = sub { if ($_[0]) { $self->{public} = $_[0]; } else { $self->{public_error} = "not available\n"; } }; $self->{public_key_query} = Mail::DKIM::PublicKey->fetch_async( Protocol => $self->protocol, Selector => $self->selector, Domain => $self->domain, Callbacks => { Success => $on_success, Error => sub { $self->{public_error} = shift }, }, ); return; } #EXPERIMENTAL sub _refetch_public_key { my $self = shift; if ($self->{public_key_query}) { # clear the existing query by waiting for it to complete $self->{public_key_query}->(); } delete $self->{public_key_query}; delete $self->{public}; delete $self->{public_error}; $self->fetch_public_key; } =head2 get_public_key() - fetches the public key referenced by this signature my $pubkey = $signature->get_public_key; Public key to fetch is determined by the protocol, selector, and domain fields. This method caches the result of the fetch, so subsequent calls will not require additional DNS queries. This method will C if an error occurs. =cut sub get_public_key { my $self = shift; # this ensures we only try fetching once, even if an error occurs if (not exists $self->{public_key_query}) { $self->fetch_public_key; } if ($self->{public_key_query}) { # wait for public key query to finish $self->{public_key_query}->(); $self->{public_key_query} = 0; } if (exists $self->{public}) { return $self->{public}; } else { die $self->{public_error}; } } =head2 get_tag() - access the raw value of a tag in this signature my $raw_identity = $signature->get_tag("i"); Use this method to access a tag not already supported by Mail::DKIM, or if you want to bypass decoding of the value by Mail::DKIM. For example, the raw i= (identity) tag is encoded in quoted-printable form. If you use the identity() method, Mail::DKIM will decode from quoted-printable before returning the value. But if you use get_tag("i"), you can access the encoded quoted-printable form of the value. =head2 hash_algorithm() - access the hash algorithm specified in this signature my $hash = $signature->hash_algorithm; Determines what hashing algorithm is used as part of the signature's specified algorithm. For algorithm "rsa-sha1", the hash algorithm is "sha1". Likewise, for algorithm "rsa-sha256", the hash algorithm is "sha256". If the algorithm is not recognized, undef is returned. =cut sub hash_algorithm { my $self = shift; my $algorithm = $self->algorithm; return $algorithm eq "rsa-sha1" ? "sha1" : $algorithm eq "rsa-sha256" ? "sha256" : undef; } =head2 headerlist() - get or set the signed header fields (h=) field $signature->headerlist("a:b:c"); my $headerlist = $signature->headerlist; my @headers = $signature->headerlist; Signed header fields. A colon-separated list of header field names that identify the header fields presented to the signing algorithm. In scalar context, the list of header field names will be returned as a single string, with the names joined together with colons. In list context, the header field names will be returned as a list. =cut sub headerlist { my $self = shift; (@_) and $self->set_tag("h", shift); my $h = $self->get_tag("h") || ""; # remove whitespace next to colons $h =~ s/\s+:/:/g; $h =~ s/:\s+/:/g; $h = lc $h; if (wantarray and $h) { my @list = split /:/, $h; @list = map { s/^\s+|\s+$//g; $_ } @list; return @list; } elsif (wantarray) { return (); } return $h; } =head2 identity() - get or set the signing identity (i=) field my $i = $signature->identity; Identity of the user or agent on behalf of which this message is signed. The identity has an optional local part, followed by "@", then a domain name. The domain name should be the same as or a subdomain of the domain returned by the C method. Ideally, the identity should match the identity listed in the From: header, or the Sender: header, but this is not required to have a valid signature. Whether the identity used is "authorized" to sign for the given message is not determined here. If using an "internationalized domain name", the domain name must be converted to ASCII (following section 4.1 of RFC 3490) before passing it to this method. Identity values are encoded in the signature in quoted-printable format. Using this method will translate to/from quoted-printable as necessary. If you want the raw quoted-printable version of the identity, use $signature->get_tag("i"). =cut sub identity { my $self = shift; # set new identity if provided (@_) and $self->set_tag("i", encode_qp(shift)); my $i = $self->get_tag("i"); if (defined $i) { return decode_qp($i); } else { return '@' . ($self->domain||""); } } sub identity_matches { my $self = shift; my ($addr) = @_; my $id = $self->identity; if ($id =~ /^\@/) { # the identity is a domain-name only, so it only needs to match # the domain part of the sender address return (lc(substr($addr, -length($id))) eq lc($id)); # TODO - compare the parent domains? } return lc($addr) eq lc($id); } =head2 key() - get or set the private key object my $key = $signature->key; $signature->key(Mail::DKIM::PrivateKey->load(File => "private.key")); The private key is used for signing messages. It is not used for verifying messages. The key object can be any object that implements the L method. (Providing your own object can be useful if your actual keys are stored out-of-process.) =cut sub key { my $self = shift; if (@_) { $self->{Key} = shift; $self->{KeyFile} = undef; } return $self->{Key}; } =head2 method() - get or set the canonicalization (c=) field Message canonicalization (default is "simple"). This informs the verifier of the type of canonicalization used to prepare the message for signing. =cut sub method { my $self = shift; if (@_) { $self->set_tag("c", shift); } return (lc $self->get_tag("c")) || "simple"; } =head2 protocol() - get or set the query methods (q=) field A colon-separated list of query methods used to retrieve the public key (default is "dns"). Each query method is of the form "type[/options]", where the syntax and semantics of the options depends on the type. =cut sub protocol { my $self = shift; (@_) and $self->set_tag("q", shift); my $q = $self->get_tag("q"); if (not defined $q) { return "dns/txt"; } elsif ($q =~ m#/#) { return $q; } else { return "$q/"; } } =head2 result() - get or set the verification result my $result = $signature->result; $signature->result("pass"); # to set the result with details $signature->result("invalid", "no public key"); =cut sub result { my $self = shift; @_ and $self->{verify_result} = shift; @_ and $self->{verify_details} = shift; return $self->{verify_result}; } =head2 result_detail() - access the result, plus details if available my $detail = $signature->result_detail; An explanation of possible detail messages can be found in the documentation for L. =cut sub result_detail { my $self = shift; croak "wrong number of arguments" unless (@_ == 0); if ($self->{verify_result} && $self->{verify_details}) { return $self->{verify_result} . " (" . $self->{verify_details} . ")"; } return $self->{verify_result}; } =head2 selector() - get or set the selector (s=) field The selector subdivides the namespace for the "d=" (domain) tag. =cut sub selector { my $self = shift; (@_) and $self->set_tag("s", shift); return $self->get_tag("s"); } =head2 prettify() - alters the signature to look "nicer" as an email header $signature->prettify; This method may alter the signature in a way that breaks signatures, so it should be done ONLY when the signature is being generated, BEFORE being fed to the canonicalization algorithm. See also prettify_safe(), which will not break signatures. =cut sub prettify { my $self = shift; $self->wrap( Start => length($self->{prefix} || $self->DEFAULT_PREFIX), Tags => { b => "b64", bh => "b64", h => "list", }, ); } =head2 prettify_safe() - same as prettify() but only touches the b= part $signature->prettify_safe; This method will not break the signature, but it only affects the b= part of the signature. =cut sub prettify_safe { my $self = shift; $self->wrap( Start => length($self->{prefix} || $self->DEFAULT_PREFIX), Tags => { b => "b64", }, PreserveNames => 1, Default => "preserve", #preserves unknown tags ); } =head2 timestamp() - get or set the signature timestamp (t=) field Signature timestamp (default is undef, meaning unknown creation time). This is the time that the signature was created. The value is an unsigned integer identifying the number of standard Unix seconds-since-1970. =cut sub timestamp { my $self = shift; (@_) and $self->set_tag("t", shift); return $self->get_tag("t"); } =head2 version() - get or set the DKIM specification version (v=) field This is the version of the DKIM specification that applies to this signature record. =cut sub version { my $self = shift; (@_) and $self->set_tag("v", shift); return $self->get_tag("v"); } =head1 SEE ALSO L for DomainKey-Signature headers =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2007 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut 1; Mail-DKIM-0.40/lib/Mail/DKIM/Policy.pm0000644030404400001440000001350312104543131016040 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2007 Messiah College. # Jason Long # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; package Mail::DKIM::Policy; use Mail::DKIM::DNS; =head1 NAME Mail::DKIM::Policy - abstract base class for originator "signing" policies =head1 SYNOPSIS # get all policies that apply to a verified message foreach my $policy ($dkim->policies) { # the name of this policy my $name = $policy->name; # the location in DNS where this policy was found my $location = $policy->location; # apply this policy to the message being verified my $result = $policy->apply($dkim); } =head1 DESCRIPTION Between the various versions of the DomainKeys/DKIM standards, several different forms of sender "signing" policies have been defined. In order for the L library to support these different policies, it uses several different subclasses. All subclasses support this general interface, so that a program using L can support any and all policies found for a message. =cut sub fetch { my $class = shift; my $waiter = $class->fetch_async(@_); return $waiter->(); } sub fetch_async { my $class = shift; my %prms = @_; ($prms{'Protocol'} eq "dns") or die "invalid protocol '$prms{Protocol}'\n"; my $host = $class->get_lookup_name(\%prms); my %callbacks = %{$prms{Callbacks} || {}}; my $on_success = $callbacks{Success} || sub { $_[0] }; $callbacks{Success} = sub { my @resp = @_; unless (@resp) { # no requested resource records or NXDOMAIN, # use default policy return $on_success->($class->default); } my $strn; foreach my $rr (@resp) { next unless $rr->type eq "TXT"; # join with no intervening spaces, RFC 5617 if (Net::DNS->VERSION >= 0.69) { # must call txtdata() in a list context $strn = join "", $rr->txtdata; } else { # char_str_list method is 'historical' $strn = join "", $rr->char_str_list; } } unless ($strn) { # empty record found in DNS, use default policy return $on_success->($class->default); } my $self = $class->parse( String => $strn, Domain => $prms{Domain}, ); return $on_success->($self); }; # # perform DNS query for domain policy... # my $waiter = Mail::DKIM::DNS::query_async( $host, "TXT", Callbacks => \%callbacks, ); return $waiter; } sub parse { my $class = shift; my %prms = @_; my $text = $prms{"String"}; my %tags; foreach my $tag (split /;/, $text) { # strip whitespace $tag =~ s/^\s+|\s+$//g; my ($tagname, $value) = split /=/, $tag, 2; unless (defined $value) { die "policy syntax error\n"; } $tagname =~ s/\s+$//; $value =~ s/^\s+//; $tags{$tagname} = $value; } $prms{tags} = \%tags; return bless \%prms, $class; } =head1 METHODS These methods are supported by all classes implementing the L interface. =head2 apply() Apply the policy to the results of a DKIM verifier. my $result = $policy->apply($dkim_verifier); The caller must provide an instance of L, one which has already been fed the message being verified. Possible results are: =over =item accept The message is approved by the sender signing policy. =item reject The message is rejected by the sender signing policy. =item neutral The message is neither approved nor rejected by the sender signing policy. It can be considered suspicious. =back =cut sub apply { my $self = shift; my ($dkim) = @_; my $first_party; foreach my $signature ($dkim->signatures) { next if $signature->result ne "pass"; my $oa = $dkim->message_sender->address; if ($signature->identity_matches($oa)) { # found a first party signature $first_party = 1; last; } } return "accept" if $first_party; return "reject" if ($self->signall && !$self->testing); return "neutral"; } =head2 as_string() The policy as a string. Note that the string returned by this method will not necessarily have the tags ordered the same as the text record found in DNS. =cut sub as_string { my $self = shift; return join("; ", map { "$_=" . $self->{tags}->{$_} } keys %{$self->{tags}}); } =head2 is_implied_default_policy() Is this policy implied? my $is_implied = $policy->is_implied_default_policy; If you fetch the policy for a particular domain, but that domain does not have a policy published, then the "default policy" is in effect. Use this method to detect when that happens. =cut sub is_implied_default_policy { my $self = shift; my $default_policy = ref($self)->default; return ($self == $default_policy); } =head2 location() Where the policy was fetched from. This is generally a domain name, the domain name where the policy was published. If nothing is published for the domain, and the default policy was returned instead, the location will be C. =cut sub location { my $self = shift; return $self->{Domain}; } =head2 name() Identify what type of policy this is. This currently returns strings like "sender", "author", and "ADSP". It is subject to change in the next version of Mail::DKIM. =cut 1; =head1 SEE ALSO L - for RFC4870(historical) DomainKeys sender signing policies L - for early draft DKIM sender signing policies L - for Author Domain Signing Practices (ADSP) =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2009 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut Mail-DKIM-0.40/lib/Mail/DKIM/TextWrap.pm0000644030404400001440000001605312055417140016367 0ustar jlongusers#!/usr/bin/perl use strict; use warnings; package Mail::DKIM::TextWrap; use Carp; =head1 NAME Mail::DKIM::TextWrap - text wrapping module written for use with DKIM =head1 SYNOPSIS (FOR MAIL::DKIM USERS) use Mail::DKIM::TextWrap; Just add the above line to any program that uses L and your signatures will automatically be wrapped to 72 characters. =head1 SYNOPSIS (FOR OTHER USERS) my $output = ""; my $tw = Mail::DKIM::TextWrap->new( Margin => 10, Output => \$output, ); $tw->add("Mary had a little lamb, whose fleece was white as snow.\n"); $tw->finish; print $output; =head1 DESCRIPTION This is a general-purpose text-wrapping module that I wrote because I had some specific needs with Mail::DKIM that none of the contemporary text-wrapping modules offered. Specifically, it offers the ability to change wrapping options in the middle of a paragraph. For instance, with a DKIM signature: DKIM-Signature: a=rsa; c=simple; h=first:second:third:fourth; b=Xr2mo2wmb1LZBwmEJElIPezal7wQQkRQ8WZtxpofkNmXTjXf8y2f0 the line-breaks can be inserted next to any of the colons of the h= tag, or any character of the b= tag. The way I implemented this was to serialize the signature one element at a time, changing the text-wrapping options at the start and end of each tag. =head1 TEXT WRAPPING OPTIONS Text wrapping options can be specified when calling new(), or by simply changing the property as needed. For example, to change the number of characters allowed per line: $tw->{Margin} = 20; =over =item Break a regular expression matching characters where a line break can be inserted. Line breaks are inserted AFTER a matching substring. The default is C. =item BreakBefore a regular expression matching characters where a line break can be inserted. Line breaks are inserted BEFORE a matching substring. Usually, you want to use Break, rather than BreakBefore. The default is C. =item Margin specifies how many characters to allow per line. The default is 72. If no place to line-break is found on a line, the line will extend beyond this margin. =item Separator the text to insert when a linebreak is needed. The default is "\n". If you want to set a following-line indent (e.g. all lines but the first begin with four spaces), use something like "\n ". =item Swallow a regular expression matching characters that can be omitted when a line break occurs. For example, if you insert a line break between two words, then you are replacing a "space" with the line break, so you are omitting the space. On the other hand, if you insert a line break between two parts of a hyphenated word, then you are breaking at the hyphen, but you still want to display the hyphen. The default is C. =back =head1 CONSTRUCTOR =head2 new() - create a new text-wrapping object my $tw = Mail::DKIM::TextWrap->new( Output => \$output, %wrapping_options, ); The text-wrapping object encapsulates the current options and the current state of the text stream. In addition to specifying text wrapping options as described in the section above, the following options are recognized: =over =item Output a scalar reference, or a glob reference, to specify where the "wrapped" text gets output to. If not specified, the default of STDOUT is used. =back =cut sub new { my $class = shift; my %args = @_; my $self = { Margin => 72, Break => qr/\s/, BreakBefore => undef, Swallow => qr/\s/, Separator => "\n", cur => 0, may_break => 0, soft_space => "", word => "", %args, }; $self->{Output} ||= \*STDOUT; return bless $self, $class; } # Internal properties: # # cur - the last known column position # # may_break - nonzero if the current location allows a linebreak # # soft_space - contains added text that will not be printed if a linebreak # occurs # # word - contains the current word # Internal methods: # # _calculate_new_column() - determine where cur would be after adding some text # # my $new_cur = _calculate_new_column($cur, "some additional\ntext"); # sub _calculate_new_column { my ($cur, $text) = @_; confess "invalid argument" unless defined($text); while ($text =~ /^(.*?)([\n\r\t])(.*)$/s) { $cur += length($1); if ($2 eq "\t") { $cur = (int($cur / 8) + 1) * 8; } else { $cur = 0; } $text = $3; } $cur += length($text); return $cur; } =head1 METHODS =head2 add() - process some text that can be wrapped $tw->add("Mary had a little lamb.\n"); You can add() all the text at once, or add() the text in parts by calling add() multiple times. =cut sub add { my ($self, $text) = @_; my $break_after = $self->{Break}; my $break_before = $self->{BreakBefore}; my $swallow = $self->{Swallow}; $self->{word} .= $text; while (length $self->{word}) { my $word; if (defined($break_before) and $self->{word} =~ s/^(.+?)($break_before)/$2/s) { # note- $1 should have at least one character $word = $1; } elsif (defined($break_after) and $self->{word} =~ s/^(.*?)($break_after)//s) { $word = $1 . $2; } elsif ($self->{NoBuffering}) { $word = $self->{word}; $self->{word} = ""; } else { last; } die "assertion failed" unless length($word) >= 1; my $next_soft_space; if (defined($swallow) && $word =~ s/($swallow)$//s) { $next_soft_space = $1; } else { $next_soft_space = ""; } my $to_print = $self->{soft_space} . $word; my $new_pos = _calculate_new_column($self->{cur}, $to_print); if ($new_pos > $self->{Margin} && $self->{may_break}) { # what would happen if we put the separator in? my $w_sep = _calculate_new_column($self->{cur}, $self->{Separator}); if ($w_sep < $self->{cur}) { # inserting the separator gives us more room, # so do it $self->output($self->{Separator}); $self->{soft_space} = ""; $self->{cur} = $w_sep; $self->{word} = $word . $next_soft_space . $self->{word}; next; } } $self->output($to_print); $self->{soft_space} = $next_soft_space; $self->{cur} = $new_pos; $self->{may_break} = 1; } } =head2 finish() - call when no more text is to be added $tw->finish; Call this when finished adding text, so that any remaining text in TextWrap's buffers will be output. =cut sub finish { my $self = shift; $self->flush; $self->reset; } =head2 flush() - output the current partial word, if any $tw->flush; Call this whenever changing TextWrap's parameters in the middle of a string of words. It explicitly allows a line-break at the current position in the string, regardless of whether it matches the current break pattern. =cut sub flush { my $self = shift; local $self->{NoBuffering} = 1; local $self->{Swallow} = undef; $self->add(""); } sub output { my $self = shift; my $to_print = shift; my $out = $self->{Output}; if (UNIVERSAL::isa($out, "GLOB")) { print $out $to_print; } elsif (UNIVERSAL::isa($out, "SCALAR")) { $$out .= $to_print; } } sub reset { my $self = shift; $self->{cur} = 0; $self->{soft_space} = ""; $self->{word} = ""; } 1; Mail-DKIM-0.40/lib/Mail/DKIM/PrivateKey.pm0000644030404400001440000000750312055417140016674 0ustar jlongusers#!/usr/bin/perl # Copyright 2005-2007 Messiah College. All rights reserved. # Jason Long # # Copyright (c) 2004 Anthony D. Urso. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. use strict; use warnings; =head1 NAME Mail::DKIM::PrivateKey - a private key loaded in memory for DKIM signing =head1 SYNOPSIS my $key1 = Mail::DKIM::PrivateKey->load( File => "/path/to/private.key"); my $key2 = Mail::DKIM::PrivateKey->load( Data => $base64); # use the loaded key in a DKIM signing object my $dkim = Mail::DKIM::Signer->new( Key => $key2, ); =cut package Mail::DKIM::PrivateKey; use base "Mail::DKIM::Key"; use Carp; *calculate_EM = \&Mail::DKIM::Key::calculate_EM; =head1 CONSTRUCTOR =head2 load() - loads a private key into memory my $key1 = Mail::DKIM::PrivateKey->load( File => "/path/to/private.key"); Loads the Base64-encoded key from the specified file. my $key2 = Mail::DKIM::PrivateKey->load(Data => $base64); Loads the Base64-encoded key from a string already in memory. my $key3 = Mail::DKIM::PrivateKey->load(Cork => $openssl_object); Creates a Mail::DKIM::PrivateKey wrapper object for the given OpenSSL key object. The key object should be of type L. =cut sub load { my $class = shift; my %prms = @_; my $self = bless {}, $class; $self->{'TYPE'} = ($prms{'Type'} or "rsa"); if ($prms{'Data'}) { $self->{'DATA'} = $prms{'Data'}; } elsif (defined $prms{'File'}) { my @data; open FILE, "<", $prms{'File'} or die "Error: cannot read $prms{File}: $!\n"; while () { chomp; /^---/ and next; push @data, $_; } $self->{'DATA'} = join '', @data; } elsif ($prms{'Cork'}) { $self->{'CORK'} = $prms{'Cork'}; } else { croak "missing required argument"; } return $self; } =head1 METHODS =head2 cork() - access the underlying OpenSSL key object $openssl_object = $key->cork; The returned object is of type L. =cut sub convert { use Crypt::OpenSSL::RSA; my $self = shift; $self->data or return; # have to PKCS1ify the privkey because openssl is too finicky... my $pkcs = "-----BEGIN RSA PRIVATE KEY-----\n"; for (my $i = 0; $i < length $self->data; $i += 64) { $pkcs .= substr $self->data, $i, 64; $pkcs .= "\n"; } $pkcs .= "-----END RSA PRIVATE KEY-----\n"; my $cork; eval { $cork = new_private_key Crypt::OpenSSL::RSA($pkcs); }; $@ and $self->errorstr($@), return; $cork or return; # segfaults on my machine # $cork->check_key or # return; $self->cork($cork); return 1; } #deprecated sub sign { my $self = shift; my $mail = shift; return $self->cork->sign($mail); } #deprecated- use sign_digest() instead sub sign_sha1_digest { my $self = shift; my ($digest) = @_; return $self->sign_digest("SHA-1", $digest); } =head2 sign_digest() Cryptographically sign the given message digest. $key->sign_digest("SHA-1", sha1("my message text")); The first parameter is the name of the digest: one of "SHA-1", "SHA-256". The second parameter is the message digest as a binary string. The result should be the signed digest as a binary string. =cut sub sign_digest { my $self = shift; my ($digest_algorithm, $digest) = @_; my $rsa_priv = $self->cork; $rsa_priv->use_no_padding; my $k = $rsa_priv->size; my $EM = calculate_EM($digest_algorithm, $digest, $k); return $rsa_priv->decrypt($EM); } =head1 AUTHOR Jason Long, Ejlong@messiah.eduE =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2008 by Messiah College This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut 1; Mail-DKIM-0.40/ChangeLog0000644030404400001440000013653312104756576013675 0ustar jlongusers2013-02-06: Jason Long * lib/Mail/DKIM/DNS.pm: revert change that enabled EDNS0 by default; provide enable_EDNS0() subroutine for enabling EDNS0 * scripts/dkimverify.pl: sample verification script updated to enable EDNS0 before performing the verification 2013-02-06: Jason Long * lib/Mail/DKIM/DNS.pm: set udppacketsize to 1240, which is small enough that packet fragmentation will not normally occur; use DNS txtdata() method on versions of Net::DNS that support it. (This patch contributed by Mark Martinec.) 2013-02-04: Jason Long * lib/Mail/DKIM/DNS.pm: set default udppacketsize to 2048, which seems to be the suggested value to use in the Net::DNS documentation. 2013-02-04: Jason Long * lib/Mail/DKIM/Verifier.pm: avoid an 'uninitialized value' warning when signature being verified is missing a d= tag; accept a selector name of '0' rather than treating it as if the s= tag was missing * lib/Mail/DKIM/PublicKey.pm: sanity check selector/domain before attempting a DNS query (this fixes another 'uninitialized value' warning) * lib/Mail/DKIM/Signature.pm: avoid an 'uninitialized value' warning when calling identity() and d= tag is missing 2013-02-04: Jason Long * lib/Mail/DKIM/DNS.pm: construct a default RESOLVER that sets udppacketsize to 1280. This enables EDNS0 (extension mechanism for DNS), allowing Mail::DKIM to handle larger keys. 2012-11-28: Jason Long * lib/Mail/DKIM/DNS.pm: replace use of query() with send(), since it is never appropriate to append the default domain, and using send() paves the way to using bgsend() in the future for async dns. Contributed by Mark Martinec. * lib/Mail/DKIM/DNS.pm: add global variable $RESOLVER which the user can override if they want to specify options to Net::DNS. 2012-11-28: Jason Long * lib/Mail/DKIM/MessageParser.pm: rewrite of line parsing logic to avoid unnecessary copying of the internal buffer. This replaces use of $self->{buf} with ${$self->{buf_ref}} in many places. Patch contributed by Mark Martinec. 2012-11-28: Jason Long * lib/Mail/DKIM/Signer.pm: throw proper error message if an invalid algorithm is requested * lib/Mail/DKIM/PublicKey.pm: further refinement to fix Perl warning about use of uninitialized value 2011-04-21: Jason Long * lib/Mail/DKIM/PublicKey.pm: fix a Perl warning about use of an uninitialized value (reported by hsk@fli-leibniz.de) -- VERSION 0.39 -- 2010-11-14: Jason Long * lib/Mail/DKIM/Signer.pm: fix an unusual error message given when no Key argument has been specified and it is time to load the key 2010-06-11: Jason Long * t/signer_dk.t: create regression tests for DomainKeys signature generation 2010-06-11: Jason Long * lib/Mail/DKIM/Verifier.pm, Signer.pm, Algorithm/Base.pm (finish_header): provide reference to entire list of headers at completion of header, so that canonicalizers do not need to store their own copy of the header * lib/Mail/DKIM/Canonicalization/DkimCommon.pm: do not store header as parsed, rather only canonicalize header within finish_header() * lib/Mail/DKIM/Canonicalization/DkCommon.pm: do not store header as parsed, rather only canonicalize header within finish_header() * lib/Mail/DKIM/Canonicalization/Base.pm (finish_header): change API * t/simple_canonicalization.t: update for the API change of Canonicalization/Base.pm 2010-06-03: Jason Long * lib/Mail/DKIM/Verifier.pm: prevent abuse- if a message has more than 50 signatures headers, we start ignoring them (it's unreasonable for a message to have more than a very few signature headers). 2010-04-08: Jason Long * t/verifier.t: wrote some tests for DomainKey signatures with empty, missing, or invalid q= tag values * lib/Mail/DKIM/Verifier.pm: move use of check_signature_identity() out of finish_header() and into check_and_verify_signature(); this fixes an issue with getting wrong error codes when q= tag is broken. -- VERSION 0.38 -- 2010-03-31: Jason Long * lib/Mail/DKIM/DkSignature.pm, Signature.pm: avoid calling lc() on an undefined value (this generates warnings in Perl 5.12.x). Patch contributed by Mark Martinec. 2010-03-01: Jason Long * lib/Mail/DKIM/PrivateKey.pm (load): fix bug where a private key file named '0' could not be loaded 2010-03-01: Jason Long * lib/Mail/DKIM/DkSignature.pm (new): accept Key parameter when constructing a DomainKey signature object 2010-02-27: Jason Long * t/external_signer.t: test use of an alternate object for Key during a "sign" operation 2010-02-24: Jason Long * lib/Mail/DKIM/Signer.pm: document use of an alternate object for PrivateKey objects 2010-02-24: Jason Long * lib/Mail/DKIM/Signer.pm: import PrivateKey.pm in this module, rather than in the Algorithm modules 2010-02-24: Jason Long * lib/Mail/DKIM/PrivateKey.pm: document the sign_digest() method * lib/Mail/DKIM/Algorithm/*: use sign_digest() rather than sign_sha1_digest() 2010-01-23: Jason Long * t/public_key.t: test that DNS failure reason is given, when DNS returns no results * lib/Mail/DKIM/DNS.pm: bugfix (introduced by async_dns branch): preserve $@ in case of no error 2010-01-23: Jason Long * lib/Mail/DKIM/{DNS,Signature,PublicKey,Policy}.pm: merged my "async dns" branch --BEGIN "ASYNC DNS" branch 2009-07-10: Jason Long * lib/Mail/DKIM/Policy.pm: new fetch_async method, seems to work 2009-07-10: Jason Long * lib/Mail/DKIM/Signature.pm: new fetch_public_key method, which starts an asynchronous query for the public key referenced by this signature; redesign get_public_key to know how to complete the query 2009-07-10: Jason Long * lib/Mail/DKIM/PublicKey.pm: new fetch_async method: starts a query and returns a subref that when called will complete the query 2009-07-10: Jason Long * lib/Mail/DKIM/DNS.pm: new query_async method: starts a query and returns a subref that when called will complete the query --END "ASYNC DNS" branch 2009-12-14: Jason Long * MANIFEST: include sample_mime_lite.pl script in tarball 2009-09-08: Jason Long * lib/Mail/DKIM/DNS.pm: restart timer after a DNS lookup; based on a patch contributed by Mark Martinec -- VERSION 0.37 -- 2009-09-02: Jason Long * t/adsp.t: a test script for checking AuthorDomainPolicy.pm * lib/Mail/DKIM/AuthorDomainPolicy.pm: use d= tag not i= tag when checking for first-party signatures; fix for testing() method not found error; fix for "all" and "discardable" not doing the right thing 2009-08-14: Jason Long * sample_mime_lite.pl: a sample script showing how to use Mail::DKIM with MIME::Lite 2009-07-10: Jason Long * lib/Mail/DKIM/Policy.pm: revert ability for subclasses to override behavior of no-results DNS query * lib/Mail/DKIM/AuthorDomainPolicy.pm: if ADSP record is not found, check whether the domain itself exists * t/policy.t: add a test for the ADSP record causing a DNS error but the domain itself still existing 2009-07-10: Jason Long * t/public_key.t: refine the testing for DNS timeouts and SERVFAIL errors * t/policy.t: add some tests for DNS failures during policy queries * lib/Mail/DKIM/AuthorDomainPolicy.pm: more explicit documentation describing how DNS errors (and NXDOMAIN results) are handled 2009-07-09: Jason Long * lib/Mail/DKIM/Policy.pm: allow subclasses to override behavior when DNS query returns no records * lib/Mail/DKIM/AuthorDomainPolicy.pm: when DNS query returns no records, check the domain itself and possibly die. 2009-07-09: Jason Long * lib/Mail/DKIM/Verifier.pm: wasn't using the right API to get the ADSP policy 2009-07-09: Jason Long * lib/Mail/DKIM/AuthorDomainPolicy.pm, DkimPolicy.pm: changed the format of how methods are documented 2009-07-07: Jason Long * lib/Mail/DKIM/AuthorDomainPolicy.pm, DkimPolicy.pm: add a description of the class to the documentation 2009-06-09: Jason Long * scripts/dkimsign.pl: fix typo in the debugging output 2009-06-09: Jason Long * t/signer.t: test case for bug 2803465: space between header field name and colon cause signature to skip that header * lib/Mail/DKIM/Common.pm (add_header): fix regexp so that a space between the header field name and the colon is not treated as part of the header field name (issue #2803465) 2009-06-02: Jason Long * t/policy.t: test for the as_string() method -- VERSION 0.36 -- 2009-06-02: Jason Long * lib/Mail/DKIM/Policy.pm (as_string): restore this method which was accidentally removed in 0.34. -- VERSION 0.35 -- 2009-05-22: Jason Long * t/signer.t: add a test-case of a message with 10000's of blank lines; this seems to DoS the canonicalization routines * lib/Mail/DKIM/Canonicalization/{simple,relaxed,dk_simple}.pm: fix for bug reported on amavis-user list, patch provided by Mark Martinec. Thanks! -- VERSION 0.34 -- 2009-05-20: Jason Long * lib/Mail/DKIM.pm: rewrite the description section of the Mail::DKIM man page * lib/Mail/DKIM/Verifier.pm: document fetch_author_domain_policies() * Makefile.PL: release 0.34 2009-05-18: Jason Long * t/signer.t: add a test-case of a message without a header * lib/Mail/DKIM/Common.pm (init): initialize variables used by methods in this class; fixes RT.CPAN.ORG bug #46179 2009-04-04: Jason Long * lib/Mail/DKIM/Verifier.pm (fetch_author_domain_policies): new method for fetching ADSP records for a particular message 2009-04-03: Jason Long * lib/Mail/DKIM/DkPolicy.pm: moved the DomainKeys-specific policy stuff from Policy.pm to here * t/policy.t: change tests to use DkPolicy instead of Policy * lib/Mail/DKIM/Verifier.pm (fetch_sender_policy): use DkPolicy class instead of Policy class 2009-04-03: Jason Long * lib/Mail/DKIM/AuthorDomainPolicy.pm: first draft of ADSP support 2009-04-03: Jason Long * lib/Mail/DKIM/Verifier.pm: rename fetch_policies() to policies() * scripts/dkimverify.pl: replace use of fetch_policies() with policies() 2009-03-30: Jason Long * lib/Mail/DKIM/Verifier.pm: document a "temperror" result, which I will soon provide support for 2009-03-30: Jason Long * t/verifier.t: add support for testing DNS failures; add tests for detail messages of public key errors * t/verifier.t: add some tests of DNS failures 2009-03-30: Jason Long * lib/Mail/DKIM/Signature.pm (get_public_key): remember errors and always report them the same way 2009-03-30: Jason Long * lib/Mail/DKIM/Policy.pm, DkPolicy.pm: new method "name" to give a short name of the policy * scripts/dkimverify.pl: use the new fetch_policies() api to list the results of applicable policies 2009-03-24: Jason Long * lib/Mail/DKIM/Verifier.pm (fetch_policies): new method for fetching all applicable policies, and is guaranteed not to "die". 2009-03-24: Jason Long * lib/Mail/DKIM/DNS.pm: use a global variable to specify what Timeout to use * lib/Mail/DKIM/DNS.pm: detect DNS resolver errors and report them * t/public_key.t: test various DNS failures 2009-03-10: Jason Long * t/simple_canonicalization.t: fix simple-canonicalization test, which broke when I removed support for prestandardized DKIM signatures 2009-03-10: Jason Long * lib/Mail/DKIM/Algorithm/*, lib/Mail/DKIM/Canonicalization/*, lib/Mail/DKIM/Signature.pm, t/verifier.t: remove support and tests for the prestandardized DKIM signatures (issue #1871948) * Makefile.PL: bump version -- VERSION 0.33 -- 2009-03-10: Jason Long * Makefile.PL: release "0.33" 2008-11-19: Jason Long * lib/Mail/DKIM/Signature.pm, DkSignature.pm (DEFAULT_PREFIX): new method which determines what prefix to use in as_string, prettify, etc. * lib/Mail/DKIM/KeyValueList.pm (wrap): when splitting the h= field, only allow breaks prior to ':' symbols 2008-11-19: Jason Long * t/signature.t: further checks for prettify signatures (found a case where the new code caused a regression) * lib/Mail/DKIM/TextWrap.pm (flush): remember to update cur position when flushing text; use a "may_break" internal variable to know whether a linebreak is acceptable; other fixes * t/textwrap.t: another test case illustrating failure in TextWrap.pm * lib/Mail/DKIM/KeyValueList.pm (wrap): call flush() right after the ';' character; this is a further fix for #2257046. 2008-11-10: Jason Long * lib/Mail/DKIM/TextWrap.pm (flush): new method to explicitly allow a break at the current point in the string * lib/Mail/DKIM/KeyValueList.pm (wrap): call flush() whenever changing TextWrap parameters (this should complete the fix for #2257046) 2008-11-06: Jason Long * t/signature.t: test Mark Martinec's bad-signature-wrapping bug 2008-11-06: Jason Long * t/verifier.t: use a "fake" dns implementation, so that this test will pass no matter the user's state of dns 2008-11-06: Jason Long * lib/Mail/DKIM/DNS.pm (query): changed API so that it now returns a list of Net::DNS::RR objects, rather than a Net::DNS::Packet object. * lib/Mail/DKIM/Policy.pm: update for the change to the DNS.pm api. * lib/Mail/DKIM/PublicKey.pm: update for the change to the DNS.pm api. 2008-11-06: Jason Long * lib/Mail/DKIM/Signature.pm (encode_qp, decode_qp): suppress a warning about an uninitialized value * lib/Mail/DKIM/Common.pm (message_originator, message_sender): if a From/Sender line is present, but blank, still return a valid object (issue #2126559) * t/public_key.t: new test to check for DNS problems -- VERSION 0.32 -- 2008-05-09: Jason Long * lib/Mail/DKIM/Algorithm/dk_rsa_sha1.pm: when populating a DomainKey signature's identity, record where the identity came from * lib/Mail/DKIM/DkSignature.pm (identity_source): make the source of the identity (i.e. sender header or from header) available as a method 2008-05-09: Jason Long * lib/Mail/DKIM/Signature.pm (identity): do quoted-printable encoding/ decoding for the i= tag (issue #1839015) * t/corpus/good_qp_1.txt, good_qp_2.txt, good_qp_3.txt: three test files for identities using quoted-printable encoding 2008-04-14: Jason Long * lib/Mail/DKIM/Signature.pm: documentation for get_tag() 2008-04-14: Jason Long * lib/Mail/DKIM/PublicKey.pm (check_granularity): do case-sensitive comparison (issue #1938112) 2008-04-14: Jason Long * t/corpus/badkey_12.txt: fix g= case-sensitivity test so it won't give false positives on dkim-milter * t/corpus/badkey_13.txt: test that the verifier checks granularity against i= tag, not the From header 2008-04-14: Jason Long * lib/Mail/DKIM/Algorithm/dk_rsa_sha1.pm, rsa_sha1.pm: replace use of Digest::SHA1 with equivalent Digest::SHA * Makefile.PL: remove requirement for Digest::SHA1 2008-04-14: Jason Long * Makefile.PL: bump version -- VERSION 0.31 -- 2008-04-08: Jason Long * lib/Mail/DKIM/PrivateKey.pm: allow Cork argument to new() (#1879209) 2008-04-07: Jason Long * lib/Mail/DKIM/DkimPolicy.pm: fix "use of uninitialized value in string" errors * lib/Mail/DKIM/Signature.pm: provide public API to public-key object of signatures, e.g. so its flags can be accessed (issue #1879215); also, if an error occurs fetching the public-key record, the failure is cached so it won't get tried again * lib/Mail/DKIM/Signer.pm: document how to create a DomainKey-Signature 2008-02-20: Jason Long * lib/Mail/DKIM/TextWrap.pm: implement BreakBefore option; provided a bunch of documentation for this module * t/textwrap.t: tests new functionality of TextWrap * lib/Mail/DKIM/KeyValueList.pm: colon-separated lists are now "wrapped" with colons appearing at the beginning of the next line instead of at the end of the current line (hoping this will fix #1868648) 2008-02-06: Jason Long * lib/Mail/DKIM/Signature.pm: conserve space by omitting "c=simple" and "q=dns/txt" (#1878518) 2008-02-06: Jason Long * lib/Mail/DKIM/Canonicalization/DkCommon.pm, DkimCommon.pm: bugfix for issue #1878954 (undef value used as an ARRAY ref) * lib/Mail/DKIM/Canonicalization/DkCommon.pm: some cleanup, possible bugfix for verifying message with two+ DomainKey signatures * lib/Mail/DKIM/Verifier.pm: allow caller of fetch_author_policy() to specify domain (#1879197) * lib/Mail/DKIM/PrivateKey.pm: cleanup- indentation style; throw error on load() if missing argument 2008-01-24: Jason Long * lib/Mail/DKIM/DkimPolicy.pm, Policy.pm, PublicKey.pm, Verifier.pm: fix parsing regexes used to split email address into localpart and domain (issue #1878994) 2008-01-10: Jason Long * Makefile.PL: bump version * lib/Mail/DKIM/Signature.pm: make sure all public key problems are prefixed with "public key:" (most were already, see below for actual changes) * lib/Mail/DKIM/PublicKey.pm: since Signature.pm is providing the "public key:" prefix, it can be omitted in PublicKey.pm error messages * lib/Mail/DKIM/Verifier: the following result_detail messages have changed: "no public key available" => "public key: not available" "key value list syntax error" => "syntax error" or "public key: syntax error" * t/verifier.t: test that "public key" is mentioned -- VERSION 0.30.1 -- 2008-01-24: Jason Long * lib/Mail/DKIM/Algorithm/*: implement wants_pre_signature_headers() for each algorithm * lib/Mail/DKIM/Verifier.pm: if the algorithm "wants_pre_signature_headers", then feed headers found prior to the signature to the signature-specific algorithm doing the verification. This fixes an issue where signatures from cisco.com fail to verify (reported by Mark Martinec). -- VERSION 0.30 -- 2007-12-10: Jason Long * lib/Mail/DKIM/Verifier.pm (is_subdomain): do case-insensitive comparison * t/corpus/good_dk_7.txt: tests DK signature with domain names that differ only in case * t/corpus/good_rfc4871_4.txt: tests DKIM signature with i= and d= contain domain names differing in case * t/corpus/badkey_12.txt: tests public key where i=JLong, g=jl*ng 2007-12-07: Jason Long * t/verifier.t: three new DK tests * t/corpus/good_dk_6.txt: tests DK signature without h= tag * t/corpus/bad_dk_2.txt: tests DK signature w/o h= tag, Sender has been added * t/corpus/dk_multiple_1.txt: tests two DK signatures (with different domains) in a single message... both should pass * Makefile.PL: version bump 2007-12-07: Jason Long * lib/Mail/DKIM/Verifier.pm, lib/Mail/DKIM/Algorithm/dk_rsa_sha1.pm, lib/Mail/DKIM/Canonicalization/DkCommon.pm: domainkeys: determine identity from algorithm object. Currently the DomainKeys identity is determined by the Verifier. It is theoretically possible for two different DomainKeys signatures on the same message to have different identities. (This happens when one DomainKey signature includes a Sender header, and the other one does not.) This patch moves the determination of identity to the algorithm object. 2007-12-07: Jason Long * lib/Mail/DKIM/Verifier.pm, lib/Mail/DKIM/Common.pm: initialize signatures early. This patch makes Mail::DKIM::Verifier initialize and check the signature object as soon as it is parsed, and subsequent headers are fed into the algorithm as they are read, instead of waiting for the end of header. * lib/Mail/DKIM/Verifier.pm: fix DK identity. The previous patch broke identity-checking for DomainKeys signatures. This patch moves some things around so that identity checking still works. 2007-12-07: Jason Long * lib/Mail/DKIM/Verifier.pm (add_signature): changed to take a signature object, instead of an unparsed header line 2007-11-21: Jason Long * t/corpus/bad_dk_2.txt renamed to dk_headers_2.txt: revert this rename from earlier... the message should "pass" after all (the Sender header was not part of the signature) 2007-11-21: Jason Long * lib/Mail/DKIM/Signer.pm: provide documentation for Key parameter * lib/Mail/DKIM/PrivateKey.pm: created documentation for this package * lib/Mail/DKIM/Signature.pm: implement Key parameter for constructor, and key() method to get/set the private key 2007-11-14: Jason Long * lib/Mail/DKIM/Verifier.pm: for DomainKeys signatures, use the message sender as the identity * lib/Mail/DKIM/DkSignature.pm: allow verifier to supply the signing identity * lib/Mail/DKIM/Verifier.pm, PublicKey.pm: hack for allowing DomainKeys signatures to use public keys with empty g= tags 2007-11-14: Jason Long * lib/Mail/DKIM/Verifier.pm, Signer.pm: update documentation for message_sender() and message_originator() methods, which are now guaranteed to return an object * lib/Mail/DKIM/Common.pm (message_sender, message_originator): always return a Mail::Address object, even if the relevant headers were not found * t/corpus/dk_headers_2.txt renamed to bad_dk_2.txt: it turns out this message should've been failing all along, since the Sender header doesn't match the domain of the signature * lib/Mail/DKIM/Verifier.pm: slight change to the "unsupported version" detail message (don't want nested parenthesis) 2007-11-14: Jason Long * t/corpus/goodkey_4.txt: tests signature with i=a@b, public key implied g= * lib/Mail/DKIM/PublicKey.pm (check_granularity): fixed broken ends-with check, reported by Mark Martinec * t/corpus/good_dk_3.txt, good_dk_4.txt, good_dk_5.txt: these files test DomainKeys signatures with g= values in the public keys * t/corpus/bad_dk_1.txt: this one should fail, since the signature domain does not match the From/Sender header * scripts/dkimsign.pl: added option to override signature's d= tag * t/corpus/badkey_11.txt: tests a valid, but unmatched h= in public key 2007-11-08: Jason Long * lib/Mail/DKIM/Canonicalization/relaxed.pm, lib/Mail/DKIM/Canonicalization/nowsp.pm, lib/Mail/DKIM/Canonicalization/DkCommon.pm, lib/Mail/DKIM/Canonicalization/dk_simple.pm, lib/Mail/DKIM/Canonicalization/simple.pm, lib/Mail/DKIM/Canonicalization/dk_nofws.pm, lib/Mail/DKIM/Canonicalization/DkimCommon.pm, lib/Mail/DKIM/MessageParser.pm: more speed-up optimizations by Mark Martinec, now multiple lines at once can be fed into the canonicalization bits 2007-11-08: Jason Long * Makefile.PL, others: version bump to 0.30 2007-11-08: Jason Long * lib/Mail/DKIM/Signature.pm: minor doc edit * scripts/dkimsign.pl: generate "pretty" signatures; die on unrecognized signature type 2007-11-07: Jason Long * lib/Mail/DKIM/Algorithm/Base.pm: cleanup (delete commented-out code) * lib/Mail/DKIM/Algorithm/dk_rsa_sha1.pm (finish_message): fix bug where DomainKeys signatures were not "pretty", reported by Byung-Hee HWANG. 2007-11-07: Jason Long * lib/Mail/DKIM/Signature.pm: allow Timestamp to be specified to new() * lib/Mail/DKIM/Signer.pm: allow Timestamp to be specified to new(), requested by Mark Martinec * t/signer.t: test timestamp creation 2007-11-06: Jason Long * lib/Mail/DKIM/Canonicalization/simple.pm, lib/Mail/DKIM/Canonicalization/Base.pm, lib/Mail/DKIM/MessageParser.pm: speedup optimizations contributed by Mark Martinec. * lib/Mail/DKIM/Canonicalization/dk_simple,pm lib/Mail/DKIM/Canonicalization/relaxed.pm: more speed up optimizations by Mark Martinec. -- VERSION 0.29 -- 2007-11-07: Jason Long * lib/Mail/DKIM/Verifier.pm: signatures() is now public 2007-10-30: Jason Long * t/corpus/good_rfc4871_3.txt: to test extra tags in signature * scripts/dkimsign.pl: allow user to specify arbitrary extra tags for putting in the signature * lib/Mail/DKIM/MessageParser.pm: make "not implemented" messages more helpful * t/corpus/badkey_10.txt: to test key with t=s * lib/Mail/DKIM/Verifier.pm: allow check_granularity() to return different detail messages; describe two additional result_detail possibilities * lib/Mail/DKIM/PublicKey.pm (check_granularity): check for empty g= value; check for subdomain usage; (subdomain_flag): helper method to look for "s" in flags (flags): return default value if no t= tag 2007-10-26: Jason Long * t/corpus/badkey_9.txt: to test empty g= in selector * t/corpus/ignore_8.txt: to test bad i= value in signature * lib/Mail/DKIM/Verifier.pm: check signature identity value 2007-10-24: Jason Long * t/corpus/badkey_8.txt, ignore_5.txt, ignore_6.txt: fix signature so it would verify if not for the flaw in the public key * scripts/dkimsign.pl: allow key protocol to be specified on command line * lib/Mail/DKIM/Verifier.pm: update documentation on possible error codes * lib/Mail/DKIM/PublicKey.pm: cleanup error code 2007-10-24: Jason Long * t/corpus/badkey_*.txt: changed subjects to indicate which test it is * t/corpus/badkey_7.txt, badkey_8.txt, goodkey_1.txt, goodkey_2.txt, goodkey_3.txt: additional tests of public key features * t/corpus/verifier.t: test the new test messages * lib/Mail/DKIM/Verifier.pm (check_public_key): check key granularity and report the problem if it doesn't match * lib/Mail/DKIM/PublicKey.pm (check_granularity): a method for testing the granularity (granularity): return the default value of '*' if g= not defined * scripts/dkimsign.pl: ability to set i= tag from command-line 2007-10-24: Jason Long * lib/Mail/DKIM/Verifier.pm (check_signature): do signature version check here (finish_header): report invalid signature details to the signature object (signatures): return all parsed signatures, not just "valid" signatures * lib/Mail/DKIM/Signature.pm: some misc. cleanup (check_version): version check is now a separate method, rather than being part of parse() * lib/Mail/DKIM/DkSignature.pm (check_version): always true * t/corpus/multiple_2.txt: a message testing multiple signatures with different results * t/verifier.t: verify that each signature's results are available and correct 2007-10-24: Jason Long * t/corpus/ignore_7.txt: a message with an expired signature * lib/Mail/DKIM/Signature.pm: recognize Expiration as a parameter * scripts/dkimsign.pl: make it possible to create a signature with an x= tag * t/verifier.t: test ignore_7.txt message, should "ignore" * lib/Mail/DKIM/Verifier.pm: check signature expiration when verifying * lib/Mail/DKIM/Signature.pm (check_expiration): new method 2007-10-08: Jason Long * lib/Mail/DKIM/Signer.pm: use "Key" property instead of "private" to store the private key; new methods key() and key_file(); allow each signature to have its own private key * t/signer_policy.t: test specifying a key file in a policy 2007-10-04: Jason Long * lib/Mail/DKIM/MessageParser.pm: return nonzero for PRINT and CLOSE * lib/Mail/DKIM/DkSignature.pm: make expiration() for DomainKey-Signature behave more compatibly with expiration() for DKIM-Signature * lib/Mail/DKIM/Verifier.pm: remove dependency on Error module * README: what I'm interested in if make test fails * lib/Mail/DKIM/Signer.pm: fix bug reported by dairiki, who noticed that the Signer class ignored the signature-specified algorithm when building the algorithm object * scripts/dkimsign.pl: update POD a bit * scripts/dkimverify.pl: add POD * lib/Mail/DKIM/Canonicalization/Base.pm, Algorithm/Base.pm: add see also, author, copyright sections to POD * lib/Mail/DKIM/Algorithm/rsa_sha1.pm, rsa_sha256.pm: remove useless pod * ...and others...: inline documentation (POD) cleanup 2007-10-03: Jason Long * lib/Mail/DKIM/Canonicalization/DkCommon.pm: attempt at making the header handling algorithm easier to understand * t/signer.t: test case for unreadable private key * lib/Mail/DKIM/PrivateKey.pm: die if unable to open private key file * lib/Mail/DKIM/Signer.pm: no more need for a separate existance check on the private key filename * lib/Mail/DKIM/Signature.pm: allow Identity to be specified in new() * lib/Mail/DKIM/Signer.pm: allow Identity to be specified in new() * t/signer.t: test that specifying Identity works 2007-10-02: Jason Long * lib/Mail/DKIM/Canonicalization/DkCommon.pm: there are some varying opinions about how to verify DomainKey-signed messages when there is an h= tag and the headers are in a different order... this is an attempt at making our verifier more compatible with other implementations * dk_headers.txt: some notes on multiple-occuring headers 2007-09-06: Jason Long * lib/Mail/DKIM/Policy.pm (get_lookup_name): new method that determines the name of the record to lookup * lib/Mail/DKIM/DkimPolicy.pm: no longer need to override fetch(), now it just overrides get_lookup_name() -- VERSION 0.28 -- 2007-07-31: Jason Long * Makefile.PL: OpenSSL 0.23 segfaults on certain signatures, so the requirement is now Crypt::OpenSSL::RSA 0.24 or better * lib/Mail/DKIM/KeyValueList.pm: don't split the header in the middle of a tag name -- VERSION 0.27 -- 2007-07-25: Jason Long * lib/Mail/DKIM/Signature.pm (identity_matches): new method to help comparing a From/Sender address with the signature identity * lib/Mail/DKIM/DkimPolicy.pm, Policy.pm: use identity_matches function 2007-07-18: Jason Long * lib/Mail/DKIM/Verifier.pm: changed fetch_policy() back to fetch_author_policy(); added fetch_sender_policy(); save results for each signature * lib/Mail/DKIM/Policy.pm: now implements just Dk policies * lib/Mail/DKIM/DkimPolicy.pm: a new module for DKIM signing practices * lib/Mail/DKIM/Signature.pm: can now get/set the verification result for each signature * scripts/dkimsign.pl: handle DOS-formated input * scripts/dkimverify.pl: show multiple signatures' results; show both policy results 2007-06-11: Jason Long * lib/Mail/DKIM/Verifier.pm: public_key no longer available as a verifier property; use eval block instead of try...otherwise; experimental signatures() method * lib/Mail/DKIM/Verifier.pm: added description section to Perldocs; replaced a couple try...otherwise blocks with eval blocks 2007-06-08: Jason Long * policies.txt: some thoughts on dealing with two different types of policy records * lib/Mail/DKIM/Policy.pm: default() is now a private class method * lib/Mail/DKIM/PublicKey.pm: moved DNS query to DNS.pm * lib/Mail/DKIM/Policy.pm: moved DNS query to DNS.pm * lib/Mail/DKIM/DNS.pm: new module that does the common DNS lookups 2007-06-07: Jason Long * lib/Mail/DKIM/Common.pm: change "parse Mail::Address" to my preferred "Mail::Address->parse" style * t/policy.t: test policy lookup by email address instead of domain; test policy lookups on a few well known domains * lib/Mail/DKIM/Verifier.pm: minor doc changes; renamed fetch_author_policy() to fetch_policy(); fetch_author_policy still works for backward compatibility * lib/Mail/DKIM/Policy.pm: allow lookups given sender/from addresses (location): new method to determine where the policy came from 2007-06-05: Jason Long * lib/Mail/DKIM/Policy.pm: create the default policy only once; (is_implied_default_policy): new method to check whether the policy was explicit or implied (fetch): get policy record at the Yahoo!-DomainKeys-defined location, rather than the location in the not-yet-finished DKIM SSP spec. * t/policy.t: test is_implied_default_policy method -- VERSION 0.26 -- 2007-05-24: Jason Long * lib/Mail/DKIM/Signature.pm: accept and use v=1 tag instead of v=0.5 * lib/Mail/DKIM/Policy.pm: oops, left a syntax error in here * t/signer.t: new expected signature value, since signature now has v=1 2007-05-10: Jason Long * lib/Mail/DKIM/PublicKey.pm: simplify error handling in fetch() * lib/Mail/DKIM/Policy.pm: simplify error handling in fetch() -- VERSION 0.25 -- 2007-05-10: Jason Long * lib/Mail/DKIM/KeyValueList.pm: separator should be "\015\012" not "\n"; allow splitting on whitespace in a colon-separated list value * Makefile.PL: it seems version 0.22 or better is required of Crypt::OpenSSL::RSA 2007-05-01: Jason Long * t/signature.t: test prettify_safe() * lib/Mail/DKIM/TextWrap.pm: new wrapping module to help with prettifying the signature * lib/Mail/DKIM/KeyValueList.pm (wrap): new method to help wrap the signature * lib/Mail/DKIM/Signature.pm: wrap the signature * lib/Mail/DKIM/Algorithm/Base.pm: oops, prettify() should only be called when we're _making_ the signature * lib/Mail/DKIM/KeyValueList.pm (wrap): only do something if TextWrap has been loaded (no longer automatically load it) * lib/Mail/DKIM/Signature.pm (prettify): better default value for Start 2007-04-16: Jason Long * t/signature.t: test that prettify() doesn't choke * lib/Mail/DKIM/Algorithm/Base.pm: call prettify() before hashing the signature header * lib/Mail/DKIM/Signer.pm: call prettify_safe() after generating the signature data * lib/Mail/DKIM/Signature.pm: stubs for prettify() and prettify_safe() * lib/Mail/DKIM/KeyValueList.pm: parse() can now be called on an existing key-value-list to regenerate the tag data from a string 2007-04-15: Jason Long * lib/Mail/DKIM/PublicKey.pm: bug fix- if Net::DNS caused an exception, there was a race condition for resetting the alarm * lib/Mail/DKIM/Signer.pm: limit which headers are signed * t/signer.t: test that undesirable headers are not signed -- VERSION 0.24 -- 2007-03-09: Jason Long * lib/Mail/DKIM/PublicKey.pm, lib/Mail/DKIM/Signature.pm, lib/Mail/DKIM/KeyValueList.pm: fix for linebreaks in public key data; provided by Mark Martinec. * lib/Mail/DKIM/Signature.pm: fix default value q=dns/txt; this fixes a bug for DKIM signatures without q= tags * t/verifier.t: added six new tests that test problems with the public key (e.g. revoked, syntax, etc.) -- VERSION 0.23 -- 2007-02-22: Jason Long * lib/Mail/DKIM/PublicKey.pm: catch certain OpenSSL errors; tweak diagnostics * lib/Mail/DKIM/Verifier.pm: changed OpenSSL error catching code to match that found in PublicKey; document more possible diagnostic codes * lib/Mail/DKIM/Signature.pm: tweaked diagnostics for missing public key * Makefile.PL: check for Test::Simple, which is required for `make test' 2007-02-21: Jason Long * t/signer_policy.t: signature should still work even if no value is returned from signer policy * t/signer.t: now uses v=0.5 signature, which changes the signature * t/verifier.t: added three tests of empty body messages * lib/Mail/DKIM/Signer.pm: fixed bug where if signer policy was a sub ref, and didn't return a true value, the message would get skipped * lib/Mail/DKIM/Canonicalization/simple.pm: argh, hack for handling empty body * lib/Mail/DKIM/MessageParser.pm: fixed bug in handling of messages without bodies * lib/Mail/DKIM/Signature.pm: output v=0.5 signatures now * scripts/dkimsign.pl: new --binary option to disable line-ending conversion 2007-02-19: Jason Long * lib/Mail/DKIM/DkSignature: provide default value for a= tag (thanks to mark.martinec@ijs.si for the patch) * t/corpus/good_dk_2.txt: test for missing q= and a= tags on DomainKey signature 2007-02-09: Jason Long * lib/Mail/DKIM/Verifier.pm, DkSignature.pm: better diagnostic messages; allow missing q= tag for domainkey signatures (thanks to mark.martinec@ijs.si for the patch) -- VERSION 0.22 -- 2007-01-19: Jason Long * t/verifiter.t: relaxed the OpenSSL check a little more * DKIM.pm, README, others: updated copyright to include 2007; updated abstract to include DomainKeys; updated version number 2007-01-19: Jason Long * lib/Mail/DKIM/Verifier.pm: in case of unsupported algorithm, canonicalization method, or key protocol, list the bad protocol in the error message to make diagnosing easier * lib/Mail/DKIM/DkSignature.pm: fix a use-of-undefined-scalar bug 2007-01-17: Jason Long * t/verifier.t: fixed testing bug that was too strict about what error message OpenSSL generates * lib/Mail/DKIM/Verifier.pm: handle OpenSSL panic message better -- VERSION 0.21 -- 2006-11-27: Jason Long * t/verifier.t: test domainkey message with trailing blank line * lib/Mail/DKIM/Canonicalization/dk_nofws.pm: fixed bug where DomainKey- signed message with trailing blank line was not canonicalized correctly, reported by Mark Martinec. 2006-11-13: Jason Long * t/verifier.t: test invalid signature length * lib/Mail/DKIM/Verifier.pm: fixed bug where OpenSSL error was not reported by moved "local $@" outside try block, thanks to Mark Martinec for finding this; detect OpenSSL error and clean up the error message -- VERSION 0.20 -- 2006-10-24: Jason Long * t/signer_policy.t, t/signer.t: use new() instead of new_object() * lib/Mail/DKIM/Algorithm/Base.pm: allow debugging body canonicalization * lib/Mail/DKIM/MessageParser.pm: removed problematic check for "control characters" * scripts/dkimsign.pl: document --type argument; replace signer policy class with signer policy subroutine 2006-10-23: Jason Long * lib/Mail/DKIM/Signer.pm: bugfix - signatures weren't setup correctly when policy built the signature * lib/Mail/DKIM/Signature.pm: bugfix - empty headerlist should return empty list * lib/Mail/DKIM/Canonicalization/Base.pm: moved support for Debug_Canonicalization here from Algorithm/* * lib/Mail/DKIM/Algorithm/Base.pm: removed Debug_Canonicalization support * lib/Mail/DKIM/Algorithm/dk_rsa_sha1.pm: removed Debug_Canonicalization support 2006-10-23: Jason Long * lib/Mail/DKIM/Signer.pm, lib/Mail/DKIM/Verifier.pm: use new() instead of new_object(); new_object() still supported; documented Debug_Canonicalization option 2006-10-20: Jason Long * t/signer_policy.t: added test for creating DomainKeys signature; added test for creating multiple signatures * lib/Mail/DKIM/SigningFilter.pm: removed (obsolete) * scripts/test_signing_filter.pl: removed (obsolete) * lib/Mail/DKIM/Signer.pm: added signatures method 2006-10-20: Jason Long * lib/Mail/DKIM.pm, lib/Mail/DKIM/Signer.pm, lib/Mail/DKIM/Verifier.pm: various documentation fixes * lib/Mail/DKIM/Verifier.pm: set signature property when result is determined * lib/Mail/DKIM/Signer.pm: support addition of multiple signatures; changed default canonicalization method to "relaxed" 2006-10-20: Jason Long * t/signer_policy.t: tests different forms of signing policies * lib/Mail/DKIM/Signer.pm: support code references as a signing policy; removed support for build_signature (I'm gonna do this a different way) 2006-10-20: Jason Long * t/verifier: added a message containing multiple signatures, only one of which is valid - t/corpus/multiple_1.txt: the new message * lib/Mail/DKIM/Algorithm/Base.pm: added signature method; changed method signature of verify method (no parameters needed any more) * lib/Mail/DKIM/Algorithm/rsa_sha1.pm, lib/Mail/DKIM/Algorithm/rsa_sha256.pm, lib/Mail/DKIM/Algorithm/dk_rsa_sha1.pm: updated verify method * lib/Mail/DKIM/Verifier.pm: support verification of multiple signatures * lib/Mail/DKIM/Signer.pm: documented use of policy _function_ rather than policy object; but no implementation yet * lib/Mail/DKIM/Common.pm (add_body): support multiple algorithms (needed to verify multiple signatures) * lib/Mail/DKIM/Signature.pm: renamed signature() to data(); signature still available for backwards compatibility 2006-10-19: Jason Long * lib/Mail/DKIM/SignerPolicy.pm: document mechanism for users to construct the signature themselves * lib/Mail/DKIM/Canonicalization/DkCommon.pm, * lib/Mail/DKIM/Canonicalization/dk_simple.pm, lib/Mail/DKIM/Canonicalization/dk_nofws.pm: implemented DomainKeys' canonicalization methods * lib/Mail/DKIM/Canonicalization/Base.pm: clarify use of add_body method * lib/Mail/DKIM/Algorithm/Base.pm: clarify use of add_body method * lib/Mail/DKIM/DkSignature.pm: implements DomainKeys signatures * lib/Mail/DKIM/Signer.pm: allow policy to construct the signature, if it implements the build_signature method; allow policy access to header field names (headers method) * lib/Mail/DKIM/Signature.pm: replace use of obsolete method() with canonicalization() * scripts/dkimsign.pl: allow user to specify signature type 2006-10-12: Jason Long * t/verifier: added two new DomainKeys messages to test - t/corpus/good_dk_yahoo.txt - t/corpus/good_dk_gmail.txt * lib/Mail/DKIM/Canonicalization/dk_nofws.pm: added support for the DomainKeys "nofws" canonicalization method * lib/Mail/DKIM/Verifier.pm: recognize DomainKeys signatures; signatures now determine which algorithm class to use * lib/Mail/DKIM/Algorithm/Base.pm: refactored a few things to better accomodate non-DKIM algorithms * lib/Mail/DKIM/Algorithm/dk_rsa_sha1.pm: implements the DomainKeys rsa-sha1 algorithm * lib/Mail/DKIM/DkSignature.pm: handles DomainKeys signatures * lib/Mail/DKIM/Signer.pm: signature now determines which algorithm class to use * lib/Mail/DKIM/PublicKey.pm: change an error message from "headers have been alterered" to "message has been altered" (if the headers have been altered, we really cannot imply that the body is still intact) * lib/Mail/DKIM/Common.pm: removed get_algorithm_class (this is now a signature method) * lib/Mail/DKIM/Signature.pm: added get_algorithm_class; documented get_public_key method 2006-09-28: Jason Long * README: include "Error" in the list of dependencies * lib/Mail/DKIM/Common.pm (get_algorithm_class): return undef instead of throwing an error when an unsupported algorithm is presented * lib/Mail/DKIM/Signature.pm (parse): allow v=0.5 tag (check_protocol): checks for dns option /txt (i.e. "dns/txt") (version): new method for getting/setting v= tag * lib/Mail/DKIM/Verifier.pm (check_signature): fixed algorithm check * t/verifier: added several additional sample emails to verify, including a ietf05 signature, and six cases where the signature should be ignored for one reason or another -- VERSION 0.19 -- 2006-06-15: Jason Long * Makefile.PL: change Perl version check to v5.6.1 instead of 5.8 * t/verifier.t: use binmode function instead of ":raw" layer, for Perl 5.6.1 compatibility -- VERSION 0.18 -- 2006-06-09: Jason Long * t/verifier.t: open message in ":raw" mode to avoid CRLF->LF conversion (reported by Eugene Pivovarav) 2006-06-08: Jason Long * lib/Mail/DKIM/PublicKey.pm: rewrote verify_digest() so that it uses the Crypt::OpenSSL::RSA module exclusively, no longer relying on Crypt::RSA::Primitives * lib/Mail/DKIM/PrivateKey.pm: rewrote sign_digest() so it uses Crypt::OpenSSL::RSA exclusively * lib/Mail/DKIM/Key.pm: calculate_EM() - remove dependency on Crypt::RSA::DataFormat * Makefile.PL, README: remove mentions of Crypt::RSA and Crypt::OpenSSL::Bignum -- VERSION 0.17 -- 2006-05-26: Jason Long * lib/Mail/DKIM/Algorithm/Base.pm: check_body_hash() - new method that verifies the body hash against the bh= tag; format for canonicalization debugging output has changed * lib/Mail/DKIM/Algorithm/rsa_sha1.pm: call check_body_hash() before returning results to verify() * lib/Mail/DKIM/Algorithm/rsa_sha256.pm: call check_body_hash() before returning results to verify() * lib/Mail/DKIM/Canonicalization/DkimCommon.pm: fixed bug where extra CRLF was being canonicalized * lib/Mail/DKIM/Key.pm: moved calculate_EM function here from PrivateKey after realizing that it would be needed when verifying * lib/Mail/DKIM/PublicKey.pm: now SHA256 hashes can be verified as well; also, the verification can distinguish between wrong hash and a bad signature * lib/Mail/DKIM/Verifier.pm: provide result details when verification fails; added documentation for the result_detail() method * t/corpus/: added several sample signed messages for the testing routines 2006-04-17: Jason Long * lib/Mail/DKIM/Algorithm/Base.pm: common class for DKIM algorithms * lib/Mail/DKIM/Algorithm/rsa_sha1.pm: now subclasses Algorithm::Base. * lib/Mail/DKIM/Algorithm/rsa_sha256.pm: new class for handling the rsa-sha256 DKIM algorithm * lib/Mail/DKIM/Common.pm: recognize rsa-sha256 algorithm * lib/Mail/DKIM/PrivateKey.pm: implemented signing of a SHA-256 digest * lib/Mail/DKIM/PublicKey.pm: implemented verifying of a SHA-256 digest * lib/Mail/DKIM/Signature.pm: allows algorithm to be "rsa-sha256"; body_hash() - new method handling the bh tag; hash_algorithm() - new method to determine what hash is being used; * lib/Mail/DKIM/Verifier.pm: check_public_key() - new method for checking the validity of a fetched public key * Makefile.PL: added Digest::SHA as a dependency 2006-03-26: Jason Long * lib/Mail/DKIM/Common.pm: remove version number from this file * lib/Mail/DKIM.pm: bump version to 0.17 * README: bump version to 0.17 -- VERSION 0.16 -- 2006-03-03: Jason Long * lib/Mail/DKIM/Policy.pm: new() and testing() are now warning-free (thanks to jm@jmason.org for the patch) * t/policy.t: tests the Policy package 2006-03-01: Jason Long * lib/Mail/DKIM/Signature.pm: correctly handle spaces around = character * t/signature.t: test for spaces around = character in signature 2006-02-24: Jason Long * t/: wrote some tests and added them to the project * README, Makefile.PL: discovered additional dependency: Crypt::OpenSSL::Bignum * lib/Mail/DKIM/Signature.pm: fixed a warning that could occur if the h= tag was left undefined 2006-02-23: Jason Long * converted to ExtUtils::MakeMaker package format, see the dkimproxy project for revision history prior to 2006-02-23. Mail-DKIM-0.40/Changes0000644030404400001440000002264312105004715013370 0ustar jlongusersThis file summarizes what's changed between releases of Mail-DKIM. See the ChangeLog file for the details. Version 0.40 - released 2013-02-07 * New/changed functionality: * a single DNS resolver is created for the lifetime of the program, rather than reinitializing the resolver for each new query. * bugfixes: * fix the error message given when an invalid algorithm is specified in the construction of Mail::DKIM::Signer. * avoid Perl warning about use of an undefined value in several places (rt.cpan.org issue #82913). * speed- improved performance of parsing the message into lines (rt.cpan.org issue #77902). Patch by Mark Martinec. * fix DNS queries to use the correct method (txtdata) of Net::DNS (rt.cpan.org issue #83170). Patch by Mark Martinec. * API changes: * global subroutines resolver() or enable_EDNS0() in module Mail::DKIM::DNS can be called to specify non-default options to Net::DNS::Resolver (see also rt.cpan.org issue #80425). Version 0.39 - released 2010-11-14 * bugfixes: * fix issue with getting wrong error codes when q= tag is empty (issue #3011005) * anti-abuse- prevent a message with thousands of signatures from thrashing the whole computer (issue #3010997) * memory usage- significantly reduced memory footprint for processing a message with a large header and many signatures * fix error message given when no KeyFile has been specified (issue #1889690) * API changes: * the Canonicalization::finish_header() method now expects a argument to be passed to it. In the unusual case that you are using this method from your own code, please update your code. Version 0.38 - released 2010-03-31 * New/changed functionality: * DNS lookups can now be started asynchronously; the queries are created as the header is parsed; the results are not actually needed until the entire message has been read. (The Mail::DKIM module does not yet do the queries asynchrously; this is just the infrastructure so that the queries can be asynchronous in the future.) * bugfixes: * DNS lookup overrides alarm() signal (issue #2854325) * documentation updates: * document use of custom PrivateKey object, for external signing * describe how to get "pretty signatures" in Signer.pm Version 0.37 - released 2009-09-08 * New/changed functionality: * ADSP records now check whether the domain itself exists, in accordance to the ADSP specification * bugfixes: * fixed regexp used to detect header field names (issue #2803465) * various fixes to ADSP checking Version 0.36 - released 2009-06-02 * API changes: * restore the as_string() method which was accidentally removed in version 0.34 Version 0.35 - released 2009-05-22 * bugfixes: * fixed a runaway regular expression in the canonicalization routines (patch provided by Mark Martinec) Version 0.34 - released 2009-05-20 * New/changed functionality: * support for ADSP (author-domain-signing-practices) records * removed support for pre-standardized DKIM signatures (i.e. these are DKIM signatures without a v= or bh= tag). * DNS resolver errors are detected and reported as such * API changes: * renamed Mail::DKIM::Policy to Mail::DKIM::DkPolicy. Programs using the former name to create policy objects directly (though it would be more expected to fetch the objects through Mail::DKIM::Verifier) should update their code * new policies() method in Mail::DKIM::Verifier for fetching all applicable sender/author signing policies * bugfixes: * Signer object would die if first line of input wasn't a header (rt.cpan.org issue #46179) Version 0.33 - released 2009-03-10 * bugfixes: * signature wrapping would sometimes cause improper preparation of DKIM signatures, with "simple" canonicalization (issue #2257046) * test scripts: * the included corpus is now verified using a fake-DNS resolver, which means the test corpus can validate even when your DNS servers are really slow Version 0.32 - released 2008-06-03 * removed requirement for Digest::SHA1 (issue #1832549). We now use the more capable Digest::SHA module for SHA-1 and SHA-256. * bugfixes: * granularity checking should be case-sensitive (issue #1938112). * identity tag now uses quoted-printable encoding (issue #1839015). * API improvement: * implemented identity_source() for DkSignature objects Version 0.31 - released 2008-04-14 * some error detail messages were changed (see ChangeLog, 2008-01-10 entry) * by default, and when possible, DKIM signatures now omit c= and q= tags (they are optional tags) (issue #1878518) * DKIM and DomainKey signatures are now wrapped so that line breaks occur before colon (':') separators instead of after; this avoids confusing some broken MUAs (issue #1868648) * bugfixes: * "undef value" error when DKIM signature appears at end of header (issue #1878954) * use proper regexp for splitting email address (issue #1878994) * API improvements: * can specify a domain for fetch_author_policy() (issue #1879197) * can access a signature's public-key object (issue #1879215) * can specify an OpenSSL-private-key object for PrivateKey->new() (issue #1879209) Version 0.30.1 - released 2008-01-24 * bugfix: * email from cisco.com was failing to verify (issue #1878523) Version 0.30 - released 2008-01-10 * includes speed-up optimizations by Mark Martinec * DomainKeys, implement proper identity matching... a DomainKey-Signature's domain should match the From/Sender address * several more test cases * API improvements: * accept additional arguments when creating Signer/Signature * bugfixes: * DomainKey-Signature headers were not "prettified" * granularity ending with '*' was not checked correctly * DomainKey-Signature granularity was checked against the wrong value Version 0.29 - released 2007-11-08 * verifiers can now access all parsed signatures and their results, not just signatures that were fully tested * signer policies can now specify what private key file to use * some other minor API improvements * bugfixes: * for DomainKeys signatures, fixed a compatibility issue handling the h= tag * for DKIM, signature expirations had been ignored * for DKIM, signature identities did not have to match the domain * for DKIM, public key granularity field had been ignored Version 0.28 - released 2007-07-31 * fixed a bug with line-wrapping a signature at the wrong place Version 0.27 - released 2007-07-25 * Sender signing policies are now better implemented * Both Yahoo! DomainKeys signing policies and the under-development IETF DKIM signing policies are supported * Yahoo! DomainKeys policies can protect the Sender: header * DKIM signing policies can protect the From: header Look at Mail::DKIM::Verifier's fetch_author_policy() and fetch_sender_policy() methods for hints. Version 0.26 - released 2007-05-24 * recognize and generate v=1 signatures (DKIM is now RFC 4871) Version 0.25 - released 2007-05-10 * we now only sign headers that IETF recommends for signing * it's now possible to "prettify" outgoing signatures, but this feature is not enabled by default. To enable, do a "use Mail::DKIM::TextWrap" in your program that signs messages. (This may change in a future release.) Version 0.24 - released 2007-03-13 * fixes two bugs, see ChangeLog for details * fixed a bug with public keys (in DNS) containing linebreak characters * fixed a bug with DKIM signatures not including the optional q= tag Version 0.23 - released 2007-02-22 * fixes some issues with verifying DomainKeys signatures * generate newer-style DKIM signatures (with v=0.5 tag) * fixed some bugs related to handling messages with no body * improved diagnostic messages for certain cases Version 0.22 - released 2007-01-19 * fixes a couple very minor bugs * some cosmetic changes to error messages Version 0.21 - released 2006-11-29 * fixes two bugs, see ChangeLog for details Version 0.20 - released 2006-10-24 * now supports verifying multiple signatures (the result returned is based on the "best" available signature) * now supports adding multiple signatures in one pass (to use this, you need to create a "signer policy"; see scripts/dkimsign.pl for an example) * now supports signing/verifying signatures for the older DomainKeys standard * now implements Internet Draft draft-ietf-dkim-base-05, including: * support for the version (v=) tag * eliminated "control character detected in message" error message Version 0.19 - released 2006-06-15 * now supports earlier versions of Perl (5.6.1 and up). Version 0.18 - released 2006-06-09 * no longer depends on Crypt::RSA or Crypt::OpenSSL::Bignum. Version 0.17 - released 2006-05-26 * now implements Internet Draft draft-ietf-dkim-base-01, including: * support for the body hash (bh=) tag * support for SHA256 digests (uses the Digest::SHA module from CPAN) * if a message fails to verify, it now distinguishes between: * headers having been altered * body having been altered * RSA key failure (i.e. the signing key does not match the public key) * added several sample messages to test against when doing `make test'. Version 0.16 - released 2006-03-03 * first version for CPAN; implements draft-allman-dkim-base-01. Mail-DKIM-0.40/sample_mime_lite.pl0000755030404400001440000000332512055417140015742 0ustar jlongusers#!/usr/bin/perl use strict; use warnings; use MIME::Lite; # MIME::Lite is a Perl module for constructing MIME messages, # as well as sending messages on their way. # # This sample script attempts to construct a message using # MIME::Lite, generate a DKIM signature for that message, then # insert the DKIM signature into the MIME::Lite message, and # use MIME::Lite to send the message. # # The result is a partial success. Some of the MIME::Lite headers # get moved above the DKIM-Signature header, which may be # problematic. I haven't tested it. # my $msg; ### Create the multipart "container": $msg = MIME::Lite->new( From =>'me@myhost.com', To =>'you@yourhost.com', Cc =>'some@other.com, some@more.com', Subject =>'A message with 2 parts...', Type =>'multipart/mixed' ); ### Add the text message part: ### (Note that "attach" has same arguments as "new"): $msg->attach( Type =>'TEXT', Data =>"Here's the GIF file you wanted" ); ### Add the image part: $msg->attach( Type =>'image/gif', Path =>'aaa000123.gif', Filename =>'logo.gif', Disposition => 'attachment' ); ### Add a DKIM signature use Mail::DKIM::Signer; my $dkim = Mail::DKIM::Signer->new( Algorithm => "rsa-sha1", Method => "relaxed", Domain => "myhost.com", Selector => "mx1", KeyFile => "./private.key", ); my $raw_data = $msg->as_string; $raw_data =~ s/\n/\015\012/gs; $dkim->PRINT($raw_data); $dkim->CLOSE; my $sig = $dkim->signature; my ($header_name, $header_content) = split /:\s*/, $sig->as_string, 2; unshift @{$msg->{Header}}, [ $header_name, $header_content ]; print $msg->as_string; Mail-DKIM-0.40/META.yml0000644030404400001440000000132312105005167013340 0ustar jlongusers--- #YAML:1.0 name: Mail-DKIM version: 0.40 abstract: Signs/verifies Internet mail with DKIM/DomainKey signatures author: - Jason Long license: unknown distribution_type: module configure_requires: ExtUtils::MakeMaker: 0 build_requires: ExtUtils::MakeMaker: 0 requires: Crypt::OpenSSL::RSA: 0.24 Digest::SHA: 0 Mail::Address: 0 MIME::Base64: 0 Net::DNS: 0 Test::Simple: 0 no_index: directory: - t - inc generated_by: ExtUtils::MakeMaker version 6.56 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 Mail-DKIM-0.40/MANIFEST0000644030404400001440000000617412105005167013231 0ustar jlongusersChanges ChangeLog Makefile.PL MANIFEST README TODO HACKING.DKIM sample_mime_lite.pl lib/Mail/DKIM.pm lib/Mail/DKIM/Algorithm/Base.pm lib/Mail/DKIM/Algorithm/dk_rsa_sha1.pm lib/Mail/DKIM/Algorithm/rsa_sha1.pm lib/Mail/DKIM/Algorithm/rsa_sha256.pm lib/Mail/DKIM/AuthorDomainPolicy.pm lib/Mail/DKIM/DNS.pm lib/Mail/DKIM/DkPolicy.pm lib/Mail/DKIM/DkimPolicy.pm lib/Mail/DKIM/Policy.pm lib/Mail/DKIM/Key.pm lib/Mail/DKIM/Canonicalization/Base.pm lib/Mail/DKIM/Canonicalization/nowsp.pm lib/Mail/DKIM/Canonicalization/DkimCommon.pm lib/Mail/DKIM/Canonicalization/simple.pm lib/Mail/DKIM/Canonicalization/relaxed.pm lib/Mail/DKIM/Canonicalization/dk_nofws.pm lib/Mail/DKIM/Canonicalization/dk_simple.pm lib/Mail/DKIM/Canonicalization/DkCommon.pm lib/Mail/DKIM/DkSignature.pm lib/Mail/DKIM/MessageParser.pm lib/Mail/DKIM/PublicKey.pm lib/Mail/DKIM/Verifier.pm lib/Mail/DKIM/PrivateKey.pm lib/Mail/DKIM/SignerPolicy.pm lib/Mail/DKIM/Signer.pm lib/Mail/DKIM/Signature.pm lib/Mail/DKIM/TextWrap.pm lib/Mail/DKIM/Common.pm lib/Mail/DKIM/KeyValueList.pm scripts/dkimsign.pl scripts/dkimverify.pl scripts/test_bare_rsa_sha1.pl scripts/test_canonicalization.pl scripts/test_nowsp_rsa_sha1.pl t/FAKE_DNS.dat t/Mail-DKIM.t t/adsp.t t/external_signer.t t/policy.t t/public_key.t t/signature.t t/simple_canonicalization.t t/signer.t t/signer_dk.t t/signer_policy.t t/textwrap.t t/verifier.t t/test.key t/test5.txt t/corpus/good_ietf00_1.txt t/corpus/good_ietf00_2.txt t/corpus/good_ietf00_3.txt t/corpus/good_ietf00_4.txt t/corpus/good_ietf00_5.txt t/corpus/good_ietf01_1.txt t/corpus/good_ietf01_2.txt t/corpus/good_dk_yahoo.txt t/corpus/good_dk_gmail.txt t/corpus/good_dk_1.txt t/corpus/good_dk_2.txt t/corpus/good_dk_3.txt t/corpus/good_dk_4.txt t/corpus/good_dk_5.txt t/corpus/good_dk_6.txt t/corpus/good_dk_7.txt t/corpus/good_rfc4871_3.txt t/corpus/good_rfc4871_4.txt t/corpus/good_1878523.txt t/corpus/dk_headers_1.txt t/corpus/dk_headers_2.txt t/corpus/dk_multiple_1.txt t/corpus/multiple_1.txt t/corpus/multiple_2.txt t/corpus/bad_dk_1.txt t/corpus/bad_dk_2.txt t/corpus/bad_dk_3.txt t/corpus/bad_dk_4.txt t/corpus/bad_dk_5.txt t/corpus/bad_ietf01_1.txt t/corpus/bad_ietf01_2.txt t/corpus/bad_ietf01_3.txt t/corpus/bad_1.txt t/corpus/bad_1878954.txt t/corpus/badkey_1.txt t/corpus/badkey_2.txt t/corpus/badkey_3.txt t/corpus/badkey_4.txt t/corpus/badkey_5.txt t/corpus/badkey_6.txt t/corpus/badkey_7.txt t/corpus/badkey_8.txt t/corpus/badkey_9.txt t/corpus/badkey_10.txt t/corpus/badkey_11.txt t/corpus/badkey_12.txt t/corpus/badkey_13.txt t/corpus/badkey_14.txt t/corpus/badkey_15.txt t/corpus/goodkey_1.txt t/corpus/goodkey_2.txt t/corpus/goodkey_3.txt t/corpus/goodkey_4.txt t/corpus/good_qp_1.txt t/corpus/good_qp_2.txt t/corpus/good_qp_3.txt t/corpus/mine_ietf01_1.txt t/corpus/mine_ietf01_2.txt t/corpus/mine_ietf01_3.txt t/corpus/mine_ietf01_4.txt t/corpus/mine_ietf05_1.txt t/corpus/ignore_1.txt t/corpus/ignore_2.txt t/corpus/ignore_3.txt t/corpus/ignore_4.txt t/corpus/ignore_5.txt t/corpus/ignore_6.txt t/corpus/ignore_7.txt t/corpus/ignore_8.txt t/corpus/no_body_1.txt t/corpus/no_body_2.txt t/corpus/no_body_3.txt META.yml Module meta-data (added by MakeMaker)