PageRenderTime 61ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/core/Associates/SwiftMailer/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php

https://gitlab.com/fiesta-framework/Mail
PHP | 512 lines | 249 code | 59 blank | 204 comment | 28 complexity | 7f8d17cdffa8b709df02db2042232aeb MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of SwiftMailer.
  4. * (c) 2004-2009 Chris Corbyn
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * DomainKey Signer used to apply DomainKeys Signature to a message
  11. *
  12. * @author Xavier De Cock <xdecock@gmail.com>
  13. */
  14. class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner
  15. {
  16. /**
  17. * PrivateKey
  18. *
  19. * @var string
  20. */
  21. protected $_privateKey;
  22. /**
  23. * DomainName
  24. *
  25. * @var string
  26. */
  27. protected $_domainName;
  28. /**
  29. * Selector
  30. *
  31. * @var string
  32. */
  33. protected $_selector;
  34. /**
  35. * Hash algorithm used
  36. *
  37. * @var string
  38. */
  39. protected $_hashAlgorithm = 'rsa-sha1';
  40. /**
  41. * Canonisation method
  42. *
  43. * @var string
  44. */
  45. protected $_canon = 'simple';
  46. /**
  47. * Headers not being signed
  48. *
  49. * @var array
  50. */
  51. protected $_ignoredHeaders = array();
  52. /**
  53. * Signer identity
  54. *
  55. * @var string
  56. */
  57. protected $_signerIdentity;
  58. /**
  59. * Must we embed signed headers?
  60. *
  61. * @var bool
  62. */
  63. protected $_debugHeaders = false;
  64. // work variables
  65. /**
  66. * Headers used to generate hash
  67. *
  68. * @var array
  69. */
  70. private $_signedHeaders = array();
  71. /**
  72. * Stores the signature header
  73. *
  74. * @var Swift_Mime_Headers_ParameterizedHeader
  75. */
  76. protected $_domainKeyHeader;
  77. /**
  78. * Hash Handler
  79. *
  80. * @var resource|null
  81. */
  82. private $_hashHandler;
  83. private $_hash;
  84. private $_canonData = '';
  85. private $_bodyCanonEmptyCounter = 0;
  86. private $_bodyCanonIgnoreStart = 2;
  87. private $_bodyCanonSpace = false;
  88. private $_bodyCanonLastChar = null;
  89. private $_bodyCanonLine = '';
  90. private $_bound = array();
  91. /**
  92. * Constructor
  93. *
  94. * @param string $privateKey
  95. * @param string $domainName
  96. * @param string $selector
  97. */
  98. public function __construct($privateKey, $domainName, $selector)
  99. {
  100. $this->_privateKey = $privateKey;
  101. $this->_domainName = $domainName;
  102. $this->_signerIdentity = '@'.$domainName;
  103. $this->_selector = $selector;
  104. }
  105. /**
  106. * Instanciate DomainKeySigner
  107. *
  108. * @param string $privateKey
  109. * @param string $domainName
  110. * @param string $selector
  111. * @return Swift_Signers_DomainKeySigner
  112. */
  113. public static function newInstance($privateKey, $domainName, $selector)
  114. {
  115. return new static($privateKey, $domainName, $selector);
  116. }
  117. /**
  118. * Resets internal states
  119. *
  120. * @return Swift_Signers_DomainKeysSigner
  121. */
  122. public function reset()
  123. {
  124. $this->_hash = null;
  125. $this->_hashHandler = null;
  126. $this->_bodyCanonIgnoreStart = 2;
  127. $this->_bodyCanonEmptyCounter = 0;
  128. $this->_bodyCanonLastChar = null;
  129. $this->_bodyCanonSpace = false;
  130. return $this;
  131. }
  132. /**
  133. * Writes $bytes to the end of the stream.
  134. *
  135. * Writing may not happen immediately if the stream chooses to buffer. If
  136. * you want to write these bytes with immediate effect, call {@link commit()}
  137. * after calling write().
  138. *
  139. * This method returns the sequence ID of the write (i.e. 1 for first, 2 for
  140. * second, etc etc).
  141. *
  142. * @param string $bytes
  143. * @return int
  144. * @throws Swift_IoException
  145. * @return Swift_Signers_DomainKeysSigner
  146. */
  147. public function write($bytes)
  148. {
  149. $this->_canonicalizeBody($bytes);
  150. foreach ($this->_bound as $is) {
  151. $is->write($bytes);
  152. }
  153. return $this;
  154. }
  155. /**
  156. * For any bytes that are currently buffered inside the stream, force them
  157. * off the buffer.
  158. *
  159. * @throws Swift_IoException
  160. * @return Swift_Signers_DomainKeysSigner
  161. */
  162. public function commit()
  163. {
  164. // Nothing to do
  165. return $this;
  166. }
  167. /**
  168. * Attach $is to this stream.
  169. * The stream acts as an observer, receiving all data that is written.
  170. * All {@link write()} and {@link flushBuffers()} operations will be mirrored.
  171. *
  172. * @param Swift_InputByteStream $is
  173. * @return Swift_Signers_DomainKeysSigner
  174. */
  175. public function bind(Swift_InputByteStream $is)
  176. {
  177. // Don't have to mirror anything
  178. $this->_bound[] = $is;
  179. return $this;
  180. }
  181. /**
  182. * Remove an already bound stream.
  183. * If $is is not bound, no errors will be raised.
  184. * If the stream currently has any buffered data it will be written to $is
  185. * before unbinding occurs.
  186. *
  187. * @param Swift_InputByteStream $is
  188. * @return Swift_Signers_DomainKeysSigner
  189. */
  190. public function unbind(Swift_InputByteStream $is)
  191. {
  192. // Don't have to mirror anything
  193. foreach ($this->_bound as $k => $stream) {
  194. if ($stream === $is) {
  195. unset($this->_bound[$k]);
  196. return;
  197. }
  198. }
  199. return $this;
  200. }
  201. /**
  202. * Flush the contents of the stream (empty it) and set the internal pointer
  203. * to the beginning.
  204. *
  205. * @throws Swift_IoException
  206. * @return Swift_Signers_DomainKeysSigner
  207. */
  208. public function flushBuffers()
  209. {
  210. $this->reset();
  211. return $this;
  212. }
  213. /**
  214. * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1 defaults to rsa-sha256
  215. *
  216. * @param string $hash
  217. * @return Swift_Signers_DomainKeysSigner
  218. */
  219. public function setHashAlgorithm($hash)
  220. {
  221. $this->_hashAlgorithm = 'rsa-sha1';
  222. return $this;
  223. }
  224. /**
  225. * Set the canonicalization algorithm
  226. *
  227. * @param string $canon simple | nofws defaults to simple
  228. * @return Swift_Signers_DomainKeysSigner
  229. */
  230. public function setCanon($canon)
  231. {
  232. if ($canon == 'nofws') {
  233. $this->_canon = 'nofws';
  234. } else {
  235. $this->_canon = 'simple';
  236. }
  237. return $this;
  238. }
  239. /**
  240. * Set the signer identity
  241. *
  242. * @param string $identity
  243. * @return Swift_Signers_DomainKeySigner
  244. */
  245. public function setSignerIdentity($identity)
  246. {
  247. $this->_signerIdentity = $identity;
  248. return $this;
  249. }
  250. /**
  251. * Enable / disable the DebugHeaders
  252. *
  253. * @param bool $debug
  254. * @return Swift_Signers_DomainKeySigner
  255. */
  256. public function setDebugHeaders($debug)
  257. {
  258. $this->_debugHeaders = (bool) $debug;
  259. return $this;
  260. }
  261. /**
  262. * Start Body
  263. *
  264. */
  265. public function startBody()
  266. {
  267. }
  268. /**
  269. * End Body
  270. *
  271. */
  272. public function endBody()
  273. {
  274. $this->_endOfBody();
  275. }
  276. /**
  277. * Returns the list of Headers Tampered by this plugin
  278. *
  279. * @return array
  280. */
  281. public function getAlteredHeaders()
  282. {
  283. if ($this->_debugHeaders) {
  284. return array('DomainKey-Signature', 'X-DebugHash');
  285. } else {
  286. return array('DomainKey-Signature');
  287. }
  288. }
  289. /**
  290. * Adds an ignored Header
  291. *
  292. * @param string $header_name
  293. * @return Swift_Signers_DomainKeySigner
  294. */
  295. public function ignoreHeader($header_name)
  296. {
  297. $this->_ignoredHeaders[strtolower($header_name)] = true;
  298. return $this;
  299. }
  300. /**
  301. * Set the headers to sign
  302. *
  303. * @param Swift_Mime_HeaderSet $headers
  304. * @return Swift_Signers_DomainKeySigner
  305. */
  306. public function setHeaders(Swift_Mime_HeaderSet $headers)
  307. {
  308. $this->_startHash();
  309. $this->_canonData = '';
  310. // Loop through Headers
  311. $listHeaders = $headers->listAll();
  312. foreach ($listHeaders as $hName) {
  313. // Check if we need to ignore Header
  314. if (! isset($this->_ignoredHeaders[strtolower($hName)])) {
  315. if ($headers->has($hName)) {
  316. $tmp = $headers->getAll($hName);
  317. foreach ($tmp as $header) {
  318. if ($header->getFieldBody() != '') {
  319. $this->_addHeader($header->toString());
  320. $this->_signedHeaders[] = $header->getFieldName();
  321. }
  322. }
  323. }
  324. }
  325. }
  326. $this->_endOfHeaders();
  327. return $this;
  328. }
  329. /**
  330. * Add the signature to the given Headers
  331. *
  332. * @param Swift_Mime_HeaderSet $headers
  333. * @return Swift_Signers_DomainKeySigner
  334. */
  335. public function addSignature(Swift_Mime_HeaderSet $headers)
  336. {
  337. // Prepare the DomainKey-Signature Header
  338. $params = array('a' => $this->_hashAlgorithm, 'b' => chunk_split(base64_encode($this->_getEncryptedHash()), 73, " "), 'c' => $this->_canon, 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'q' => 'dns', 's' => $this->_selector);
  339. $string = '';
  340. foreach ($params as $k => $v) {
  341. $string .= $k.'='.$v.'; ';
  342. }
  343. $string = trim($string);
  344. $headers->addTextHeader('DomainKey-Signature', $string);
  345. return $this;
  346. }
  347. /* Private helpers */
  348. protected function _addHeader($header)
  349. {
  350. switch ($this->_canon) {
  351. case 'nofws' :
  352. // Prepare Header and cascade
  353. $exploded = explode(':', $header, 2);
  354. $name = strtolower(trim($exploded[0]));
  355. $value = str_replace("\r\n", "", $exploded[1]);
  356. $value = preg_replace("/[ \t][ \t]+/", " ", $value);
  357. $header = $name.":".trim($value)."\r\n";
  358. case 'simple' :
  359. // Nothing to do
  360. }
  361. $this->_addToHash($header);
  362. }
  363. protected function _endOfHeaders()
  364. {
  365. $this->_bodyCanonEmptyCounter = 1;
  366. }
  367. protected function _canonicalizeBody($string)
  368. {
  369. $len = strlen($string);
  370. $canon = '';
  371. $nofws = ($this->_canon == "nofws");
  372. for ($i = 0; $i < $len; ++$i) {
  373. if ($this->_bodyCanonIgnoreStart > 0) {
  374. --$this->_bodyCanonIgnoreStart;
  375. continue;
  376. }
  377. switch ($string[$i]) {
  378. case "\r" :
  379. $this->_bodyCanonLastChar = "\r";
  380. break;
  381. case "\n" :
  382. if ($this->_bodyCanonLastChar == "\r") {
  383. if ($nofws) {
  384. $this->_bodyCanonSpace = false;
  385. }
  386. if ($this->_bodyCanonLine == '') {
  387. ++$this->_bodyCanonEmptyCounter;
  388. } else {
  389. $this->_bodyCanonLine = '';
  390. $canon .= "\r\n";
  391. }
  392. } else {
  393. // Wooops Error
  394. throw new Swift_SwiftException('Invalid new line sequence in mail found \n without preceding \r');
  395. }
  396. break;
  397. case " " :
  398. case "\t" :
  399. case "\x09": //HTAB
  400. if ($nofws) {
  401. $this->_bodyCanonSpace = true;
  402. break;
  403. }
  404. default :
  405. if ($this->_bodyCanonEmptyCounter > 0) {
  406. $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter);
  407. $this->_bodyCanonEmptyCounter = 0;
  408. }
  409. $this->_bodyCanonLine .= $string[$i];
  410. $canon .= $string[$i];
  411. }
  412. }
  413. $this->_addToHash($canon);
  414. }
  415. protected function _endOfBody()
  416. {
  417. if (strlen($this->_bodyCanonLine) > 0) {
  418. $this->_addToHash("\r\n");
  419. }
  420. $this->_hash = hash_final($this->_hashHandler, true);
  421. }
  422. private function _addToHash($string)
  423. {
  424. $this->_canonData .= $string;
  425. hash_update($this->_hashHandler, $string);
  426. }
  427. private function _startHash()
  428. {
  429. // Init
  430. switch ($this->_hashAlgorithm) {
  431. case 'rsa-sha1' :
  432. $this->_hashHandler = hash_init('sha1');
  433. break;
  434. }
  435. $this->_canonLine = '';
  436. }
  437. /**
  438. * @throws Swift_SwiftException
  439. * @return string
  440. */
  441. private function _getEncryptedHash()
  442. {
  443. $signature = '';
  444. $pkeyId = openssl_get_privatekey($this->_privateKey);
  445. if (!$pkeyId) {
  446. throw new Swift_SwiftException('Unable to load DomainKey Private Key ['.openssl_error_string().']');
  447. }
  448. if (openssl_sign($this->_canonData, $signature, $pkeyId, OPENSSL_ALGO_SHA1)) {
  449. return $signature;
  450. }
  451. throw new Swift_SwiftException('Unable to sign DomainKey Hash ['.openssl_error_string().']');
  452. }
  453. }