Skip to content

Instantly share code, notes, and snippets.

@hfossli
Last active July 28, 2024 09:34
Show Gist options
  • Save hfossli/7165dc023a10046e2322b0ce74c596f8 to your computer and use it in GitHub Desktop.
Save hfossli/7165dc023a10046e2322b0ce74c596f8 to your computer and use it in GitHub Desktop.
AES 256 in swift 4 with CommonCrypto
#import <CommonCrypto/CommonCrypto.h>
import Foundation
struct AES256 {
private var key: Data
private var iv: Data
public init(key: Data, iv: Data) throws {
guard key.count == kCCKeySizeAES256 else {
throw Error.badKeyLength
}
guard iv.count == kCCBlockSizeAES128 else {
throw Error.badInputVectorLength
}
self.key = key
self.iv = iv
}
enum Error: Swift.Error {
case keyGeneration(status: Int)
case cryptoFailed(status: CCCryptorStatus)
case badKeyLength
case badInputVectorLength
}
func encrypt(_ digest: Data) throws -> Data {
return try crypt(input: digest, operation: CCOperation(kCCEncrypt))
}
func decrypt(_ encrypted: Data) throws -> Data {
return try crypt(input: encrypted, operation: CCOperation(kCCDecrypt))
}
private func crypt(input: Data, operation: CCOperation) throws -> Data {
var outLength = Int(0)
var outBytes = [UInt8](repeating: 0, count: input.count + kCCBlockSizeAES128)
var status: CCCryptorStatus = CCCryptorStatus(kCCSuccess)
input.withUnsafeBytes { (encryptedBytes: UnsafePointer<UInt8>!) -> () in
iv.withUnsafeBytes { (ivBytes: UnsafePointer<UInt8>!) in
key.withUnsafeBytes { (keyBytes: UnsafePointer<UInt8>!) -> () in
status = CCCrypt(operation,
CCAlgorithm(kCCAlgorithmAES128), // algorithm
CCOptions(kCCOptionPKCS7Padding), // options
keyBytes, // key
key.count, // keylength
ivBytes, // iv
encryptedBytes, // dataIn
input.count, // dataInLength
&outBytes, // dataOut
outBytes.count, // dataOutAvailable
&outLength) // dataOutMoved
}
}
}
guard status == kCCSuccess else {
throw Error.cryptoFailed(status: status)
}
return Data(bytes: UnsafePointer<UInt8>(outBytes), count: outLength)
}
static func createKey(password: Data, salt: Data) throws -> Data {
let length = kCCKeySizeAES256
var status = Int32(0)
var derivedBytes = [UInt8](repeating: 0, count: length)
password.withUnsafeBytes { (passwordBytes: UnsafePointer<Int8>!) in
salt.withUnsafeBytes { (saltBytes: UnsafePointer<UInt8>!) in
status = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), // algorithm
passwordBytes, // password
password.count, // passwordLen
saltBytes, // salt
salt.count, // saltLen
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1), // prf
10000, // rounds
&derivedBytes, // derivedKey
length) // derivedKeyLen
}
}
guard status == 0 else {
throw Error.keyGeneration(status: Int(status))
}
return Data(bytes: UnsafePointer<UInt8>(derivedBytes), count: length)
}
static func randomIv() -> Data {
return randomData(length: kCCBlockSizeAES128)
}
static func randomSalt() -> Data {
return randomData(length: 8)
}
static func randomData(length: Int) -> Data {
var data = Data(count: length)
let status = data.withUnsafeMutableBytes { mutableBytes in
SecRandomCopyBytes(kSecRandomDefault, length, mutableBytes)
}
assert(status == Int32(0))
return data
}
}
import UIKit
extension Data {
var hexString: String {
return map { String(format: "%02hhx", $0) }.joined()
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
do {
let digest = "hello world".data(using: .utf8)!
let password = "foo"
let salt = AES256.randomSalt()
let iv = AES256.randomIv()
let key = try AES256.createKey(password: password.data(using: .utf8)!, salt: salt)
let aes = try AES256(key: key, iv: iv)
let encrypted = try aes.encrypt(digest)
let decrypted = try aes.decrypt(encrypted)
print("Encrypted: \(encrypted.hexString)")
print("Decrypted: \(decrypted.hexString)")
print("Password: \(password)")
print("Key: \(key.hexString)")
print("IV: \(iv.hexString)")
print("Salt: \(salt.hexString)")
print(" ")
print("#! /bin/sh")
print("echo \(digest.hexString) | xxd -r -p > digest.txt")
print("echo \(encrypted.hexString) | xxd -r -p > encrypted.txt")
print("openssl aes-256-cbc -K \(key.hexString) -iv \(iv.hexString) -e -in digest.txt -out encrypted-openssl.txt")
print("openssl aes-256-cbc -K \(key.hexString) -iv \(iv.hexString) -d -in encrypted.txt -out decrypted-openssl.txt")
} catch {
print("Failed")
print(error)
}
}
}
Encrypted: 76be70e3e63890e12572bc55ed8f95b1
Decrypted: 68656c6c6f20776f726c64
Password: foo
Key: 848496354b13779568e1ee81f1f73428678f131c3d7501ef9ed99f7a7b4bf70c
IV: 4bb9f6f8bfb7adaad584e032a4cd412f
Salt: eedb0c8de0a9d09f
----
echo 68656c6c6f20776f726c64 | xxd -r -p > digest.txt
echo 76be70e3e63890e12572bc55ed8f95b1 | xxd -r -p > encrypted.txt
openssl aes-256-cbc -K 848496354b13779568e1ee81f1f73428678f131c3d7501ef9ed99f7a7b4bf70c -iv 4bb9f6f8bfb7adaad584e032a4cd412f -e -in digest.txt -out encrypted-openssl.txt
openssl aes-256-cbc -K 848496354b13779568e1ee81f1f73428678f131c3d7501ef9ed99f7a7b4bf70c -iv 4bb9f6f8bfb7adaad584e032a4cd412f -d -in encrypted.txt -out decrypted-openssl.txt
@quangDecember
Copy link

