Gooey
Gooey

Reputation: 4778

Encrypt data using Objective-C and Decrypt in Python

I have the same issue as this question but unfortunately there was no answer on it.

I have the following objective-c code to encrypt using CCCrypt:

(NSData *)doCrypt:(NSData *)data usingKey:(NSData *)key withInitialVector:(NSData *)iv mode:(int)mode error: (NSError *)error
{
    int buffersize = 0;
    if(data.length % 16 == 0) { buffersize = data.length + 16; }
    else { buffersize = (data.length / 16 + 1) * 16 + 16; }

    // int buffersize = (data.length <= 16) ? 16 : data.length;
    size_t numBytesEncrypted = 0;
    void *buffer = malloc(buffersize * sizeof(uint8_t));
    CCCryptorStatus result = CCCrypt(mode, 0x0, 0x1, [key bytes], [key length], [iv bytes], [data bytes], [data length], buffer, buffersize, &numBytesEncrypted);

    return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted freeWhenDone:YES];
}

I use kCCAlgorithmAES128 with kCCOptionPKCS7Padding as options and call the function with [Cryptor doCrypt:data usingKey:key withInitialVector:nil mode:0x0 error:nil];

Now I would like to decrypt it using python and to do so I have the following code:

def decrypt(self, data, key):
        iv = '\x00' * 16

        encoder = PKCS7Encoder()
        padded_text = encoder.encode(data)

        mode = AES.MODE_CBC
        cipher = AES.new(key, mode, iv)
        decoded = cipher.decrypt(padded_text)
        return decoded

The PKCS7Encoder looks like this:

class PKCS7Encoder():
    """
    Technique for padding a string as defined in RFC 2315, section 10.3,
    note #2
    """
    class InvalidBlockSizeError(Exception):
        """Raised for invalid block sizes"""
        pass

    def __init__(self, block_size=16):
        if block_size < 2 or block_size > 255:
            raise PKCS7Encoder.InvalidBlockSizeError('The block size must be ' \
                    'between 2 and 255, inclusive')
        self.block_size = block_size

    def encode(self, text):
        text_length = len(text)
        amount_to_pad = self.block_size - (text_length % self.block_size)
        if amount_to_pad == 0:
            amount_to_pad = self.block_size
        pad = chr(amount_to_pad)
        return text + pad * amount_to_pad

    def decode(self, text):
        pad = ord(text[-1])
        return text[:-pad]

Yet whenever I call the decrypt() function, it returns garbage. Am I missing something or having a wrong option enabled somewhere?


Example in and output:

NSData *keyData = [[NSData alloc] initWithRandomData:16];
    NSLog(@"key: %@", [keyData hex]);
    NSString *str = @"abcdefghijklmno";
    NSLog(@"str: %@", str);
    NSData *encrypted = [Cryptor encrypt:[str dataUsingEncoding:NSUTF8StringEncoding] usingKey:keyData];
    NSLog(@"encrypted str: %@", [encrypted hex]);

Gives:

key: 08b6cb24aaec7d0229312195e43ed829
str: a
encrypted str: 52d61265d22a05efee2c8c0c6cd49e9a

And python:

cryptor = Cryptor()
encrypted_hex_string = "52d61265d22a05efee2c8c0c6cd49e9a"
hex_key = "08b6cb24aaec7d0229312195e43ed829"
print cryptor.decrypt(encrypted_hex_string.decode("hex"), hex_key.decode("hex"))

Result:

láz

Which is weird, but if dump the hex I get 610f0f0f0f0f0f0f0f0f0f0f0f0f0f0fb02b09fd58cccf04f042e2c90d6ce17a and 61 = a so I think it just shows wrong.

A bigger input:

key: 08b6cb24aaec7d0229312195e43ed829
str: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
encrypted str: 783fce3eca7ebe60d58b01da3d90105a93bf2d659cfcffc1c2b7f7be7cc0af4016b310551965526ac211f4d6168e3cc5

Result:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaôNÍ“ƒ˜�Üšw6C%

Here you see that the a's are printed with garbage... so I assume this is a padding error or something like that

The IV is nill at the iOs side and 16x 0's at the Python side (see the code)

Upvotes: 4

Views: 642

Answers (1)

Artjom B.
Artjom B.

Reputation: 61952

Your decryption: aes_decrypt(pkcs7_pad(ciphertext))
Correct decryption: pkcs7_unpad(aes_decrypt(ciphertext))

It has to be done this way, because AES in CBC mode expects plaintexts of a multiple of the block size, but you generally want to encrypt arbitrary plaintexts. Therefore, you need to apply the padding before encryption and remove the padding after decryption.


Keep in mind that a - (b % a) cannot be 0 for any (positive) value of a or b. This means that

if amount_to_pad == 0:
    amount_to_pad = self.block_size

is unreachable code and can be removed. Good thing is that a - (b % a) already does what you wanted to do with the if block.

You also should extend the unpad (decode) function to actually check whether every padding byte is the same byte. You should also check that the every padding byte is not zero or larger than the block size.

Upvotes: 2

Related Questions