Apple recently introduced a new framework named CryptoKit at WWDC19, which makes generating hashes, encrypting data and verifying/authenticating data even easier.

Previously, the only way to use cryptography in Swift was via lower level APIs and complicated solutions. CryptoKit improves on this as it is much easier to use and doesn't require knowledge of low level operations.

In this tutorial, we'll explore how to perform different cryptographic operations using CryptoKit, including signing data, encrypting information, creating hashes, and performing key agreement.

Getting started

Note: This tutorial requires Xcode 11 and the latest beta versions of macOS 10.15 or iOS 13, which are currently in beta, as it relies on new features or frameworks that are not available on previous versions.

As CryptoKit is built into Apple's platforms, no installation or configuration is needed. However, Apple has a sample Swift playground that demonstrates different cryptographic operations, which I'll use as the base for this tutorial.

Download Materials

I'll include examples in this tutorial, but feel free to download the playground if you'd like to follow along and test out different examples.

If you aren't using Apple's sample playground, make sure to add import CryptoKit to the top of your file to be able to use it in your code.

Generating Hashes

Hash functions generate a unique key that will stay the same as long as the input data is exactly the same. For example, this could be very useful to verify that a shared piece of data is the same.

CryptoKit makes it easy to generate different types of hashes with good collision resistance, which prevents the possibility of two different pieces of input data generating the same hash.

CryptoKit supports the SHA256, SHA384, and SHA512 algorithms (as well as some older, insecure algorithms that should not be used). As computers have gotten more powerful, hash sizes have continued to increase to make it harder for attackers to attempt to force collisions.

Use the hash(using:) function on any algorithm to generate a hash for a piece of data, such as a string or a file.

Generating a hash for a file

if let filePath = Bundle.main.path(forResource: "JulianSchiavo", ofType: "blog"),
    let data = FileManager.default.contents(atPath: filePath) {
    let hash = SHA256.hash(data: data)
    print(hash.description) // SHA256 digest: 8f9809a8ce23abad17966f88545989294d912146feab32682012ed116f4ea267
}

As you can see, generating hashes for files is as easy as loading the file, selecting an algorithm, and hashing it. Apple's Swift playground also includes an example of hashing files via a buffer.

Generating a hash for text or other data

if let data = "Julian Schiavo's Blog".data(using: .utf8) {
    let hash = SHA256.hash(data: data)
    print(hash.description) // SHA256 digest: 527f640b5cd67ac074eaf63e4fb3d1ceb2a22339318b95177a756ce60fec8a23
}

To generate a hash for text or other data, just do the same thing as with generating file hashes, except directly with the data or by encoding the string as data.

Encrypting Data

Encrypting files allows you to achieve both authenticity and confidentiality by converting the input data into cipher text that can only be read with the original, randomly generated key that is shared between parties.

CryptoKit supports both the AES-GCM and ChaChaPoly algorithms, although ChaChaPoly is preferred as it is typically faster on mobile devices.

To encrypt a file, just seal it with the chosen algorithm:

let key = SymmetricKey(size: .bits256)

if let filePath = Bundle.main.path(forResource: "JulianSchiavo", ofType: "blog"),
    let encryptedData = FileManager.default.contents(atPath: filePath),
    let sealedBox = try? ChaChaPoly.seal(encryptedData, using: key) {
    // Share the `sealedBox.combined` data to the other party
}

The sealed box contains 3 outputs from the operation: the sealedBox.ciphertext (the encrypted data, always the same size as the input data), the sealedBox.tag (used to ensure the data remains intact), and the sealedBox.nonce (which should be different every time an encryption operation is performed, and is generated randomly if not provided during encryption).

Share the sealedBox.combined property with the other party, which is generated from the tag, nonce, and cipher text.

// Receive the `sealedBox.combined` data from the other party and use it to decrypt the data
if let sealedBox = ChaChaPoly.SealedBox(combined: data),
    let decryptedData = try? ChaChaPoly.open(sealedBox, using: key) {
    // Use the `decryptedData` as necessary
}

Then, the receiving party, or client, can decrypt the content using the same key (which should not be shared in the same way to avoid attackers from gaining access to the key).

Creating and Validating Digital Signatures

Digital signatures are used to validate the authenticity and integrity of a message or piece of data. After signing the data with a private key, others can verify the signature using your public key.

CryptoKit has built in support for 4 different elliptic curve types, which are used to create and verify cryptographic signatures: Curve25519, P521, P384, and P256. The different types have different levels of security and speed, but Curve25519 is typically best.

First, generate a random secure public and private key-pair, and publish the public key.

let privateKey = Curve25519.Signing.PrivateKey() // Keep the private key safe

let publicKey = privateKey.publicKey
let publicKeyData = publicKey.rawRepresentation // Publish the public key

Then, sign a piece of data with the private key, such as a string.

if let data = "Julian Schiavo's Blog".data(using: .utf8),
    let signature = try? privateKey.signature(for: data) {
    // Share the data (for example, a document) and corresponding signature publicly
}

Finally, anyone can check that the signature is indeed authentic and from you using the public key:

if publicKey.isValidSignature(signature, for: data) {
    print("The signature is valid")
}

If anyone tries to publish a fake signature, it will be rejected when someone tries to validate it.

Performing Key Agreement

Key agreement allows multiple parties to determine a shared encryption key that can be used to sign or encrypt data that they want to exchange.

First, create a unique salt that you will use when deriving keys. Here, we are using an if let block to avoid the salt's encoded data from being optional, but you can also use a guard let block instead.

if let salt = "Salty Blog".data(using: .utf8) {
    // Derive keys using the salt
}

Then, both users A and B generate a public and private key-pair, and publish the public key while keeping the private key secret. You can use any of the elliptic curve types from the digital signatures section, such as P521.

let privateKeyA = P521.KeyAgreement.PrivateKey()
let publicKeyA = privateKeyA.publicKey

let privateKeyB = P521.KeyAgreement.PrivateKey()
let publicKeyB = privateKeyB.publicKey

User A derives a shared secret with both their private key and User B's public key.

let sharedSecretA = try? privateKeyA.sharedSecretFromKeyAgreement(with: publicKeyB)
let symmetricKeyA = sharedSecretA.hkdfDerivedSymmetricKey(using: SHA256.self, salt: salt, sharedInfo: Data(), outputByteCount: 32)

Then, User B performs the same operation using their private key and User A's public key.

let sharedSecretB = try? privateKeyB.sharedSecretFromKeyAgreement(with: publicKeyA)
let symmetricKeyB = sharedSecretB.hkdfDerivedSymmetricKey(using: SHA256.self, salt: salt, sharedInfo: Data(), outputByteCount: 32)

You can check that both of the generated keys in this example are equal like this:

if symmetricKeyA == symmetricKeyB {
    print("User A and User B's symmetric keys are equal")
}

Both User A and User B now have a copy of the same key that they can each use to authenticate or encrypt messages to and from each other.

Conclusion

In this tutorial, you explored multiple different cryptographic operations that can be performed using Apple's new CryptoKit framework in iOS 13.

Apple has a sample Swift playground that demonstrates similar cryptographic operations as in this tutorial, which I've included below as a reference.

Download Materials

I hope this tutorial was useful and helped you to learn more about using CryptoKit. If you have any questions or feedback, feel free to send them or email me: [email protected]. Thanks for reading 🔑