Saturday, December 12, 2015

CommonCrypto2

I've been exploring how to use CommonCrypto on OS X (docs). As described in a previous post, I found an article on the web which describes how to make the CommonCrypto library available to Swift playgrounds as well as Swift Cocoa applications.

I decided to look into it a bit more. I had help from Mike Ash, as well as another knowledgable guy. Looking at the header helped a lot as well. It is extremely detailed. Since the project was somewhat challenging, I thought I would sketch out what I learned and post the code.

CommonCrypto is a C library. So, for example, the function that does one-step encryption or decryption, CCCrypt, is declared like this in CommonCrypto.h



The first two arguments can be defined in Swift like this:

let operation = CCOperation(kCCEncrypt)
let algorithm = CCAlgorithm(kCCAlgorithmAES)

We're first going to encrypt, and the encryption scheme will be AES.

If we look in the header, it tells us that the block size for AES is 16 bytes or 128 bits.

kCCAlgorithmAES128 Advanced Encryption Standard, 128-bit block

Reading further
One option for block ciphers is padding, as defined in PKCS7; when padding is enabled, the total amount of data encrypted does not have to be an even multiple of the block size, and the actual length of plaintext is calculated during decryption.

Another option for block ciphers is Cipher Block Chaining, known as CBC mode. When using CBC mode, an Initialization Vector (IV) is provided along with the key when starting an encrypt or decrypt operation. If CBC mode is selected and no IV is provided, an IV of all zeroes will be used.

To begin with we'll use ECB with PKCS7 padding because it seems simpler.


let options = CCOptions(kCCOptionPKCS7Padding | kCCOptionECBMode)


CCCrypt takes three arguments of type const void * and one of type void *. The three const arguments are the key, the initialization vector iv, and the plaintext or data (dataIn). These can be provided as Swift arrays: [UInt8], or even (for the key and plaintext) as Swift Strings. No need for NSData or UnsafePointer or whatever.

The return type for this function is CCCryptorStatus, which is 0 for success, and something else for an error. The codes are also shown in the header:



So, when I received -4301 as the result, which at first I thought was garbage, CCCrypt was actually telling me "insufficient buffer provided for the specified operation."

Of special note: I found that although the message does not have to be 128 bits or a multiple, the key does. If it is not, the encrypt operation returns 0 for success and some encrypted data, but when decrypted we don't get our plaintext back!

A buffer is needed, into which the encrypted data will be written.

let bufferSize = 128
var cipherData = [UInt8](count: bufferSize, repeatedValue: 0)
var resultLen = 0
var status: Int32 = 0

Another thing that confused me was that these sizes (except for resultLen and status) are in bits, not bytes.

When the buffer is passed into CCCrypt we need a cast:

UnsafeMutablePointer<Void>(cipherData)

We provide the address of the Int variable &resultLen, and after the function returns, that value tells how much data was written.

Finally, the other argument is the initialization vector iv.



In our first pass at this, we specify ECB mode and PKCS7Padding, so no IV is needed, and we just pass nil for this argument.

It works. The first 9 bytes of the output at the end are the same as what we put in. I put the playground on github here.

The only thing I haven't figured out with this one is how to know the size of the message when decrypting.

The second approach uses CBC. It's the same as the first, except we change the options to the default

let options = CCOptions()

define an initialization vector, and then fill it with random bytes.

var iv = [UInt8](count: blockSize, repeatedValue: 0)
SecRandomCopyBytes(kSecRandomDefault, blockSize, &iv)


Other than that, the only thing is to be sure and pad the message to 16 bytes. It works. The playground is here.

UPDATE: I implemented the other code sketched out in Mike's article: encrypting in steps for a longer message (playground), and key stretching (playground).

No comments: