Common cryptographic operations with CryptoKit

Dimitri James Tsiflitzis

What is CryptoKit?

CryptoKit is an iOS 13+ library that performs cryptographic operations securely and efficiently. Its features include:

  • A (nice) Swift API.
  • Access to cryptographic functions.
  • Ease of use for easy to misuse functions.
  • An improvement over legacy systems.

Existing cryptography solutions on iOS

There are two popular, trusted and open-source solutions:

  • OpenSSL - https
    //www.openssl.org/
  • CommonCrypto
    https://opensource.apple.com/source/CommonCrypto/​

Existing cryptography solutions on iOS

  • With the first solution the size of your app will be inflated by several megabytes.
  • With the second solution you might be exposed to a library swap attack where an attacker can replace your copy with their own (also possible, but harder, in the first case).

These are lower-level systems that deal with raw pointers and explicit memory management.


CryptoKit provides a high-level swift API.

When to use cryptography in your apps

  • You might want to enforce the protection of your users or your organizations' data by keeping sensitive information inaccessible to anyone without explicit access; transaction and account information in a banking app for instance.
  • You also might want to maintain the confidentiality of communication between peers in a chat or email application.
  • Finally, you might want to protect and provide authentication of the origin of some information; for example, if you are performing receipt validation on in-app purchases.

Getting started

  • Hashing.

We are going to go through some of the most common cases covered by CryptoKit. These are:

  • Encrypting and decrypting data.
  • Creating and Verifying Signatures.
  • Authenticating Data.
  • Performing Key Agreement.

Hashing Data

A hash function is any function that can be used to map data of arbitrary size to fixed-size values. This means that a hash function takes an input and transforms it into an output (a hash or digest) that represents this input. This hash value is virtually unique.

Hashing Data

Hashing Data


import CryptoKit
import Foundation

let path = Bundle.main.path(forResource: "video", 
                            ofType: "mp4")!
let data = FileManager.default.contents(atPath: path)!

let digest = SHA256.hash(data: data)
print(digest)

// or a better representation
let stringHash = digest.map { String(format: "%02hhx", $0) }.joined()
print(stringHash)

Hashing Data


var hasher = SHA256()
let path = Bundle.main.path(forResource: "video", ofType: "mp4")!
let stream = InputStream(fileAtPath: path)!stream.open()

let bufferSize = 512
let buffer = UnsafeMutablePointer<UInt8>
             .allocate(capacity: bufferSize)

while stream.hasBytesAvailable {
    let read = stream.read(buffer, maxLength: bufferSize)
    let bufferPointer = UnsafeRawBufferPointer(start: buffer,
                                               count: read)
    hasher.update(bufferPointer: bufferPointer)
}

let digest = hasher.finalize()
print(digest)

Encrypting and decrypting data

In the data encryption security method, information is encoded in a way that can only be accessed with the correct encryption key that is held by an authorized party. If the encryption key is incorrect or missing the data cannot be accessed and appears random and unreadable.

Encrypting and decrypting data

let key = SymmetricKey(size: .bits256)
let path = Bundle.main.path(forResource: "video", 
                            ofType: "mp4")!
let data = FileManager.default.contents(atPath: path)!

// To encrypt
let encryptedData = try! ChaChaPoly.seal(data, using: key)

// To decrypt
let sealedBox = try! ChaChaPoly.SealedBox(combined: encryptedData)
let decryptedData = try! ChaChaPoly.open(sealedBox, using: key)

ChaChaPoly is an implementation of the ChaCha20-Poly1305 cipher in case you were wondering.

Creating and Verifying Signatures

A digital signature in cryptography is a mathematical scheme for verifying the authenticity of digital information such as messages or documents.

A signature that has been confirmed to be valid by a recipient, gives them a strong indication that the information was indeed created by the person claiming to have created it and hasn’t been tampered with.

Creating and Verifying Signatures

Creating and Verifying Signatures


let privateKey = Curve25519.Signing.PrivateKey()
let publicKey = privateKey.publicKey // publicize freely
let publicKeyData = publicKey.rawRepresentation
let signingPublicKey = try! Curve25519
                                .Signing
                                .PublicKey(rawRepresentation: publicKeyData)
                                
// sign some data with your private key
let data = "All your base are belong to us".data(using: .utf8)!
let signature = try! privateKey.signature(for: data)

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

Authenticating Data

In data authentication, the aim is to verify the identity of a user. A common case where we would be interested in doing that is when a user requests to send data to a server we control but we would like them to verify their identity before we allow them to do so.

Authenticating Data


let path = Bundle.main.path(forResource: "video", ofType: "mp4")!
let data = FileManager.default.contents(atPath: path)!
let key = SymmetricKey(size: .bits256) // key shared by both parties
let authentication = HMAC<SHA256>
                     .authenticationCode(for: data,
                                       using: key)
let authenticationData = Data(authentication)

// ~~~~~~ travel accross the network ~~~~~~~

if (HMAC<SHA256>.isValidAuthenticationCode(authenticationData,
                                           authenticating: data,
                                                    using: key)) {
    print("Validated ✅")
}

Performing Key Agreement

In cryptography, a key-agreement protocol is a process during which two parties, each with their own separate private key, establish a shared secret between them.

This allows them to encrypt or sign data they wish to exchange. Also, this protocol, when executed, prevents unauthorized parties from eavesdropping.

Performing Key Agreement

Performing Key Agreement


let salt = "Salt".data(using: .utf8)!

let firstPrivateKey = P256.KeyAgreement.PrivateKey()
let firstPublicKey = firstPrivateKey.publicKey

let secondPrivateKey = P256.KeyAgreement.PrivateKey()
let secondPublicKey = secondPrivateKey.publicKey

Performing Key Agreement


let firstSharedSecret = try! firstPrivateKey
                              .sharedSecretFromKeyAgreement(with: secondPublicKey)
let firstSymmetricKey = firstSharedSecret.hkdfDerivedSymmetricKey(
                                          using: SHA256.self,
                                           salt: salt,
                                     sharedInfo: Data(),
                                outputByteCount: 32)
                                
let secondSharedSecret = try! secondPrivateKey
                              .sharedSecretFromKeyAgreement(with: firstPublicKey)
let secondSymmetricKey = secondSharedSecret.hkdfDerivedSymmetricKey(
                                           using: SHA256.self,
                                            salt: salt,
                                      sharedInfo: Data(),
                                 outputByteCount: 32)
                                 
if firstSymmetricKey == secondSymmetricKey {
    print("First and second have the same key to do as they please")
}

Conclusion & further reading

So far, we have explored five different cases using CrypoKit, but there are so many more.

 

Ευχαριστούμε 🎈

Common cryptographic operations in Swift with CryptoKit

By tsif

Common cryptographic operations in Swift with CryptoKit

  • 295