Search code examples
swiftencryptionaes-gcmnonceapple-cryptokit

iOS CryptoKit AES-GCM is it possible to use a nonce with fewer than 12 bytes?


I am attempting to interface with an existing device that uses AES-GCM with a 4-byte nonce (UInt32). This is a simple incremental counter that increases each time an operation occurs:

var cryptoCounter: UInt32 = 0

I then attempt to encrypt it and retrieve the values like so:

let key = SymmetricKey(data: sharedKey) // This is a 32-byte key.
let nonceData = withUnsafeBytes(of: cryptoCounter.bigEndian, Array.init) // Convert UInt32 to 4-byte data.
let nonce = try! AES.GCM.Nonce(data: Data(nonceData)) // This throws an invalid parameter size exception.
let encrypted = try! AES.GCM.seal(serialized, using: key, nonce: nonce)

However, the AES.GCM.Nonce doesn't work with fewer than 12 bytes, so the 4-byte nonce causes it to throw an error. I've tried padding the nonce with a spare 0'ed 8-bytes:

let nonceData = [0, 0, 0, 0, 0, 0, 0, 0] + withUnsafeBytes(of: cryptoCounter.bigEndian, Array.init)

But the encrypted value is rejected by the device, so I assume this isn't correct. If anyone has any suggestions on the best way to implement this Swift, that would be amazing! Thank you.


Solution

  • I figured out a solution that worked for me.

    I used the excellent CryptoSwift library (7.8k stars on GitHub at time of writing):

    let gcm = GCM(iv: withUnsafeBytes(of: cryptoCounter.bigEndian, Array.init), mode: .detached)
    let aes = try! AES(key: [UInt8](sharedKey), blockMode: gcm, padding: .noPadding)
    let encrypted = try! aes.encrypt([UInt8](serialized))