Last active
March 25, 2023 19:20
-
-
Save VaslD/05a3416b46c238c7f86d7624b43fe325 to your computer and use it in GitHub Desktop.
East-to-use AES-CBC in Swift, UnsafePointer & Data supported.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import CommonCrypto | |
import CryptoKit | |
import Foundation | |
// MARK: - AES.CBC | |
public extension AES { | |
enum CBC {} | |
} | |
public extension AES.CBC { | |
// MARK: Known IV | |
static func encrypt<R>(_ data: some ContiguousBytes, key: SymmetricKey, | |
initializationVector: some ContiguousBytes, | |
completion: (_ buffer: UnsafeBufferPointer<UInt8>?) throws -> R) rethrows -> R { | |
try initializationVector.withUnsafeBytes { ivIn in | |
guard ivIn.count >= kCCBlockSizeAES128 else { | |
return try completion(nil) | |
} | |
return try data.withUnsafeBytes { dataIn in | |
try key.withUnsafeBytes { keyIn in | |
var sizeOut = dataIn.count + kCCBlockSizeAES128 | |
let bufferOut = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: sizeOut) | |
defer { bufferOut.deallocate() } | |
bufferOut.initialize(repeating: 0x00) | |
guard CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), | |
CCOptions(kCCOptionPKCS7Padding), | |
keyIn.baseAddress!, keyIn.count, | |
ivIn.baseAddress!, | |
dataIn.baseAddress!, dataIn.count, | |
bufferOut.baseAddress!, sizeOut, &sizeOut) == kCCSuccess else { | |
return try completion(nil) | |
} | |
let result = try completion(UnsafeBufferPointer(rebasing: bufferOut[..<sizeOut])) | |
bufferOut.update(repeating: 0x00) | |
return result | |
} | |
} | |
} | |
} | |
static func decrypt<R>(_ data: some ContiguousBytes, key: SymmetricKey, | |
initializationVector: some ContiguousBytes, | |
completion: (_ buffer: UnsafeBufferPointer<UInt8>?) throws -> R) rethrows -> R { | |
try initializationVector.withUnsafeBytes { ivIn in | |
guard ivIn.count >= kCCBlockSizeAES128 else { | |
return try completion(nil) | |
} | |
return try data.withUnsafeBytes { dataIn in | |
guard dataIn.count > kCCBlockSizeAES128 else { | |
return try completion(nil) | |
} | |
return try key.withUnsafeBytes { keyIn in | |
let bufferOut = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: dataIn.count) | |
defer { bufferOut.deallocate() } | |
bufferOut.initialize(repeating: 0x00) | |
var sizeOut = 0 | |
guard CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), | |
CCOptions(kCCOptionPKCS7Padding), | |
keyIn.baseAddress!, keyIn.count, | |
ivIn.baseAddress!, | |
dataIn.baseAddress!, dataIn.count, | |
bufferOut.baseAddress!, bufferOut.count, &sizeOut) == kCCSuccess else { | |
return try completion(nil) | |
} | |
let result = try completion(UnsafeBufferPointer(rebasing: bufferOut[..<sizeOut])) | |
bufferOut.update(repeating: 0x00) | |
return result | |
} | |
} | |
} | |
} | |
static func encrypt(_ data: UnsafeMutableRawBufferPointer, key: SymmetricKey, | |
initializationVector: some ContiguousBytes) -> Int { | |
guard data.count >= kCCBlockSizeAES128 else { | |
return 0 | |
} | |
return initializationVector.withUnsafeBytes { ivIn in | |
guard ivIn.count >= kCCBlockSizeAES128 else { | |
return 0 | |
} | |
return key.withUnsafeBytes { keyIn in | |
var sizeOut = 0 | |
guard CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), | |
CCOptions(kCCOptionPKCS7Padding), | |
keyIn.baseAddress!, keyIn.count, | |
ivIn.baseAddress!, | |
data.baseAddress!, data.count - kCCBlockSizeAES128, | |
data.baseAddress!, data.count, | |
&sizeOut) == kCCSuccess else { | |
return 0 | |
} | |
return sizeOut | |
} | |
} | |
} | |
static func decrypt(_ buffer: UnsafeMutableRawBufferPointer, key: SymmetricKey, | |
initializationVector: some ContiguousBytes) -> Int { | |
guard buffer.count > kCCBlockSizeAES128, buffer.count.isMultiple(of: kCCBlockSizeAES128) else { | |
return 0 | |
} | |
return initializationVector.withUnsafeBytes { ivIn in | |
key.withUnsafeBytes { keyIn in | |
var sizeOut = 0 | |
guard CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), | |
CCOptions(kCCOptionPKCS7Padding), | |
keyIn.baseAddress!, keyIn.count, | |
ivIn.baseAddress!, | |
buffer.baseAddress!, buffer.count, | |
buffer.baseAddress!, buffer.count, | |
&sizeOut) == kCCSuccess else { | |
return 0 | |
} | |
return sizeOut | |
} | |
} | |
} | |
// MARK: Random IV | |
static func encrypt<R>(_ data: some ContiguousBytes, key: SymmetricKey, | |
completion: (_ buffer: UnsafeBufferPointer<UInt8>?) throws -> R) rethrows -> R { | |
let bufferIV = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: kCCBlockSizeAES128) | |
defer { bufferIV.deallocate() } | |
bufferIV.initialize(repeating: 0x00) | |
guard SecRandomCopyBytes(kSecRandomDefault, bufferIV.count, bufferIV.baseAddress!) == errSecSuccess else { | |
return try completion(nil) | |
} | |
return try data.withUnsafeBytes { dataIn in | |
try key.withUnsafeBytes { keyIn in | |
var sizeOut = dataIn.count + kCCBlockSizeAES128 * 2 | |
let bufferOut = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: sizeOut) | |
defer { bufferOut.deallocate() } | |
bufferOut.initialize(repeating: 0x00) | |
_ = bufferOut.update(fromContentsOf: bufferIV) | |
sizeOut -= kCCBlockSizeAES128 | |
guard CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), | |
CCOptions(kCCOptionPKCS7Padding), | |
keyIn.baseAddress!, keyIn.count, | |
bufferIV.baseAddress!, | |
dataIn.baseAddress!, dataIn.count, | |
bufferOut.baseAddress! + kCCBlockSizeAES128, sizeOut, &sizeOut) == kCCSuccess else { | |
return try completion(nil) | |
} | |
sizeOut += kCCBlockSizeAES128 | |
let result = try completion(UnsafeBufferPointer(rebasing: bufferOut[..<sizeOut])) | |
bufferIV.update(repeating: 0x00) | |
bufferOut.update(repeating: 0x00) | |
return result | |
} | |
} | |
} | |
static func decrypt<R>(_ data: some ContiguousBytes, key: SymmetricKey, | |
completion: (_ buffer: UnsafeBufferPointer<UInt8>?) throws -> R) rethrows -> R { | |
try data.withUnsafeBytes { dataIn in | |
guard dataIn.count > kCCBlockSizeAES128 else { | |
return try completion(nil) | |
} | |
return try key.withUnsafeBytes { keyIn in | |
let bufferOut = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: dataIn.count) | |
defer { bufferOut.deallocate() } | |
bufferOut.initialize(repeating: 0x00) | |
var sizeOut = 0 | |
guard CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), | |
CCOptions(kCCOptionPKCS7Padding), | |
keyIn.baseAddress!, keyIn.count, | |
dataIn.baseAddress!, | |
dataIn.baseAddress! + kCCBlockSizeAES128, dataIn.count - kCCBlockSizeAES128, | |
bufferOut.baseAddress!, bufferOut.count, &sizeOut) == kCCSuccess else { | |
return try completion(nil) | |
} | |
let result = try completion(UnsafeBufferPointer(rebasing: bufferOut[..<sizeOut])) | |
bufferOut.update(repeating: 0x00) | |
return result | |
} | |
} | |
} | |
static func encrypt(_ data: UnsafeMutableRawBufferPointer, key: SymmetricKey) -> Int { | |
guard data.count >= kCCBlockSizeAES128 * 2 else { | |
return 0 | |
} | |
let bufferIV = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: kCCKeySizeAES128) | |
defer { bufferIV.deallocate() } | |
bufferIV.initialize(repeating: 0x00) | |
guard SecRandomCopyBytes(kSecRandomDefault, kCCKeySizeAES128, bufferIV.baseAddress!) == errSecSuccess else { | |
return 0 | |
} | |
return key.withUnsafeBytes { | |
var sizeOut = 0 | |
guard CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), | |
CCOptions(kCCOptionPKCS7Padding), | |
$0.baseAddress!, $0.count, | |
bufferIV.baseAddress!, | |
data.baseAddress!, data.count - 2 * kCCBlockSizeAES128, | |
data.baseAddress!, data.count - kCCBlockSizeAES128, | |
&sizeOut) == kCCSuccess else { | |
return 0 | |
} | |
memmove(data.baseAddress! + kCCBlockSizeAES128, data.baseAddress!, sizeOut) | |
data.copyBytes(from: bufferIV) | |
sizeOut += kCCBlockSizeAES128 | |
return sizeOut | |
} | |
} | |
static func decrypt(_ buffer: UnsafeMutableRawBufferPointer, key: SymmetricKey) -> Int { | |
guard buffer.count > kCCBlockSizeAES128, buffer.count.isMultiple(of: kCCBlockSizeAES128) else { | |
return 0 | |
} | |
return key.withUnsafeBytes { keyIn in | |
var sizeOut = 0 | |
guard CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), | |
CCOptions(kCCOptionPKCS7Padding), | |
keyIn.baseAddress!, keyIn.count, | |
buffer.baseAddress!, | |
buffer.baseAddress! + kCCBlockSizeAES128, buffer.count - kCCBlockSizeAES128, | |
buffer.baseAddress! + kCCBlockSizeAES128, buffer.count - kCCBlockSizeAES128, | |
&sizeOut) == kCCSuccess else { | |
return 0 | |
} | |
memmove(buffer.baseAddress!, buffer.baseAddress! + kCCBlockSizeAES128, sizeOut) | |
memset(buffer.baseAddress! + sizeOut, 0x00, buffer.count - sizeOut) | |
return sizeOut | |
} | |
} | |
} | |
// MARK: - Data Extensions | |
public extension AES.CBC { | |
static func encrypt(_ data: Data, key: SymmetricKey, initializationVector: Data) -> Data? { | |
Self.encrypt(data, key: key, initializationVector: initializationVector) { | |
guard let buffer = $0 else { return nil } | |
return Data(buffer) | |
} | |
} | |
static func decrypt(_ data: Data, key: SymmetricKey, initializationVector: Data) -> Data? { | |
Self.decrypt(data, key: key, initializationVector: initializationVector) { | |
guard let buffer = $0 else { return nil } | |
return Data(buffer) | |
} | |
} | |
static func encrypt(_ data: inout Data, key: SymmetricKey, initializationVector: Data) -> Bool { | |
data.count += 16 | |
let size = data.withUnsafeMutableBytes { | |
Self.encrypt($0, key: key, initializationVector: initializationVector) | |
} | |
guard size > 0 else { | |
data.count -= 16 | |
return false | |
} | |
data.count = size | |
return true | |
} | |
static func decrypt(_ data: inout Data, key: SymmetricKey, initializationVector: Data) -> Bool { | |
let size = data.withUnsafeMutableBytes { | |
Self.decrypt($0, key: key, initializationVector: initializationVector) | |
} | |
guard size > 0 else { return false } | |
data.count = size | |
return true | |
} | |
static func encrypt(_ data: Data, key: SymmetricKey) -> Data? { | |
Self.encrypt(data, key: key) { | |
guard let buffer = $0 else { return nil } | |
return Data(buffer) | |
} | |
} | |
static func decrypt(_ data: Data, key: SymmetricKey) -> Data? { | |
Self.decrypt(data, key: key) { | |
guard let buffer = $0 else { return nil } | |
return Data(buffer) | |
} | |
} | |
static func encrypt(_ data: inout Data, key: SymmetricKey) -> Bool { | |
data.count += 32 | |
let size = data.withUnsafeMutableBytes { Self.encrypt($0, key: key) } | |
guard size > 0 else { | |
data.count -= 32 | |
return false | |
} | |
data.count = size | |
return true | |
} | |
static func decrypt(_ data: inout Data, key: SymmetricKey) -> Bool { | |
let size = data.withUnsafeMutableBytes { Self.decrypt($0, key: key) } | |
guard size > 0 else { return false } | |
data.count = size | |
return true | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment