-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/sdk 4227/Encryption v2 #400
base: develop
Are you sure you want to change the base?
Changes from all commits
9965acc
6d98aaf
7a1c00c
5b1ed52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,6 +151,7 @@ | |
07FD65A2223BC26300A845B7 /* CTCoverViewController~iphoneland.xib in Resources */ = {isa = PBXBuildFile; fileRef = 07FD65A1223BC26300A845B7 /* CTCoverViewController~iphoneland.xib */; }; | ||
07FD65A4223BCB8200A845B7 /* CTCoverViewController~ipadland.xib in Resources */ = {isa = PBXBuildFile; fileRef = 07FD65A3223BCB8200A845B7 /* CTCoverViewController~ipadland.xib */; }; | ||
0B5564562C25946C00B87284 /* CTUserInfoMigratorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B5564552C25946C00B87284 /* CTUserInfoMigratorTest.m */; }; | ||
0B80D41B2D4A111900521AD9 /* CTEncryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B80D41A2D4A111900521AD9 /* CTEncryptor.swift */; }; | ||
0B995A4A2C36AEDC00AF6006 /* CTLocalDataStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B995A492C36AEDC00AF6006 /* CTLocalDataStoreTests.m */; }; | ||
1F1C18806B7F29B3374F2448 /* libPods-shared-CleverTapSDKTestsApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E303560B5EE1D154C1E3D9EF /* libPods-shared-CleverTapSDKTestsApp.a */; }; | ||
32394C1F29FA251E00956058 /* CTEventBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 32394C1E29FA251E00956058 /* CTEventBuilderTest.m */; }; | ||
|
@@ -769,6 +770,7 @@ | |
07FD65A1223BC26300A845B7 /* CTCoverViewController~iphoneland.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = "CTCoverViewController~iphoneland.xib"; sourceTree = "<group>"; }; | ||
07FD65A3223BCB8200A845B7 /* CTCoverViewController~ipadland.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = "CTCoverViewController~ipadland.xib"; sourceTree = "<group>"; }; | ||
0B5564552C25946C00B87284 /* CTUserInfoMigratorTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTUserInfoMigratorTest.m; sourceTree = "<group>"; }; | ||
0B80D41A2D4A111900521AD9 /* CTEncryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CTEncryptor.swift; sourceTree = "<group>"; }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Swift version 6.0 is incorrectly specified The project is configured to use Swift 6.0, which is not yet released. Please update the 🔗 Analysis chainVerify Swift version compatibility with deployment target. The addition of Swift code requires careful consideration of deployment target compatibility. Please ensure that Swift 6.0 is compatible with the iOS 9.0 deployment target specified in the project settings. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check Swift version compatibility
xcodebuild -showBuildSettings | grep SWIFT
Length of output: 145 Script: #!/bin/bash
# Search for Swift version and deployment target settings
rg "SWIFT_VERSION|IPHONEOS_DEPLOYMENT_TARGET" CleverTapSDK.xcodeproj/project.pbxproj
Length of output: 592 |
||
0B995A492C36AEDC00AF6006 /* CTLocalDataStoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTLocalDataStoreTests.m; sourceTree = "<group>"; }; | ||
0CA46771B6F202E37DAC9F70 /* Pods-shared-CleverTapSDKTestsApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-shared-CleverTapSDKTestsApp.debug.xcconfig"; path = "Target Support Files/Pods-shared-CleverTapSDKTestsApp/Pods-shared-CleverTapSDKTestsApp.debug.xcconfig"; sourceTree = "<group>"; }; | ||
129AEC403AFA828F591B756E /* Pods-shared-CleverTapSDKTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-shared-CleverTapSDKTests.release.xcconfig"; path = "Target Support Files/Pods-shared-CleverTapSDKTests/Pods-shared-CleverTapSDKTests.release.xcconfig"; sourceTree = "<group>"; }; | ||
|
@@ -1330,6 +1332,16 @@ | |
name = Frameworks; | ||
sourceTree = "<group>"; | ||
}; | ||
0B80D4192D4A10EB00521AD9 /* Encryption */ = { | ||
isa = PBXGroup; | ||
children = ( | ||
4803951A2A7ABAD200C4D254 /* CTAES.h */, | ||
480395192A7ABAD200C4D254 /* CTAES.m */, | ||
0B80D41A2D4A111900521AD9 /* CTEncryptor.swift */, | ||
); | ||
path = Encryption; | ||
sourceTree = "<group>"; | ||
}; | ||
4847D16B2CCB9018008DC327 /* EventDatabase */ = { | ||
isa = PBXGroup; | ||
children = ( | ||
|
@@ -1754,11 +1766,10 @@ | |
D0C7BBBF207D82C0001345EF /* CleverTapSDK */ = { | ||
isa = PBXGroup; | ||
children = ( | ||
0B80D4192D4A10EB00521AD9 /* Encryption */, | ||
4847D16B2CCB9018008DC327 /* EventDatabase */, | ||
6B9E95B62C2AE6740002D557 /* FileDownload */, | ||
3242D7DA2B1DDA2E00A5E37A /* PrivacyInfo.xcprivacy */, | ||
4803951A2A7ABAD200C4D254 /* CTAES.h */, | ||
480395192A7ABAD200C4D254 /* CTAES.m */, | ||
4808030D292EB4FB00C06E2F /* CleverTap+PushPermission.h */, | ||
4E838C4429A0C94B00ED0875 /* CleverTap+CTVar.h */, | ||
4E64855A287440BA00C2F409 /* AmazonRootCA1.cer */, | ||
|
@@ -2296,6 +2307,7 @@ | |
}; | ||
D0C7BBBC207D82C0001345EF = { | ||
CreatedOnToolsVersion = 9.3; | ||
LastSwiftMigration = 1610; | ||
}; | ||
}; | ||
}; | ||
|
@@ -2769,6 +2781,7 @@ | |
4E49AE55275D24570074A774 /* CTValidationResultStack.m in Sources */, | ||
D01A0895207EC2D400423D6F /* CleverTapInstanceConfig.m in Sources */, | ||
071EB4C8217F6427008F0FAB /* CTDismissButton.m in Sources */, | ||
0B80D41B2D4A111900521AD9 /* CTEncryptor.swift in Sources */, | ||
6BB778C82BECEC2700A41628 /* CTCustomTemplateInAppData.m in Sources */, | ||
4EF0D5472AD84BCA0044C48F /* CTSessionManager.m in Sources */, | ||
4EB3638B2C087A8200C00AE2 /* CTUserInfoMigrator.m in Sources */, | ||
|
@@ -3211,6 +3224,7 @@ | |
isa = XCBuildConfiguration; | ||
buildSettings = { | ||
APPLICATION_EXTENSION_API_ONLY = YES; | ||
CLANG_ENABLE_MODULES = YES; | ||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; | ||
CODE_SIGN_IDENTITY = ""; | ||
CODE_SIGN_STYLE = Manual; | ||
|
@@ -3249,6 +3263,8 @@ | |
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; | ||
SKIP_INSTALL = YES; | ||
SUPPORTS_MACCATALYST = NO; | ||
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||
SWIFT_VERSION = 6.0; | ||
TARGETED_DEVICE_FAMILY = "1,2"; | ||
}; | ||
name = Debug; | ||
|
@@ -3257,6 +3273,7 @@ | |
isa = XCBuildConfiguration; | ||
buildSettings = { | ||
APPLICATION_EXTENSION_API_ONLY = YES; | ||
CLANG_ENABLE_MODULES = YES; | ||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; | ||
CODE_SIGN_IDENTITY = ""; | ||
CODE_SIGN_STYLE = Manual; | ||
|
@@ -3286,6 +3303,7 @@ | |
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; | ||
SKIP_INSTALL = YES; | ||
SUPPORTS_MACCATALYST = NO; | ||
SWIFT_VERSION = 6.0; | ||
TARGETED_DEVICE_FAMILY = "1,2"; | ||
}; | ||
name = Release; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// | ||
// AESCrypt.h | ||
// Pods | ||
// | ||
// Created by Kushagra Mishra on 29/01/25. | ||
// | ||
#import <Foundation/Foundation.h> | ||
#import <CommonCrypto/CommonCrypto.h> | ||
|
||
@interface AESCrypt : NSObject | ||
|
||
+ (NSData *)AES128WithOperation:(CCOperation)operation | ||
key:(NSString *)key | ||
identifier:(NSString *)identifier | ||
data:(NSData *)data; | ||
|
||
@end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// | ||
// AESCrypt.m | ||
// Pods | ||
// | ||
// Created by Kushagra Mishra on 29/01/25. | ||
// | ||
#import "AESCrypt.h" | ||
|
||
@implementation AESCrypt : NSObject | ||
|
||
+ (NSData *)AES128WithOperation:(CCOperation)operation | ||
key:(NSString *)key | ||
identifier:(NSString *)identifier | ||
data:(NSData *)data { | ||
// Note: The key will be 0's but we intentionally are keeping it this way to maintain | ||
// compatibility. The correct code is: | ||
// char keyPtr[[key length] + 1]; | ||
char keyCString[kCCKeySizeAES128 + 1]; | ||
memset(keyCString, 0, sizeof(keyCString)); | ||
[key getCString:keyCString maxLength:sizeof(keyCString) encoding:NSUTF8StringEncoding]; | ||
|
||
char identifierCString[kCCBlockSizeAES128 + 1]; | ||
memset(identifierCString, 0, sizeof(identifierCString)); | ||
[identifier getCString:identifierCString | ||
maxLength:sizeof(identifierCString) | ||
encoding:NSUTF8StringEncoding]; | ||
|
||
size_t outputAvailableSize = [data length] + kCCBlockSizeAES128; | ||
void *output = malloc(outputAvailableSize); | ||
|
||
Comment on lines
+28
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential memory leak in output buffer handling. The - return [NSData dataWithBytesNoCopy:output length:outputMovedSize];
+ return [NSData dataWithBytesNoCopy:output length:outputMovedSize freeWhenDone:YES]; Also applies to: 50-50 |
||
size_t outputMovedSize = 0; | ||
CCCryptorStatus cryptStatus = CCCrypt(operation, | ||
kCCAlgorithmAES128, | ||
kCCOptionPKCS7Padding, | ||
keyCString, | ||
kCCBlockSizeAES128, | ||
identifierCString, | ||
[data bytes], | ||
[data length], | ||
output, | ||
outputAvailableSize, | ||
&outputMovedSize); | ||
|
||
if (cryptStatus != kCCSuccess) { | ||
NSLog(@"Failed to encode/decode the string with error code: %d", cryptStatus); | ||
free(output); | ||
return nil; | ||
} | ||
|
||
return [NSData dataWithBytesNoCopy:output length:outputMovedSize]; | ||
} | ||
|
||
@end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// | ||
// AESGCMCrypt.swift | ||
// Pods | ||
// | ||
// Created by Kushagra Mishra on 29/01/25. | ||
// | ||
|
||
import Foundation | ||
import CryptoKit | ||
|
||
@objc public enum AESGCMEncryptionErrorCode: Int { | ||
case encryptionFailed | ||
case decryptionFailed | ||
case invalidData | ||
case dataNotFound | ||
} | ||
|
||
@objcMembers | ||
class AESGCMCryptResult: NSObject { | ||
let iv: Data | ||
let data: Data | ||
|
||
init(iv: Data, data: Data) { | ||
self.iv = iv | ||
self.data = data | ||
super.init() | ||
} | ||
} | ||
|
||
@objc enum CryptError: Int, Error { | ||
case invalidMode = 0 | ||
case keyGenerationFailed = 1 | ||
case encryptionFailed = 2 | ||
case decryptionFailed = 3 | ||
case ivRequired = 4 | ||
} | ||
|
||
@available(iOS 13, *) | ||
@objcMembers | ||
class AESGCMCrypt: NSObject { | ||
private let keyIdentifier = "your.key.identifier" | ||
private static let ivSize = 12 | ||
|
||
private func generateOrGetKey() throws -> SymmetricKey { | ||
if let existingKey = try? getKeyFromKeychain() { | ||
return existingKey | ||
} | ||
|
||
let newKey = SymmetricKey(size: .bits256) | ||
try storeKeyInKeychain(newKey) | ||
return newKey | ||
} | ||
Comment on lines
+38
to
+52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Handle Keychain concurrency or errors more explicitly. |
||
|
||
func performCryptOperation(mode: Bool, data: Data, iv: Data? = nil) throws -> AESGCMCryptResult { | ||
let key = try generateOrGetKey() | ||
|
||
if mode { // Encrypt | ||
let nonce = try AES.GCM.Nonce(data: iv ?? Data()) | ||
let sealedBox = try AES.GCM.seal(data, using: key, nonce: nonce) | ||
|
||
return AESGCMCryptResult( | ||
iv: sealedBox.nonce.withUnsafeBytes { Data($0) }, | ||
data: sealedBox.ciphertext + sealedBox.tag | ||
) | ||
} else { // Decrypt | ||
guard let iv = iv else { | ||
throw CryptError.ivRequired | ||
} | ||
|
||
let tagLength = 16 | ||
let ciphertext = data.dropLast(tagLength) | ||
let tag = data.suffix(tagLength) | ||
|
||
let nonce = try AES.GCM.Nonce(data: iv) | ||
let sealedBox = try AES.GCM.SealedBox( | ||
nonce: nonce, | ||
ciphertext: ciphertext, | ||
tag: tag | ||
) | ||
|
||
let decryptedData = try AES.GCM.open(sealedBox, using: key) | ||
return AESGCMCryptResult(iv: iv, data: decryptedData) | ||
} | ||
} | ||
|
||
|
||
private func storeKeyInKeychain(_ key: SymmetricKey) throws { | ||
// Implement secure key storage in Keychain | ||
} | ||
|
||
private func getKeyFromKeychain() throws -> SymmetricKey? { | ||
// Implement secure key retrieval from Keychain | ||
return nil | ||
} | ||
} | ||
Comment on lines
+85
to
+95
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implement or remove placeholder methods. - private func storeKeyInKeychain(_ key: SymmetricKey) throws {
- // Implement secure key storage in Keychain
- }
+ private func storeKeyInKeychain(_ key: SymmetricKey) throws {
+ // Suggested implementation example or future integration:
+ // 1. Convert SymmetricKey to raw data
+ // 2. Use Keychain Services to store
+ // 3. Handle OSStatus errors
+ }
- private func getKeyFromKeychain() throws -> SymmetricKey? {
- // Implement secure key retrieval from Keychain
- return nil
- }
+ private func getKeyFromKeychain() throws -> SymmetricKey? {
+ // 1. Obtain raw data from Keychain
+ // 2. Convert raw data to SymmetricKey
+ // 3. Return the key object or nil if not found
+ }
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,9 @@ | |
#import "CTConstants.h" | ||
#import "CTPreferences.h" | ||
#import "CTUtils.h" | ||
#import "AESCrypt.h" | ||
#import "CTEncryptor.h" | ||
|
||
|
||
NSString *const kENCRYPTION_KEY = @"CLTAP_ENCRYPTION_KEY"; | ||
NSString *const kCRYPT_KEY_PREFIX = @"Lq3fz"; | ||
|
@@ -129,53 +132,20 @@ - (NSString *)getDecryptedString:(NSString *)identifier { | |
|
||
- (NSData *)convertData:(NSData *)data | ||
withOperation:(CCOperation)operation { | ||
NSData *outputData = [self AES128WithOperation:operation | ||
key:[self generateKeyPassword] | ||
identifier:CLTAP_ENCRYPTION_IV | ||
data:data]; | ||
return outputData; | ||
} | ||
|
||
- (NSData *)AES128WithOperation:(CCOperation)operation | ||
key:(NSString *)key | ||
identifier:(NSString *)identifier | ||
data:(NSData *)data { | ||
// Note: The key will be 0's but we intentionally are keeping it this way to maintain | ||
// compatibility. The correct code is: | ||
// char keyPtr[[key length] + 1]; | ||
char keyCString[kCCKeySizeAES128 + 1]; | ||
memset(keyCString, 0, sizeof(keyCString)); | ||
[key getCString:keyCString maxLength:sizeof(keyCString) encoding:NSUTF8StringEncoding]; | ||
|
||
char identifierCString[kCCBlockSizeAES128 + 1]; | ||
memset(identifierCString, 0, sizeof(identifierCString)); | ||
[identifier getCString:identifierCString | ||
maxLength:sizeof(identifierCString) | ||
encoding:NSUTF8StringEncoding]; | ||
|
||
size_t outputAvailableSize = [data length] + kCCBlockSizeAES128; | ||
void *output = malloc(outputAvailableSize); | ||
|
||
size_t outputMovedSize = 0; | ||
CCCryptorStatus cryptStatus = CCCrypt(operation, | ||
kCCAlgorithmAES128, | ||
kCCOptionPKCS7Padding, | ||
keyCString, | ||
kCCBlockSizeAES128, | ||
identifierCString, | ||
[data bytes], | ||
[data length], | ||
output, | ||
outputAvailableSize, | ||
&outputMovedSize); | ||
|
||
if (cryptStatus != kCCSuccess) { | ||
CleverTapLogStaticInternal(@"Failed to encode/deocde the string with error code: %d", cryptStatus); | ||
free(output); | ||
return nil; | ||
// NSData *outputData = [AESCrypt AES128WithOperation:operation | ||
// key:[self generateKeyPassword] | ||
// identifier:CLTAP_ENCRYPTION_IV | ||
// data:data]; | ||
NSError *error = nil; | ||
BOOL keyGenerated = [CTEncryptor generateKeyWithError:&error]; | ||
if (!keyGenerated) { | ||
NSLog(@"Error generating key: %@", error); | ||
} | ||
NSData *outputData = [CTEncryptor performCryptOperation:data error:&error]; | ||
if (error) { | ||
NSLog(@"Encryption failed: %@", error.localizedDescription); | ||
Comment on lines
+140
to
+146
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pass CCOperation to CTEncryptor or handle encryption/decryption paths. NSData *outputData = nil;
NSError *error = nil;
BOOL keyGenerated = [CTEncryptor generateKeyWithError:&error];
if (!keyGenerated) {
NSLog(@"Error generating key: %@", error);
}
- outputData = [CTEncryptor performCryptOperation:data error:&error];
+ outputData = [CTEncryptor performCryptOperation:data operation:operation error:&error];
if (error) {
NSLog(@"Encryption failed: %@", error.localizedDescription);
}
return outputData; Also applies to: 148-148 |
||
} | ||
|
||
return [NSData dataWithBytesNoCopy:output length:outputMovedSize]; | ||
return outputData; | ||
} | ||
|
||
- (NSString *)getCachedKey:(NSString *)value { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// | ||
// EncryptionBridge.h | ||
// Pods | ||
// | ||
// Created by Kushagra Mishra on 29/01/25. | ||
// | ||
#import <Foundation/Foundation.h> | ||
#import <CommonCrypto/CommonCrypto.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface CTEncryptor : NSObject | ||
|
||
// Key Management | ||
+ (BOOL)generateKeyWithError:(NSError **)error; | ||
+ (BOOL)deleteKeyWithError:(NSError **)error; | ||
|
||
// Encryption/Decryption | ||
+ (NSString * _Nullable)performCryptOperation:(NSString *)string error:(NSError **)error; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Add Swift version 6.0 to podspec
The main SDK project uses Swift 6.0, and the podspec should align with this version for the newly added Swift files:
s.ios.source_files = 'CleverTapSDK/**/*.{h,m,swift}' +s.swift_version = '6.0'
🔗 Analysis chain
Verify Swift version compatibility.
The addition of Swift files requires specifying the minimum Swift version for compatibility.
Consider adding the Swift version specification:
s.ios.source_files = 'CleverTapSDK/**/*.{h,m,swift}' +s.swift_version = '5.0'
Let's verify the Swift files in the project:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 2715