使用原始 rijndael 构建 PHP 扩展,正确 encryption/decrypt 但尾随额外字节
Building a PHP Extension using original rijndael, correct encryption/decrypt however trailing extra bytes
所以我刚刚开始编写 PHP 扩展,使用原始的 Rijndael 代码(现在正式为 AES)。
然而似乎代码中某处存在错误,我知道它 encrypts/decrypts 正确,但在输出中它向字符串添加了额外的 6 个字节,我认为这与从 uint8_t 数组转换为字符数组。
我没有在这里发布代码,因为它会占用一半的页面,所以我将它发布到 github 此处 :
https://github.com/Hect0rius/PHPEXT-Rijndael
我指的主要代码如下在php_rijndael.c中(2个函数:
/* {{{ proto resource rijndael_encrypt(string inData, string key)
用 rijndael 加密字符串 /
PHP_FUNCTION(rijndael_encrypt)
{
/ 输入 */
字符*数据; // 数据指针。
size_t inDataLen; // 数据长度。
字符*键; // 关键点
size_t keyLen; // 密钥长度。
zend_ulong 键位; // 位,在 128/192/256 之间。
/* Get Parameters from Zend */
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s|l", &inData, &inDataLen, &key, &keyLen, &keyBits) == FAILURE) {
return;
}
/* Since rijndael takes what it needs via key bits, then we just allow the overflow of the key. */
switch(keyBits) {
case 128:
if(keyLen < 16) { php_error_docref(NULL, E_WARNING, "Key length must be 16 characters long."); RETURN_FALSE; }
break;
case 192:
if(keyLen < 24) { php_error_docref(NULL, E_WARNING, "Key length must be 24 characters long."); RETURN_FALSE; }
break;
case 256:
if(keyLen < 32) { php_error_docref(NULL, E_WARNING, "Key length must be 32 characters long."); RETURN_FALSE; }
break;
}
/* Convert from original pointers to uin8_t arrays */
uint8_t dataU8[16];
uint8_t keyU8[16];
uint8_t output[16], i = 0;
do {
dataU8[i] = (uint8_t)inData[i];
keyU8[i] = (uint8_t)key[i];
i++;
}
while(i < 16);
/* Setup Rijndael stack */
uint32_t rk[4 * (MAXNR + 1)];
int32_t Nr = rijndaelKeySetupEnc(rk, keyU8, keyBits);
/* Decrypt Buffer. */
rijndaelEncrypt(rk, Nr, dataU8, output);
/* Now return data back into a char array*/
char outChar[16], *ptr = outChar;
i = 0;
do {
ptr[i] = (char)output[i];
i++;
}
while(i < 16);
RETURN_STRING(outChar);
}
/* }}} */
/* {{{ proto resource rijndael_decrypt(string inData, string key)
用 rijndael 解密字符串 /
PHP_FUNCTION(rijndael_decrypt)
{
/ 输入 */
字符*数据; // 数据指针。
size_t inDataLen; // 数据长度。
字符*键; // 关键点
size_t keyLen; // 密钥长度。
zend_ulong 键位; // 位,在 128/192/256 之间。
/* Get Parameters from Zend */
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s|l", &inData, &inDataLen, &key, &keyLen, &keyBits) == FAILURE) {
return;
}
/* Since rijndael takes what it needs via key bits, then we just allow the overflow of the key. */
switch(keyBits) {
case 128:
if(keyLen < 16) { php_error_docref(NULL, E_WARNING, "Key length must be 16 characters long."); RETURN_FALSE; }
break;
case 192:
if(keyLen < 24) { php_error_docref(NULL, E_WARNING, "Key length must be 24 characters long."); RETURN_FALSE; }
break;
case 256:
if(keyLen < 32) { php_error_docref(NULL, E_WARNING, "Key length must be 32 characters long."); RETURN_FALSE; }
break;
}
/* Convert from original pointers to uin8_t arrays */
uint8_t dataU8[16];
uint8_t keyU8[16];
uint8_t output[16], i = 0;
do {
dataU8[i] = (uint8_t)inData[i];
keyU8[i] = (uint8_t)key[i];
i++;
}
while(i < 16);
/* Setup Rijndael Stack */
uint32_t rk[4 * (MAXNR + 1)];
int32_t Nr = rijndaelKeySetupDec(rk, keyU8, keyBits);
/* Decrypt input uint8_t array */
rijndaelDecrypt(rk, Nr, dataU8, output);
/* Convert data back to a char */
char outChar[16], *ptr = outChar;
i = 0;
do {
ptr[i] = (char)output[i];
i++;
}
while(i < 16);
RETURN_STRING(ptr);
}
/* }}} */
#endif /* HAVE_RIJNDAEL */
我只是猜测它正确解密了加密的缓冲区,因为它输出回全零,这里是 test.php 文件:
您看到了填充。
AES(和 Rijndael)是块密码,因此以块的形式处理数据,AES 为 16 字节。
如果输入不是块大小的精确倍数,则需要将一些填充添加到要加密的数据并在解密时删除。最常见的填充是 PKCS#7 然而有些实现是脑残的并且不支持 PKCS#7,有时使用不支持加密二进制数据的空填充。
注意:PHP mcrypt
实现不支持标准 PKCS#7(née PKCS#5)填充,仅支持甚至不能用于二进制数据的非标准空填充.
所以我刚刚开始编写 PHP 扩展,使用原始的 Rijndael 代码(现在正式为 AES)。
然而似乎代码中某处存在错误,我知道它 encrypts/decrypts 正确,但在输出中它向字符串添加了额外的 6 个字节,我认为这与从 uint8_t 数组转换为字符数组。
我没有在这里发布代码,因为它会占用一半的页面,所以我将它发布到 github 此处 :
https://github.com/Hect0rius/PHPEXT-Rijndael
我指的主要代码如下在php_rijndael.c中(2个函数:
/* {{{ proto resource rijndael_encrypt(string inData, string key)
用 rijndael 加密字符串 / PHP_FUNCTION(rijndael_encrypt) { / 输入 */ 字符*数据; // 数据指针。 size_t inDataLen; // 数据长度。 字符*键; // 关键点 size_t keyLen; // 密钥长度。 zend_ulong 键位; // 位,在 128/192/256 之间。
/* Get Parameters from Zend */
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s|l", &inData, &inDataLen, &key, &keyLen, &keyBits) == FAILURE) {
return;
}
/* Since rijndael takes what it needs via key bits, then we just allow the overflow of the key. */
switch(keyBits) {
case 128:
if(keyLen < 16) { php_error_docref(NULL, E_WARNING, "Key length must be 16 characters long."); RETURN_FALSE; }
break;
case 192:
if(keyLen < 24) { php_error_docref(NULL, E_WARNING, "Key length must be 24 characters long."); RETURN_FALSE; }
break;
case 256:
if(keyLen < 32) { php_error_docref(NULL, E_WARNING, "Key length must be 32 characters long."); RETURN_FALSE; }
break;
}
/* Convert from original pointers to uin8_t arrays */
uint8_t dataU8[16];
uint8_t keyU8[16];
uint8_t output[16], i = 0;
do {
dataU8[i] = (uint8_t)inData[i];
keyU8[i] = (uint8_t)key[i];
i++;
}
while(i < 16);
/* Setup Rijndael stack */
uint32_t rk[4 * (MAXNR + 1)];
int32_t Nr = rijndaelKeySetupEnc(rk, keyU8, keyBits);
/* Decrypt Buffer. */
rijndaelEncrypt(rk, Nr, dataU8, output);
/* Now return data back into a char array*/
char outChar[16], *ptr = outChar;
i = 0;
do {
ptr[i] = (char)output[i];
i++;
}
while(i < 16);
RETURN_STRING(outChar);
}
/* }}} */
/* {{{ proto resource rijndael_decrypt(string inData, string key)
用 rijndael 解密字符串 / PHP_FUNCTION(rijndael_decrypt) { / 输入 */ 字符*数据; // 数据指针。 size_t inDataLen; // 数据长度。 字符*键; // 关键点 size_t keyLen; // 密钥长度。 zend_ulong 键位; // 位,在 128/192/256 之间。
/* Get Parameters from Zend */
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s|l", &inData, &inDataLen, &key, &keyLen, &keyBits) == FAILURE) {
return;
}
/* Since rijndael takes what it needs via key bits, then we just allow the overflow of the key. */
switch(keyBits) {
case 128:
if(keyLen < 16) { php_error_docref(NULL, E_WARNING, "Key length must be 16 characters long."); RETURN_FALSE; }
break;
case 192:
if(keyLen < 24) { php_error_docref(NULL, E_WARNING, "Key length must be 24 characters long."); RETURN_FALSE; }
break;
case 256:
if(keyLen < 32) { php_error_docref(NULL, E_WARNING, "Key length must be 32 characters long."); RETURN_FALSE; }
break;
}
/* Convert from original pointers to uin8_t arrays */
uint8_t dataU8[16];
uint8_t keyU8[16];
uint8_t output[16], i = 0;
do {
dataU8[i] = (uint8_t)inData[i];
keyU8[i] = (uint8_t)key[i];
i++;
}
while(i < 16);
/* Setup Rijndael Stack */
uint32_t rk[4 * (MAXNR + 1)];
int32_t Nr = rijndaelKeySetupDec(rk, keyU8, keyBits);
/* Decrypt input uint8_t array */
rijndaelDecrypt(rk, Nr, dataU8, output);
/* Convert data back to a char */
char outChar[16], *ptr = outChar;
i = 0;
do {
ptr[i] = (char)output[i];
i++;
}
while(i < 16);
RETURN_STRING(ptr);
}
/* }}} */
#endif /* HAVE_RIJNDAEL */
我只是猜测它正确解密了加密的缓冲区,因为它输出回全零,这里是 test.php 文件:
您看到了填充。
AES(和 Rijndael)是块密码,因此以块的形式处理数据,AES 为 16 字节。
如果输入不是块大小的精确倍数,则需要将一些填充添加到要加密的数据并在解密时删除。最常见的填充是 PKCS#7 然而有些实现是脑残的并且不支持 PKCS#7,有时使用不支持加密二进制数据的空填充。
注意:PHP mcrypt
实现不支持标准 PKCS#7(née PKCS#5)填充,仅支持甚至不能用于二进制数据的非标准空填充.