136 lines
4.5 KiB
Swift
136 lines
4.5 KiB
Swift
//
|
|
// Cipher.swift
|
|
//
|
|
// NativeCryptoPlugin
|
|
//
|
|
// Copyright (c) 2020
|
|
// Author: Hugo Pointcheval
|
|
//
|
|
|
|
import Foundation
|
|
import CommonCrypto
|
|
|
|
enum CipherAlgorithm: String {
|
|
case AES = "aes"
|
|
case BlowFish = "blowfish"
|
|
|
|
var instance: Int {
|
|
switch self {
|
|
case .AES: return kCCAlgorithmAES
|
|
case .BlowFish: return kCCAlgorithmBlowfish
|
|
}
|
|
}
|
|
}
|
|
|
|
enum BlockCipherMode: String {
|
|
case ECB = "ecb"
|
|
case CBC = "cbc"
|
|
|
|
var instance: Int {
|
|
switch self {
|
|
case .CBC: return 0
|
|
case .ECB: return kCCOptionECBMode
|
|
}
|
|
}
|
|
}
|
|
|
|
enum Padding: String {
|
|
case PKCS5 = "pkcs5"
|
|
case None = "none"
|
|
|
|
var instance: Int {
|
|
switch self {
|
|
case .PKCS5: return kCCOptionPKCS7Padding
|
|
case .None: return 0
|
|
}
|
|
}
|
|
}
|
|
|
|
class Cipher {
|
|
func encrypt(data : Data, key : Data, algorithm : CipherAlgorithm, mode : BlockCipherMode, padding : Padding) -> [Data]? {
|
|
// Calculate Mac
|
|
let mac = Hash().digest(data: key + data, algorithm: .SHA256)
|
|
let payload = mac! + data
|
|
|
|
// Generate IV
|
|
let ivBytes = UnsafeMutableRawPointer.allocate(byteCount: kCCBlockSizeAES128, alignment: 1)
|
|
defer { ivBytes.deallocate() }
|
|
let ivStatus = CCRandomGenerateBytes(ivBytes, kCCBlockSizeAES128)
|
|
if (ivStatus != kCCSuccess) {
|
|
return nil
|
|
}
|
|
let ivData = Data(bytes: ivBytes, count: kCCBlockSizeAES128)
|
|
|
|
let algo = algorithm.instance
|
|
let options: CCOptions = UInt32(mode.instance + padding.instance)
|
|
|
|
|
|
guard var ciphertext = crypt(operation: kCCEncrypt,
|
|
algorithm: algo,
|
|
options: options,
|
|
key: key,
|
|
initializationVector: ivData,
|
|
dataIn: payload) else { return nil }
|
|
|
|
return [ciphertext, ivData]
|
|
}
|
|
|
|
func decrypt(payload : [Data], key : Data, algorithm : CipherAlgorithm, mode : BlockCipherMode, padding : Padding) -> Data? {
|
|
let encrypted = payload[1] + payload[0]
|
|
|
|
guard encrypted.count > kCCBlockSizeAES128 else { return nil }
|
|
let iv = encrypted.prefix(kCCBlockSizeAES128)
|
|
let ciphertext = encrypted.suffix(from: kCCBlockSizeAES128)
|
|
|
|
let algo = algorithm.instance
|
|
let options: CCOptions = UInt32(mode.instance + padding.instance)
|
|
|
|
guard var decrypted = crypt(operation: kCCDecrypt,
|
|
algorithm: algo,
|
|
options: options,
|
|
key: key,
|
|
initializationVector: iv,
|
|
dataIn: ciphertext) else {return nil}
|
|
|
|
|
|
// Create a range based on the length of data to return
|
|
let range = 0..<32
|
|
|
|
// Get a new copy of data
|
|
let mac = decrypted.subdata(in: range)
|
|
decrypted.removeSubrange(range)
|
|
|
|
let vmac = Hash().digest(data: key + decrypted, algorithm: .SHA256)
|
|
|
|
if (mac.base64EncodedData() == vmac!.base64EncodedData()) {
|
|
return decrypted
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private func crypt(operation: Int, algorithm: Int, options: UInt32, key: Data,
|
|
initializationVector: Data, dataIn: Data) -> Data? {
|
|
|
|
return key.withUnsafeBytes { keyUnsafeRawBufferPointer in
|
|
return dataIn.withUnsafeBytes { dataInUnsafeRawBufferPointer in
|
|
return initializationVector.withUnsafeBytes { ivUnsafeRawBufferPointer in
|
|
let dataOutSize: Int = dataIn.count + kCCBlockSizeAES128*2
|
|
let dataOut = UnsafeMutableRawPointer.allocate(byteCount: dataOutSize,
|
|
alignment: 1)
|
|
defer { dataOut.deallocate() }
|
|
var dataOutMoved: Int = 0
|
|
let status = CCCrypt(CCOperation(operation), CCAlgorithm(algorithm),
|
|
CCOptions(options),
|
|
keyUnsafeRawBufferPointer.baseAddress, key.count,
|
|
ivUnsafeRawBufferPointer.baseAddress,
|
|
dataInUnsafeRawBufferPointer.baseAddress, dataIn.count,
|
|
dataOut, dataOutSize, &dataOutMoved)
|
|
guard status == kCCSuccess else { return nil }
|
|
return Data(bytes: dataOut, count: dataOutMoved)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|