Summit marcin cover?fm=jpg&fl=progressive&q=75&w=300

CryptoSwift: Cryptography You Can Do

Cryptography is the ancient science of writing in secret code, and of course, for centuries writing meant pen and paper. Today, cryptography is critically important for modern applications, but what options do we have for cryptography in Swift? Marcin Krzyżanowski outlines existing libraries for cryptography, before presenting his work in Swift. He demonstrates some basic cryptographic algorithms, alongside example implementations in Swift. We end with a call to join in, learn, and implement!

You can contribute to Marcin’s project on GitHub.


Early Cryptography (0:26)

Cryptography itself is not bound to any programming language in particular, or even programming languages at all. It can be performed with a pen and paper, or with a mechanical machine just like Enigma. The Enigma has three wheels, and wires that make it somewhat unpredictable. The operator of the machine presses the keyboard buttons, but sometimes the same key will produce different results. This helped make the Enigma so hard to crack. Invented by a German engineer at the end of World War I, it was commercialized shortly after, and used militarily until about 1970. It was cracked several times, first by the Polish Cipher Bureau in 1932, and more famously during World War II, by the British at Bletchley Park. Today, we have computers to do the same calculations that the Enigma once did.

Existing Frameworks (1:35)

When it comes to software, all of the operations are handled by cryptoframeworks, or libraries. In particular, the Apple platform has CommonCrypto within the system library. There is also OpenSSL. Both of these are C libraries that can be used with Swift through C interoperability. SwiftSSL is a wrapper around OpenSSL that we can use. NaCl only does a fraction of what OpenSSL can do, but is still a very good library. CryptoSwift is an approach to implement cryptofunctions in pure Swift, which I will talk about further on. Finally, you can use JavaScript with CryptoJS. Bridging is painful, but it can be used.

Get more development news like this

CommonCrypto (2:48)

CommonCrypto is a C library, and it is part of the system available to iOS and OS X. It is an open source project, so you can explore its internals. Thanks to C interoperability, it can be used with Swift. However, you do have to use unsafe pointers. CommonCrypto is fairly easy to use. The following example demonstrates the encryption of data using the AES cipher.

CCCrypt(
    UInt32(kCCEncrypt),
    UInt32(kCCAlgorithmAES128),
    UInt32(kCCOptionPKCS7Padding),
    keyBytes, // UnsafePointer<Void>(keyData.bytes)
    key.count,
    ivBytes, // UnsafePointer<Void>(ivData.bytes)
    dataBytes, // UnsafePointer<Void>(data.bytes)
    dataLength,
    cryptPointer, // UnsafeMutablePointer<Void>(cryptData!.mutableBytes)
    cryptLength,
    &numBytesEncrypted
)

CryptoSwift (3:44)

A few months ago, I started working on this project named CryptoSwift. I created it out of curiosity, and a need to learn. In the very early days, when there was only a wrapper around OpenSSL, I wondered what was inside its MD5 hash function. I wondered if I could implement it, and I did. I continued, and started to explore new areas, learning new things about cryptography. As an engineer, I have to challenge myself constantly.

CryptoSwift is a Swift framework available for iOS and OS X. My principles in building this as a pure Swift project were to avoid C code and unsafe pointers. It’s constantly improving, and there is still a lot to do. It also comes with extensions over NSData and strings, so data can be encrypted immediately. To start, I have implemented some algorithms.

Hashes (5:21)

The first group of algorithms are hashes. These are the functions, and they can be used to verify or check the integrity of data. They are used around the cryptographic protocols, but have no secrecy. Everyone can calculate the hash around the data. With CryptoSwift, it looks like this:

import CryptoSwift

"SwiftSummit".md5()
"SwiftSummit".sha1()
"SwiftSummit".sha512()
"SwiftSummit".crc32()

I implemented MD5, SHA1, and SHA2 with variants. Using the extensions around Swift strings, these can be calculated straightaway.

Ciphers (6:07)

The next group that I implemented were ciphers. A cipher is a well-defined algorithm. I implemented two symmetric ciphers: AES and ChaCha. Ciphers start with plaintext. During encryption, a key is applied, some operations are performed and ciphertext is the output. Decryption is a similar, but reversed process.

AES, or Advanced Encryption Standard, is perhaps the most popular cipher you may have heard of, and it is mathematically proven to be safe. It is so popular that vendors have started to implement support with their hardware. It can be used with assembler to make things very, very fast. Even though it’s good, it is hard to use with Swift because we do not have access to the assembler. So, if we want to use it, we have to use C. To use it, we need a key and initialization vector iv. We have an instance of aes, and an encryption area of [1,2].

import CryptoSwift

let key = "1234567890123456" // key
let iv = "1234567890123456" // random
if let aes = AES(key: key, iv: iv, blockMode: .CBC) {
    if let encrypted = aes.encrypt([1,2], padding: PKCS7())
    {
        let data = NSData.withBytes(encrypted)
    }
}

The other cipher I implemented is ChaCha. It was invented by the mathematician programmer Daniel J. Bernstein after he tried to come up with something faster but just as secure as AES. Apple chose to use it with HomeKit, and Google uses it with Chrome. However, there is a lack of official support in OpenSSL. Using ChaCha is similar to the previous example with AES. There is an instance of chacha and an encrypt function.

import CryptoSwift
let key = "1234567890123456" // key
let iv = "1234567890123456" // random
if let chacha = ChaCha20(key: key, iv: iv) {
    if let encrypted = chacha.encrypt([1,2])
    {
        let data = NSData.withBytes(encrypted)
    }
}

For the API, I created an enum Cipher. It has two options, ChaCha20 and AES, as well as two functions, encrypt and decrypt. For this, I chose to work with an array of bytes.

enum Cipher {
    case ChaCha20(key: [UInt8], iv: [UInt8])
    case AES(key: [UInt8], iv: [UInt8], blockMode: CipherBlockMode)

    func encrypt(bytes: [UInt8]) -> [UInt8]?
    func decrypt(bytes: [UInt8]) -> [UInt8]?

    static func randomIV(blockSize: Int) -> [UInt8]
}

Block Mode (9:45)

When encryption happens, a cipher works on a block. When you encrypt a longer message, you have to somehow concatenate the output. This is why mathematicians invented block modes. This algorithm uses a block cipher to encrypt a large message. I have implemented four of these algorithms: ECB, CBC, CFB, and CTR. In this talk, I will show you two of these algorithms, EBC and CBC.

The most naive is ECB, or Electronic Codebook. Each of the three blocks of text are encrypted independently — there is plaintext, encryption, and ciphertext for each of the blocks. The result is obtained by concatenating one to the next. But, the problem with this mode is that it somehow exposes some of the information of plaintext, because it repeatedly performs the same operation.

CBC, or Cipher Block Chaining, is something of an opposite approach. This is the default block mode that exists in CryptoSwift. The difference with this mode is that the input for every block uses the output from the previous block. The encryption is done sequentially, so although it cannot be parallelized, it ends up more secure. Notice that, in this mode, we have an initialization vector, which is the only random value in the encryption.

Authenticators (12:11)

The last group of algorithms are authenticators. The message authentication code is a short piece of information that can provide integrity and authenticity assurances. When you receive a message, you can check that it is the file you expect, from the person you expected. The key is a part of the authentication operation. I implemented two of them: Poly, because of ChaCha, and HMAC, because of AES. The enum I used to implement has these two cases, as well as the function authenticate. HMAC has a variant due to the hash function.

enum Authenticator {
    case Poly1305(key: [UInt8])
    case HMAC(key: [UInt8], variant: HMAC.Variant)

    func authenticate(message: [UInt8]) -> [UInt8]?
}

Performance (13:24)

There are currently some problems with performance. This implementation is slower than CommonCrypto, although not everywhere. It is significantly slower for AES - it takes about forty seconds to encrypt one megabyte, compared to only a couple seconds. AES uses a lot of operations and loops, so perhaps that is the reason for its poor performance. However, ChaCha is extremely fast. It takes only a second and a half for the same code. Recently, I improved performance by 40% just be reserving memory for the array using the function reserveCapacity. From what I can see, the allocation of small chunks of memory is visible.

Crypto You Can Do (14:52)

So, Crypto you can do. How? Well, you can certainly contribute to CryptoSwift. I would also recommend Daniel Bernstein’s site. Again, he was the author of the library NaCl.

Of course, you might understand nothing at first. Keep re-reading, and at some point, try to write some code. Implement it and do tests. These ciphers are hard to keep track of in your head. When you have some code, share it and ask for feedback! The worst that can happen will be nothing; the best is that you will learn something new.

There is always a lot to fix and improve, especially with a project like this. You have seen the performance issues; there are other issues, too, as well as a lot of missing pieces. I encourage you to contribute, even if all you might do is work on performance or API design. It isn’t about cryptography, but Swift.

Thank you!


About the content

This talk was delivered live in March 2015 at Swift Summit London. The video was transcribed by Realm and is published here with the permission of the conference organizers.

Marcin Krzyżanowski

Marcin Krzyżanowski

4 design patterns for a RESTless mobile integration »

close