3

I want to implement AES128 CTR with iv and key. I'm looking for any advice how to do that in best way and not reinvent wheel.

I found good lib for this RNCryptor, but looks like this aes is not supported there.

Also I test this approach, but looks like this is not CTR.

EDIT


I used zpproach from @zaph

NSData *result = [NSData cryptData:dataStr
                         operation:kCCEncrypt
                              mode:kCCModeCTR
                         algorithm:kCCAlgorithmAES128
                           padding:ccNoPadding
                         keyLength:kCCKeySizeAES128
                                iv:ivHex
                               key:keyHex
                             error:&error];

but receive CCCryptorCreate status: -4305

Just found in sources

@constant kCCUnimplemented Function not implemented for the current algorithm.

Link

zaph
  • 111,848
  • 21
  • 189
  • 228
hbk
  • 10,908
  • 11
  • 91
  • 124
  • Is there some particular reason CTR mode is required? It is not easy to get there CTR correct and care must be taken that the same counter value is never used with the same key. – zaph Feb 08 '18 at 15:33
  • `kCCAlgorithmAES128` should be `kCCAlgorithmAES` – zaph Feb 08 '18 at 16:00
  • @zaph just test - result same =/ – hbk Feb 08 '18 at 16:05

2 Answers2

6

You need to use CommonCrypto but not the one-shot CCCrypt version, but instead the full CCCryptorCreateWithMode, CCCryptorUpdate, CCCryptorFinal and CCCryptorRelease with mode kCCModeCTR version.

Here is sample code I have, it may not fit your needs and you will have to decide on the counter (IV) form.

#import <CommonCrypto/CommonCrypto.h>

+ (NSData *)cryptData:(NSData *)dataIn
            operation:(CCOperation)operation  // kCC Encrypt, Decrypt
                 mode:(CCMode)mode            // kCCMode ECB, CBC, CFB, CTR, OFB, RC4, CFB8
            algorithm:(CCAlgorithm)algorithm  // CCAlgorithm AES DES, 3DES, CAST, RC4, RC2, Blowfish
              padding:(CCPadding)padding      // cc NoPadding, PKCS7Padding
            keyLength:(size_t)keyLength       // kCCKeySizeAES 128, 192, 256
                   iv:(NSData *)iv            // CBC, CFB, CFB8, OFB, CTR
                  key:(NSData *)key
                error:(NSError **)error
{
    if (key.length != keyLength) {
        NSLog(@"CCCryptorArgument key.length: %lu != keyLength: %zu", (unsigned long)key.length, keyLength);
        if (error) {
            *error = [NSError errorWithDomain:@"kArgumentError key length" code:key.length userInfo:nil];
        }
        return nil;
    }

    size_t dataOutMoved = 0;
    size_t dataOutMovedTotal = 0;
    CCCryptorStatus ccStatus = 0;
    CCCryptorRef cryptor = NULL;

    ccStatus = CCCryptorCreateWithMode(operation, mode, algorithm,
                                       padding,
                                       iv.bytes, key.bytes,
                                       keyLength,
                                       NULL, 0, 0, // tweak XTS mode, numRounds
                                       kCCModeOptionCTR_BE, // CCModeOptions
                                       &cryptor);

    if (cryptor == 0 || ccStatus != kCCSuccess) {
        NSLog(@"CCCryptorCreate status: %d", ccStatus);
        if (error) {
            *error = [NSError errorWithDomain:@"kCreateError" code:ccStatus userInfo:nil];
        }
        CCCryptorRelease(cryptor);
        return nil;
    }

    size_t dataOutLength = CCCryptorGetOutputLength(cryptor, dataIn.length, true);
    NSMutableData *dataOut = [NSMutableData dataWithLength:dataOutLength];
    char *dataOutPointer = (char *)dataOut.mutableBytes;

    ccStatus = CCCryptorUpdate(cryptor,
                               dataIn.bytes, dataIn.length,
                               dataOutPointer, dataOutLength,
                               &dataOutMoved);
    dataOutMovedTotal += dataOutMoved;

    if (ccStatus != kCCSuccess) {
        NSLog(@"CCCryptorUpdate status: %d", ccStatus);
        if (error) {
            *error = [NSError errorWithDomain:@"kUpdateError" code:ccStatus userInfo:nil];
        }
        CCCryptorRelease(cryptor);
        return nil;
    }

    ccStatus = CCCryptorFinal(cryptor,
                              dataOutPointer + dataOutMoved, dataOutLength - dataOutMoved,
                              &dataOutMoved);
    if (ccStatus != kCCSuccess) {
        NSLog(@"CCCryptorFinal status: %d", ccStatus);
        if (error) {
            *error = [NSError errorWithDomain:@"kFinalError" code:ccStatus userInfo:nil];
        }
        CCCryptorRelease(cryptor);
        return nil;
    }

    CCCryptorRelease(cryptor);

    dataOutMovedTotal += dataOutMoved;
    dataOut.length = dataOutMovedTotal;

    return dataOut;
}

