Ios – AES 128 with CBC

aescbc-modeencryptioniosobjective c

I'm going mad with a simple AES 128 in Objective C and there's no way to get the expected ciphered text in a simple test. Could anyone tell me what am I doing wrong?

Test:

- (void)testAES128_1
{
    NSString *testVector    = @"6bc1bee22e409f96e93d7e117393172a";
    NSString *initVector    = @"000102030405060708090A0B0C0D0E0F";
    NSString *key           = @"2b7e151628aed2a6abf7158809cf4f3c";
    NSString *expected      = @"7649abac8119b246cee98e9b12e9197d";

    NSData *inputData = [self dataFromHexString:testVector];
    NSData *keyData = [self dataFromHexString:key];
    NSData *expectedData = [self dataFromHexString:expected];

    NSData *current = [inputData AES128EncryptWithIV:initVector andKey:key];
    // What I get in current = cf2ea38a123be20765eb8c5c56caf224 != expected

    BOOL res = [inputData isEqualToData:current];

    XCTAssertTrue(res);
}

// For Converting incoming HexString into NSData
- (NSData *)dataFromHexString:(NSString *)string
{
    NSMutableData *stringData = [[NSMutableData alloc] init];
    unsigned char whole_byte;
    char byte_chars[3] = {'\0','\0','\0'};
    int i;
    for (i=0; i < [string length] / 2; i++) {
        byte_chars[0] = [string characterAtIndex:i*2];
        byte_chars[1] = [string characterAtIndex:i*2+1];
        whole_byte = strtol(byte_chars, NULL, 16);
        [stringData appendBytes:&whole_byte length:1];
    }
    return stringData;
}

Category NSData (AES):

- (NSData *)AES128EncryptWithIV:(NSString *)iv andKey:(NSString *)key {
    char ivPtr[kCCKeySizeAES128 + 1];
    bzero(ivPtr, sizeof(ivPtr));

    // fetch iv data
    [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];

    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:NSUTF8StringEncoding];

    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 numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, 0,
                                          keyPtr, kCCKeySizeAES128,
                                          ivPtr /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }

    free(buffer); //free the buffer;
    return nil;
}

I am working with these set of AES test vectors:
http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors#aes-cbc-128

Thanks

Best Answer

Simply you are not converting the iv in hex ascii to an NSData. The example test vector have an iv so to obtain the matching cipher text you need to use the Initialization vector.

Note: The line: BOOL res = [inputData isEqualToData:current]; should be: BOOL res = [expectedData isEqualToData:current];

Note: The output block count is not larger if there is no padding.

Here is my test code:
No need for a Category, I just make there methods class methods.

+ (void)testAES128_1 {
    NSString *testVector = @"6bc1bee22e409f96e93d7e117393172a";
    NSString *initVector = @"000102030405060708090A0B0C0D0E0F";
    NSString *key        = @"2b7e151628aed2a6abf7158809cf4f3c";
    NSString *expected   = @"7649abac8119b246cee98e9b12e9197d";

    NSData *inputData    = [self dataFromHexString:testVector];
    NSData *keyData      = [self dataFromHexString:key];
    NSData *ivData       = [self dataFromHexString:initVector];
    NSData *expectedData = [self dataFromHexString:expected];

    NSError *error;
    NSData *current = [Test doCipher:inputData
                                  iv:ivData
                                 key:keyData
                             context:kCCEncrypt
                               error:&error];

    BOOL res = [expectedData isEqualToData:current];
    NSLog(@"Match: %@", res ? @"Yes" : @"No"); // Match: Yes
}

+ (NSData *)doCipher:(NSData *)dataIn
                  iv:(NSData *)iv
                 key:(NSData *)symmetricKey
             context:(CCOperation)encryptOrDecrypt // kCCEncrypt or kCCDecrypt
               error:(NSError **)error
{
    CCCryptorStatus ccStatus   = kCCSuccess;
    size_t          cryptBytes = 0;
    NSMutableData  *dataOut    = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];

    ccStatus = CCCrypt( encryptOrDecrypt,
                       kCCAlgorithmAES128,
                       0, //kCCOptionPKCS7Padding,
                       symmetricKey.bytes,
                       kCCKeySizeAES128,
                       iv.bytes,
                       dataIn.bytes,
                       dataIn.length,
                       dataOut.mutableBytes,
                       dataOut.length,
                       &cryptBytes);

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

    return dataOut;
}
Related Topic