mcrypt_generic関数とmdecrypt_generic関数
パディング方法
パディング方法を変更するためには'padding'オプションを使ってください。平文の最後のブロックがブロックサイズよりも短いとき、それはパディングされなければなりません。
パディング方法には以下のものがあります。
standard: (デフォルト) バイナリに対しても安全
切り落されるべきバイト数で埋められます。そのためブロックサイズが
8であれば、"0A0B0C"は"05"で埋められ、結果は"0A0B0C0505050505"に
なります。もし最後のブロックがブロック全体で、ブロックサイズが8で
あれば、"0808080808080808"というブロックが追加されます。
null: テキストのみ
ブロックを一杯にするために必要なだけ"00"で埋めます。最後の
ブロックがブロック全体で、ブロックサイズが8であれば、
"0000000000000000"というブロックが追加されます。
standard と oneandzeroes の2つのパディングはバイナリに対しても安全です。
space と null のパディングはテキスト・データにたいしてのみ推奨されます。
データ長をn * blocksizeとするためにデータは"\0"で埋められます。
533 PHP_FUNCTION(mcrypt_generic)
548 /* Check blocksize */
549 if (mcrypt_enc_is_block_mode (td) == 1) { /* It's a block algorithm */
550 block_size = mcrypt_enc_get_block_size (td);
551 data_size = (((Z_STRLEN_PP(data) - 1) / block_size) + 1) * block_size;
552 data_s = emalloc (data_size + 1);
553 memset (data_s, 0, data_size);
554 memcpy (data_s, Z_STRVAL_PP(data), Z_STRLEN_PP(data));
555 }574 PHP_FUNCTION(mdecrypt_generic)
590 /* Check blocksize */
591 if (mcrypt_enc_is_block_mode (td) == 1) { /* It's a block algorithm */
592 block_size = mcrypt_enc_get_block_size (td);
593 data_size = (((Z_STRLEN_PP(data) - 1) / block_size) + 1) * block_size;
594 data_s = emalloc (data_size + 1);
595 memset (data_s, 0, data_size);
596 memcpy (data_s, Z_STRVAL_PP(data), Z_STRLEN_PP(data));
597 }
606 RETVAL_STRINGL (data_s, data_size, 1);
つまり、この関数はバイナリ・データを処理するには適していません。
という訳で(久々の)タコさんパッチを敢行だっ。計算処理がショボいん。。。
もちっとエラーチェックを強化した方がよいだろうか。
--- php-4.4.0,orig/ext/mcrypt/mcrypt.c 2005-08-30 10:15:16.000000000 +0900 +++ php-4.4.0/ext/mcrypt/mcrypt.c 2005-08-30 10:15:15.000000000 +0900 @@ -528,7 +528,7 @@ /* }}} */ -/* {{{ proto string mcrypt_generic(resource td, string data) +/* {{{ proto string mcrypt_generic(resource td, string data[, mixed pkcs#5]) This function encrypts the plaintext */ PHP_FUNCTION(mcrypt_generic) { @@ -537,9 +537,10 @@ int argc; unsigned char* data_s; int block_size, data_size; + int pkcs5; argc = ZEND_NUM_ARGS(); - MCRYPT_CHECK_PARAM_COUNT_EX (2,2) + MCRYPT_CHECK_PARAM_COUNT_EX (2,3) zend_get_parameters_ex(2, &mcryptind, &data); ZEND_FETCH_RESOURCE (td, MCRYPT, mcryptind, -1, "MCrypt", le_mcrypt); @@ -549,8 +550,21 @@ if (mcrypt_enc_is_block_mode (td) == 1) { /* It's a block algorithm */ block_size = mcrypt_enc_get_block_size (td); data_size = (((Z_STRLEN_PP(data) - 1) / block_size) + 1) * block_size; + pkcs5 = block_size; + if(argc > 2 ){ + if( Z_STRLEN_PP(data) < pkcs5 ){ + pkcs5 = pkcs5 - Z_STRLEN_PP(data); + } else if( Z_STRLEN_PP(data) % pkcs5 != 0 ){ + pkcs5 = Z_STRLEN_PP(data) % pkcs5; + pkcs5 = block_size - pkcs5; + } else { + data_size += pkcs5; + } + } else{ + pkcs5 = 0; + } data_s = emalloc (data_size + 1); - memset (data_s, 0, data_size); + memset (data_s, pkcs5, data_size); memcpy (data_s, Z_STRVAL_PP(data), Z_STRLEN_PP(data)); } else { /* It's not a block algorithm */ @@ -569,7 +583,7 @@ /* }}} */ -/* {{{ proto string mdecrypt_generic(resource td, string data) +/* {{{ proto string mdecrypt_generic(resource td, string data[, mixed pkcs#5]) This function decrypts the plaintext */ PHP_FUNCTION(mdecrypt_generic) { @@ -580,7 +594,7 @@ int block_size, data_size; argc = ZEND_NUM_ARGS(); - MCRYPT_CHECK_PARAM_COUNT_EX (2,2) + MCRYPT_CHECK_PARAM_COUNT_EX (2,3) zend_get_parameters_ex(2, &mcryptind, &data); ZEND_FETCH_RESOURCE (td, MCRYPT, mcryptind, -1, "MCrypt", le_mcrypt); @@ -603,6 +617,12 @@ mdecrypt_generic (td, data_s, data_size); + if( mcrypt_enc_is_block_mode (td) == 1 && argc > 2 ){ + if( data_size >= data_s[data_size-1] ){ + data_size -= data_s[data_size-1]; + } + } + RETVAL_STRINGL (data_s, data_size, 1); efree (data_s); }
このタコさんパッチを適用すると以下の動作になります。
<?php $in = pack( "H*", "0000" ); var_dump( bin2hex( func_decrypt( func_encrypt( $in ) ) ) ); var_dump( bin2hex( func_decrypt( func_encrypt( $in, true ), true ) ) ); $in = "1234567"; var_dump( bin2hex( func_decrypt( func_encrypt( $in ) ) ) ); var_dump( bin2hex( func_decrypt( func_encrypt( $in, true ), true ) ) ); $in = "12345678"; var_dump( bin2hex( func_decrypt( func_encrypt( $in ) ) ) ); var_dump( bin2hex( func_decrypt( func_encrypt( $in, true ), true ) ) ); $in = "12345678901234"; var_dump( bin2hex( func_decrypt( func_encrypt( $in ) ) ) ); var_dump( bin2hex( func_decrypt( func_encrypt( $in, true ), true ) ) ); function func_encrypt( $in, $flg = false ) { $td = func_open( ); if( $flg == true ) { $enc = mcrypt_generic( $td, $in, $flg ); } else { $enc = mcrypt_generic( $td, $in ); } func_close( $td ); return $enc; } function func_decrypt( $in, $flg = false ) { $td = func_open( ); if( $flg == true ) { $dec = mdecrypt_generic( $td, $in, $flg ); } else { $dec = mdecrypt_generic( $td, $in ); } func_close( $td ); return $dec; } function func_open( $key ) { $td = mcrypt_module_open( MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "" ); mcrypt_generic_init( $td, "87654321", "12345678" ); return $td; } function func_close( $td ) { mcrypt_generic_end( $td ); mcrypt_module_close( $td ); } ?> 結果 --- string(16) "0000000000000000" string(4) "0000" string(16) "3132333435363700" string(14) "31323334353637" string(16) "3132333435363738" string(16) "3132333435363738" string(32) "31323334353637383930313233340000" string(28) "3132333435363738393031323334"
よく見るとphp_mcrypt_do_crypt関数も似たような事になってるなぁ。
ちゅー事は、全般的にバイナリ・データには適していなのか。。。
ま、Perlでも同じっぽいんだけどね(1・2)。
PADDINGデータの扱いは(本来)ユーザーがやるべき作業だと思っているので微妙な判断。
やってくれれば、それに越した事はない。。。のですが。
暗号処理に(NULL以外の)ダミーのデータをくっつける方法でも回避できるんだけどね。
復号時にダミーのデータををネグるという荒業。