## Filtres de chiffrement

Les filtres de chiffrement sont particulièrement utiles pour le chiffrement des fichiers/flux.

## mcrypt.* et mdecrypt.*

Avertissement

Cette fonctionnalité est OBSOLÈTE à partir de PHP 7.1.0. Dépendre de cette fonctionnalité est fortement déconseillé.

`mcrypt.*` et `mdecrypt.*` fournissent un système de chiffrement symétrique utilisant libmcrypt. Les deux jeux de filtres supportent les mêmes algorithmes disponibles à l'extension mcrypt sous la forme `mcrypt.ciphername` où `ciphername` est le nom du chiffrement qui sera transmis à mcrypt_module_open(). Les cinq paramètres suivants sont aussi disponibles :

Paramètres des filtres mcrypt
Paramètre | Obligatoire ? | Par défaut | Valeurs possibles
--- | --- | --- | ---
mode | Optionnel | cbc | cbc, cfb, ecb, nofb, ofb, stream
algorithms_dir | Optionnel | ini_get('mcrypt.algorithms_dir') | Chemin vers les algorithmes de modules
modes_dir | Optionnel | ini_get('mcrypt.modes_dir') | Chemin vers les modes de modules
iv | Obligatoire | N/A | Généralement 8, 16 ou 32 octets de données binaires. Dépend du chiffrement
key | Obligatoire | N/A | Généralement 8, 16 ou 32 octets de données binaires. Dépend du chiffrement

Exemple #1 Chiffrement / Déchiffrement avec Blowfish

    <?php
    //$key supposé être précédemment généré
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);
    $fp = fopen('encrypted-file.enc', 'wb');
    fwrite($fp, $iv);
    $opts = array('mode'=>'cbc','iv'=>$iv, 'key'=>$key);
    stream_filter_append($fp, 'mcrypt.blowfish', STREAM_FILTER_WRITE, $opts);
    fwrite($fp, 'message to encrypt');
    fclose($fp);

    //déchiffrement...
    $fp = fopen('encrypted-file.enc', 'rb');
    $iv = fread($fp, $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC));
    $opts = array('mode'=>'cbc','iv'=>$iv, 'key'=>$key);
    stream_filter_append($fp, 'mdecrypt.blowfish', STREAM_FILTER_READ, $opts);
    $data = rtrim(stream_get_contents($fp));//supprime le remplissage nul
    fclose($fp);
    echo $data;
    ?>

