Simple Encryption/Decryption Code Library for Swift Projects

The Problem

Although there are some excellent code libraries already out there for encryption/decryption in Swift projects, like CryptoSwift and the marvelous cross-platform-AES-encryption library, I decided that I needed something much lighter weight. I decided to learn a little about encryption and in particular about how to make the encrypted result less predictable, in terms of repeating byte patterns.

A Little Theory First

To begin with, I learned that AES is a symmetrical encryption standard. Symmetrical means that the same key that’s used to encrypt the data is also used to decrypt it.

Next, I learned what is meant by the bit density of the AES encryption, usually referred to as AES 128 bit or AES 256 bit encryption. The key you use to encrypt a secret usually consists of characters from the ASCII set of characters – 26 letters and 10 digits. That’s 36 possible character values for each element of the key string. Now each character consists of 8 bits, so a 256 bit key would be 256 bits / 8 bits per character = 32 characters. So our key for AES 256 bit encryption should be at least 32 characters long. A 32 character key string gives you essentially 2^256 possible bit patterns making AES 256 bit encryption very tough to hack.

Next up, there’s the issue of repeated bit patterns in the encrypted result. This happens because we are using the CommonCrypto library on iOS and macOS and it uses Cipher Block Chaining (or CBC) for the Block Cipher Mode. What this means is that the data you are encrypting gets broken up into small blocks and, starting with the first block of data, the AES algorithm is applied and the result is used in the calculation for encrypting the next block, etc. So each block’s encrypted result is dependent on the previous block’s encryption, which is what is meant by Cipher Block Chaining or CBC.

If you have two separate pieces of data with the first few blocks of bytes the same, and you encrypt them using the same key, the two encrypted pieces of data will have the same byte patterns to begin with. For the AES algorithm, regardless of key size specified, the block size is always 128 bits long which means for ASCII characters each block is 16 characters long.

Because of the similarities in the encrypted bytes, an attacker will know you used the same key to encrypt both pieces of data and could use this fact to work out differences with each block and in turn figure out the contents and maybe, given time, even the key used to do the encryption!

The solution to this problem is to somehow “psuedo-randomize” the byte patterns between encryptions, so patterns are not easily detectable. An initialization vector is a psuedo-randomly generated piece of data that is the same length as a block, which in our case would be 128 bits long or 16 characters long. The initialization vector adjusts the initial block of data to encrypt by an offset. Because each block is chained, every subsequent block is different every time even if you encrypt the same data more than once. If you use a different initialization vector for every piece of data you encrypt, the encrypted bytes will never have the same pattern.

Solution

My code library CRDCrypt is a very easy-to-use Swift library for encrypting/decrypting data using AES 256 bit encryption. CRDCrypt extends the Swift class Data with methods to encrypt and decrypt and to generate a unique initialization vector.

Just import CRDCrypt and generate an initialization vector:

import CRDCrypt

let iv = Data.generateInitializationVector()

Then we can use this initialization vector in the encryption call to encrypt some string data and make the encryption less predictable between encryptions, for example:

let myStringToEncrypt = "This is the string I want to encrypt with the library."
let myPrivateKey = "This is my master key"
var encryptedData: Data? = nil
var myStringDecrypted: String? = nil

do {

  encryptedData = try myStringToEncrypt.data(using: .utf8)?.aes256Encrypt(withKey: myPrivateKey, initializationVector: iv)

} catch let error as NSError {

  print("\(error)")
}

To decrypt, we use the same initialization vector that was used to encrypt and our key again:

if let encryptedData = encryptedData {

  do {

    myStringDecrypted = String(bytes: try encryptedData.aes256Decrypt(withKey: myPrivateKey, initializationVector: iv), encoding: .utf8)

  } catch let error as NSError {

    print("\(error)")
  }
}

print("Unencrypted: \(myStringDecrypted)")

Observations

The initialization vector generated by Data.generateInitializationVector() is just the first 16 bytes of a newly generated UUID.

Only the first kCCKeySizeAES256 characters of the key specified in the methods aes256Encrypt and aes256Decrypt is used because, as previously discussed in the theory, the key should be equal to the block size, which is 128 bits or 32 characters long.

The CommonCrypto method CCCrypt is used to do the encryption/decryption and the initialization vector and key are passed directly to this Apple API call.

One thing I noticed that’s particularly nice, which I didn’t realize before I worked on this code, was that in the Objective C code (see NSData+MyExtensions.m) in my library which actually calls the CCCrypt API, passing a reference to an NSError object in the parameter list of the Objective C function allows me to throw that error automatically in the Swift wrapper code in Data+MyExtensions.swift (see my article on this).

cdisdero

Software Engineer

2 thoughts to “Simple Encryption/Decryption Code Library for Swift Projects”

  1. Thank you for a “easy to understand” library for encryption/decryption! The only one that works “right out of the box”(well, at least for me)!
    I am working on a project were I need to store and retrieve the encrypted data in a database(Firebase) as text. How do I convert “encryptedData” to text an back?

    1. If I understand you correctly, you want to convert the encryptedData to a string for storage in a database? I’m not sure whether Firebase has a binary data type for storage and retrieval, but if it does, you should use that instead of converting to a string. But if not, I’d recommend doing something like:

      let encryptedDataAsString = String(data: encryptedData, encoding: .utf8)

      then to get it back as data:

      let encryptedDataAsData = encryptedDataAsString.data(using: .utf8)

      Hope that helps.

Leave a Reply

Your email address will not be published. Required fields are marked *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax