Skip to content

Commit

Permalink
Refactor AESCryptoModule and CommExpoPackage so that AES stack is ava…
Browse files Browse the repository at this point in the history
…ilable in NSE

Summary:
This differential implements Objective C compatibility class for AESCryptoModule that internally calls the same functionality as AESCryptoModule expo module. It also introduces necessary refactors and build config files changes so that we can
use Compatibility class from any pure Objective C place like NSE.

Test Plan:
1. Change NSE code according to: https://gist.github.com/marcinwasowicz/442f91ecb3e9781ab4e6e997692e3a46 (literally copy paste).
2. Send notification to the app running on physical device.
3. Examine NSE logs in console app.

Reviewers: bartek

Reviewed By: bartek

Subscribers: ashoat, tomek

Differential Revision: https://phab.comm.dev/D8424
  • Loading branch information
marcinwasowicz committed Nov 16, 2023
1 parent 8dd1906 commit fd2781d
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 27 deletions.
129 changes: 103 additions & 26 deletions native/expo-modules/comm-expo-package/ios/AESCryptoModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,114 @@ private let KEY_SIZE = 32 // bytes
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)

Expand All @@ -46,24 +121,26 @@ private func encrypt(rawKey: Uint8Array,
// 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
Expand Down
10 changes: 10 additions & 0 deletions native/ios/Comm.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@
CB90951929531663002F2A7F /* CommIOSNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommIOSNotifications.h; path = Comm/CommIOSNotifications/CommIOSNotifications.h; sourceTree = "<group>"; };
CBCA09042A8E0E6B00F75B3E /* StaffUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StaffUtils.h; sourceTree = "<group>"; };
CBCA09052A8E0E6B00F75B3E /* StaffUtils.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StaffUtils.cpp; sourceTree = "<group>"; };
CBCF57AB2B05096F00EC4BC0 /* AESCryptoModuleObjCCompat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AESCryptoModuleObjCCompat.h; path = Comm/CommAESCryptoUtils/AESCryptoModuleObjCCompat.h; sourceTree = "<group>"; };
CBDEC69928ED859600C17588 /* GlobalDBSingleton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GlobalDBSingleton.h; sourceTree = "<group>"; };
CBDEC69A28ED867000C17588 /* GlobalDBSingleton.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = GlobalDBSingleton.mm; path = Comm/GlobalDBSingleton.mm; sourceTree = "<group>"; };
CBFE58272885852B003B94C9 /* ThreadOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadOperations.h; path = PersistentStorageUtilities/ThreadOperationsUtilities/ThreadOperations.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -353,6 +354,7 @@
71B8CCB626BD30EC0040C0A2 /* CommCoreImplementations */ = {
isa = PBXGroup;
children = (
CBCF57A92B05091D00EC4BC0 /* CommAESCryptoUtils */,
CB90951729531647002F2A7F /* CommIOSNotifications */,
8E43C32E291E5B9D009378F5 /* TerminateApp.h */,
8E43C32B291E5B4A009378F5 /* TerminateApp.mm */,
Expand Down Expand Up @@ -682,6 +684,14 @@
name = CommIOSNotifications;
sourceTree = "<group>";
};
CBCF57A92B05091D00EC4BC0 /* CommAESCryptoUtils */ = {
isa = PBXGroup;
children = (
CBCF57AB2B05096F00EC4BC0 /* AESCryptoModuleObjCCompat.h */,
);
name = CommAESCryptoUtils;
sourceTree = "<group>";
};
CBED0E2C284E086100CD3863 /* PersistentStorageUtilities */ = {
isa = PBXGroup;
children = (
Expand Down
17 changes: 17 additions & 0 deletions native/ios/Comm/CommAESCryptoUtils/AESCryptoModuleObjCCompat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#import <Foundation/Foundation.h>

@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
1 change: 1 addition & 0 deletions native/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ target 'NotificationService' do
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
Expand Down
2 changes: 1 addition & 1 deletion native/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,6 @@ SPEC CHECKSUMS:
Yoga: dc109b79db907f0f589fc423e991b09ec42d2295
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb

PODFILE CHECKSUM: e45238be1b6262e5cd4dbe34492a16945fc2ae43
PODFILE CHECKSUM: 8b75c667b4679ba1a92a975823401ba7614ea7a1

COCOAPODS: 1.12.1

0 comments on commit fd2781d

Please sign in to comment.