Trying to perform encryption and decryption using AES ( common crypto ) in swift, For some reason while decrypting the first 16 characters are getting trimmed off. Can anyone please look at the below playground code and suggest what could be going wrong here ?. The decodedString variable trims off the intial 16 characters of the original payloadString during decryption.
playground code
import UIKit
import CommonCrypto
class AESNew {
enum MyError: LocalizedError {
case first(message: String)
case second(message: String)
var errorDescription: String? { return "Some description here!" }
}
func encrypt(plainText: String, keyData:Data, iv: Data, options:Int = kCCOptionPKCS7Padding) -> Result<Data, Error> {
let plainData = Data(plainText.utf8)
if let cryptData = NSMutableData(length: Int((plainData.count)) + kCCBlockSizeAES128) {
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(options)
var numBytesEncrypted :size_t = 0
//let value = iv.withUnsafeBytes { $0.load(as: UInt32.self) }
let cryptResult = iv.withUnsafeBytes { (dataBytes : UnsafePointer<UInt8>) -> Result<Data, Error> in
print(dataBytes)
print(UnsafeRawPointer(dataBytes))
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes,
keyLength,
UnsafeRawPointer(dataBytes),
(plainData as NSData).bytes, plainData.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
let base64cryptString = cryptData.base64EncodedString(options: .lineLength64Characters)
print("base64cryptString: \(base64cryptString)")
return .success(cryptData as Data)
} else {
print("failure")
return .failure(MyError.first(message: "crypt failed"))
}
}
return cryptResult
}
return .failure(MyError.second(message: "no value failure"))
}
// The iv is prefixed to the encrypted data
func decrypt(data: Data, keyData: Data) throws -> Data? {
let keyLength = keyData.count
let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
if validKeyLengths.contains(keyLength) == false {
print("validKeyLengths does not match")
}
let ivSize = kCCBlockSizeAES128
let clearLength = size_t(data.count - ivSize)
var clearData = Data(count: clearLength)
var numBytesDecrypted: size_t = 0
let options = CCOptions(kCCOptionPKCS7Padding)
let cryptStatus = clearData.withUnsafeMutableBytes {cryptBytes in
data.withUnsafeBytes {dataBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(kCCDecrypt),
CCAlgorithm(kCCAlgorithmAES128),
options,
keyBytes,
keyLength,
dataBytes,
dataBytes+kCCBlockSizeAES128,
clearLength,
cryptBytes,
clearLength,
&numBytesDecrypted)
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
clearData.count = numBytesDecrypted
} else {
print("Decryption failed")
}
return clearData
}
}
func randomGenerateBytes(count: Int) -> Data? {
let bytes = UnsafeMutableRawPointer.allocate(byteCount: count, alignment: 1)
defer { bytes.deallocate() }
let status = CCRandomGenerateBytes(bytes, count)
guard status == kCCSuccess else { return nil }
return Data(bytes: bytes, count: count)
}
var ivBytesNew = Data()
if let ivBytes = randomGenerateBytes(count: 16) {
ivBytesNew = ivBytes
} else {
print("randomGenerateBytes failed")
}
let keyString = "keyData890123456"
let keyData = "keyData890123456".data(using:String.Encoding.utf8)!
let payloadString = "asdflkasfdkaslfd12345"
let aesObject = AESNew()
let encrytedObject = aesObject.encrypt(plainText: payloadString, keyData: keyData, iv: ivBytesNew, options: kCCOptionPKCS7Padding)
var encryptedData = Data()
switch encrytedObject {
case .success(let encData):
print(encData)
encryptedData = encData
case .failure(let error):
print(error)
default:
print("enc failed")
}
if let decrypedData = try? aesObject.decrypt(data: encryptedData, keyData: keyData) {
if let decodedString = String(data: decrypedData, encoding: .utf8) {
print(decodedString) // 1234, first 16 characters are omitted.
} else {
print("conversion from data to string failed")
}
} else {
print("decryption failed")
}
Seems you have picked up two different kinds of encrypt and decrypt.
Do you see your decrypt
does not have a parameter iv
which is needed for AES decryption?
Your decrypt
expects first 16 bytes of data
as iv
and the rest as encrypted data. But your encrypt
returns only encrypted data.
Please try changing the line calling decrypt
as follows:
if let decrypedData = try? aesObject.decrypt(data: ivBytesNew + encryptedData, keyData: keyData) {
Generally, your encrypt
uses NSMutableData
and handling bytes
inappropriately. That is fragile and it might crash on some different context. I strongly recommend you not to use your current encrypt
.
Anyway, you should make encrypt
and decrypt
consistent, or sort of symmetric, currently they are not.