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/ios/Comm.xcodeproj/project.pbxproj b/native/ios/Comm.xcodeproj/project.pbxproj --- a/native/ios/Comm.xcodeproj/project.pbxproj +++ b/native/ios/Comm.xcodeproj/project.pbxproj @@ -264,6 +264,7 @@ CB90951929531663002F2A7F /* CommIOSNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommIOSNotifications.h; path = Comm/CommIOSNotifications/CommIOSNotifications.h; sourceTree = ""; }; CBCA09042A8E0E6B00F75B3E /* StaffUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StaffUtils.h; sourceTree = ""; }; CBCA09052A8E0E6B00F75B3E /* StaffUtils.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StaffUtils.cpp; sourceTree = ""; }; + CBCF57AB2B05096F00EC4BC0 /* AESCryptoModuleObjCCompat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AESCryptoModuleObjCCompat.h; path = Comm/CommAESCryptoUtils/AESCryptoModuleObjCCompat.h; sourceTree = ""; }; CBDEC69928ED859600C17588 /* GlobalDBSingleton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GlobalDBSingleton.h; sourceTree = ""; }; CBDEC69A28ED867000C17588 /* GlobalDBSingleton.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = GlobalDBSingleton.mm; path = Comm/GlobalDBSingleton.mm; sourceTree = ""; }; CBFE58272885852B003B94C9 /* ThreadOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadOperations.h; path = PersistentStorageUtilities/ThreadOperationsUtilities/ThreadOperations.h; sourceTree = ""; }; @@ -353,6 +354,7 @@ 71B8CCB626BD30EC0040C0A2 /* CommCoreImplementations */ = { isa = PBXGroup; children = ( + CBCF57A92B05091D00EC4BC0 /* CommAESCryptoUtils */, CB90951729531647002F2A7F /* CommIOSNotifications */, 8E43C32E291E5B9D009378F5 /* TerminateApp.h */, 8E43C32B291E5B4A009378F5 /* TerminateApp.mm */, @@ -682,6 +684,14 @@ name = CommIOSNotifications; sourceTree = ""; }; + CBCF57A92B05091D00EC4BC0 /* CommAESCryptoUtils */ = { + isa = PBXGroup; + children = ( + CBCF57AB2B05096F00EC4BC0 /* AESCryptoModuleObjCCompat.h */, + ); + name = CommAESCryptoUtils; + sourceTree = ""; + }; CBED0E2C284E086100CD3863 /* PersistentStorageUtilities */ = { isa = PBXGroup; children = ( diff --git a/native/ios/Comm/CommAESCryptoUtils/AESCryptoModuleObjCCompat.h b/native/ios/Comm/CommAESCryptoUtils/AESCryptoModuleObjCCompat.h new file mode 100644 --- /dev/null +++ b/native/ios/Comm/CommAESCryptoUtils/AESCryptoModuleObjCCompat.h @@ -0,0 +1,17 @@ +#import + +@interface AESCryptoModuleObjCCompat : NSObject +- (BOOL)generateKey:(NSMutableData *_Nonnull)destination + withError:(NSError *_Nullable *_Nullable)error; +- (NSInteger)encryptedLength:(NSData *_Nonnull)plaintext; +- (BOOL)encryptWithKey:(NSData *_Nonnull)rawKey + plaintext:(NSData *_Nonnull)plaintext + destination:(NSMutableData *_Nonnull)destination + withError:(NSError *_Nullable *_Nullable)error; +- (NSInteger)decryptedLength:(NSData *_Nonnull)sealedData; +- (BOOL)decryptWithKey:(NSData *_Nonnull)rawKey + sealedData:(NSData *_Nonnull)sealedData + destination:(NSMutableData *_Nonnull)destination + withError:(NSError *_Nullable *_Nullable)error; +- (nonnull instancetype)init; +@end 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 @@ -946,6 +946,6 @@ Yoga: dc109b79db907f0f589fc423e991b09ec42d2295 ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: e45238be1b6262e5cd4dbe34492a16945fc2ae43 +PODFILE CHECKSUM: 8b75c667b4679ba1a92a975823401ba7614ea7a1 COCOAPODS: 1.12.1