Fix/Update #1
| @ -3,93 +3,144 @@ import UIKit | |||||||
| 
 | 
 | ||||||
| @available(iOS 13.0, *) | @available(iOS 13.0, *) | ||||||
| public class SwiftNativeCryptoIosPlugin: NSObject, FlutterPlugin { | public class SwiftNativeCryptoIosPlugin: NSObject, FlutterPlugin { | ||||||
|  |     static let name: String = "plugins.hugop.cl/native_crypto" | ||||||
|  |      | ||||||
|     public static func register(with registrar: FlutterPluginRegistrar) { |     public static func register(with registrar: FlutterPluginRegistrar) { | ||||||
|     let channel = FlutterMethodChannel(name: "plugins.hugop.cl/native_crypto", binaryMessenger: registrar.messenger()) |         let channel = FlutterMethodChannel(name: name, binaryMessenger: registrar.messenger()) | ||||||
|         let instance = SwiftNativeCryptoIosPlugin() |         let instance = SwiftNativeCryptoIosPlugin() | ||||||
|         registrar.addMethodCallDelegate(instance, channel: channel) |         registrar.addMethodCallDelegate(instance, channel: channel) | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |     public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { | ||||||
|         switch call.method { |         switch call.method { | ||||||
|     case "digest": |         case "digest": _call(task: handleDigest(call: call), result: result) | ||||||
|  |         case "generateSecretKey": _call(task: handleGenerateSecretKey(call: call), result: result) | ||||||
|  |         case "pbkdf2": _call(task: handlePbkdf2(call: call), result: result) | ||||||
|  |         case "encryptAsList": _call(task: handleEncryptAsList(call: call), result: result) | ||||||
|  |         case "decryptAsList": _call(task: handleDecryptAsList(call: call), result: result) | ||||||
|  |         case "encrypt": _call(task: handleCrypt(call: call, forEncryption: true), result: result) | ||||||
|  |         case "decrypt": _call(task: handleCrypt(call: call, forEncryption: false), result: result) | ||||||
|  |         default: result(FlutterMethodNotImplemented) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     private func _call<T>(task: Task<T>, result: @escaping FlutterResult) { | ||||||
|  |         task.call() | ||||||
|  |         task.finalize(callback: {(task: Task<T>) in | ||||||
|  |             if (task.isSuccessful()) { | ||||||
|  |                 result(task.getResult()!) | ||||||
|  |             } else { | ||||||
|  |                 let exception: Error = task.getException() | ||||||
|  |                 let message = exception.localizedDescription | ||||||
|  |                 result(FlutterError(code: "native_crypto", message: message, details: nil)) | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     private func handleDigest(call: FlutterMethodCall) -> Task<FlutterStandardTypedData> { | ||||||
|  |         return Task(task: { | ||||||
|             let args : NSDictionary = call.arguments as! NSDictionary |             let args : NSDictionary = call.arguments as! NSDictionary | ||||||
|              |              | ||||||
|             let data : Data = (args["data"] as! FlutterStandardTypedData).data |             let data : Data = (args["data"] as! FlutterStandardTypedData).data | ||||||
|         let algo : String = args["algorithm"] as! String |             let algorithm : String = args["algorithm"] as! String | ||||||
|         let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: algo) |              | ||||||
|         // TODO(hpcl): check if algorithm is null |             return FlutterStandardTypedData.init(bytes: try HashAlgorithm.digest(data: data, algorithm: algorithm)) | ||||||
|         // TODO(hpcl): check if digest is null |         }) | ||||||
|         result(FlutterStandardTypedData.init(bytes: Hash.digest(data: data, algorithm: algorithm!))) |     } | ||||||
|     case "generateSecretKey": |      | ||||||
|  |     private func handleGenerateSecretKey(call: FlutterMethodCall) -> Task<FlutterStandardTypedData> { | ||||||
|  |         return Task(task: { | ||||||
|             let args : NSDictionary = call.arguments as! NSDictionary |             let args : NSDictionary = call.arguments as! NSDictionary | ||||||
|              |              | ||||||
|             let bitsCount : NSNumber = args["bitsCount"] as! NSNumber |             let bitsCount : NSNumber = args["bitsCount"] as! NSNumber | ||||||
|         // TODO(hpcl): check if secure random is null |              | ||||||
|         result(FlutterStandardTypedData.init(bytes: Key.fromSecureRandom(bitsCount: bitsCount.intValue))) |             return FlutterStandardTypedData.init(bytes: SecretKey(fromSecureRandom: bitsCount.intValue).bytes) | ||||||
|     case "generateKeyPair": |         }) | ||||||
|         result(FlutterStandardTypedData.init(bytes: KeyPair.fromCurve())) |     } | ||||||
|     case "pbkdf2": |      | ||||||
|  |     private func handlePbkdf2(call: FlutterMethodCall) -> Task<FlutterStandardTypedData> { | ||||||
|  |         return Task(task: { | ||||||
|             let args : NSDictionary = call.arguments as! NSDictionary |             let args : NSDictionary = call.arguments as! NSDictionary | ||||||
|              |              | ||||||
|             let password : String = args["password"] as! String |             let password : String = args["password"] as! String | ||||||
|             let salt : String = args["salt"] as! String |             let salt : String = args["salt"] as! String | ||||||
|             let keyBytesCount : NSNumber = args["keyBytesCount"] as! NSNumber |             let keyBytesCount : NSNumber = args["keyBytesCount"] as! NSNumber | ||||||
|             let iterations : NSNumber = args["iterations"] as! NSNumber |             let iterations : NSNumber = args["iterations"] as! NSNumber | ||||||
|         let algo : String = args["algorithm"] as! String |             let algorithm : String = args["algorithm"] as! String | ||||||
|         let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: algo) |              | ||||||
|         // TODO(hpcl): check if algorithm is null |             let pbkdf2 : Pbkdf2 = Pbkdf2(keyBytesCount: keyBytesCount.intValue, iterations: iterations.intValue) | ||||||
|         // TODO(hpcl): check if derivation is null |             pbkdf2.hash = HashAlgorithm.init(rawValue: algorithm) ?? pbkdf2.hash | ||||||
|         result(FlutterStandardTypedData.init(bytes: Key.fromPBKDF2(password: password, salt: salt, keyBytesCount: keyBytesCount.intValue, iterations: iterations.intValue, algorithm: algorithm!)!)) |             pbkdf2.initialize(password: password, salt: salt) | ||||||
|     case "encrypt": |              | ||||||
|  |             return FlutterStandardTypedData.init(bytes: try pbkdf2.derive().bytes) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     private func handleEncryptAsList(call: FlutterMethodCall) -> Task<Array<Data>> { | ||||||
|  |         return Task(task: { | ||||||
|             let args : NSDictionary = call.arguments as! NSDictionary |             let args : NSDictionary = call.arguments as! NSDictionary | ||||||
|              |              | ||||||
|             let data : Data = (args["data"] as! FlutterStandardTypedData).data |             let data : Data = (args["data"] as! FlutterStandardTypedData).data | ||||||
|             let key : Data = (args["key"] as! FlutterStandardTypedData).data |             let key : Data = (args["key"] as! FlutterStandardTypedData).data | ||||||
|         let algo : String = args["algorithm"] as! String |             let algorithm : String = args["algorithm"] as! String | ||||||
|         // TODO(hpcl): check if algorithm is null |              | ||||||
|         // TODO(hpcl): check if ciphertext is null |             let cipherAlgorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algorithm) | ||||||
|         var ciphertext : Data |             var cipher : Cipher | ||||||
|         switch algo { |             if (cipherAlgorithm != nil) { | ||||||
|         case "aes": |                 cipher = cipherAlgorithm!.getCipher | ||||||
|             ciphertext = AESCipher.encrypt(plaintext: data, key: key)! |             } else { | ||||||
|         case "chachapoly": |                 throw NativeCryptoError.cipherError | ||||||
|             ciphertext = CHACHACipher.encrypt(plaintext: data, key: key)! |  | ||||||
|         default: |  | ||||||
|             ciphertext = Data.init(); |  | ||||||
|             } |             } | ||||||
|         result(FlutterStandardTypedData.init(bytes: ciphertext)) |              | ||||||
|     case "decrypt": |             return try cipher.encryptAsList(data: data, key: key) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     private func handleDecryptAsList(call: FlutterMethodCall) -> Task<FlutterStandardTypedData> { | ||||||
|  |         return Task(task: { | ||||||
|  |             let args : NSDictionary = call.arguments as! NSDictionary | ||||||
|  |              | ||||||
|  |             let data = args["data"] as! NSArray | ||||||
|  |             let key : Data = (args["key"] as! FlutterStandardTypedData).data | ||||||
|  |             let algorithm : String = args["algorithm"] as! String | ||||||
|  |              | ||||||
|  |             let iv = (data[0] as! FlutterStandardTypedData).data | ||||||
|  |             let encrypted = (data[1] as! FlutterStandardTypedData).data | ||||||
|  |              | ||||||
|  |             let cipherAlgorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algorithm) | ||||||
|  |             var cipher : Cipher | ||||||
|  |             if (cipherAlgorithm != nil) { | ||||||
|  |                 cipher = cipherAlgorithm!.getCipher | ||||||
|  |             } else { | ||||||
|  |                 throw NativeCryptoError.cipherError | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             return FlutterStandardTypedData.init(bytes: try cipher.decryptAsList(data: [iv, encrypted], key: key)) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     private func handleCrypt(call: FlutterMethodCall, forEncryption: Bool) -> Task<FlutterStandardTypedData> { | ||||||
|  |         return Task(task: { | ||||||
|             let args : NSDictionary = call.arguments as! NSDictionary |             let args : NSDictionary = call.arguments as! NSDictionary | ||||||
|              |              | ||||||
|             let data : Data = (args["data"] as! FlutterStandardTypedData).data |             let data : Data = (args["data"] as! FlutterStandardTypedData).data | ||||||
|             let key : Data = (args["key"] as! FlutterStandardTypedData).data |             let key : Data = (args["key"] as! FlutterStandardTypedData).data | ||||||
|         let algo : String = args["algorithm"] as! String |             let algorithm : String = args["algorithm"] as! String | ||||||
|         // TODO(hpcl): check if algorithm is null |  | ||||||
|         // TODO(hpcl): check if ciphertext is null |  | ||||||
|         var ciphertext : Data |  | ||||||
|         switch algo { |  | ||||||
|         case "aes": |  | ||||||
|             ciphertext = AESCipher.decrypt(ciphertext: data, key: key)! |  | ||||||
|         case "chachapoly": |  | ||||||
|             ciphertext = CHACHACipher.decrypt(ciphertext: data, key: key)! |  | ||||||
|         default: |  | ||||||
|             ciphertext = Data.init(); |  | ||||||
|         } |  | ||||||
|         result(FlutterStandardTypedData.init(bytes: ciphertext)) |  | ||||||
|     case "generateSharedSecretKey": |  | ||||||
|         let args : NSDictionary = call.arguments as! NSDictionary |  | ||||||
|              |              | ||||||
|         let salt : Data = (args["salt"] as! FlutterStandardTypedData).data |             let cipherAlgorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algorithm) | ||||||
|         let keyBytesCount : NSNumber = args["keyBytesCount"] as! NSNumber |             var cipher : Cipher | ||||||
|         let ephemeralPrivateKey : Data = (args["ephemeralPrivateKey"] as! FlutterStandardTypedData).data |             if (cipherAlgorithm != nil) { | ||||||
|         let otherPublicKey : Data = (args["otherPublicKey"] as! FlutterStandardTypedData).data |                 cipher = cipherAlgorithm!.getCipher | ||||||
|         let hkdfAlgorithm : String = args["hkdfAlgorithm"] as! String |             } else { | ||||||
|         let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: hkdfAlgorithm) |                 throw NativeCryptoError.cipherError | ||||||
|         // TODO(hpcl): check if algorithm is null |             } | ||||||
|         // TODO(hpcl): check if generated key is null |  | ||||||
|         result(FlutterStandardTypedData.init(bytes: ECDH.generateSharedSecretKey(salt: salt, hash: algorithm!, keyBytesCount: keyBytesCount.intValue, privateKey: ephemeralPrivateKey, publicKey: otherPublicKey)!)) |  | ||||||
|              |              | ||||||
|     default: result(FlutterMethodNotImplemented) |             if (forEncryption) { | ||||||
|  |                 return FlutterStandardTypedData.init(bytes: try cipher.encrypt(data: data, key: key)) | ||||||
|  |             } else { | ||||||
|  |                 return FlutterStandardTypedData.init(bytes: try cipher.decrypt(data: data, key: key)) | ||||||
|             } |             } | ||||||
|  |         }) | ||||||
|     } |     } | ||||||
|  |      | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,28 +1,57 @@ | |||||||
| // | // | ||||||
| //  AES.swift | //  AESCipher.swift | ||||||
| //  native_crypto_ios | //  native_crypto_ios | ||||||
| // | // | ||||||
| //  Created by Hugo Pointcheval on 25/05/2022. | //  Created by Hugo Pointcheval on 25/05/2022. | ||||||
| // | // | ||||||
| 
 | 
 | ||||||
| import Foundation | import Foundation | ||||||
|  | import CryptoKit | ||||||
|  | 
 | ||||||
|  | class AESCipher : Cipher { | ||||||
|  |     var algorithm: CipherAlgorithm = CipherAlgorithm.aes | ||||||
|      |      | ||||||
| class AES : Cipher { |  | ||||||
|     /// Encrypts plaintext with key using AES GCM |     /// Encrypts plaintext with key using AES GCM | ||||||
|     @available(iOS 13.0, *) |     @available(iOS 13.0, *) | ||||||
|     static func encrypt(plaintext: Data, key: Data) -> Data? { |     func encrypt(data: Data, key: Data) throws -> Data { | ||||||
|         let symmetricKey = SymmetricKey.init(data: key) |         let symmetricKey : SymmetricKey = SymmetricKey.init(data: key) | ||||||
|         let encrypted = try? AES.GCM.seal(plaintext, using: symmetricKey) |         let encrypted : AES.GCM.SealedBox? = try? AES.GCM.seal(data, using: symmetricKey) | ||||||
|         return encrypted?.combined |          | ||||||
|  |         let encryptedData : Data? = encrypted?.combined | ||||||
|  |         if (encryptedData == nil) { | ||||||
|  |             throw NativeCryptoError.encryptionError | ||||||
|  |         } | ||||||
|  |         return encryptedData! | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     /// Decrypts ciphertext with key using AES GCM |     /// Decrypts ciphertext with key using AES GCM | ||||||
|     @available(iOS 13.0, *) |     @available(iOS 13.0, *) | ||||||
|     static func decrypt(ciphertext: Data, key: Data) -> Data? { |     func decrypt(data: Data, key: Data) throws -> Data { | ||||||
|         let symmetricKey = SymmetricKey.init(data: key) |         let symmetricKey = SymmetricKey.init(data: key) | ||||||
|         let sealedBox = try? AES.GCM.SealedBox(combined: ciphertext) |         let sealedBox = try? AES.GCM.SealedBox(combined: data) | ||||||
|         if (sealedBox == nil) { return nil } |         if (sealedBox == nil) { return Data.init() } | ||||||
|         let decryptedData = try? AES.GCM.open(sealedBox!, using: symmetricKey) |         let decryptedData = try? AES.GCM.open(sealedBox!, using: symmetricKey) | ||||||
|  |         if (decryptedData == nil) { | ||||||
|  |             throw NativeCryptoError.decryptionError | ||||||
|  |         } | ||||||
|  |         return decryptedData! | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     func encryptAsList(data: Data, key: Data) throws -> [Data] { | ||||||
|  |         let encryptedData = try encrypt(data: data, key: key) | ||||||
|  |          | ||||||
|  |         let iv = encryptedData.prefix(12) | ||||||
|  |         let data = encryptedData.suffix(from: 12) | ||||||
|  |          | ||||||
|  |         return [iv, data] | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     func decryptAsList(data: [Data], key: Data) throws -> Data { | ||||||
|  |         var encryptedData = data.first! | ||||||
|  |         let data = data.last! | ||||||
|  |         encryptedData.append(data) | ||||||
|  |          | ||||||
|  |         let decryptedData = try decrypt(data: encryptedData, key: key) | ||||||
|         return decryptedData |         return decryptedData | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,3 +6,58 @@ | |||||||
| // | // | ||||||
| 
 | 
 | ||||||
| import Foundation | import Foundation | ||||||
|  | import CommonCrypto | ||||||
|  | 
 | ||||||
|  | class Pbkdf2 : KeyDerivation { | ||||||
|  |     var algorithm: KdfAlgorithm = KdfAlgorithm.pbkdf2 | ||||||
|  |      | ||||||
|  |     var keyBytesCount: Int | ||||||
|  |     var iterations: Int | ||||||
|  |     var hash: HashAlgorithm = HashAlgorithm.HashSHA256 | ||||||
|  |      | ||||||
|  |     var password: String? = nil | ||||||
|  |     var salt: String? = nil | ||||||
|  |      | ||||||
|  |     init(keyBytesCount: Int, iterations: Int) { | ||||||
|  |         self.keyBytesCount = keyBytesCount | ||||||
|  |         self.iterations = iterations | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     func initialize(password: String, salt: String) { | ||||||
|  |         self.password = password | ||||||
|  |         self.salt = salt | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     func derive() throws -> SecretKey { | ||||||
|  |         if (password == nil || salt == nil) { | ||||||
|  |             throw NativeCryptoError.pbkdf2Error | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         let passwordData = password!.data(using: .utf8)! | ||||||
|  |         let saltData = salt!.data(using: .utf8)! | ||||||
|  |          | ||||||
|  |         var derivedKeyData = Data(repeating: 0, count: keyBytesCount) | ||||||
|  |         let localDerivedKeyData = derivedKeyData | ||||||
|  |          | ||||||
|  |         let status = derivedKeyData.withUnsafeMutableBytes { (derivedKeyBytes: UnsafeMutableRawBufferPointer) in | ||||||
|  |             saltData.withUnsafeBytes { (saltBytes: UnsafeRawBufferPointer) in | ||||||
|  |                 CCKeyDerivationPBKDF( | ||||||
|  |                     CCPBKDFAlgorithm(kCCPBKDF2), | ||||||
|  |                     password, | ||||||
|  |                     passwordData.count, | ||||||
|  |                     saltBytes.bindMemory(to: UInt8.self).baseAddress, | ||||||
|  |                     saltData.count, | ||||||
|  |                     hash.pbkdf2identifier, | ||||||
|  |                     UInt32(iterations), | ||||||
|  |                     derivedKeyBytes.bindMemory(to: UInt8.self).baseAddress, | ||||||
|  |                     localDerivedKeyData.count) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (status != kCCSuccess) { | ||||||
|  |             throw NativeCryptoError.pbkdf2Error | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         return SecretKey(derivedKeyData) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -6,3 +6,20 @@ | |||||||
| // | // | ||||||
| 
 | 
 | ||||||
| import Foundation | import Foundation | ||||||
|  | import CryptoKit | ||||||
|  | 
 | ||||||
|  | class SecretKey : Key { | ||||||
|  |     var bytes: Data | ||||||
|  |      | ||||||
|  |     init(_ bytes: Data) { | ||||||
|  |         self.bytes = bytes | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     init(fromSecureRandom bitsCount: Int) { | ||||||
|  |         let symmetricKey = SymmetricKey.init(size: SymmetricKeySize(bitCount: bitsCount)) | ||||||
|  |         bytes = symmetricKey.withUnsafeBytes | ||||||
|  |         { | ||||||
|  |             return Data(Array($0)) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -6,3 +6,11 @@ | |||||||
| // | // | ||||||
| 
 | 
 | ||||||
| import Foundation | import Foundation | ||||||
|  | 
 | ||||||
|  | protocol Cipher { | ||||||
|  |     var algorithm: CipherAlgorithm { get } | ||||||
|  |     func encrypt(data: Data, key: Data) throws -> Data | ||||||
|  |     func decrypt(data: Data, key: Data) throws -> Data | ||||||
|  |     func encryptAsList(data: Data, key: Data) throws -> [Data] | ||||||
|  |     func decryptAsList(data: [Data], key: Data) throws-> Data | ||||||
|  | } | ||||||
|  | |||||||
| @ -6,3 +6,7 @@ | |||||||
| // | // | ||||||
| 
 | 
 | ||||||
| import Foundation | import Foundation | ||||||
|  | 
 | ||||||
|  | protocol Key { | ||||||
|  |     var bytes: Data { get set } | ||||||
|  | } | ||||||
|  | |||||||
| @ -6,3 +6,8 @@ | |||||||
| // | // | ||||||
| 
 | 
 | ||||||
| import Foundation | import Foundation | ||||||
|  | 
 | ||||||
|  | protocol KeyDerivation { | ||||||
|  |     var algorithm : KdfAlgorithm { get } | ||||||
|  |     func derive() throws -> SecretKey | ||||||
|  | } | ||||||
|  | |||||||
| @ -6,3 +6,13 @@ | |||||||
| // | // | ||||||
| 
 | 
 | ||||||
| import Foundation | import Foundation | ||||||
|  | 
 | ||||||
|  | enum CipherAlgorithm : String { | ||||||
|  |     case aes = "aes" | ||||||
|  |      | ||||||
|  |     var getCipher: Cipher { | ||||||
|  |         switch self { | ||||||
|  |         case .aes: return AESCipher() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -6,3 +6,40 @@ | |||||||
| // | // | ||||||
| 
 | 
 | ||||||
| import Foundation | import Foundation | ||||||
|  | import CommonCrypto | ||||||
|  | import CryptoKit | ||||||
|  | 
 | ||||||
|  | enum HashAlgorithm: String { | ||||||
|  |     case HashSHA256 = "sha256" | ||||||
|  |     case HashSHA384 = "sha384" | ||||||
|  |     case HashSHA512 = "sha512" | ||||||
|  |      | ||||||
|  |     var pbkdf2identifier: UInt32 { | ||||||
|  |         switch self { | ||||||
|  |         case .HashSHA256: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256) | ||||||
|  |         case .HashSHA384: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA384) | ||||||
|  |         case .HashSHA512: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     @available(iOS 13.0, *) | ||||||
|  |     func digest(data: Data) -> Data { | ||||||
|  |         switch self { | ||||||
|  |         case .HashSHA256: | ||||||
|  |             return Data(SHA256.hash(data: data)) | ||||||
|  |         case .HashSHA384: | ||||||
|  |             return Data(SHA384.hash(data: data)) | ||||||
|  |         case .HashSHA512: | ||||||
|  |             return Data(SHA512.hash(data: data)) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     @available(iOS 13.0, *) | ||||||
|  |     static func digest(data: Data, algorithm: String) throws -> Data { | ||||||
|  |         let algo = HashAlgorithm.init(rawValue: algorithm) | ||||||
|  |         if (algo == nil) { | ||||||
|  |             throw NativeCryptoError.messageDigestError | ||||||
|  |         } | ||||||
|  |         return algo!.digest(data: data) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -6,3 +6,7 @@ | |||||||
| // | // | ||||||
| 
 | 
 | ||||||
| import Foundation | import Foundation | ||||||
|  | 
 | ||||||
|  | enum KdfAlgorithm { | ||||||
|  |     case pbkdf2 | ||||||
|  | } | ||||||
|  | |||||||
| @ -6,3 +6,13 @@ | |||||||
| // | // | ||||||
| 
 | 
 | ||||||
| import Foundation | import Foundation | ||||||
|  | 
 | ||||||
|  | enum NativeCryptoError : Error { | ||||||
|  | case decryptionError | ||||||
|  | case encryptionError | ||||||
|  | case messageDigestError | ||||||
|  | case pbkdf2Error | ||||||
|  | case cipherError | ||||||
|  | case resultError | ||||||
|  | case exceptionError | ||||||
|  | } | ||||||
|  | |||||||
| @ -6,3 +6,47 @@ | |||||||
| // | // | ||||||
| 
 | 
 | ||||||
| import Foundation | import Foundation | ||||||
|  | 
 | ||||||
|  | class Task<T> { | ||||||
|  |      | ||||||
|  |     var task: () throws -> T | ||||||
|  |     private var successful: Bool = false | ||||||
|  |     private var result: T? = nil | ||||||
|  |     private var exception: Error? = nil | ||||||
|  |      | ||||||
|  |     init(task: @escaping () throws -> T) { | ||||||
|  |         self.task = task | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     func isSuccessful() -> Bool { | ||||||
|  |         return successful | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     func getResult() -> T? { | ||||||
|  |         return result | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     func getException() -> Error { | ||||||
|  |         if (exception != nil) { | ||||||
|  |             return exception! | ||||||
|  |         } else { | ||||||
|  |             return NativeCryptoError.exceptionError | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     func call() { | ||||||
|  |         do { | ||||||
|  |             result = try task() | ||||||
|  |             exception = nil | ||||||
|  |             successful = true | ||||||
|  |         } catch { | ||||||
|  |             exception = error | ||||||
|  |             result = nil | ||||||
|  |             successful = false | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     func finalize(callback: (_ task: Task<T>) -> Void) { | ||||||
|  |         callback(self) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user