0

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.

Sam Holmes
  • 1,594
  • 13
  • 31
  • 1
    There is a good reason for that. If you generate the nonce randomly, due to the generic birthday attack, after 2^{32/2} = 2^{16} = 65536 nonce generation you will generate the same nonce under the same key. This will cause the crib-dragging attack that will cause the failure of the confidentiality. It may also more catastrophic result in forgery. As NIST recommended use incremental counter to avoid the collision and change key once the counter reaches the limit. There can be re-use issue that can be solved with [combined mode](https://crypto.stackexchange.com/a/77986/18298) – kelalaka Jun 10 '20 at 20:42
  • We agree with each other! I prefer having the nonce be an incremental counter, I just needed a way to use the incremental counter available to me even though the value is fewer than 12 bytes. CryptoKit doesn't include the same level of support for the GHASH function implemented in other solutions like Bouncy Castle on Java, which runs the right kind of hashing algorithm when the nonce is either greater than or less than exactly 12 bytes. – Sam Holmes Jun 11 '20 at 10:24
  • Don't forget that during the failure you may repeat the counter. That is why is it advised to have counter+random. – kelalaka Jun 11 '20 at 10:33

1 Answers1

1

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))
Sam Holmes
  • 1,594
  • 13
  • 31