diff --git a/native/expo-modules/comm-expo-package/ios/AESCryptoModule.swift b/native/expo-modules/comm-expo-package/ios/AESCryptoModule.swift --- a/native/expo-modules/comm-expo-package/ios/AESCryptoModule.swift +++ b/native/expo-modules/comm-expo-package/ios/AESCryptoModule.swift @@ -5,39 +5,114 @@ private let IV_LENGTH = 12 // bytes, IV - unique Initialization Vector (nonce) private let TAG_LENGTH = 16 // bytes - GCM auth tag +// Expo module called from the RN app JS code public class AESCryptoModule: Module { public func definition() -> ModuleDefinition { Name("AESCrypto") - Function("generateKey", generateKey) - Function("encrypt", encrypt) - Function("decrypt", decrypt) + Function("generateKey") { (destination: Uint8Array) throws in + try! generateKey(destinationPtr: destination.rawBufferPtr(), + byteLength: destination.byteLength) + } + + Function("encrypt") { (rawKey: Uint8Array, + plaintext: Uint8Array, + destination: Uint8Array) throws in + try! encrypt(rawKey: rawKey.data(), + plaintext: plaintext.data(), + plaintextLength: plaintext.byteLength, + destinationPtr: destination.rawBufferPtr(), + destinationLength: destination.byteLength) + } + + Function("decrypt") { (rawKey: Uint8Array, + sealedData: Uint8Array, + destination: Uint8Array) throws in + try! decrypt(rawKey: rawKey.data(), + sealedData: sealedData.data(), + sealedDataLength: sealedData.byteLength, + destinationPtr: destination.rawBufferPtr(), + destinationLength: destination.byteLength) + } + } +} + +// ObjC-compatible module, used from Objective-C code in NSE +@objc(AESCryptoModuleObjCCompat) +public class AESCryptoModuleObjCCompat: NSObject { + + @objc(generateKey:withError:) + public func generateKeyCompat(destination: NSMutableData) throws { + let destinationPtr = UnsafeMutableRawBufferPointer(start: destination.mutableBytes, + count: KEY_SIZE) + try! generateKey(destinationPtr: destinationPtr, + byteLength: destination.length) + } + + @objc(encryptedLength:) + public func encryptedLengthCompat(plaintext: Data) -> NSInteger { + return plaintext.count + IV_LENGTH + TAG_LENGTH + } + + @objc(encryptWithKey:plaintext:destination:withError:) + public func encryptCompat(rawKey: Data, + plaintext: Data, + destination: NSMutableData) throws { + let destinationPtr = UnsafeMutableRawBufferPointer(start: destination.mutableBytes, + count: destination.length) + try! encrypt(rawKey: rawKey, + plaintext: plaintext, + plaintextLength: plaintext.count, + destinationPtr: destinationPtr, + destinationLength: destination.length) } + + @objc(decryptedLength:) + public func decryptedLengthCompat(sealedData: Data) -> NSInteger { + return sealedData.count - IV_LENGTH - TAG_LENGTH + } + + @objc(decryptWithKey:sealedData:destination:withError:) + public func decryptCompat(rawKey: Data, + sealedData: Data, + destination: NSMutableData) throws { + let destinationPtr = UnsafeMutableRawBufferPointer(start: destination.mutableBytes, + count: destination.length) + try! decrypt(rawKey: rawKey, + sealedData: sealedData, + sealedDataLength: sealedData.count, + destinationPtr: destinationPtr, + destinationLength: destination.length) + } + } // MARK: - Function implementations -private func generateKey(destination: Uint8Array) throws { - guard destination.byteLength == KEY_SIZE else { +private func generateKey(destinationPtr: UnsafeMutableRawBufferPointer, + byteLength: Int) throws { + guard byteLength == KEY_SIZE else { throw InvalidKeyLengthException() } let key = SymmetricKey(size: .bits256) key.withUnsafeBytes { bytes in - let _ = bytes.copyBytes(to: destination.rawBufferPtr()) + let _ = bytes.copyBytes(to: destinationPtr) } } -private func encrypt(rawKey: Uint8Array, - plaintext: Uint8Array, - destination: Uint8Array) throws { - guard destination.byteLength == plaintext.byteLength + IV_LENGTH + TAG_LENGTH +private func encrypt(rawKey: Data, + plaintext: Data, + plaintextLength: Int, + destinationPtr: UnsafeMutableRawBufferPointer, + destinationLength: Int) throws { + guard destinationLength == plaintextLength + IV_LENGTH + TAG_LENGTH else { throw InvalidDataLengthException() } - let key = SymmetricKey(data: rawKey.data()) + let key = SymmetricKey(data: rawKey) let iv = AES.GCM.Nonce() - let encryptionResult = try AES.GCM.seal(plaintext.data(), + let encryptionResult = try AES.GCM.seal(plaintext, using: key, nonce: iv) @@ -46,24 +121,26 @@ // this happens only if Nonce/IV != 12 bytes long throw EncryptionFailedException("Incorrect AES configuration") } - guard sealedData.count == destination.byteLength else { + guard sealedData.count == destinationLength else { throw EncryptionFailedException("Encrypted data has unexpected length") } - sealedData.copyBytes(to: destination.rawBufferPtr()) + sealedData.copyBytes(to: destinationPtr) } -private func decrypt(rawKey: Uint8Array, - sealedData: Uint8Array, - destination: Uint8Array) throws { - guard destination.byteLength == sealedData.byteLength - IV_LENGTH - TAG_LENGTH - else { - throw InvalidDataLengthException() - } - - let key = SymmetricKey(data: rawKey.data()) - let sealedBox = try AES.GCM.SealedBox(combined: sealedData.data()) - let plaintext = try AES.GCM.open(sealedBox, using: key) - plaintext.copyBytes(to: destination.rawBufferPtr()) +private func decrypt(rawKey: Data, + sealedData: Data, + sealedDataLength: Int, + destinationPtr: UnsafeMutableRawBufferPointer, + destinationLength: Int) throws { + guard destinationLength == sealedDataLength - IV_LENGTH - TAG_LENGTH + else { + throw InvalidDataLengthException() + } + + let key = SymmetricKey(data: rawKey) + let sealedBox = try AES.GCM.SealedBox(combined: sealedData) + let plaintext = try AES.GCM.open(sealedBox, using: key) + plaintext.copyBytes(to: destinationPtr) } // MARK: - Exception definitions diff --git a/native/expo-modules/comm-expo-package/ios/CommExpoPackage.podspec b/native/expo-modules/comm-expo-package/ios/CommExpoPackage.podspec --- a/native/expo-modules/comm-expo-package/ios/CommExpoPackage.podspec +++ b/native/expo-modules/comm-expo-package/ios/CommExpoPackage.podspec @@ -19,9 +19,18 @@ # Swift/Objective-C compatibility s.pod_target_xcconfig = { + 'USE_HEADERMAP' => 'YES', 'DEFINES_MODULE' => 'YES', 'SWIFT_COMPILATION_MODE' => 'wholemodule' } + user_header_search_paths = [ + '"${PODS_CONFIGURATION_BUILD_DIR}/CommExpoPackage/Swift Compatibility Header"', + ] + s.user_target_xcconfig = { + "HEADER_SEARCH_PATHS" => user_header_search_paths, + } + s.source_files = "**/*.{h,m,swift}" + s.public_header_files = '**/*.h' end diff --git a/native/expo-modules/comm-expo-package/ios/CommExpoPackageObjCCompat.h b/native/expo-modules/comm-expo-package/ios/CommExpoPackageObjCCompat.h new file mode 100644 --- /dev/null +++ b/native/expo-modules/comm-expo-package/ios/CommExpoPackageObjCCompat.h @@ -0,0 +1,15 @@ +/* + XCode generated Swift compatibility header is available only + in the project that implements its header (it is private). + Therefore we create public header that includes private Swift + compatibility header so that other projects can use generated + Objective C interfaces. + + Macro `__has_include` is used since this header is generated + during library compilation so it is available only after the + library is compiled. This solution is inspired by: + https://github.com/expo/expo/blob/895fafd4aa32e64fa5b77e9e88d6d4f8c3638f9c/packages/expo-modules-core/ios/Swift.h#L10-L16 +*/ +#if __has_include("CommExpoPackage-Swift.h") +#import "CommExpoPackage-Swift.h" +#endif diff --git a/native/ios/Podfile b/native/ios/Podfile --- a/native/ios/Podfile +++ b/native/ios/Podfile @@ -54,6 +54,7 @@ common_comm_target_pods pod 'OLMKit', :path => "../node_modules/olm" pod 'RCT-Folly', :podspec => "../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" + pod 'CommExpoPackage', :path => "../expo-modules/comm-expo-package/ios" react_native_config end diff --git a/native/ios/Podfile.lock b/native/ios/Podfile.lock --- a/native/ios/Podfile.lock +++ b/native/ios/Podfile.lock @@ -838,7 +838,7 @@ SPEC CHECKSUMS: boost: a7c83b31436843459a1961bfd74b96033dc77234 - CommExpoPackage: 3f9d8a25537fb9d80f79a63fd1e73c51f55b87e9 + CommExpoPackage: 71183609c6fd4263fff6b24bde778f3fb2d035ef DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 DVAssetLoaderDelegate: 0caec20e4e08b8560b691131539e9180024d4bce EXApplication: d8f53a7eee90a870a75656280e8d4b85726ea903 @@ -936,6 +936,6 @@ SQLCipher-Amalgamation: cbd36045fe7b458b8a442958a01aefdbc44c20f8 Yoga: d6133108734e69e8c0becc6ba587294b94829687 -PODFILE CHECKSUM: 60ed9de6b14a66c6022cd82cafcb04594edd7eaf +PODFILE CHECKSUM: b1546433c0563b58e7a664c1e21b4fa8402fd644 COCOAPODS: 1.11.3