Sample invocation:

NSData *dataIn  = [@"DataInDataInData" dataUsingEncoding: NSUTF8StringEncoding];
NSData *key     = [@"KeyKeyKeyKeyKeyK" dataUsingEncoding: NSUTF8StringEncoding];
NSData *counter = [@"CounterCounterCo" dataUsingEncoding: NSUTF8StringEncoding];
NSError *error;
NSData *encrpted = [Crypto
                    cryptData:dataIn
                    operation:kCCEncrypt
                    mode:kCCModeCTR
                    algorithm:kCCAlgorithmAES
                    padding:ccNoPadding
                    keyLength:kCCKeySizeAES128
                    iv:counter
                    key:key
                    error:&error];
NSLog(@"encrypted: %@", encrpted);

Output: encrypted: 064e8073 76973eba 3192474f 9831db34

zaph
  • 111,848
  • 21
  • 189
  • 228
  • Have you ever tried to implement encryption using CommonCrypto in separate framework (e.g. in cocoa pod)? I had an issue with importing non modular .h in pod’s umbrella file. Any thoughts on workaround? – Yury Bogdanov Feb 08 '18 at 15:24
  • 1
    Sorry, I can't help with CocoaPods. You might check hoe RNCryptor accomplished this, see [cocoapods & RNCryptor](https://cocoapods.org/pods/RNCryptor) – zaph Feb 08 '18 at 15:32
  • @zaph I try to use your approach but got error `CCCryptorCreate status: -4305` (please see edit), can u advice? – hbk Feb 08 '18 at 15:38
  • CommonCrypto error codes from : kCCSuccess = 0, kCCParamError = -4300, kCCBufferTooSmall = -4301, kCCMemoryFailure = -4302, kCCAlignmentError = -4303, kCCDecodeError = -4304, kCCUnimplemented = -4305, kCCOverflow = -4306, kCCRNGFailure = -4307, kCCUnspecifiedError = -4308, kCCCallSequenceError= -4309, kCCKeySizeError = -4310, – zaph Feb 08 '18 at 15:57
  • 1
    Missing and needed for CTR mode is `CCModeOptions` `kCCModeOptionCTR_LE` or `kCCModeOptionCTR_BE`, added to the sample code. I had forgotten this for CTR mode. Yes they are deprecated but needed. – zaph Feb 08 '18 at 16:47
  • I will check this again and let you know – hbk Feb 08 '18 at 20:05
  • @zaph - i tested proposed variant - and all works as expected - choose this variant because it's much better that SwiftCrypto. BTW, do you know any good tutorial related to CommonCrypto usage? – hbk Feb 08 '18 at 22:54
  • Not really. For CBC & ECB modes there is a combined call. Most usage is CBC and PKCS#7 padding. – zaph Feb 08 '18 at 23:17
0

Perhaps you are looking for CryptoSwift library. It supports AES and CTR.

Another popular crypoto-library is libsodium, but I can't be sure it supports CTR.

Yury Bogdanov
  • 417
  • 7
  • 13
  • It is best to avoid using CryptoSwift, amoung other things it is 500 to 1000 times slower than Common Crypto based implementations. Apple's Common Crypto is FIPS certified and as such has been well vetted, using CryptoSwift is taking a chance on correctness and security such as timing and power attacks. – zaph Feb 08 '18 at 15:13