quangDecember commented Oct 31, 2019

do you have update for this Swift 4.2 and later warning?
Thanks

'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R instead

@kvs-coder
Copy link

it appears that IV should be hardcoded or saved somewhere, because if I use it as random during the encryption and then try to decrypt the string with a new generated IV it does not work. IV bytes are not being cut.

@slinkeeeee
Copy link

slinkeeeee commented Dec 29, 2020

If you need this for Swift 5

`
import Foundation
import CommonCrypto

struct AES256 {
private var key: Data
private var iv: Data

public init(key: Data, iv: Data) throws {
    guard key.count == kCCKeySizeAES256 else {
        throw Error.badKeyLength
    }
    guard iv.count == kCCBlockSizeAES128 else {
        throw Error.badInputVectorLength
    }
    self.key = key
    self.iv = iv
}

enum Error: Swift.Error {
    case keyGeneration(status: Int)
    case cryptoFailed(status: CCCryptorStatus)
    case badKeyLength
    case badInputVectorLength
}

func encrypt(_ digest: Data) throws -> Data {
    return try crypt(input: digest, operation: CCOperation(kCCEncrypt))
}

func decrypt(_ encrypted: Data) throws -> Data {
    return try crypt(input: encrypted, operation: CCOperation(kCCDecrypt))
}

private func crypt(input: Data, operation: CCOperation) throws -> Data {
    var outLength = Int(0)
    var outBytes = [UInt8](repeating: 0, count: input.count + kCCBlockSizeAES128)
    var status: CCCryptorStatus = CCCryptorStatus(kCCSuccess)
    
    input.withUnsafeBytes { rawBufferPointer in
        let encryptedBytes = rawBufferPointer.baseAddress!
        
        iv.withUnsafeBytes { rawBufferPointer in
            let ivBytes = rawBufferPointer.baseAddress!
            
            key.withUnsafeBytes { rawBufferPointer in
                let keyBytes = rawBufferPointer.baseAddress!
                
                status = CCCrypt(operation,
                                 CCAlgorithm(kCCAlgorithmAES128),            // algorithm
                                 CCOptions(kCCOptionPKCS7Padding),           // options
                                 keyBytes,                                   // key
                                 key.count,                                  // keylength
                                 ivBytes,                                    // iv
                                 encryptedBytes,                             // dataIn
                                 input.count,                                // dataInLength
                                 &outBytes,                                  // dataOut
                                 outBytes.count,                             // dataOutAvailable
                                 &outLength)                                 // dataOutMoved
            }
        }
    }
    
    guard status == kCCSuccess else {
        throw Error.cryptoFailed(status: status)
    }
            
    return Data(bytes: &outBytes, count: outLength)
}

static func createKey(password: Data, salt: Data) throws -> Data {
    let length = kCCKeySizeAES256
    var status = Int32(0)
    var derivedBytes = [UInt8](repeating: 0, count: length)
    
    
    password.withUnsafeBytes { rawBufferPointer in
        let passwordRawBytes = rawBufferPointer.baseAddress!
        let passwordBytes = passwordRawBytes.assumingMemoryBound(to: Int8.self)
        
        salt.withUnsafeBytes { rawBufferPointer in
            let saltRawBytes = rawBufferPointer.baseAddress!
            let saltBytes = saltRawBytes.assumingMemoryBound(to: UInt8.self)
            
            status = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2),                  // algorithm
                                          passwordBytes,                                // password
                                          password.count,                               // passwordLen
                                          saltBytes,                                    // salt
                                          salt.count,                                   // saltLen
                                          CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1),   // prf
                                          10000,                                        // rounds
                                          &derivedBytes,                                // derivedKey
                                          length)                                       // derivedKeyLen
        }
    }
    

    guard status == 0 else {
        throw Error.keyGeneration(status: Int(status))
    }
    return Data(bytes: &derivedBytes, count: length)
}

static func randomIv() -> Data {
    return randomData(length: kCCBlockSizeAES128)
}

static func iV() -> Data {
    let arr: [UInt8] = [0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1]
    return Data(arr)
}

static func randomSalt() -> Data {
    return randomData(length: 8)
}

static func randomData(length: Int) -> Data {
    var data = Data(count: length)
    
    var mutableBytes: UnsafeMutableRawPointer!
    
    data.withUnsafeMutableBytes { rawBufferPointer in
        mutableBytes = rawBufferPointer.baseAddress!
    }
    
    let status = SecRandomCopyBytes(kSecRandomDefault, length, mutableBytes)
    
    assert(status == Int32(0))
    return data
}

}`

@Gutty1
Copy link

Gutty1 commented Jan 25, 2021

very interesting implementation,
could you please publish also the data extension hexString?

@Aizaz-Abbasi
Copy link

How to encrypt image ??

@hfossli
Copy link
Author

hfossli commented Jun 24, 2021

Big question. Lots of good articles on the subject

@Aizaz-Abbasi
Copy link

I used crypto swift but it is slow. any suggestions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment