* @version $Id$ * @license http://opensource.org/licenses/gpl-2.0.php GNU GPL 2 */ /** * Implementation de l'algorithme de chiffrage RSA * * @link http://pear.php.net/package/Crypt_RSA * @link http://cryptez.ifrance.com/histo3.htm */ class Rsa extends Fsb_model { /** * @var Rsa_bcmath */ private $math; /** * Clef publique * * @var Rsa_key */ public $public_key = null; /** * Clef privee * * @var Rsa_key */ public $private_key = null; /** * Constructeur */ public function __construct() { $this->math = new Rsa_bcmath(); } /** * Determine si on peut utiliser le cryptage RSA (si la librairie bcmath est activee) * * @return bool */ public static function can_use() { return (extension_loaded('bcmath')); } /** * Generation des clefs privees et publiques * * @param int $keysize Taille des clefs */ public function generate_keys($keysize = 20) { // Generation d'un nombre arbitraire E (valeur 65537) $e = $this->math->bin2int("\x01\x00\x01"); // Generation de deux nombre premiers P et Q avec cette condition : PGCD(E, P - 1) = 1 et PGCD(E, Q - 1) = 1 foreach (array('p', 'q') AS $varname) { do { $$varname = $this->math->getPrime($keysize, 'rand'); $pgcd = $this->math->GCD($$varname - 1, $e); } while (!$this->math->isOne($pgcd)); } // Calcul d'un nombre N valant P * Q $n = $this->math->mul($p, $q); // Calcul d'un nombre D valant 1 / E % ((P - 1) * (Q - 1)) $p1 = $this->math->dec($p); $q1 = $this->math->dec($q); $d = $this->math->invmod($e, $this->math->mul($p1, $q1)); // Generation de la clef publique $this->public_key = new Rsa_key($n, $e, 'public'); $this->private_key = new Rsa_key($n, $d, 'private'); } /** * Regenere les clefs dans la base de donnee du forum */ public function regenerate_keys() { //On sauvegarde la clef privee actuelle Fsb::$cfg->update('rsa_old_private_key', Fsb::$cfg->get('rsa_private_key')); //Regen des cles $this->generate_keys(); //On stocke l'heure du regen pour pouvoir utiliser l'ancienne clef si besoin pendant un certain temps Fsb::$cfg->update('rsa_last_regen', CURRENT_TIME); Fsb::$cfg->update('rsa_public_key', $this->public_key->to_string()); Fsb::$cfg->update('rsa_private_key', $this->private_key->to_string(), true); //On vide le cache } /** * Crypte une chaine de caractere. * La version Javascript de cette fonction (disponible dans ~/main/javascript/rsa.js) est utilisee * pour coder les informations, cote client. * * @param string $str Chaine a crypter * @return string */ public function encrypt($str) { if (!Rsa_key::is_valid($this->public_key)) { return ($str); } // Pour le decryptage de la clef $str .= "\x01"; // Conversion en int $str = $this->math->bin2int($str); // Decoupage de la chaine de caracteres $length = $this->math->bitLen($str); $chunk_length = $this->math->bitLen($this->public_key->_get('mod')) - 1; $block_length = (int) ceil($chunk_length / 8); $curr_pos = 0; $enc_data = ''; while ($curr_pos < $length) { $tmp = $this->math->subint($str, $curr_pos, $chunk_length); $enc_data .= str_pad($this->math->int2bin($this->math->powmod($tmp, $this->public_key->_get('exp'), $this->public_key->_get('mod'))), $block_length, "\0"); $curr_pos += $chunk_length; } return (base64_encode($enc_data)); } /** * Decrypte une chaine de caractere * * @param string $str Chaine a decrypter * @return string */ public function decrypt($str) { $str = base64_decode($str); if (!Rsa_key::is_valid($this->private_key)) { return ($str); } // Decryptage du resultat $data_length = strlen($str); $chunk_length = $this->math->bitLen($this->private_key->_get('mod')) - 1; $block_length = (int) ceil($chunk_length / 8); $curr_pos = 0; $bit_pos = 0; $plain_data = $this->math->bin2int("\0"); while ($curr_pos < $data_length) { $tmp = $this->math->bin2int(substr($str, $curr_pos, $block_length)); $tmp = $this->math->powmod($tmp, $this->private_key->_get('exp'), $this->private_key->_get('mod')); $plain_data = $this->math->bitOr($plain_data, $tmp, $bit_pos); $bit_pos += $chunk_length; $curr_pos += $block_length; } $result = $this->math->int2bin($plain_data); // Suppression du \x01 final $tail = ord($result[strlen($result) - 1]); if ($tail != 1) { return ($str); } return (utf8_encode(substr($result, 0, -1))); } } /** * Clef RSA */ class Rsa_key extends Fsb_model { public $mod; public $exp; public $type; /** * Constructeur * * @param int $mod * @param int $exp * @param int $type */ public function __construct($mod, $exp, $type) { $this->mod = $mod; $this->exp = $exp; $this->type = $type; } /** * Conversion objet -> chaine de caractere * * @return string */ public function to_string() { return (base64_encode(serialize(array( 'mod' => $this->mod, 'exp' => $this->exp, 'type' => $this->type, )))); } /** * Conversion chaine de caractere -> objet * * @param string $string * @return Rsa_key */ public static function &from_string($string) { $data = @unserialize(base64_decode($string)); if ($data === false) { $foo = null; $bar = &$foo; return ($bar); } $instance = new Rsa_key($data['mod'], $data['exp'], $data['type']); return ($instance); } /** * Verifie la validite d'une clef * * @param Rsa_key $key * @return bool */ public function is_valid($key) { return ((is_object($key) && strtolower(get_class($key)) === strtolower(__CLASS__)) ? true : false); } } /** * Crypt_RSA_Math_BCMath class. * * Provides set of math functions, which are used by Crypt_RSA package * This class is a wrapper for PHP BCMath extension. * See http://php.net/manual/en/ref.bc.php for details. * * @category Encryption * @package Crypt_RSA * @author Alexander Valyalkin * @copyright 2005, 2006 Alexander Valyalkin * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @link http://pear.php.net/package/Crypt_RSA * @version @package_version@ * @access public */ class Rsa_bcmath extends Fsb_model { /** * Performs Miller-Rabin primality test for number $num * with base $base. Returns true , if $num is strong pseudoprime * by base $base. Else returns false. * * @param string $num * @param string $base * @return bool * @access private */ private function _millerTest($num, $base) { if (!bccomp($num, '1')) { // 1 is not prime ;) return false; } $tmp = bcsub($num, '1'); $zero_bits = 0; while (!bccomp(bcmod($tmp, '2'), '0')) { $zero_bits++; $tmp = bcdiv($tmp, '2'); } $tmp = $this->powmod($base, $tmp, $num); if (!bccomp($tmp, '1')) { // $num is probably prime return true ; } while ($zero_bits--) { if (!bccomp(bcadd($tmp, '1'), $num)) { // $num is probably prime return true ; } $tmp = $this->powmod($tmp, '2', $num); } // $num is composite return false; } /** * Transforms binary representation of large integer into its native form. * * Example of transformation: * $str = "\x12\x34\x56\x78\x90"; * $num = 0x9078563412; * * @param string $str * @return string * @access public */ public function bin2int($str) { $result = '0'; $n = strlen($str); do { $result = bcadd(bcmul($result, '256'), ord($str[--$n])); } while ($n > 0); return $result; } /** * Transforms large integer into binary representation. * * Example of transformation: * $num = 0x9078563412; * $str = "\x12\x34\x56\x78\x90"; * * @param string $num * @return string * @access public */ public function int2bin($num) { $result = ''; do { $result .= chr(bcmod($num, '256')); $num = bcdiv($num, '256'); } while (bccomp($num, '0')); return $result; } /** * Calculates pow($num, $pow) (mod $mod) * * @param string $num * @param string $pow * @param string $mod * @return string * @access public */ public function powmod($num, $pow, $mod) { return bcpowmod($num, $pow, $mod); } /** * Calculates $num1 * $num2 * * @param string $num1 * @param string $num2 * @return string * @access public */ public function mul($num1, $num2) { return bcmul($num1, $num2); } /** * Calculates $num1 % $num2 * * @param string $num1 * @param string $num2 * @return string * @access public */ public function mod($num1, $num2) { return bcmod($num1, $num2); } /** * Compares abs($num1) to abs($num2). * Returns: * -1, if abs($num1) < abs($num2) * 0, if abs($num1) == abs($num2) * 1, if abs($num1) > abs($num2) * * @param string $num1 * @param string $num2 * @return int * @access public */ public function cmpAbs($num1, $num2) { return bccomp($num1, $num2); } /** * Tests $num on primality. Returns true , if $num is strong pseudoprime. * Else returns false. * * @param string $num * @return bool * @access private */ private function isPrime($num) { static $primes = null; static $primes_cnt = 0; if (is_null($primes)) { // generate all primes up to 10000 $primes = array(); for ($i = 0; $i < 10000; $i++) { $primes[] = $i; } $primes[0] = $primes[1] = 0; for ($i = 2; $i < 100; $i++) { while (!$primes[$i]) { $i++; } $j = $i; for ($j += $i; $j < 10000; $j += $i) { $primes[$j] = 0; } } $j = 0; for ($i = 0; $i < 10000; $i++) { if ($primes[$i]) { $primes[$j++] = $primes[$i]; } } $primes_cnt = $j; } // try to divide number by small primes for ($i = 0; $i < $primes_cnt; $i++) { if (bccomp($num, $primes[$i]) <= 0) { // number is prime return true ; } if (!bccomp(bcmod($num, $primes[$i]), '0')) { // number divides by $primes[$i] return false; } } /* try Miller-Rabin's probable-primality test for first 7 primes as bases */ for ($i = 0; $i < 7; $i++) { if (!$this->_millerTest($num, $primes[$i])) { // $num is composite return false; } } // $num is strong pseudoprime return true ; } /** * Generates prime number with length $bits_cnt * using $random_generator as random generator function. * * @param int $bits_cnt * @param string $rnd_generator * @access public */ public function getPrime($bits_cnt, $random_generator) { $bytes_n = intval($bits_cnt / 8); $bits_n = $bits_cnt % 8; do { $str = ''; for ($i = 0; $i < $bytes_n; $i++) { $str .= chr(call_user_func($random_generator) & 0xff); } $n = call_user_func($random_generator) & 0xff; $n |= 0x80; $n >>= 8 - $bits_n; $str .= chr($n); $num = $this->bin2int($str); // search for the next closest prime number after [$num] if (!bccomp(bcmod($num, '2'), '0')) { $num = bcadd($num, '1'); } while (!$this->isPrime($num)) { $num = bcadd($num, '2'); } } while ($this->bitLen($num) != $bits_cnt); return $num; } /** * Calculates $num - 1 * * @param string $num * @return string * @access public */ public function dec($num) { return bcsub($num, '1'); } /** * Returns true , if $num is equal to one. Else returns false * * @param string $num * @return bool * @access public */ public function isOne($num) { return !bccomp($num, '1'); } /** * Finds greatest common divider (GCD) of $num1 and $num2 * * @param string $num1 * @param string $num2 * @return string * @access public */ public function GCD($num1, $num2) { do { $tmp = bcmod($num1, $num2); $num1 = $num2; $num2 = $tmp; } while (bccomp($num2, '0')); return $num1; } /** * Finds inverse number $inv for $num by modulus $mod, such as: * $inv * $num = 1 (mod $mod) * * @param string $num * @param string $mod * @return string * @access public */ public function invmod($num, $mod) { $x = '1'; $y = '0'; $num1 = $mod; do { $tmp = bcmod($num, $num1); $q = bcdiv($num, $num1); $num = $num1; $num1 = $tmp; $tmp = bcsub($x, bcmul($y, $q)); $x = $y; $y = $tmp; } while (bccomp($num1, '0')); if (bccomp($x, '0') < 0) { $x = bcadd($x, $mod); } return $x; } /** * Returns bit length of number $num * * @param string $num * @return int * @access public */ public function bitLen($num) { $tmp = $this->int2bin($num); $bit_len = strlen($tmp) * 8; $tmp = ord($tmp[strlen($tmp) - 1]); if (!$tmp) { $bit_len -= 8; } else { while (!($tmp & 0x80)) { $bit_len--; $tmp <<= 1; } } return $bit_len; } /** * Calculates bitwise or of $num1 and $num2, * starting from bit $start_pos for number $num1 * * @param string $num1 * @param string $num2 * @param int $start_pos * @return string * @access public */ public function bitOr($num1, $num2, $start_pos) { $start_byte = intval($start_pos / 8); $start_bit = $start_pos % 8; $tmp1 = $this->int2bin($num1); $num2 = bcmul($num2, 1 << $start_bit); $tmp2 = $this->int2bin($num2); if ($start_byte < strlen($tmp1)) { $tmp2 |= substr($tmp1, $start_byte); $tmp1 = substr($tmp1, 0, $start_byte) . $tmp2; } else { $tmp1 = str_pad($tmp1, $start_byte, "\0") . $tmp2; } return $this->bin2int($tmp1); } /** * Returns part of number $num, starting at bit * position $start with length $length * * @param string $num * @param int start * @param int length * @return string * @access public */ public function subint($num, $start, $length) { $start_byte = intval($start / 8); $start_bit = $start % 8; $byte_length = intval($length / 8); $bit_length = $length % 8; if ($bit_length) { $byte_length++; } $num = bcdiv($num, 1 << $start_bit); $tmp = substr($this->int2bin($num), $start_byte, $byte_length); $tmp = str_pad($tmp, $byte_length, "\0"); $bit = $tmp[$byte_length - 1]; $chr = chr(0xff >> (8 - $bit_length)); $tmp = substr_replace($tmp, $bit & $chr, $byte_length - 1, 1); return $this->bin2int($tmp); } } /* EOF */