[ Index ]

MailPress 544

[ Index ]     [ Classes ]     [ Functions ]     [ Variables ]     [ Constants ]     [ Statistics ]    

title

Body

[close]

/mp-includes/Swiftmailer/classes/Swift/Signers/ -> DKIMSigner.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of SwiftMailer.
   5   * (c) 2004-2009 Chris Corbyn
   6   *
   7   * For the full copyright and license information, please view the LICENSE
   8   * file that was distributed with this source code.
   9   */
  10  
  11  /**
  12   * DKIM Signer used to apply DKIM Signature to a message.
  13   *
  14   * @author Xavier De Cock <xdecock@gmail.com>
  15   */
  16  class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner
  17  {
  18      /**
  19       * PrivateKey.
  20       *
  21       * @var string
  22       */
  23      protected $_privateKey;
  24  
  25      /**
  26       * DomainName.
  27       *
  28       * @var string
  29       */
  30      protected $_domainName;
  31  
  32      /**
  33       * Selector.
  34       *
  35       * @var string
  36       */
  37      protected $_selector;
  38  
  39      /**
  40       * Hash algorithm used.
  41       *
  42       * @var string
  43       */
  44      protected $_hashAlgorithm = 'rsa-sha1';
  45  
  46      /**
  47       * Body canon method.
  48       *
  49       * @var string
  50       */
  51      protected $_bodyCanon = 'simple';
  52  
  53      /**
  54       * Header canon method.
  55       *
  56       * @var string
  57       */
  58      protected $_headerCanon = 'simple';
  59  
  60      /**
  61       * Headers not being signed.
  62       *
  63       * @var array
  64       */
  65      protected $_ignoredHeaders = array();
  66  
  67      /**
  68       * Signer identity.
  69       *
  70       * @var unknown_type
  71       */
  72      protected $_signerIdentity;
  73  
  74      /**
  75       * BodyLength.
  76       *
  77       * @var int
  78       */
  79      protected $_bodyLen = 0;
  80  
  81      /**
  82       * Maximum signedLen.
  83       *
  84       * @var int
  85       */
  86      protected $_maxLen = PHP_INT_MAX;
  87  
  88      /**
  89       * Embbed bodyLen in signature.
  90       *
  91       * @var bool
  92       */
  93      protected $_showLen = false;
  94  
  95      /**
  96       * When the signature has been applied (true means time()), false means not embedded.
  97       *
  98       * @var mixed
  99       */
 100      protected $_signatureTimestamp = true;
 101  
 102      /**
 103       * When will the signature expires false means not embedded, if sigTimestamp is auto
 104       * Expiration is relative, otherwhise it's absolute.
 105       *
 106       * @var int
 107       */
 108      protected $_signatureExpiration = false;
 109  
 110      /**
 111       * Must we embed signed headers?
 112       *
 113       * @var bool
 114       */
 115      protected $_debugHeaders = false;
 116  
 117      // work variables
 118      /**
 119       * Headers used to generate hash.
 120       *
 121       * @var array
 122       */
 123      protected $_signedHeaders = array();
 124  
 125      /**
 126       * If debugHeaders is set store debugDatas here.
 127       *
 128       * @var string
 129       */
 130      private $_debugHeadersData = '';
 131  
 132      /**
 133       * Stores the bodyHash.
 134       *
 135       * @var string
 136       */
 137      private $_bodyHash = '';
 138  
 139      /**
 140       * Stores the signature header.
 141       *
 142       * @var Swift_Mime_Headers_ParameterizedHeader
 143       */
 144      protected $_dkimHeader;
 145  
 146      /**
 147       * Hash Handler.
 148       *
 149       * @var hash_ressource
 150       */
 151      private $_headerHashHandler;
 152  
 153      private $_bodyHashHandler;
 154  
 155      private $_headerHash;
 156  
 157      private $_headerCanonData = '';
 158  
 159      private $_bodyCanonEmptyCounter = 0;
 160  
 161      private $_bodyCanonIgnoreStart = 2;
 162  
 163      private $_bodyCanonSpace = false;
 164  
 165      private $_bodyCanonLastChar = null;
 166  
 167      private $_bodyCanonLine = '';
 168  
 169      private $_bound = array();
 170  
 171      /**
 172       * Constructor.
 173       *
 174       * @param string $privateKey
 175       * @param string $domainName
 176       * @param string $selector
 177       */
 178      public function __construct($privateKey, $domainName, $selector)
 179      {
 180          $this->_privateKey = $privateKey;
 181          $this->_domainName = $domainName;
 182          $this->_signerIdentity = '@'.$domainName;
 183          $this->_selector = $selector;
 184      }
 185  
 186      /**
 187       * Instanciate DKIMSigner.
 188       *
 189       * @param string $privateKey
 190       * @param string $domainName
 191       * @param string $selector
 192       *
 193       * @return Swift_Signers_DKIMSigner
 194       */
 195      public static function newInstance($privateKey, $domainName, $selector)
 196      {
 197          return new static($privateKey, $domainName, $selector);
 198      }
 199  
 200      /**
 201       * Reset the Signer.
 202       *
 203       * @see Swift_Signer::reset()
 204       */
 205      public function reset()
 206      {
 207          $this->_headerHash = null;
 208          $this->_signedHeaders = array();
 209          $this->_headerHashHandler = null;
 210          $this->_bodyHash = null;
 211          $this->_bodyHashHandler = null;
 212          $this->_bodyCanonIgnoreStart = 2;
 213          $this->_bodyCanonEmptyCounter = 0;
 214          $this->_bodyCanonLastChar = null;
 215          $this->_bodyCanonSpace = false;
 216      }
 217  
 218      /**
 219       * Writes $bytes to the end of the stream.
 220       *
 221       * Writing may not happen immediately if the stream chooses to buffer.  If
 222       * you want to write these bytes with immediate effect, call {@link commit()}
 223       * after calling write().
 224       *
 225       * This method returns the sequence ID of the write (i.e. 1 for first, 2 for
 226       * second, etc etc).
 227       *
 228       * @param string $bytes
 229       *
 230       * @throws Swift_IoException
 231       *
 232       * @return int
 233       */
 234      public function write($bytes)
 235      {
 236          $this->_canonicalizeBody($bytes);
 237          foreach ($this->_bound as $is) {
 238              $is->write($bytes);
 239          }
 240      }
 241  
 242      /**
 243       * For any bytes that are currently buffered inside the stream, force them
 244       * off the buffer.
 245       *
 246       * @throws Swift_IoException
 247       */
 248      public function commit()
 249      {
 250          // Nothing to do
 251          return;
 252      }
 253  
 254      /**
 255       * Attach $is to this stream.
 256       * The stream acts as an observer, receiving all data that is written.
 257       * All {@link write()} and {@link flushBuffers()} operations will be mirrored.
 258       *
 259       * @param Swift_InputByteStream $is
 260       */
 261      public function bind(Swift_InputByteStream $is)
 262      {
 263          // Don't have to mirror anything
 264          $this->_bound[] = $is;
 265  
 266          return;
 267      }
 268  
 269      /**
 270       * Remove an already bound stream.
 271       * If $is is not bound, no errors will be raised.
 272       * If the stream currently has any buffered data it will be written to $is
 273       * before unbinding occurs.
 274       *
 275       * @param Swift_InputByteStream $is
 276       */
 277      public function unbind(Swift_InputByteStream $is)
 278      {
 279          // Don't have to mirror anything
 280          foreach ($this->_bound as $k => $stream) {
 281              if ($stream === $is) {
 282                  unset($this->_bound[$k]);
 283  
 284                  return;
 285              }
 286          }
 287  
 288          return;
 289      }
 290  
 291      /**
 292       * Flush the contents of the stream (empty it) and set the internal pointer
 293       * to the beginning.
 294       *
 295       * @throws Swift_IoException
 296       */
 297      public function flushBuffers()
 298      {
 299          $this->reset();
 300      }
 301  
 302      /**
 303       * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1 defaults to rsa-sha256.
 304       *
 305       * @param string $hash
 306       *
 307       * @return Swift_Signers_DKIMSigner
 308       */
 309      public function setHashAlgorithm($hash)
 310      {
 311          // Unable to sign with rsa-sha256
 312          if ($hash == 'rsa-sha1') {
 313              $this->_hashAlgorithm = 'rsa-sha1';
 314          } else {
 315              $this->_hashAlgorithm = 'rsa-sha256';
 316          }
 317  
 318          return $this;
 319      }
 320  
 321      /**
 322       * Set the body canonicalization algorithm.
 323       *
 324       * @param string $canon
 325       *
 326       * @return Swift_Signers_DKIMSigner
 327       */
 328      public function setBodyCanon($canon)
 329      {
 330          if ($canon == 'relaxed') {
 331              $this->_bodyCanon = 'relaxed';
 332          } else {
 333              $this->_bodyCanon = 'simple';
 334          }
 335  
 336          return $this;
 337      }
 338  
 339      /**
 340       * Set the header canonicalization algorithm.
 341       *
 342       * @param string $canon
 343       *
 344       * @return Swift_Signers_DKIMSigner
 345       */
 346      public function setHeaderCanon($canon)
 347      {
 348          if ($canon == 'relaxed') {
 349              $this->_headerCanon = 'relaxed';
 350          } else {
 351              $this->_headerCanon = 'simple';
 352          }
 353  
 354          return $this;
 355      }
 356  
 357      /**
 358       * Set the signer identity.
 359       *
 360       * @param string $identity
 361       *
 362       * @return Swift_Signers_DKIMSigner
 363       */
 364      public function setSignerIdentity($identity)
 365      {
 366          $this->_signerIdentity = $identity;
 367  
 368          return $this;
 369      }
 370  
 371      /**
 372       * Set the length of the body to sign.
 373       *
 374       * @param mixed $len (bool or int)
 375       *
 376       * @return Swift_Signers_DKIMSigner
 377       */
 378      public function setBodySignedLen($len)
 379      {
 380          if ($len === true) {
 381              $this->_showLen = true;
 382              $this->_maxLen = PHP_INT_MAX;
 383          } elseif ($len === false) {
 384              $this->showLen = false;
 385              $this->_maxLen = PHP_INT_MAX;
 386          } else {
 387              $this->_showLen = true;
 388              $this->_maxLen = (int) $len;
 389          }
 390  
 391          return $this;
 392      }
 393  
 394      /**
 395       * Set the signature timestamp.
 396       *
 397       * @param timestamp $time
 398       *
 399       * @return Swift_Signers_DKIMSigner
 400       */
 401      public function setSignatureTimestamp($time)
 402      {
 403          $this->_signatureTimestamp = $time;
 404  
 405          return $this;
 406      }
 407  
 408      /**
 409       * Set the signature expiration timestamp.
 410       *
 411       * @param timestamp $time
 412       *
 413       * @return Swift_Signers_DKIMSigner
 414       */
 415      public function setSignatureExpiration($time)
 416      {
 417          $this->_signatureExpiration = $time;
 418  
 419          return $this;
 420      }
 421  
 422      /**
 423       * Enable / disable the DebugHeaders.
 424       *
 425       * @param bool $debug
 426       *
 427       * @return Swift_Signers_DKIMSigner
 428       */
 429      public function setDebugHeaders($debug)
 430      {
 431          $this->_debugHeaders = (bool) $debug;
 432  
 433          return $this;
 434      }
 435  
 436      /**
 437       * Start Body.
 438       */
 439      public function startBody()
 440      {
 441          // Init
 442          switch ($this->_hashAlgorithm) {
 443              case 'rsa-sha256' :
 444                  $this->_bodyHashHandler = hash_init('sha256');
 445                  break;
 446              case 'rsa-sha1' :
 447                  $this->_bodyHashHandler = hash_init('sha1');
 448                  break;
 449          }
 450          $this->_bodyCanonLine = '';
 451      }
 452  
 453      /**
 454       * End Body.
 455       */
 456      public function endBody()
 457      {
 458          $this->_endOfBody();
 459      }
 460  
 461      /**
 462       * Returns the list of Headers Tampered by this plugin.
 463       *
 464       * @return array
 465       */
 466      public function getAlteredHeaders()
 467      {
 468          if ($this->_debugHeaders) {
 469              return array('DKIM-Signature', 'X-DebugHash');
 470          } else {
 471              return array('DKIM-Signature');
 472          }
 473      }
 474  
 475      /**
 476       * Adds an ignored Header.
 477       *
 478       * @param string $header_name
 479       *
 480       * @return Swift_Signers_DKIMSigner
 481       */
 482      public function ignoreHeader($header_name)
 483      {
 484          $this->_ignoredHeaders[strtolower($header_name)] = true;
 485  
 486          return $this;
 487      }
 488  
 489      /**
 490       * Set the headers to sign.
 491       *
 492       * @param Swift_Mime_HeaderSet $headers
 493       *
 494       * @return Swift_Signers_DKIMSigner
 495       */
 496      public function setHeaders(Swift_Mime_HeaderSet $headers)
 497      {
 498          $this->_headerCanonData = '';
 499          // Loop through Headers
 500          $listHeaders = $headers->listAll();
 501          foreach ($listHeaders as $hName) {
 502              // Check if we need to ignore Header
 503              if (!isset($this->_ignoredHeaders[strtolower($hName)])) {
 504                  if ($headers->has($hName)) {
 505                      $tmp = $headers->getAll($hName);
 506                      foreach ($tmp as $header) {
 507                          if ($header->getFieldBody() != '') {
 508                              $this->_addHeader($header->toString());
 509                              $this->_signedHeaders[] = $header->getFieldName();
 510                          }
 511                      }
 512                  }
 513              }
 514          }
 515  
 516          return $this;
 517      }
 518  
 519      /**
 520       * Add the signature to the given Headers.
 521       *
 522       * @param Swift_Mime_HeaderSet $headers
 523       *
 524       * @return Swift_Signers_DKIMSigner
 525       */
 526      public function addSignature(Swift_Mime_HeaderSet $headers)
 527      {
 528          // Prepare the DKIM-Signature
 529          $params = array('v' => '1', 'a' => $this->_hashAlgorithm, 'bh' => base64_encode($this->_bodyHash), 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'i' => $this->_signerIdentity, 's' => $this->_selector);
 530          if ($this->_bodyCanon != 'simple') {
 531              $params['c'] = $this->_headerCanon.'/'.$this->_bodyCanon;
 532          } elseif ($this->_headerCanon != 'simple') {
 533              $params['c'] = $this->_headerCanon;
 534          }
 535          if ($this->_showLen) {
 536              $params['l'] = $this->_bodyLen;
 537          }
 538          if ($this->_signatureTimestamp === true) {
 539              $params['t'] = time();
 540              if ($this->_signatureExpiration !== false) {
 541                  $params['x'] = $params['t'] + $this->_signatureExpiration;
 542              }
 543          } else {
 544              if ($this->_signatureTimestamp !== false) {
 545                  $params['t'] = $this->_signatureTimestamp;
 546              }
 547              if ($this->_signatureExpiration !== false) {
 548                  $params['x'] = $this->_signatureExpiration;
 549              }
 550          }
 551          if ($this->_debugHeaders) {
 552              $params['z'] = implode('|', $this->_debugHeadersData);
 553          }
 554          $string = '';
 555          foreach ($params as $k => $v) {
 556              $string .= $k.'='.$v.'; ';
 557          }
 558          $string = trim($string);
 559          $headers->addTextHeader('DKIM-Signature', $string);
 560          // Add the last DKIM-Signature
 561          $tmp = $headers->getAll('DKIM-Signature');
 562          $this->_dkimHeader = end($tmp);
 563          $this->_addHeader(trim($this->_dkimHeader->toString())."\r\n b=", true);
 564          $this->_endOfHeaders();
 565          if ($this->_debugHeaders) {
 566              $headers->addTextHeader('X-DebugHash', base64_encode($this->_headerHash));
 567          }
 568          $this->_dkimHeader->setValue($string.' b='.trim(chunk_split(base64_encode($this->_getEncryptedHash()), 73, ' ')));
 569  
 570          return $this;
 571      }
 572  
 573      /* Private helpers */
 574  
 575      protected function _addHeader($header, $is_sig = false)
 576      {
 577          switch ($this->_headerCanon) {
 578              case 'relaxed' :
 579                  // Prepare Header and cascade
 580                  $exploded = explode(':', $header, 2);
 581                  $name = strtolower(trim($exploded[0]));
 582                  $value = str_replace("\r\n", '', $exploded[1]);
 583                  $value = preg_replace("/[ \t][ \t]+/", ' ', $value);
 584                  $header = $name.':'.trim($value).($is_sig ? '' : "\r\n");
 585              case 'simple' :
 586                  // Nothing to do
 587          }
 588          $this->_addToHeaderHash($header);
 589      }
 590  
 591      protected function _endOfHeaders()
 592      {
 593          //$this->_headerHash=hash_final($this->_headerHashHandler, true);
 594      }
 595  
 596      protected function _canonicalizeBody($string)
 597      {
 598          $len = strlen($string);
 599          $canon = '';
 600          $method = ($this->_bodyCanon == 'relaxed');
 601          for ($i = 0; $i < $len; ++$i) {
 602              if ($this->_bodyCanonIgnoreStart > 0) {
 603                  --$this->_bodyCanonIgnoreStart;
 604                  continue;
 605              }
 606              switch ($string[$i]) {
 607                  case "\r" :
 608                      $this->_bodyCanonLastChar = "\r";
 609                      break;
 610                  case "\n" :
 611                      if ($this->_bodyCanonLastChar == "\r") {
 612                          if ($method) {
 613                              $this->_bodyCanonSpace = false;
 614                          }
 615                          if ($this->_bodyCanonLine == '') {
 616                              ++$this->_bodyCanonEmptyCounter;
 617                          } else {
 618                              $this->_bodyCanonLine = '';
 619                              $canon .= "\r\n";
 620                          }
 621                      } else {
 622                          // Wooops Error
 623                          // todo handle it but should never happen
 624                      }
 625                      break;
 626                  case ' ' :
 627                  case "\t" :
 628                      if ($method) {
 629                          $this->_bodyCanonSpace = true;
 630                          break;
 631                      }
 632                  default :
 633                      if ($this->_bodyCanonEmptyCounter > 0) {
 634                          $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter);
 635                          $this->_bodyCanonEmptyCounter = 0;
 636                      }
 637                      if ($this->_bodyCanonSpace) {
 638                          $this->_bodyCanonLine .= ' ';
 639                          $canon .= ' ';
 640                          $this->_bodyCanonSpace = false;
 641                      }
 642                      $this->_bodyCanonLine .= $string[$i];
 643                      $canon .= $string[$i];
 644              }
 645          }
 646          $this->_addToBodyHash($canon);
 647      }
 648  
 649      protected function _endOfBody()
 650      {
 651          // Add trailing Line return if last line is non empty
 652          if (strlen($this->_bodyCanonLine) > 0) {
 653              $this->_addToBodyHash("\r\n");
 654          }
 655          $this->_bodyHash = hash_final($this->_bodyHashHandler, true);
 656      }
 657  
 658      private function _addToBodyHash($string)
 659      {
 660          $len = strlen($string);
 661          if ($len > ($new_len = ($this->_maxLen - $this->_bodyLen))) {
 662              $string = substr($string, 0, $new_len);
 663              $len = $new_len;
 664          }
 665          hash_update($this->_bodyHashHandler, $string);
 666          $this->_bodyLen += $len;
 667      }
 668  
 669      private function _addToHeaderHash($header)
 670      {
 671          if ($this->_debugHeaders) {
 672              $this->_debugHeadersData[] = trim($header);
 673          }
 674          $this->_headerCanonData .= $header;
 675      }
 676  
 677      /**
 678       * @throws Swift_SwiftException
 679       *
 680       * @return string
 681       */
 682      private function _getEncryptedHash()
 683      {
 684          $signature = '';
 685          switch ($this->_hashAlgorithm) {
 686              case 'rsa-sha1':
 687                  $algorithm = OPENSSL_ALGO_SHA1;
 688                  break;
 689              case 'rsa-sha256':
 690                  $algorithm = OPENSSL_ALGO_SHA256;
 691                  break;
 692          }
 693          $pkeyId = openssl_get_privatekey($this->_privateKey);
 694          if (!$pkeyId) {
 695              throw new Swift_SwiftException('Unable to load DKIM Private Key ['.openssl_error_string().']');
 696          }
 697          if (openssl_sign($this->_headerCanonData, $signature, $pkeyId, $algorithm)) {
 698              return $signature;
 699          }
 700          throw new Swift_SwiftException('Unable to sign DKIM Hash ['.openssl_error_string().']');
 701      }
 702  }


Generated: Thu Apr 28 18:38:52 2016 Cross-referenced by PHPXref 0.7.1