ます’s Blog - どうでもいい記事100選

どうでもいい記事100選

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でも同じっぽいんだけどね()。
PADDINGデータの扱いは(本来)ユーザーがやるべき作業だと思っているので微妙な判断。
やってくれれば、それに越した事はない。。。のですが。


暗号処理に(NULL以外の)ダミーのデータをくっつける方法でも回避できるんだけどね。
復号時にダミーのデータををネグるという荒業。