So I have just begun writing a PHP Extension, using the original Rijndael code (which is now formally AES).
However it seems there is a bug somewhere in the code, I know it encrypts/decrypts correctly, but on the output it adds a extra 6 bytes to the string, which I assume is something to do with the conversion from uint8_t array to char array.
I havent posted the code here as it would take up half the page, so I have posted it to github here :
https://github.com/Hect0rius/PHPEXT-Rijndael
The main code I am referring to is the following is in php_rijndael.c (2 functions:
/* {{{ proto resource rijndael_encrypt(string inData, string key)
encrypts a string with rijndael / PHP_FUNCTION(rijndael_encrypt) { / Inputs */ char *inData; // Data Ptr. size_t inDataLen; // Data Length. char *key; // Key Ptr. size_t keyLen; // Key Length. zend_ulong keyBits; // Bits, between 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)
decrypts a string with rijndael / PHP_FUNCTION(rijndael_decrypt) { / Inputs */ char *inData; // Data Ptr. size_t inDataLen; // Data Length. char *key; // Key Ptr. size_t keyLen; // Key Length. zend_ulong keyBits; // Bits, between 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 */
I only guess it is correctly decrypting the encrypted buffer as it outputs back to all zero's, here is the test.php file:
You are seeing padding.
AES (and Rijndael) are block ciphers and as such process data in blocks, 16-bytes for AES.
If the input is not an exact multiple of the block size some padding needs to be added to the data to be encrypted and removed on decryption. The most common padding is PKCS#7 however some implementations are brain-dead and do not support PKCS#7, sometimes using null padding which can not support encrypting binary data.
Note: The PHP mcrypt
implementation does not support standard PKCS#7 (née PKCS#5) padding, only non-standard null padding that can't even be used with binary data.