ykaganovich ykaganovich - 1 month ago 44
iOS Question

Unwrap AES key on iOS

We have a client-server architecture where the server returns AES keys wrapped with other AES keys using AES Key Wrap algorithm (rfc 3394) to the client. We need to implement a client on iOS that unwraps these keys.

I know absolutely nothing about iOS development (I'm responsible for the server and the web service API, so I specified AES Key Wrap assuming this wouldn't be an issue), and the client-side guys are telling me they're having a hard time implementing this.

So, how does one go about unwrapping an AES key on iOS? Is there a library that can do this? i've found this documentation which seems to be exactly what I need, but they claim it's unavailable.

Answer

I guess I am answering this question a bit loo late ... hopefully the answer will be useful for someone else.

Anyway, Apple provides these methods to wrap/unwrap a key:

  • CCSymmetricKeyWrap
  • CCSymmetricKeyUnwrap

They are located in CommonCrypto/CommonSymmetricKeywrap.h and are available in MacOS as well as iOS.

An example of use of these 2 methods would be as follows (notice that this code can not be used straight away in your own code because of the XCTAssert's):

#import <CommonCrypto/CommonCrypto.h>

// The size of the Key Encryptions Key has to be:
// kCCKeySizeAES128 (16), kCCKeySizeAES192 (24) or kCCKeySizeAES256 (32) bytes
u_int8_t kekBytes[kCCKeySizeAES128] = {
    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
    0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
};

// The size of the data to cipher has to be a multiple of 64 bits
u_int8_t plainBytes[2 * sizeof(u_int64_t)] = {
    0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
    0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0x11
};

// WRAP KEY

// The size of the ciphered data is equal to the size of the plain data
// + another 64 bits
size_t cipheredBytesSize = (sizeof(plainBytes) + sizeof(u_int64_t));
u_int8_t cipheredBytes[cipheredBytesSize];

CCCryptorStatus status = CCSymmetricKeyWrap(kCCWRAPAES,
                                            CCrfc3394_iv,
                                            CCrfc3394_ivLen,
                                            kekBytes,
                                            sizeof(kekBytes),
                                            plainBytes,
                                            sizeof(plainBytes),
                                            cipheredBytes,
                                            &cipheredBytesSize);
XCTAssertEqual(status, kCCSuccess);
XCTAssertEqual(cipheredBytesSize, sizeof(plainBytes) + sizeof(u_int64_t));

// UNWRAP KEY

size_t sizeDecipheredPlainData = sizeof(plainBytes);
u_int8_t decipheredPlainBytes[sizeDecipheredPlainData];

status = CCSymmetricKeyUnwrap(kCCWRAPAES,
                              CCrfc3394_iv,
                              CCrfc3394_ivLen,
                              kekBytes,
                              sizeof(kekBytes),
                              cipheredBytes,
                              cipheredBytesSize,
                              decipheredPlainBytes,
                              &sizeDecipheredPlainData);
XCTAssertEqual(status, kCCSuccess);
XCTAssertEqual(sizeDecipheredPlainData, sizeof(plainBytes));

XCTAssertEqual(memcmp(plainBytes, decipheredPlainBytes, sizeof(plainBytes)), 0);

Now, sometime ago I coded a library that conforms to RFC 3394 and also to RFC 5649. The latter, called AES Key Wrap with Padding, describes how to wrap/unwrap a key which size is not a multiple of 64 bits, in fact, it can be of any size. This library is located here in GitHub and is also available in CocoaPods. But, to be honest, unless you have to wrap keys of any size, there is not much point on using it.

Nevertheless, below there is another example about how to use it:

#import <CommonCrypto/CommonCrypto.h>

// The size of the Key Encryptions Key has to be:
// kCCKeySizeAES128 (16), kCCKeySizeAES192 (24) or kCCKeySizeAES256 (32) bytes
u_char kekBytes[kCCKeySizeAES128] = {
    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
    0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
};
NSData *keyEncryptionKey = [NSData dataWithBytes:kekBytes length:sizeof(kekBytes)];

// AES Key Wrap

// The size of the data to cipher has to be a multiple of 64 bits
u_char plainBytes[2 * sizeof(uint64_t)] = {
    0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
    0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0x11
};
NSData *expectedPlainData = [NSData dataWithBytes:plainBytes length:sizeof(plainBytes)];

NSData *cipheredData = [AKWAesKeyWrap cipheredDataByWrappingPlainData:expectedPlainData
                                                 withKeyEncryptionKey:keyEncryptionKey
                                                                error:nil];
NSData *plainData = [AKWAesKeyWrap plainDataByUnwrappingCipheredData:cipheredData
                                                withKeyEncryptionKey:keyEncryptionKey
                                                               error:nil];

XCTAssertEqualObjects(expectedPlainData, plainData);

// AES Key Wrap with Padding

// The plain data can be as small as 1 byte
u_char plainBytesWithPadding[1] = {0x10};
expectedPlainData = [NSData dataWithBytes:plainBytesWithPadding length:sizeof(plainBytesWithPadding)];

cipheredData = [AKWAesKeyWrap cipheredDataByWrappingWithPaddingPlainData:expectedPlainData
                                                   usingKeyEncryptionKey:keyEncryptionKey
                                                                   error:nil];
plainData = [AKWAesKeyWrap plainDataByUnwrappingWithPaddingCipheredData:cipheredData
                                                  usingKeyEncryptionKey:keyEncryptionKey
                                                                  error:nil];

XCTAssertEqualObjects(expectedPlainData, plainData);

Regards.

Comments