Understanding the Zaph’s AES CBC encryption code

Adam Dev
5 min readJan 26, 2023

--

Recently I was implementing the AES/CBC encryption using the CommonCrypto library for Objective C code base and i found a code as answered by Zaph in which he recommends to prefix the IV with the encrypted text which we receive at the end of CommonCrypto’s kCCEncrypt operation method zaph's answer. The bunch of code for AES encryption as suggested by him in one of his answer looks like this :

+ (NSData *)aesCBCEncrypt:(NSData *)data
key:(NSData *)key
error:(NSError **)error
{
if (key.length != 16 && key.length != 24 && key.length != 32) {
*error = [NSError errorWithDomain:@"keyLengthError" code:-1 userInfo:nil];
return nil;
}

CCCryptorStatus ccStatus = kCCSuccess;
int ivLength = kCCBlockSizeAES128;
size_t cryptBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:ivLength + data.length + kCCBlockSizeAES128];

int status = SecRandomCopyBytes(kSecRandomDefault, ivLength, dataOut.mutableBytes);
if (status != 0) {
*error = [NSError errorWithDomain:@"ivError" code:status userInfo:nil];
return nil;
}
ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
key.bytes, key.length,
dataOut.bytes,
data.bytes, data.length,
dataOut.mutableBytes + ivLength, dataOut.length,
&cryptBytes);

if (ccStatus == kCCSuccess) {
dataOut.length = cryptBytes + ivLength;
}
else {
if (error) {
*error = [NSError errorWithDomain:@"kEncryptionError" code:ccStatus userInfo:nil];
}
dataOut = nil;
}

return dataOut;
}

So what is the logic behind this code? As per the CommonCrypto library description in CommonCryptor.h file the description looks like this:

CommonCryptor.h

If we compare the Zaph’s code and the screenshot i got various doubts. My first doubt is at which line and how the IV is getting prefixed with the Encrypted text in the above code? Because you can see dataOut.bytes being passed to IV parameter in CCCrypt.

Worry not i am explaining all this below:

There are four parts to it.

   1) The NSMutableData(NSMutableData  *dataOut) is initialized to size that's 
big enough to hold the ciphertext and the IV (IV length + data length + block
size). You can see the above code

2) The random IV is generated in place, at the beginning of the
data, using SecRandomCopyBytes.

3) Using a tiny bit of pointer arithmetics, the ciphertext is also
written into the same Data, but the starting pointer is moved after
the IV (dataOut.mutableBytes + ivLength). Technically it means that the
dataOut.mutableBytes pointer is moved forward by ivLength
bytes (so it points at immediately after the IV in the large buffer)

4) And finally, any excess noise is cut off by setting the length of
the Data to the ivLength + the length of the ciphertext that CCCrypt
reported

Below is the more detailed explanation:

  1. The NSMutableData(NSMutableData *dataOut) is initialized to size that’s big enough to hold the ciphertext and the IV (IV length + data length + block
NSMutableData  *dataOut     = [NSMutableData dataWithLength:ivLength + data.length + kCCBlockSizeAES128];
int status = SecRandomCopyBytes(kSecRandomDefault, ivLength, dataOut.mutableBytes);

First NSMutableData *dataOut is considered as a buffer by CCCrypt . In the next line of code int status = SecRandomCopyBytes(kSecRandomDefault, ivLength, dataOut.mutableBytes);the random IV of length 16 is written to dataOut.mutableBytes buffer.

2) Second Explanation

ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
key.bytes, key.length,
dataOut.bytes, // IV passed , explanation given below
data.bytes, // Explanation below
data.length, // Explanation below
dataOut.mutableBytes + ivLength,
dataOut.length,
&cryptBytes);

When dataOut.bytes is passed as IV in CCCrypt then it means the CCCRypt gets the IV from the dataOut by its pointer as it has already the random IV in it which is generated by SecRandomCopyBytes . dataOut.bytes by default points to the beginning of the dataOut buffer .The IV in AES is always of a constant size, of 16 bytes, which CCCrypt also knows, and thus will take the first 16 bytes as the IV. data.bytesis the pointer by default it points to the start of the data to be encrypted , and data.lengthis the number of data bytes to be encrypted. when looking for the ciphertext, CCCrypt doesn't care about anything else, but the pointer to the start and the length.

3) Third explanation

ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
key.bytes, key.length,
dataOut.bytes,
data.bytes,
data.length,
dataOut.mutableBytes + ivLength, // Explanation below
dataOut.length, // Explanation below
&cryptBytes);

Also dataOut.mutableBytes + ivLength means the starting pointer is moved forward by ivLength bytes (so it points at immediately after the IV in the large buffer) of dataOut for storing cipher text as it has already the IV till that position.

4) Fourth explanation

ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
key.bytes, key.length,
dataOut.bytes,
data.bytes,
data.length,
dataOut.mutableBytes + ivLength,
dataOut.length,
&cryptBytes); // Explained below

cryptBytes which is returned just contains the length of the ciphered text. Thus the ciphered text as explained above is stored from the pointer position which was moved. Thus in short the NSMutableData was initialised with enough space by the code NSMutableData *dataOut = [NSMutableData dataWithLength:ivLength + data.length + kCCBlockSizeAES128];for writing IV and Ciphered text, in the next line of code int status = SecRandomCopyBytes(kSecRandomDefault, ivLength, dataOut.mutableBytes);the random IV of length 16 is written to dataOut.mutableBytes buffer. Now the NSMutableData has IV bytes in it. Next in CCCrypt dataOut.bytes can be passed as IV parameter as NSMutableData already has IV bytes. Moving next we have to give the buffer position for writing the crypted text , thus we are writing to NSMutableData by moving the pointer position defined by dataOut.mutableBytes + ivLength so this is actually the buffer position( + ivLength(16)) from where we have to write ciphered text after IV. At last cryptoBytes returns the length of the ciphered text(it does not has length of IV included as starting pointer was moved by +ivLength )

5) Fifth explanation

if (ccStatus == kCCSuccess) {
dataOut.length = cryptBytes + ivLength; // Explained below
}

So finally NSMutableData has IV(length of 16 bytes) and Ciphered text(length given by cryptBytes) in it and thus the extra buffer space in NSMutableData is trimmed of by giving the length of dataOut as dataOut.length = cryptBytes + ivLength;So final dataOut variable has IV plus ciphered text and it is returned in return dataOut;

return dataOut;

Note : it’s important to consider that CCCrypt is a C function, it doesn’t work on objects like NSData or NSStrings, but pointers to memory addresses. in other words, it doesn’t care where you feed it inputs from and where it will write its output, as long as the pointed addresses are valid and there’s enough space starting from that address allocated. (and of course, for inputs, the actual data is already there).

--

--

Adam Dev

MOBILITY ENGINEER/ARCHITECTURE,FRAMEWORK CREATOR/GITHUB OWL