swiftencryptioncryptographyaescryptoswift

Swift - AES 128 ctr, ciphertext too long


I want to use the aes-128-ctr in Swift with the CryptoSwift library, however my resulting ciphertext is too long.

My IV is 16 bytes, salt 32 bytes and the aes plaintext is also 32 bytes, why is the resulting ciphertext 48 bytes, thus padded with another 16 bytes?

let salt: [UInt8] = Array("tkmlidnonknkqgvapjrpdcductebsozn".utf8) 
let derivedKey = try PKCS5.PBKDF2(password: password, salt: salt, iterations: numberOfIterations, variant: .sha256).calculate()
let iv: [UInt8] = Array("abcdefgthksdfghj".utf8)
let aesKey: [UInt8] = Array(derivedKey[..<16])
let aes = try AES(key: aesKey, blockMode: .CTR(iv: iv))
let ciphertext = try aes.encrypt(password)

here the password is the mentioned 32 bytes plaintext.

Additionally, is there any way to generate a random salt? I found that

let iv: [UInt8] = AES.randomIV(AES.blockSize)

generates a random IV, however how do I get such a salt?


Solution

  • According to the CryptoSwift documentation, it uses PKCS7 padding by default. Since CTR mode does not need padding, you can (and should) disable it by adding the padding: .noPadding to your AES() constructor call.

    There are no special format requirements on the PBKDF2 salt (it can be literally any random string of bytes), so you should be able to use AES.randomIV() (or any other source of random bytes) to generate one.

    (Internally, the CryptoSwift AES.randomIV() code seems to use RandomBytesSequence, but unfortunately that part of CryptoSwitf appears to be undocumented. The only usage example I could find, besides the randomIV() source code itself, is this test case. Which, BTW, looks like a really bad unit test — all it appears to be testing is that an AnyIterator<UInt8> actually returns UInt8 values. It's not even checking that the iterator actually returns the requested number of bytes, which it could totally fail to do, e.g. if opening /dev/urandom failed for some reason.)