views:

482

answers:

2

Hello,

I'm new to cryptography and I'm building some test applications to try and understand the basics of it. I'm not trying to build the algorithms from scratch but I'm trying to make two different AES-256 implementation talk to each other.

I've got a database that was populated with this Javascript implementation stored in Base64. Now, I'm trying to get an Objective-C method to decrypt its content but I'm a little lost as to where the differences in the implementations are. I'm able to encrypt/decrypt in Javascript and I'm able to encrypt/decrypt in Cocoa but cannot make a string encrypted in Javascript decrypted in Cocoa or vice-versa.

I'm guessing it's related to the initialization vector, nonce, counter mode of operation or all of these, which quite frankly, doesn't speak to me at the moment.

Here's what I'm using in Objective-C, adapted mainly from this and this:

@implementation NSString (Crypto)

- (NSString *)encryptAES256:(NSString *)key {
    NSData *input = [self dataUsingEncoding: NSUTF8StringEncoding]; 
    NSData *output = [NSString cryptoAES256:input key:key doEncrypt:TRUE];  
    return [Base64 encode:output];
}

- (NSString *)decryptAES256:(NSString *)key {
    NSData *input = [Base64 decode:self];
    NSData *output = [NSString cryptoAES256:input key:key doEncrypt:FALSE];
    return [[[NSString alloc] initWithData:output encoding:NSUTF8StringEncoding] autorelease];
}

+ (NSData *)cryptoAES256:(NSData *)input key:(NSString *)key doEncrypt:(BOOL)doEncrypt {
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256 + 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 = [input 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 numBytesCrypted = 0;
    CCCryptorStatus cryptStatus =
        CCCrypt(doEncrypt ? kCCEncrypt : kCCDecrypt,
            kCCAlgorithmAES128,
            kCCOptionECBMode | kCCOptionPKCS7Padding,
            keyPtr, kCCKeySizeAES256,
            nil, // initialization vector (optional)
            [input bytes], dataLength, // input
            buffer, bufferSize, // output
            &numBytesCrypted
        );  
    if (cryptStatus == kCCSuccess) {
        // the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
    }   
    free(buffer); // free the buffer;
    return nil;
}

@end

Of course, the input is Base64 decoded beforehand.

I see that each encryption with the same key and same content in Javascript gives a different encrypted string, which is not the case with the Objective-C implementation that always give the same encrypted string. I've read the answers of this post and it makes me believe I'm right about something along the lines of vector initialization but I'd need your help to pinpoint what's going on exactly.

Thank you!

A: 

For testing, you should consider using the test values from the NIST website for AES and FIPS 197.

GregS
And how do I interpret the results? What will I learn with those values if I already know the resulting encrypted strings from both implementation are different?
lpfavreau
+2  A: 

Yup, there are a number of differences between the two implementations.

  • The Javascript implementation uses CTR mode while the Objective-C implementation uses ECB mode (ECB is weak and should not be used.)

  • The Javascript implemenation uses a key expansion. I.e. it transforms the key before passing it to the AES code. Not sure about the Objective-C implementation. Anyway, you can almost be sure that the two implementations are not using the same key for the encryption.

  • The Javascript implementation uses the current time to generate a 8 byte IV that is prepended to the ciphertext. This IV is used to initialize the counter for the CTR mode. Because of the IV changes, encrypting the same plaintext twice will result in different ciphertexts. Also using the current time for generating an IV for CTR mode is ok, as long as you don't encrypt two ciphertexts within the same clock tick(). The Objective-C implementation doesn't use an IV (since it uses ECB mode).

  • The Objective-C code uses PKCS #7 padding, the Javascript code uses none. This is a consequence of using distinct encryption modes.

  • Furthermore, you also have to check, how the ciphertext is encoded. The Javascript code uses Base64 encoding. The Objective-C code is too much distributed over several postings, that I didn't find the relevant code.

Accipitridae
I also noticed that your Objective-C code says `kCCAlgorithmAES128`, but your text says you're trying to implement AES-256. Are you accidentally using a smaller block size?
Noah Lavine
Thank you for this nice answer. About the block size, all implementations of AES256 I've seen in Objective-C (using the default CCCrypt) uses kCCAlgorithmAES128 as the block size, there is no kCCAlgorithmAES256 available that I know of. Not knowing a lot in cryptography, I can't say if this has an impact or is it is standard practice. About the encoding, both are using Base64 for the resulting cyphered text.
lpfavreau
The block size for AES is always 128-bit. However, the key size can vary. I.e. the key size can be either 128, 192 or 256 bit. Hence the specification above for block and key size looks ok.
Accipitridae