-
Notifications
You must be signed in to change notification settings - Fork 333
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
Support client side encryption via SEGCrypto protocol #592
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
9101eb3
Adding SEGStorage and SEGCrypto classes
tonyxiao b013ae2
Adding SEGUserDefaultsStorage
tonyxiao 63f1fec
Route all disk access through SEGStorage classes and expose SEGCrypto as
tonyxiao 7869fed
Run pod install again
tonyxiao 91ac918
Fix anonymousId loading. Removing no longer used anonymousIdURL
tonyxiao ca65b60
Removing unused SEGAnalyticsURLForFilename
tonyxiao a3baacb
Adding CryptoTest, FileStorageTest and UserDefaultsStorageTest from r…
tonyxiao 90da8a7
Adding changes to Podfile
tonyxiao File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// | ||
// SEGAES256Crypto.h | ||
// Analytics | ||
// | ||
// Copyright © 2016 Segment. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
#import "SEGCrypto.h" | ||
|
||
@interface SEGAES256Crypto : NSObject <SEGCrypto> | ||
|
||
@property (nonatomic, readonly, nonnull) NSString *password; | ||
@property (nonatomic, readonly, nonnull) NSData *salt; | ||
@property (nonatomic, readonly, nonnull) NSData *iv; | ||
|
||
- (instancetype _Nonnull)initWithPassword:(NSString * _Nonnull)password salt:(NSData * _Nonnull)salt iv:(NSData * _Nonnull)iv; | ||
// Convenient shorthand. Will randomly generate salt and iv. | ||
- (instancetype _Nonnull)initWithPassword:(NSString * _Nonnull)password; | ||
|
||
+ (NSData * _Nonnull)randomDataOfLength:(size_t)length; | ||
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. Does this need to be public? 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. Yes, it's a convenience method for people to generate |
||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// | ||
// SEGAES256Crypto.m | ||
// Analytics | ||
// | ||
// Copyright © 2016 Segment. All rights reserved. | ||
// | ||
|
||
|
||
#import <CommonCrypto/CommonCryptor.h> | ||
#import <CommonCrypto/CommonKeyDerivation.h> | ||
#import "SEGAES256Crypto.h" | ||
#import "SEGUtils.h" | ||
|
||
// Implementation courtesy of http://robnapier.net/aes-commoncrypto | ||
|
||
static NSString * const kRNCryptManagerErrorDomain = @"com.segment.crypto"; | ||
|
||
static const CCAlgorithm kAlgorithm = kCCAlgorithmAES; | ||
static const NSUInteger kAlgorithmKeySize = kCCKeySizeAES256; | ||
static const NSUInteger kAlgorithmBlockSize = kCCBlockSizeAES128; | ||
static const NSUInteger kAlgorithmIVSize = kCCBlockSizeAES128; | ||
static const NSUInteger kPBKDFSaltSize = 8; | ||
static const NSUInteger kPBKDFRounds = 10000; // ~80ms on an iPhone 4 | ||
|
||
@implementation SEGAES256Crypto | ||
|
||
- (instancetype)initWithPassword:(NSString *)password salt:(NSData *)salt iv:(NSData * _Nonnull)iv { | ||
if (self = [super init]) { | ||
_password = password; | ||
_salt = salt; | ||
_iv = iv; | ||
} | ||
return self; | ||
} | ||
|
||
- (instancetype)initWithPassword:(NSString *)password { | ||
NSData *iv = [SEGAES256Crypto randomDataOfLength:kAlgorithmIVSize]; | ||
NSData *salt = [SEGAES256Crypto randomDataOfLength:kPBKDFSaltSize]; | ||
return [self initWithPassword:password salt:salt iv:iv]; | ||
} | ||
|
||
- (NSData *)aesKey { | ||
return [[self class] AESKeyForPassword:self.password salt:self.salt]; | ||
} | ||
|
||
- (NSData *)encrypt:(NSData *)data { | ||
size_t outLength; | ||
NSMutableData *cipherData = [NSMutableData dataWithLength:data.length + kAlgorithmBlockSize]; | ||
|
||
CCCryptorStatus | ||
result = CCCrypt(kCCEncrypt, // operation | ||
kAlgorithm, // Algorithm | ||
kCCOptionPKCS7Padding, // options | ||
self.aesKey.bytes, // key | ||
self.aesKey.length, // keylength | ||
self.iv.bytes,// iv | ||
data.bytes, // dataIn | ||
data.length, // dataInLength, | ||
cipherData.mutableBytes, // dataOut | ||
cipherData.length, // dataOutAvailable | ||
&outLength); // dataOutMoved | ||
|
||
if (result == kCCSuccess) { | ||
cipherData.length = outLength; | ||
} else { | ||
NSError *error = [NSError errorWithDomain:kRNCryptManagerErrorDomain | ||
code:result | ||
userInfo:nil]; | ||
SEGLog(@"Unable to encrypt data", error); | ||
return nil; | ||
} | ||
return cipherData; | ||
} | ||
|
||
- (NSData *)decrypt:(NSData *)data { | ||
size_t outLength; | ||
NSMutableData *decryptedData = [NSMutableData dataWithLength:data.length + kAlgorithmBlockSize]; | ||
|
||
CCCryptorStatus | ||
result = CCCrypt(kCCDecrypt, // operation | ||
kAlgorithm, // Algorithm | ||
kCCOptionPKCS7Padding, // options | ||
self.aesKey.bytes, // key | ||
self.aesKey.length, // keylength | ||
self.iv.bytes,// iv | ||
data.bytes, // dataIn | ||
data.length, // dataInLength, | ||
decryptedData.mutableBytes, // dataOut | ||
decryptedData.length, // dataOutAvailable | ||
&outLength); // dataOutMoved | ||
|
||
if (result == kCCSuccess) { | ||
decryptedData.length = outLength; | ||
} else { | ||
NSError *error = [NSError errorWithDomain:kRNCryptManagerErrorDomain | ||
code:result | ||
userInfo:nil]; | ||
SEGLog(@"Unable to encrypt data", error); | ||
return nil; | ||
} | ||
return decryptedData; | ||
} | ||
|
||
+ (NSData *)randomDataOfLength:(size_t)length { | ||
NSMutableData *data = [NSMutableData dataWithLength:length]; | ||
|
||
int result = SecRandomCopyBytes(kSecRandomDefault, | ||
length, | ||
data.mutableBytes); | ||
if (result != kCCSuccess) { | ||
SEGLog(@"Unable to generate random bytes: %d", result); | ||
} | ||
|
||
return data; | ||
} | ||
|
||
// Replace this with a 10,000 hash calls if you don't have CCKeyDerivationPBKDF | ||
+ (NSData *)AESKeyForPassword:(NSString *)password | ||
salt:(NSData *)salt { | ||
NSMutableData *derivedKey = [NSMutableData dataWithLength:kAlgorithmKeySize]; | ||
|
||
int result = CCKeyDerivationPBKDF(kCCPBKDF2, // algorithm | ||
password.UTF8String, // password | ||
[password lengthOfBytesUsingEncoding:NSUTF8StringEncoding], // passwordLength | ||
salt.bytes, // salt | ||
salt.length, // saltLen | ||
kCCPRFHmacAlgSHA1, // PRF | ||
kPBKDFRounds, // rounds | ||
derivedKey.mutableBytes, // derivedKey | ||
derivedKey.length); // derivedKeyLen | ||
|
||
// Do not log password here | ||
if (result != kCCSuccess) { | ||
SEGLog(@"Unable to create AES key for password: %d", result); | ||
} | ||
|
||
return derivedKey; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// | ||
// SEGCrypto.h | ||
// Analytics | ||
// | ||
// Copyright © 2016 Segment. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
@protocol SEGCrypto <NSObject> | ||
|
||
- (NSData * _Nullable)encrypt:(NSData * _Nonnull)data; | ||
- (NSData * _Nullable)decrypt:(NSData * _Nonnull)data; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// | ||
// SEGFileStorage.h | ||
// Analytics | ||
// | ||
// Copyright © 2016 Segment. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
#import "SEGStorage.h" | ||
|
||
@interface SEGFileStorage : NSObject <SEGStorage> | ||
|
||
@property (nonatomic, strong, nullable) id<SEGCrypto> crypto; | ||
|
||
- (instancetype _Nonnull)init; | ||
- (instancetype _Nonnull)initWithFolder:(NSURL * _Nonnull)folderURL crypto:(id<SEGCrypto> _Nullable)crypto; | ||
|
||
- (NSURL * _Nonnull)urlForKey:(NSString * _Nonnull)key; | ||
|
||
+ (NSURL * _Nullable)applicationSupportDirectoryURL; | ||
|
||
@end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Do these properties need to be public?
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.
Yes, user will need to persist password, iv, and salt in order to properly initialize the crypto object next time.
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.
This is especially true in the case where iv and salt are auto-generated.