Skip to content

Instantly share code, notes, and snippets.

@VaslD
Last active March 25, 2023 19:20
Show Gist options
  • Save VaslD/05a3416b46c238c7f86d7624b43fe325 to your computer and use it in GitHub Desktop.
Save VaslD/05a3416b46c238c7f86d7624b43fe325 to your computer and use it in GitHub Desktop.
East-to-use AES-CBC in Swift, UnsafePointer & Data supported.
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