From f402e8bdf90df5f67d48b306a806a50f0f3bd4ca Mon Sep 17 00:00:00 2001 From: Pointcheval Hugo Date: Sat, 19 Dec 2020 21:45:56 +0100 Subject: [PATCH] Clean swift plugin main file --- ios/Classes/SwiftNativeCryptoPlugin.swift | 292 +++++----------------- 1 file changed, 65 insertions(+), 227 deletions(-) diff --git a/ios/Classes/SwiftNativeCryptoPlugin.swift b/ios/Classes/SwiftNativeCryptoPlugin.swift index 9b9a6be..63889d8 100644 --- a/ios/Classes/SwiftNativeCryptoPlugin.swift +++ b/ios/Classes/SwiftNativeCryptoPlugin.swift @@ -5,9 +5,6 @@ // Author: Hugo Pointcheval // import Flutter -import UIKit -import CommonCrypto -import Security extension FlutterStandardTypedData { var uint8Array: Array { @@ -20,276 +17,117 @@ extension FlutterStandardTypedData { } } -@available(iOS 10.0, *) -func generateKeypair() { - var publicKeySec, privateKeySec: SecKey? - let keyattribute = [ - kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, - kSecAttrKeySizeInBits as String : 256 - ] as CFDictionary - SecKeyGeneratePair(keyattribute, &publicKeySec, &privateKeySec) - -} - -func crypt(operation: Int, algorithm: Int, options: Int, key: Data, - initializationVector: Data, dataIn: Data) -> Data? { - return key.withUnsafeBytes { keyUnsafeRawBufferPointer in - return dataIn.withUnsafeBytes { dataInUnsafeRawBufferPointer in - return initializationVector.withUnsafeBytes { ivUnsafeRawBufferPointer in - // Give the data out some breathing room for PKCS7's padding. - 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) - } - } - } -} - -func pbkdf2(hash: CCPBKDFAlgorithm, password: String, salt: String, keyByteCount: Int, rounds: Int) -> Data? { - let passwordData = password.data(using: .utf8)! - let saltData = salt.data(using: .utf8)! - var derivedKeyData = Data(repeating: 0, count: keyByteCount) - - var localDerivedKeyData = derivedKeyData - - let derivationStatus = derivedKeyData.withUnsafeMutableBytes { derivedKeyBytes in - saltData.withUnsafeBytes { saltBytes in - - CCKeyDerivationPBKDF( - CCPBKDFAlgorithm(kCCPBKDF2), - password, passwordData.count, - saltBytes, saltData.count, - hash, - UInt32(rounds), - derivedKeyBytes, localDerivedKeyData.count) - } - } - if (derivationStatus != kCCSuccess) { - print("Error: \(derivationStatus)") - return nil; - } - - return derivedKeyData -} - -func pbkdf2sha512(password: String, salt: String, keyByteCount: Int, rounds: Int) -> Data? { - return pbkdf2(hash: CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512), password: password, salt: salt, keyByteCount: keyByteCount, rounds: rounds) -} - -func pbkdf2sha256(password: String, salt: String, keyByteCount: Int, rounds: Int) -> Data? { - return pbkdf2(hash: CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256), password: password, salt: salt, keyByteCount: keyByteCount, rounds: rounds) -} - -func pbkdf2sha1(password: String, salt: String, keyByteCount: Int, rounds: Int) -> Data? { - return pbkdf2(hash: CCPBKDFAlgorithm(kCCPRFHmacAlgSHA1), password: password, salt: salt, keyByteCount: keyByteCount, rounds: rounds) -} - - -func randomGenerateBytes(count: Int) -> Data? { - let bytes = UnsafeMutableRawPointer.allocate(byteCount: count, alignment: 1) - defer { bytes.deallocate() } - let status = CCRandomGenerateBytes(bytes, count) - guard status == kCCSuccess else { return nil } - return Data(bytes: bytes, count: count) -} - -extension Data { - /// Encrypts for you with all the good options turned on: CBC, an IV, PKCS7 - /// padding (so your input data doesn't have to be any particular length). - /// Key can be 128, 192, or 256 bits. - /// Generates a fresh IV for you each time, and prefixes it to the - /// returned ciphertext. - func encryptAES256_CBC_PKCS7_IV(key: Data) -> Data? { - guard let iv = randomGenerateBytes(count: kCCBlockSizeAES128) else { return nil } - // No option is needed for CBC, it is on by default. - guard let ciphertext = crypt(operation: kCCEncrypt, - algorithm: kCCAlgorithmAES, - options: kCCOptionPKCS7Padding, - key: key, - initializationVector: iv, - dataIn: self) else { return nil } - return iv + ciphertext - } - - /// Decrypts self, where self is the IV then the ciphertext. - /// Key can be 128/192/256 bits. - func decryptAES256_CBC_PKCS7_IV(key: Data) -> Data? { - guard count > kCCBlockSizeAES128 else { return nil } - let iv = prefix(kCCBlockSizeAES128) - let ciphertext = suffix(from: kCCBlockSizeAES128) - return crypt(operation: kCCDecrypt, algorithm: kCCAlgorithmAES, - options: kCCOptionPKCS7Padding, key: key, initializationVector: iv, - dataIn: ciphertext) - } - - enum Algorithm { - case sha256 - - var digestLength: Int { - switch self { - case .sha256: return Int(CC_SHA256_DIGEST_LENGTH) - } - } - } - - func hash(for algorithm: Algorithm) -> Data { - let hashBytes = UnsafeMutablePointer.allocate(capacity: algorithm.digestLength) - defer { hashBytes.deallocate() } - switch algorithm { - case .sha256: - withUnsafeBytes { (buffer) -> Void in - CC_SHA256(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes) - } - } - - return Data(bytes: hashBytes, count: algorithm.digestLength) - } - -} - public class SwiftNativeCryptoPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "native.crypto.helper", binaryMessenger: registrar.messenger()) + let channel = FlutterMethodChannel(name: "native.crypto", binaryMessenger: registrar.messenger()) let instance = SwiftNativeCryptoPlugin() registrar.addMethodCallDelegate(instance, channel: channel) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { + case "digest": + let args = call.arguments as! NSDictionary + + let message = (args["message"] as! FlutterStandardTypedData).data + let algo = args["algorithm"] as! String + + let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: algo) + + let hash = Hash().digest(data: message, algorithm: algorithm!) + + if hash != nil { + result(FlutterStandardTypedData.init(bytes: hash!)) + } else { + result(FlutterError(code: "DIGESTERROR", + message: "DIGEST IS NIL.", + details: nil) + ) + } case "pbkdf2": let args = call.arguments as! NSDictionary + let password = args["password"] as! String let salt = args["salt"] as! String let keyLength = args["keyLength"] as! NSNumber let iteration = args["iteration"] as! NSNumber let algo = args["algorithm"] as! String - var keyBytes: Data? + let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: algo) - if (algo == "sha1") { - keyBytes = pbkdf2sha1(password: password, salt: salt, keyByteCount: keyLength.intValue, rounds: iteration.intValue) - } else if (algo == "sha256"){ - keyBytes = pbkdf2sha256(password: password, salt: salt, keyByteCount: keyLength.intValue, rounds: iteration.intValue) - } else if (algo == "sha512"){ - keyBytes = pbkdf2sha512(password: password, salt: salt, keyByteCount: keyLength.intValue, rounds: iteration.intValue) - } + let key = KeyDerivation().pbkdf2(password: password, salt: salt, keyLength: keyLength.intValue, iteration: iteration.intValue, algorithm: algorithm!) - if keyBytes != nil { - result(FlutterStandardTypedData.init(bytes: keyBytes!)) + if key != nil { + result(FlutterStandardTypedData.init(bytes: key!)) } else { result(FlutterError(code: "PBKDF2ERROR", message: "PBKDF2 KEY IS NIL.", - details: nil)) + details: nil) + ) } - - - case "symKeygen": + case "keygen": let args = call.arguments as! NSDictionary - let keySize = args["size"] as! NSNumber - let keyBytes = symKeygen(keySize: keySize) + let size = args["size"] as! NSNumber - if keyBytes != nil { - result(FlutterStandardTypedData.init(bytes: keyBytes!)) + let key = KeyGeneration().keygen(size: size) + + if key != nil { + result(FlutterStandardTypedData.init(bytes: key!)) } else { - result(FlutterError(code: "SYMKEYGENERROR", + result(FlutterError(code: "KEYGENERROR", message: "GENERATED KEY IS NIL.", details: nil)) } - - case "symEncrypt": + case "encrypt": let args = call.arguments as! NSDictionary - let payload = (args["payload"] as! FlutterStandardTypedData).data - let aesKey = (args["aesKey"] as! FlutterStandardTypedData).data - let encryptedPayloadIV = symEncrypt(payload: payload, aesKey: aesKey) + let data = (args["data"] as! FlutterStandardTypedData).data + let key = (args["key"] as! FlutterStandardTypedData).data + let algo = args["algorithm"] as! String + let mode = args["mode"] as! String + let padding = args["padding"] as! String - result(encryptedPayloadIV) - - case "symDecrypt": + let algorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algo) + let modeEnum : BlockCipherMode? = BlockCipherMode.init(rawValue: mode) + let paddingEnum : Padding? = Padding.init(rawValue: padding) + + let ciphertext = Cipher().encrypt(data: data, key: key, algorithm: algorithm!, mode: modeEnum!, padding: paddingEnum!) + + if ciphertext != nil { + result(ciphertext) + } else { + result(FlutterError(code: "ENCRYPTIONERROR", + message: "ENCRYPTED PAYLOAD IS EMPTY.", + details: nil)) + } + case "decrypt": let args = call.arguments as! NSDictionary + let payload = args["payload"] as! NSArray + let key = (args["key"] as! FlutterStandardTypedData).data + let algo = args["algorithm"] as! String + let mode = args["mode"] as! String + let padding = args["padding"] as! String let encrypted = (payload[0] as! FlutterStandardTypedData).data let iv = (payload[1] as! FlutterStandardTypedData).data let encryptedPayload = [encrypted, iv] - let aesKey = (args["aesKey"] as! FlutterStandardTypedData).data + let algorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algo) + let modeEnum : BlockCipherMode? = BlockCipherMode.init(rawValue: mode) + let paddingEnum : Padding? = Padding.init(rawValue: padding) + + let decrypted = Cipher().decrypt(payload: encryptedPayload, key: key, algorithm: algorithm!, mode: modeEnum!, padding: paddingEnum!) - let decryptedPayload = symDecrypt(payload: encryptedPayload, aesKey: aesKey) - - if decryptedPayload != nil { - result(FlutterStandardTypedData.init(bytes: decryptedPayload!)) + if decrypted != nil { + result(FlutterStandardTypedData.init(bytes: decrypted!)) } else { result(FlutterError(code: "DECRYPTIONERROR", message: "DECRYPTED PAYLOAD IS NIL. MAYBE VERIFICATION MAC IS UNVALID.", details: nil)) } - default: result(FlutterMethodNotImplemented) - } } - - func digest(input : Data) -> Data { - let hashed = input.hash(for: .sha256) - return hashed - } - - func symKeygen(keySize : NSNumber) -> Data? { - var bytes = [Int8](repeating: 0, count: keySize.intValue / 8) - let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes) - - if status == errSecSuccess { // Always test the status. - let keyBytes = bytes.withUnsafeBytes {return Data(Array($0))} - return keyBytes - } - return nil - } - - func symEncrypt(payload : Data, aesKey : Data) -> [Data] { - let mac = digest(input: aesKey + payload) - let dataToEncrypt = mac + payload - var encrypted = dataToEncrypt.encryptAES256_CBC_PKCS7_IV(key: aesKey)! - - // Create a range based on the length of data to return - let range = 0..<16 - - // Get a new copy of data - let iv = encrypted.subdata(in: range) - - encrypted.removeSubrange(range) - - return [encrypted, iv] - } - - func symDecrypt(payload : [Data], aesKey : Data) -> Data? { - let encrypted = payload[1] + payload[0] - var decrypted = encrypted.decryptAES256_CBC_PKCS7_IV(key: aesKey)! - - // 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 verificationMac = digest(input: aesKey + decrypted) - - if (mac.base64EncodedData() == verificationMac.base64EncodedData()) { - return decrypted - } else { - return nil - } - } }