12

I am working in AES256 to be able to encrypt/decrypt between iOS and PHP using insecure channels.

I have seen many similar questions that move around the key size, the mode (CBC or ECB), the use of a random iv, etc. But in this case, I found a weird behaviour as follows.

Configuration in both environments: - Key: 32 bytes(256 bits) - Block size: 128 bits (standard) - iv: 16 bytes (static for testing purposes) - Mode: CBC

If I encrypt a 16 or 32 bytes text (to match the AES block size), the result in Swift, and PHP are similar but not quite the same:

key = "12345678901234567890123456789012" plainText = "12345678901234567890123456789012" iv = "1234567890123456"

Swift cipher = e5RnnlJkv4QGnGhkMwfvgMHr80NWUVhbvvfCdPQ5V2KyKJTx4KfWmn4HXi4dG0b8 PHP cipher = e5RnnlJkv4QGnGhkMwfvgMHr80NWUVhbvvfCdPQ5V2I=

As you can see, there is a difference in the cipher length and in the last 2 characters of the PHP Base64 String.

But if I use a text that is not a AES128 Block Size multiplier, let´s say "Hello World", bot environments report different (but same size) ciphers as follows

Swift cipher = bdwO/5C8a+pliIoIXtuzfA==

PHP cipher = oPotHCkxpOwQhIaCz6hNMw==

In both cases (Swift and PHP), the cipher is decrypted correctly regardless of the size of the plaintext. Also, the Swift results are consistent with the Objective-C version of the code

Attached the simplified code used:

PHP

    $key = "12345678901234567890123456789012"; 
    $iv = "1234567890123456";
    $plaintext = "Hello World";
    $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $iv);
    $ciphertext_base64 = base64_encode($ciphertext);
    echo  "ciphertext: ".$ciphertext_base64."</br>";

Swift

let keyData: NSData! = (key as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
let keyBytes         = UnsafePointer<UInt8>(keyData.bytes)
let keyLength        = size_t(kCCKeySizeAES256)

let plainData = (plainText as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
let dataLength    = UInt(plainData.length)
let dataBytes     = UnsafePointer<UInt8>(plainData.bytes)

var bufferData    = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
var bufferPointer = UnsafeMutablePointer<UInt8>(bufferData.mutableBytes)
let bufferLength  = size_t(bufferData.length)

let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options = UInt32(kCCOptionPKCS7Padding)

let ivData: NSData! = (iv as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
let ivPointer = UnsafePointer<UInt8>(ivData.bytes)

var numBytesEncrypted: UInt = 0

var cryptStatus = CCCrypt(operation, algoritm, options, keyBytes, keyLength, ivPointer, dataBytes, dataLength, bufferPointer, bufferLength, &numBytesEncrypted)

bufferData.length = Int(numBytesEncrypted)
let base64cryptString = bufferData.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
println(base64cryptString)

Why are these different?

eharo2
  • 2,553
  • 1
  • 29
  • 39

1 Answers1

9

This is due to padding mode differences.

PHP uses "zero padding" if the plain text is not N-times the block size. So PHP pads 0..15 bytes with value 00 for 128 bit block ciphers such as AES. For plaintext that ends on a block boundary it will not add any padding bytes.

Most other languages use PKCS#7 padding, which pads up to the next block boundary, where the padding byte reflects the number of bytes added. So that would be 1..16 bytes with a value of 1..16 (or 01 to 10 in hexadecimals). For plaintext that ends on a block boundary it will add 16 bytes of padding.

PKCS#7 padding is deterministic and does not depend on the plaintext value (which could consist of bytes with any value, not just text); in other words, it can always be applied and removed independent of the content.

Zero padding has the issue that plain text ending with 00 bytes may have those 00 bytes removed during unpadding. This is usually not an issue for ASCII compatible strings as 00 is a control character, usually meaning End Of File (EOF).

Please check the comments on mcrypt_encrypt to see how you can apply PKCS#7 padding to PHP.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • 1
    Note that the underlying `mcrypt` libraries have not been updated since 2007 or so. PHP mcrypt is badly maintained and has horrible samples and documentation (I managed to rewrite the `mcrypt_encrypt` sample code, but now the comments on the code are out of sync). – Maarten Bodewes Sep 22 '14 at 18:31
  • 1
    Thx @owlstead. I will look into the PKCS#7 padding. But I think that it should not be an issue if the data block size is a multiplier of the AES 128 bits block size (e.g. 16 or 32 bytes). Am I correct?. If that is the case, do you think that the different cipher size in PHP could be a flaw in the bad maintained, old library?. Any alternative that you suggest?... – eharo2 Sep 22 '14 at 18:54
  • 1
    No, it is. If the size is 16 bytes then PHP won't add any padding. In case of PKCS#7 padding, it will add 16 bytes. Note that I could not decrypt any data with the keys and IV you've indicated, so I could not check the Swift implementation. – Maarten Bodewes Sep 22 '14 at 19:44
  • 2
    The @owlstead. 2 lines to do the PKCS#7 padding in PHP fixed it. $padding = 16 - (strlen($plaintext) % 16); $data = $plaintext.str_repeat(chr($padding), $padding); – eharo2 Sep 22 '14 at 20:01
  • 1
    Hey eharo2 I didn't understand how to fix the problem yet... Where we have to put the last two lines you wrote? – fancoolo Oct 12 '14 at 10:09
  • 1
    @fancoolo You apply it to the plaintext (the message) before you provide it to `mcrypt_encrypt` and you perform the reverse operation directly after `mcrypt_decrypt` (removing X bytes from the result, where X is the value of the last byte). – Maarten Bodewes Oct 12 '14 at 16:26
  • 2
    @fancoolo. You need to add the 2 lines just before calling the crypt_encrypt() function, as follows: $padding = 16 - (strlen($text) % 16); $data = $text.str_repeat(chr($padding), $padding); $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv); I hope it helps..... e – eharo2 Nov 21 '14 at 20:22
  • 1
    @eharo2 Same for decrypt? – brimstone Jul 17 '16 at 20:01