AES Encryption in Google App Engine (Python) and Decryption on iOS (Objective-C)
Question: I'm trying to encrypt some data from python (Google App Engine) and then
decrypt it on iOS.

There are several issues surrounding this based on the fact that there are so
many options with AES Encryption and the different formats available in Python
and Objective-C.

Because of the limited availability of the PyCrypto libraries on Google App
Engine and the AES code on the iOS/Objective-C side requiring PKCS7Padding, I
decided to use slowAES on the python side.

I'm also using a 16-bit key, CBC Mode, and PKCS7Padding.

Given that, this is my encrypt function and helper variables/function:

    
    
    def str2nums(s):
        return map(ord, s)
    
    key = "hjt4mndfy234n5fs"
    moo = aes.AESModeOfOperation()
    iv = [12, 34, 96, 15] * 4
    CBC_mode = moo.modeOfOperation['CBC']
    nkey = str2nums(key)
    
    
    def encrypt(plaintext):
      funcName = inspect.stack()[0][3]
      logging.debug("Enter " + funcName)
      logging.debug("Input string: " + plaintext)
      m, s, encData = moo.encrypt(plaintext, CBC_mode, nkey, len(nkey), iv)
      fmt = len(encData)*'B'
      dataAsStr = ""
      for j in encData:
            dataAsStr = dataAsStr + str(j) + ","
      logging.debug("Output encrypted data:[" + dataAsStr + "]")
      encoded = base64.b64encode(struct.pack(fmt, *encData))
      logging.debug("Output encrypted string: " + encoded)
      decoded = struct.unpack(fmt, base64.b64decode(encoded))
      decrypted = moo.decrypt(decoded, s, CBC_mode, nkey, len(nkey), iv)
      logging.debug("Output decrypted back: " + decrypted)
      return encoded
    

Note that on the Python side, I am encrypting, packing, and then base64
encoding the data. Before returning this encrypted data, I'm also doing a test
run at decrypting it just to show that in the logs and it does indeed work.

On the iOS side, I'm using the following AES NSData addition:

    
    
    - (NSData *)AES128DecryptWithKey:(NSString *)key {
        // 'key' should be 16 bytes for AES128, will be null-padded otherwise
        char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
        bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
    
        // fetch key data
        [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSASCIIStringEncoding];
    
        NSUInteger dataLength = [self length];
    
        //See the doc: For block ciphers, the output size will always be less than or 
        //equal to the input size plus the size of one block.
        //That's why we need to add the size of one block here
        size_t bufferSize = dataLength + kCCBlockSizeAES128;
        void *buffer = malloc(bufferSize);
    
        size_t numBytesDecrypted = 0;
        CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                              keyPtr, kCCKeySizeAES128,
                                              NULL /* initialization vector (optional) */,
                                              [self bytes], dataLength, /* input */
                                              buffer, bufferSize, /* output */
                                              &numBytesDecrypted);
    
        if (cryptStatus == kCCSuccess) {
            //the returned NSData takes ownership of the buffer and will free it on deallocation
            return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
        }
    
        free(buffer); //free the buffer;
        return nil;
    }
    

And I'm making use of this when I pull down the base64/packed/encrypted data
like so:

    
    
    NSMutableString * result = [[NSMutableString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
    if (LOG) { NSLog(@"Base64 encoded / Encrypted string: %@", result); }
    
    NSData* encryptedData = [NSData decodeBase64ForString:result]; // base64 decode
    
    if (LOG) { NSLog(@"Encrypted string: %@", encryptedData); }
    
    NSData* decryptedData = [encryptedData AES128DecryptWithKey:@"hjt4mndfy234n5fs"]; // AES Decrypt
    

The problem is, I can't seem to get the data to decrypt correctly on the
client (iOS) side even though it decrypts just fine on the server (in python).

During the decryption, the cryptStatus always ends up : **kCCAlignmentError.**
Which I don't quite understand.

I've also messed with AES 256 but I need a 32bit key I think and that doesn't
seem to be an option for slowAES in CBC mode (at least according to the
examples?).

Logging of the Server (Notice the actual unencrypted data is merely an empty
set [] . That's a JSON representation of such to return to the client.

    
    
    2012-01-04 08:48:13.962
    Enter encrypt
    D 2012-01-04 08:48:13.962
    Input string: []
    D 2012-01-04 08:48:13.967
    Output encrypted data:[4,254,226,26,101,240,22,113,44,54,209,203,233,64,208,255,]
    D 2012-01-04 08:48:13.967
    Output encrypted string: BP7iGmXwFnEsNtHL6UDQ/w==
    D 2012-01-04 08:48:13.971
    Output decrypted back: []
    

Logging of the client (iOS):

    
    
    2012-01-04 12:45:13.891 Base64 encoded / Encrypted string: BP7iGmXwFnEsNtHL6UDQ/w==
    2012-01-04 12:45:13.892 Encrypted string: <04fee21a 65f01671 2c36d1cb e940d0ff>
    2012-01-04 12:45:29.126 Decrypted string: 
    

So my questions are:

  1. What does it mean by an "Alignment Error"? Or what am I doing wrong that it doesn't want to decrypt on the client?
  2. Do I need to worry about unpacking the data at all considering it looks like it matches up just fine? And if so, how would I go about an unpack() function in C or Objective-C?
  3. Is there just plain a better way to do AES encryption between Google App Engine and iOS?

EDIT: I should also note that besides the answer of using the same
Initialization Vector, I found a discrepancy between the python and iOS code
for the padding. The encrypt/decrypt worked fine for small amounts of data but
then I ran into failure to decrypt with larger ones. I fixed this by changing
the iOS side to NOT use PKCS7Padding and put 0 there instead.


Answer: The IV needs to match on both ends.

The IV (initialization vector) is a string of bytes that's sent through the
encryptor/decryptor to place its "memory" in a pseudo-random state before the
"real" data is sent through. Since the encryption results depend on what's
gone through before, this initialization makes it impossible (without knowing
the IV) for a malicious 3rd party o know whether a given cleartext and key
could have produced a given cypertext.

Ideally the IV is itself somehow variable, based, perhaps, on a serial number
or some other text that's sent along with the cyphertext, or based on a
counter that's synchronized between ends.

The presence of the IV (even if semi-predictable) significantly increases the
difficulty of using a "known cleartext" attack. This is especially important
for relatively short, frequent messages.

