Skip to content

Merge changes from teonsystems #8

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 0 additions & 33 deletions DKIM/Sign.php

This file was deleted.

1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
The MIT License (MIT)

Copyright (c) 2012-2015 Randall Kahler <rkahler@gmail.com>
Modified work Copyright 2015 (c) Bostjan Skufca @ Teon d.o.o. <bostjan@teon.si>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
40 changes: 16 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,27 @@
php-dkim
========
# PHP DKIM validator

**Finally, a PHP5 class for not just signing, but _verifying_ DKIM signatures.**

Requirements
------------
Currently this package requires PHP 5.1.2 or greater (or PECL `hash` >= 1.1), which provides the `hash()` function.

Also required, at least one of the following present alongside your PHP installation.
## Installation

* [openssl](http://us1.php.net/manual/en/openssl.installation.php)
* [phpseclib](http://phpseclib.sourceforge.net/)
```
composer require teon/dkim
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

namespace needs to be amended for the PR

```

At least one of those packages must be present in order to compute the RSA signature verification.

Usage
-----
&lt;pending&gt;

## Usage

Changelog
---------
```php
$msgContent = "...";
$dkimValidator = new \Teon\DKIM\Validator($msgContent);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amend the namespace

if (!$dkimValidator->validateBoolean()) {
throw new Exception("DKIM validation FAILED!");
}
```

**v0.02**
_5:36 PM 1/2/2013_

* Splitting TODOs into separate file.
* Finally got the header hash to match my expected value, based on debugging output from Mail::DKIM::Validate.
* Removed var_dump() calls
* Still doesn't verify signatures properly - not sure where to go from here.

**v0.01**
_10:55 AM 12/31/2012_
Initial commit. Most of the structure is in place, and the body hashes are validating, but I haven't been able to get the signature validation correct just yet. I must have some whitespace issue or some random public key problem.
# Changelog

See git history :)
21 changes: 15 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
{
"name": "angrychimp/php-dkim",
"description": "Finally, a PHP5 class for not just signing, but _verifying_ DKIM signatures.",
"name": "teon/dkim",
"description": "Update of angrychimp/php-dkim package.",
"license": "MIT",
"authors": [
{
"name": "angrychimp",
"email": "rk@angrychimp.net"
},
{
"name": "Teon d.o.o. - Bostjan Skufca",
"email": "bostjan@teon.si"
}
],
"require": {
"phpseclib/phpseclib": ">=0.3.6"
"ext-openssl": "*",
"ext-hash": "*"
},
"suggest": {
"angrychimp/php-dkim": "Original, psr-0 compatible php-dkim package"
},
"autoload": {
"psr-0": {
"DKIM": "./"
}
"psr-4": {
"Teon\\DKIM\\": "src/Teon/DKIM/"
}
}
}
49 changes: 18 additions & 31 deletions DKIM.php → src/Teon/DKIM/AbstractDKIM.php
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
<?php

/**
* @see phpseclib/Crypt/RSA
*/
// require_once 'phpseclib/Crypt/RSA.php';

/**
* @see phpseclib/Crypt/Hash
* @link http://phpseclib.sourceforge.net
*/
// require_once 'phpseclib/Crypt/Hash.php';

define('PHPSECLIB_USE_EXCEPTIONS', true);
namespace Teon\DKIM;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amend namespace


abstract class DKIM {


abstract class AbstractDKIM {

/**
*
Expand All @@ -38,13 +31,13 @@ abstract class DKIM {
*
* @param string $rawMessage
* @return DKIM
* @throws DKIM_Exception
* @throws Exception
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd really prefer to avoid throwing generic exceptions, since the code could be wrapped in libraries with multiple exceptions, and an precise exception type is very useful

*/
public function __construct($rawMessage='', $params=array()) {

$this->_raw = $rawMessage;
if (!$this->_raw) {
throw new DKIM_Exception('No message content provided');
throw new Exception('No message content provided');
}

$this->_params = $params;
Expand All @@ -61,12 +54,12 @@ public function __construct($rawMessage='', $params=array()) {
* @param array $headers
* @param string $style
* @return string
* @throws DKIM_Exception
* @throws Exception
*/
protected function _canonicalizeHeader($headers=array(), $style="simple") {
$headers = (array)$headers;
if (sizeof($headers) == 0) {
throw new DKIM_Exception("Attempted to canonicalize empty header array");
throw new Exception("Attempted to canonicalize empty header array");
}

$cHeader = '';
Expand Down Expand Up @@ -106,7 +99,7 @@ protected function _canonicalizeHeader($headers=array(), $style="simple") {
* @param string $style
* @param int $length
* @return string
* @throws DKIM_Exception
* @throws Exception
*/
protected function _canonicalizeBody($style='simple', $length=-1) {

Expand Down Expand Up @@ -204,10 +197,15 @@ protected function _getBodyFromRaw($style='string') {
return (string)$this->_params['body'];
}

$lines = explode("\r\n", $this->_raw);
// Do not explode by \r\n, rather do it by \n and strip \r from line endif if it is found - see comment below
$lines = explode("\n", $this->_raw);
// Jump past all the headers
$on = false;
while ($line = array_shift($lines)) {
// Remove trailing carriage-return if present
// It might be present if emails are read from Unix maildirs directly instead of via IMAP/POP3
$line = preg_replace('/\r$/', '', $line);

if ($on === true && $line != '') {
break;
}
Expand All @@ -225,19 +223,8 @@ protected function _getBodyFromRaw($style='string') {
*
*/
protected static function _hashBody($body, $method='sha1') {

// prefer to use phpseclib
// http://phpseclib.sourceforge.net
if (class_exists('Crypt_Hash')) {
$hash = new Crypt_Hash($method);
return base64_encode($hash->hash($body));
} else {
// try standard PHP hash function
return base64_encode(hash($method, $body, true));
}

}
}

return base64_encode(hash($method, $body, true));

class DKIM_Exception extends Exception { }
}
}
15 changes: 15 additions & 0 deletions src/Teon/DKIM/Exception.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php



namespace Teon\DKIM;



use \Exception as ParentException;



class Exception
extends ParentException
{}
66 changes: 43 additions & 23 deletions DKIM/Verify.php → src/Teon/DKIM/Validator.php
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,23 +1,51 @@
<?php

/**
* @see DKIM
*/
require_once __DIR__.'/../DKIM.php';

class DKIM_Verify extends DKIM {

namespace Teon\DKIM;



class Validator
extends AbstractDKIM
{

/**
*
*
*/
private $_publicKeys;

/**
* Validation wrapper - return boolean true/false about validation success/failure
*
* @return bool
* @throws Exception
*/
public function validateBoolean() {

// Executte original validation method
$res = $this->validate();

// Only return true in this case
if (
true
&& (count($res) == 1)
&& (count($res[0]) == 1)
&& ($res[0][0]['status'] == 'pass' )
) {
return true;
}

// Return failure on all other occasions
return false;
}

/**
* Validates all present DKIM signatures
*
* @return array
* @throws DKIM_Exception
* @throws Exception
*/
public function validate() {

Expand Down Expand Up @@ -237,6 +265,12 @@ public static function fetchPublicKey($domain, $selector) {
$parts = explode(';', trim($record['txt']));
$record = array();
foreach ($parts as $part) {
// Last record is empty if there is trailing semicolon
$part = trim($part);
if (empty($part)) {
continue;
}

list($key, $val) = explode('=', trim($part), 2);
$record[$key] = $val;
}
Expand All @@ -253,22 +287,8 @@ public static function fetchPublicKey($domain, $selector) {
protected static function _signatureIsValid($pub, $sig, $str, $hash='sha1') {
// Convert key back into PEM format
$key = sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----", wordwrap($pub, 64, "\n", true));

// prefer Crypt_RSA
// http://phpseclib.sourceforge.net
# [DG]: X3 how Crypt_RSA works, skip
if (class_exists('Crypt_RSA')) {
$rsa = new Crypt_RSA();
$rsa->setHash($hash);
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$rsa->loadKey($pub);
return $rsa->verify($str, base64_decode($sig));
} else {
#$pubkeyid = openssl_get_publickey($key);
$signature_alg = constant('OPENSSL_ALGO_'.strtoupper($hash));
return openssl_verify($str, base64_decode($sig), $key, $signature_alg);
}


$signature_alg = constant('OPENSSL_ALGO_'.strtoupper($hash));
return openssl_verify($str, base64_decode($sig), $key, $signature_alg);
}

}