CTR モードでは、カウンター (AES の場合は 128 ビット) が暗号化されてキー ストリームが生成され、それがプレーンテキストまたは暗号文と XOR されます。通常、IV は 64 ビットまたは 96 ビットであると想定され、残りのビットは実際には 0 に設定されます。最初の 64 ビットまたは 96 ビットはnonceと呼ばれます。
nonce のサイズは、多数のタイム パッドを作成せずに一度に暗号化できるデータの量を決定します。nonce が大きいほど、安全なメッセージの長さは短くなりますが、2 つの nonce がランダムに生成された場合に衝突する可能性も低くなります。ナンスの大きさの仕様がないため、多くのフレームワークはナンスのサイズを特定のサイズに制限していません。
mcrypt ではノンスにフル ブロック サイズを使用できます。
あなたはできる
- 最初から使っていたIVを取って、
- その IV を大きな整数として解析します (PHP の整数型には適合しません)。
- スキップするブロック数 (AES の場合は 16 バイト ブロック) を表す数値をそれに追加し、
- 数値をバイナリ表現に変換し、
- 後のバイトから復号化を開始します。
手順 2 ~ 4 はadd
、次のコードの関数によって実行されます。
大きなファイルがあり、バイト 512 (簡単にするためにブロック サイズの倍数) から復号化したい場合を考えてみましょう。IVに512/16=32を追加します。
コード例を次に示します。
<?php
$d = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
$k = "k0k1k2k3k4k5k6k7"; // 16 byte AES key
$bs = 16; // 16 byte block size
$iv = mcrypt_create_iv($bs);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'ctr', '');
mcrypt_generic_init($td, $k, $iv);
$ct = mcrypt_generic($td, $d);
$dec_offset = 32;
$ct_slice = substr($ct, $dec_offset);
$iv_slice = add($iv, $dec_offset / $bs);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'ctr', '');
mcrypt_generic_init($td, $k, $iv_slice);
$d_slice = mdecrypt_generic($td, $ct_slice);
var_dump($d_slice);
function add($big_num_str, $to_add){
$big_num = str_split(strrev($big_num_str));
for($i = 0; $i < count($big_num) ; $i++){
$tmp = ord($big_num[$i]) + $to_add;
$big_num[$i] = $tmp % 256;
$to_add = floor( $tmp / 256 );
}
while($to_add){
$big_num[$i++] = $to_add % 256;
$to_add = floor( $to_add / 256 );
}
for($i = 0; $i < count($big_num) ; $i++){
$big_num[$i] = chr($big_num[$i]);
}
return strrev(implode('', $big_num) );
}
出力:
文字列(32) "101112131415161718191a1b1c1d1e1f"
これは、PHP の OpenSSL 拡張機能でもまったく同じように機能します。これがコードです。
もちろん、ブロック境界で始まらないチャンクを取得したい場合、これはもう少し複雑になります。ブロックを早めに開始し、余分なバイトを削除する必要があります。