Exemple #2 Chiffrer un fichier en utilisant AES-128 CBC avec SHA256 HMAC

    <?php
    AES_CBC::encryptFile($password, "plaintext.txt", "encrypted.enc");
    AES_CBC::decryptFile($password, "encrypted.enc", "decrypted.txt");

    class AES_CBC
    {
       protected static $KEY_SIZES = array('AES-128'=>16,'AES-192'=>24,'AES-256'=>32);
       protected static function key_size() { return self::$KEY_SIZES['AES-128']; } //AES-128 par défaut
       public static function encryptFile($password, $input_stream, $aes_filename){
          $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
          $fin = fopen($input_stream, "rb");
          $fc = fopen($aes_filename, "wb+");
          if (!empty($fin) && !empty($fc)) {
             fwrite($fc, str_repeat("_", 32) );//espace réservé, le SHA256 HMAC sera placé ici plus tard
             fwrite($fc, $hmac_salt = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM));
             fwrite($fc, $esalt = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM));
             fwrite($fc, $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM));
             $ekey = hash_pbkdf2("sha256", $password, $esalt, $it=1000, self::key_size(), $raw=true);
             $opts = array('mode'=>'cbc', 'iv'=>$iv, 'key'=>$ekey);
             stream_filter_append($fc, 'mcrypt.rijndael-128', STREAM_FILTER_WRITE, $opts);
             $infilesize = 0;
             while (!feof($fin)) {
                $block = fread($fin, 8192);
                $infilesize+=strlen($block);
                fwrite($fc, $block);
             }
             $block_size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
             $padding = $block_size - ($infilesize % $block_size);//$padding est un nombre de 1 à 16
             fwrite($fc, str_repeat(chr($padding), $padding) );//applique le remplissage PKCS7
             fclose($fin);
             fclose($fc);
             $hmac_raw = self::calculate_hmac_after_32bytes($password, $hmac_salt, $aes_filename);
             $fc = fopen($aes_filename, "rb+");
             fwrite($fc, $hmac_raw);//écrase l'espace réservé
             fclose($fc);
          }
       }
       public static function decryptFile($password, $aes_filename, $out_stream) {
          $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
          $hmac_raw = file_get_contents($aes_filename, false, NULL,  0, 32);
          $hmac_salt = file_get_contents($aes_filename, false, NULL, 32, $iv_size);
          $hmac_calc = self::calculate_hmac_after_32bytes($password, $hmac_salt, $aes_filename);
          $fc = fopen($aes_filename, "rb");
          $fout = fopen($out_stream, 'wb');
          if (!empty($fout) && !empty($fc) && self::hash_equals($hmac_raw,$hmac_calc)) {
             fread($fc, 32+$iv_size);//saute le sha256 hmac et le sel
             $esalt = fread($fc, $iv_size);
             $iv    = fread($fc, $iv_size);
             $ekey = hash_pbkdf2("sha256", $password, $esalt, $it=1000, self::key_size(), $raw=true);
             $opts = array('mode'=>'cbc', 'iv'=>$iv, 'key'=>$ekey);
             stream_filter_append($fc, 'mdecrypt.rijndael-128', STREAM_FILTER_READ, $opts);
             while (!feof($fc)) {
                $block = fread($fc, 8192);
                if (feof($fc)) {
                   $padding = ord($block[strlen($block) - 1]);//suppose un remplissage PKCS7
                   $block = substr($block, 0, 0-$padding);
                }
                fwrite($fout, $block);
             }
             fclose($fout);
             fclose($fc);
          }
       }
       private static function hash_equals($str1, $str2) {
          if(strlen($str1) == strlen($str2)) {
             $res = $str1 ^ $str2;
             for($ret=0,$i = strlen($res) - 1; $i >= 0; $i--) $ret |= ord($res[$i]);
             return !$ret;
          }
          return false;
       }
       private static function calculate_hmac_after_32bytes($password, $hsalt, $filename) {
          static $init=0;
          $init or $init = stream_filter_register("user-filter.skipfirst32bytes", "FileSkip32Bytes");
          $stream = 'php://filter/read=user-filter.skipfirst32bytes/resource=' . $filename;
          $hkey = hash_pbkdf2("sha256", $password, $hsalt, $iterations=1000, 24, $raw=true);
          return hash_hmac_file('sha256', $stream, $hkey, $raw=true);
       }
    }
    class FileSkip32Bytes extends php_user_filter
    {
       private $skipped=0;
       function filter($in, $out, &$consumed, $closing)  {
          while ($bucket = stream_bucket_make_writeable($in)) {
             $outlen = $bucket->datalen;
             if ($this->skipped<32){
                $outlen = min($bucket->datalen,32-$this->skipped);
                $bucket->data = substr($bucket->data, $outlen);
                $bucket->datalen = $bucket->datalen-$outlen;
                $this->skipped+=$outlen;
             }
             $consumed += $outlen;
             stream_bucket_append($out, $bucket);
          }
          return PSFS_PASS_ON;
       }
    }
    class AES_128_CBC extends AES_CBC {
       protected static function key_size() { return self::$KEY_SIZES['AES-128']; }
    }
    class AES_192_CBC extends AES_CBC {
       protected static function key_size() { return self::$KEY_SIZES['AES-192']; }
    }
    class AES_256_CBC extends AES_CBC {
       protected static function key_size() { return self::$KEY_SIZES['AES-256']; }
    }

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open