Advertisement
backslash-f

[Swift 5] CommonCrypto + AES + 256 key + iv

Apr 2nd, 2019
446
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Swift 5.51 KB | None | 0 0
  1. import Foundation
  2. import CommonCrypto
  3.  
  4. protocol Cryptable {
  5.     func encrypt(_ string: String) throws -> Data
  6.     func decrypt(_ data: Data) throws -> String
  7. }
  8.  
  9. enum AESError: Error {
  10.     case invalidKeySize
  11.     case generateRandomIVFailed
  12.     case encryptDataFailed
  13.     case stringToDataFailed
  14.     case decryptDataFailed
  15.     case dataToStringFailed
  16. }
  17.  
  18. struct AES {
  19.     // Seems like the easiest way to avoid the `withUnsafeBytes` mess is to use NSData.bytes.
  20.     private let key: NSData
  21.  
  22.     private let ivSize: Int                     = kCCBlockSizeAES128
  23.     private let options: CCOptions              = CCOptions(kCCOptionPKCS7Padding)
  24.  
  25.     init(keyString: String) throws {
  26.         guard keyString.count == kCCKeySizeAES256 else {
  27.             throw AESError.invalidKeySize
  28.         }
  29.         guard let keyData: Data = keyString.data(using: .utf8) else {
  30.             throw AESError.stringToDataFailed
  31.         }
  32.         self.key = NSData(data: keyData)
  33.     }
  34. }
  35.  
  36. extension AES: Cryptable {
  37.  
  38.     func encrypt(_ string: String) throws -> Data {
  39.         guard let dataToEncrypt: Data = string.data(using: .utf8) else {
  40.             throw AESError.stringToDataFailed
  41.         }
  42.  
  43.         // Seems like the easiest way to avoid the `withUnsafeBytes` mess is to use NSData.bytes.
  44.         let dataToEncryptNSData = NSData(data: dataToEncrypt)
  45.  
  46.         let bufferSize: Int = ivSize + dataToEncryptNSData.length + kCCBlockSizeAES128
  47.         let buffer = UnsafeMutablePointer<NSData>.allocate(capacity: bufferSize)
  48.         defer { buffer.deallocate() }
  49.  
  50.         // iv creation
  51.         let status: Int32 = SecRandomCopyBytes(
  52.             kSecRandomDefault,
  53.             kCCBlockSizeAES128,
  54.             buffer
  55.         )
  56.         guard status == 0 else {
  57.             throw AESError.generateRandomIVFailed
  58.         }
  59.  
  60.         var numberBytesEncrypted: Int = 0
  61.  
  62.         let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation
  63.             CCOperation(kCCEncrypt),                // op: CCOperation
  64.             CCAlgorithm(kCCAlgorithmAES),           // alg: CCAlgorithm
  65.             options,                                // options: CCOptions
  66.             key.bytes,                              // key: the "password"
  67.             key.length,                             // keyLength: the "password" size
  68.             buffer,                                 // iv: Initialization Vector
  69.             dataToEncryptNSData.bytes,              // dataIn: Data to encrypt bytes
  70.             dataToEncryptNSData.length,             // dataInLength: Data to encrypt size
  71.             buffer + kCCBlockSizeAES128,            // dataOut: encrypted Data buffer
  72.             bufferSize,                             // dataOutAvailable: encrypted Data buffer size
  73.             &numberBytesEncrypted                   // dataOutMoved: the number of bytes written
  74.         )
  75.  
  76.         guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
  77.             throw AESError.encryptDataFailed
  78.         }
  79.  
  80.         return Data(bytes: buffer, count: numberBytesEncrypted + ivSize)
  81.     }
  82.  
  83.     func decrypt(_ data: Data) throws -> String {
  84.  
  85.         // Seems like the easiest way to avoid the `withUnsafeBytes` mess is to use NSData.bytes.
  86.         let dataToDecryptNSData = NSData(data: data)
  87.  
  88.         let bufferSize: Int = dataToDecryptNSData.length - ivSize
  89.         let buffer = UnsafeMutablePointer<NSData>.allocate(capacity: bufferSize)
  90.         defer { buffer.deallocate() }
  91.  
  92.         var numberBytesDecrypted: Int = 0
  93.        
  94.         let cryptStatus: CCCryptorStatus = CCCrypt(         // Stateless, one-shot encrypt operation
  95.             CCOperation(kCCDecrypt),                        // op: CCOperation
  96.             CCAlgorithm(kCCAlgorithmAES128),                // alg: CCAlgorithm
  97.             options,                                        // options: CCOptions
  98.             key.bytes,                                      // key: the "password"
  99.             key.length,                                     // keyLength: the "password" size
  100.             dataToDecryptNSData.bytes,                      // iv: Initialization Vector
  101.             dataToDecryptNSData.bytes + kCCBlockSizeAES128, // dataIn: Data to decrypt bytes
  102.             bufferSize,                                     // dataInLength: Data to decrypt size
  103.             buffer,                                         // dataOut: decrypted Data buffer
  104.             bufferSize,                                     // dataOutAvailable: decrypted Data buffer size
  105.             &numberBytesDecrypted                           // dataOutMoved: the number of bytes written
  106.         )
  107.  
  108.         guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
  109.             throw AESError.decryptDataFailed
  110.         }
  111.  
  112.         let decryptedData = Data(bytes: buffer, count: numberBytesDecrypted)
  113.  
  114.         guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
  115.             throw AESError.dataToStringFailed
  116.         }
  117.  
  118.         return decryptedString
  119.     }
  120. }
  121.  
  122. // Test.
  123. do {
  124.     let aes = try AES(keyString: "FiugQTgPNwCWUY,VhfmM4cKXTLVFvHFe")
  125.  
  126.     let stringToEncrypt: String = "please encrypt meeee"
  127.     print("String to encrypt:\t\t\t\(stringToEncrypt)")
  128.  
  129.     let encryptedData: Data = try aes.encrypt(stringToEncrypt)
  130.     print("String encrypted (base64):\t\(encryptedData.base64EncodedString())")
  131.  
  132.     let decryptedData: String = try aes.decrypt(encryptedData)
  133.     print("String decrypted:\t\t\t\(decryptedData)")
  134.  
  135. } catch {
  136.     print("Something went wrong: \(error)")
  137. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement