PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/SpotSigning.php

http://github.com/spotweb/spotweb
PHP | 248 lines | 133 code | 44 blank | 71 comment | 22 complexity | 81aa4da27d4ef0a77a62884a2a21b83f MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, Apache-2.0, LGPL-3.0
  1. <?php
  2. require_once "Crypt/RSA.php";
  3. class SpotSigning {
  4. private $_nativeVerify = null;
  5. public function __construct() {
  6. if (!defined('CRYPT_RSA_MODE')) {
  7. if (extension_loaded("openssl")) {
  8. define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
  9. } else {
  10. define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
  11. } # else
  12. } # if not defined
  13. if (CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL) {
  14. $this->_nativeVerify = new SpotSeclibToOpenSsl();
  15. } # if
  16. } # ctor
  17. private function checkRsaSignature($toCheck, $signature, $rsaKey, $useCache) {
  18. # de signature is base64 encoded, eerst decoden
  19. $signature = base64_decode($signature);
  20. # Controleer of we de native OpenSSL libraries moeten
  21. # gebruiken om RSA signatures te controleren
  22. if (CRYPT_RSA_MODE != CRYPT_RSA_MODE_OPENSSL) {
  23. # Initialize the public key to verify with
  24. $pubKey['n'] = new Math_BigInteger(base64_decode($rsaKey['modulo']), 256);
  25. $pubKey['e'] = new Math_BigInteger(base64_decode($rsaKey['exponent']), 256);
  26. # and verify the signature
  27. $rsa = new Crypt_RSA();
  28. $rsa->loadKey($pubKey, CRYPT_RSA_PUBLIC_FORMAT_RAW);
  29. $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  30. # Supress notice if the signature was invalid
  31. $saveErrorReporting = error_reporting(E_ERROR);
  32. $tmpSave = $rsa->verify($toCheck, $signature);
  33. error_reporting($saveErrorReporting);
  34. } else {
  35. $tmpSave = $this->_nativeVerify->verify($rsaKey, $toCheck, $signature, $useCache);
  36. } # else
  37. return $tmpSave;
  38. } # checkRsaSignature
  39. /*
  40. * Creeert een private en public key paar
  41. */
  42. public function createPrivateKey($sslCnfPath) {
  43. $rsa = new Crypt_RSA();
  44. $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  45. # We hebben deze code geconfigureerd uit Crypt/RSA.php omdat
  46. # we anders de configuratie parameter niet mee kunnen geven aan
  47. # openssl_pkey_new()
  48. if (CRYPT_RSA_MODE != CRYPT_RSA_MODE_OPENSSL) {
  49. # We krijgen de keys base encoded terug
  50. $keyPair = $rsa->createKey();
  51. return array('public' => $keyPair['publickey'],
  52. 'private' => $keyPair['privatekey']);
  53. } else {
  54. $opensslPrivKey = openssl_pkey_new(array('private_key_bits' => 1024, 'config' => $sslCnfPath));
  55. openssl_pkey_export($opensslPrivKey, $privateKey, null, array('config' => $sslCnfPath));
  56. $publicKey = openssl_pkey_get_details($opensslPrivKey);
  57. $publicKey = $publicKey['key'];
  58. openssl_free_key($opensslPrivKey);
  59. return array('public' => $publicKey,
  60. 'private' => $privateKey);
  61. } # else
  62. } # createPrivateKey
  63. /*
  64. * RSA signed een bericht, en geeft alle componenten terug
  65. * die nodig zijn om dit te valideren, dus:
  66. *
  67. * - base64 encoded signature (signature)
  68. * - Public key (publickey)
  69. * - Het bericht dat gesigned is (message)
  70. */
  71. public function signMessage($privatekey, $message) {
  72. /**
  73. * Test code:
  74. *
  75. * $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  76. * extract($rsa->createKey());
  77. * $spotSigning = new SpotSigning();
  78. * $x = $spotSigning->signMessage($privatekey, 'testmessage');
  79. * var_dump($x);
  80. * var_dump($spotSigning->checkRsaSignature('testmessage', $x['signature'], $x['publickey'], false));
  81. *
  82. */
  83. if (empty($privatekey)) {
  84. throw new Exception("Given privatekey is invalid, please correct (eg: run upgrade-db.php when testinstall.php is without errors)");
  85. } # if
  86. $rsa = new Crypt_RSA();
  87. $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  88. $rsa->loadKey($privatekey);
  89. # extract de public key
  90. $signature = $rsa->sign($message);
  91. $publickey = $rsa->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
  92. return array('signature' => base64_encode($signature),
  93. 'publickey' => array('modulo' => base64_encode($publickey['n']->toBytes()), 'exponent' => base64_encode($publickey['e']->toBytes())),
  94. 'message' => $message);
  95. } # signMessage
  96. /*
  97. * Returns a public key
  98. */
  99. function getPublicKey($privateKey) {
  100. $rsa = new Crypt_RSA();
  101. $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  102. $rsa->loadKey($privateKey);
  103. # extract de public key
  104. $publicKey = $rsa->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
  105. return array('publickey' => array('modulo' => base64_encode($publicKey['n']->toBytes()), 'exponent' => base64_encode($publicKey['e']->toBytes())));
  106. } # getPublicKey
  107. /*
  108. * Converteer een voor ons bruikbare publickey, naar een publickey
  109. * formaat gebruikt door de SpotNet native client
  110. */
  111. public function pubkeyToXml($pubkey) {
  112. return "<RSAKeyValue><Modulus>" . $pubkey['modulo'] . '</Modulus><Exponent>' . $pubkey['exponent'] . '</Exponent></RSAKeyValue>';
  113. } # pubkeyToXml
  114. /*
  115. * Helper functie om een spot header (resultaat uit een xover of getHeader()) te verifieeren
  116. */
  117. public function verifySpotHeader($spot, $signature, $rsaKeys) {
  118. # This is the string to verify
  119. $toCheck = $spot['title'] . substr($spot['header'], 0, strlen($spot['header']) - strlen($spot['headersign']) - 1) . $spot['poster'];
  120. # Check the RSA signature on the spot
  121. return $this->checkRsaSignature($toCheck, $signature, $rsaKeys[$spot['keyid']], true);
  122. } # verifySpotHeader()
  123. /*
  124. * Helper functie om een fullspot te verifieeren
  125. */
  126. public function verifyFullSpot($spot) {
  127. if ((empty($spot['user-signature'])) || (empty($spot['user-key']))) {
  128. return false;
  129. } # if
  130. $verified = $this->checkRsaSignature('<' . $spot['messageid'] . '>', $spot['user-signature'], $spot['user-key'], false);
  131. if ((!$verified) && (!empty($spot['xml-signature']))) {
  132. $verified = $this->checkRsaSignature($spot['xml-signature'], $spot['user-signature'], $spot['user-key'], false);
  133. } # if
  134. return $verified;
  135. } # verifyFullSpot()
  136. /*
  137. * Helper functie om een comment header te verifieeren
  138. */
  139. public function verifyComment($comment) {
  140. $verified = false;
  141. if ((!empty($comment['user-signature'])) && (!empty($comment['user-key']))) {
  142. $verified = $this->checkRsaSignature('<' . $comment['messageid'] . '>', $comment['user-signature'], $comment['user-key'], false);
  143. if (!$verified) {
  144. $verified = $this->checkRsaSignature('<' . $comment['messageid'] . '>' .
  145. implode("\r\n", $comment['body']) . "\r\n" .
  146. $comment['fromhdr'],
  147. $comment['user-signature'],
  148. $comment['user-key'],
  149. false);
  150. } # if
  151. } # if
  152. # als een spot qua RSA signature al klopt, kunnen we ook nog controleren op de users'
  153. # hash, deze zou eigenlijk ook moeten kloppen.
  154. # Deze hash is puur gemaakt om rekenkracht te vereisen aan de kant van de poster om
  155. # eventuele floods te voorkomen, de hash is dus ook op zich door iedereen te creeeren.
  156. #
  157. if ($verified) {
  158. # $userSignedHash = sha1('<' . $comment['messageid'] . '>', false);
  159. # $verified = (substr($userSignedHash, 0, 4) == '0000');
  160. } # if
  161. return $verified;
  162. } # verifyComment()
  163. /*
  164. * Bereken een SHA1 hash van het bericht en doe dit net zo lang tot de eerste bytes
  165. * bestaan uit 0000.
  166. *
  167. * Normaal gebruik je hiervoor de JS variant.
  168. */
  169. function makeExpensiveHash($prefix, $suffix) {
  170. $runCount = 0;
  171. $hash = $prefix . $suffix;
  172. while(substr($hash, 0, 4) !== '0000') {
  173. if ($runCount > 400000) {
  174. throw new Exception("Unable to calculate SHA1 hash: " . $runCount);
  175. } # if
  176. $runCount++;
  177. $uniquePart = $this->makeRandomStr(15);
  178. $hash = sha1($prefix . $uniquePart . $suffix, false);
  179. } # while
  180. return $prefix . $uniquePart . $suffix;
  181. } # makeExpensiveHash
  182. /*
  183. * Creeert een random strng van A-Za-z,0-9 van $len length
  184. */
  185. function makeRandomStr($len) {
  186. $possibleChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
  187. $unique = '';
  188. for($i = 0; $i < $len; $i++) {
  189. $unique .= $possibleChars[mt_rand(0, strlen($possibleChars) - 1)];
  190. } # for
  191. return $unique;
  192. } # makeRandomStr
  193. /*
  194. * 'Bereken' de userid aan de hand van z'n publickey
  195. */
  196. public function calculateSpotterId($userKey) {
  197. $userSignCrc = crc32(base64_decode($userKey));
  198. $userIdTmp = chr($userSignCrc & 0xFF) .
  199. chr(($userSignCrc >> 8) & 0xFF ).
  200. chr(($userSignCrc >> 16) & 0xFF) .
  201. chr(($userSignCrc >> 24) & 0xFF);
  202. return str_replace(array('/', '+', '='), '', base64_encode($userIdTmp));
  203. } # calculateSpotterId
  204. } # class SpotSigning