import UIKit import CommonCrypto import Foundation extension StringProtocol { var hexaData: Data { .init(hexa) } var hexaBytes: [UInt8] { .init(hexa) } private var hexa: UnfoldSequence<UInt8, Index> { sequence(state: startIndex) { startIndex in guard startIndex < self.endIndex else { return nil } let endIndex = self.index(startIndex, offsetBy: 2, limitedBy: self.endIndex) ?? self.endIndex defer { startIndex = endIndex } return UInt8(self[startIndex..<endIndex], radix: 16) } } } 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 } } extension Data { var hexString: String { return map { String(format: "%02hhx", $0) }.joined() } } do { let digest = "PS631192".data(using: .utf8)! let ivString = "687b9509c25a14b8ad076346d8353d67" let string = "687b9509c25a14b8ad076346d8353d67" let data = string.hexaData // 16 bytes let bytes = string.hexaBytes // [224, 105, 99, 73, 119, 70, 6, 241, 181, 96, 47, 250, 108, 45, 149, 63] let statKey = Data("d95acd54c6a821ff32c52825b931c194".utf8) let aes = try AES256(key: statKey, iv: data) let encrypted = try aes.encrypt(digest) let decrypted = try aes.decrypt(encrypted) print("Encrypted: \(encrypted.hexString)") print("Decrypted: \(decrypted.hexString)") print("KeyRequired: userID -> OjyniekMW7VnBJ8ylR9F4w%3D%3D") } catch { print("Failed") print(error) }