Skip to content

Commit

Permalink
Support client side encryption via SEGCrypto protocol (#592)
Browse files Browse the repository at this point in the history
* Adding SEGStorage and SEGCrypto classes

* Adding SEGUserDefaultsStorage

* Route all disk access through SEGStorage classes and expose SEGCrypto as
part of SEGAnalyticsConfiguration

* Run pod install again

* Fix anonymousId loading. Removing no longer used anonymousIdURL

* Removing unused SEGAnalyticsURLForFilename

* Adding CryptoTest, FileStorageTest and UserDefaultsStorageTest from redux branch

* Adding changes to Podfile
  • Loading branch information
tonyxiao authored and f2prateek committed Sep 1, 2016
1 parent ed43a2f commit ab7a7db
Show file tree
Hide file tree
Showing 133 changed files with 11,371 additions and 1,026 deletions.
2 changes: 2 additions & 0 deletions Analytics.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ Pod::Spec.new do |s|
s.ios.deployment_target = '7.0'
s.tvos.deployment_target = '9.0'

s.framework = 'Security'

s.source_files = 'Analytics/Classes/**/*'
end
23 changes: 23 additions & 0 deletions Analytics/Classes/Crypto/SEGAES256Crypto.h
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;

@end
140 changes: 140 additions & 0 deletions Analytics/Classes/Crypto/SEGAES256Crypto.m
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
15 changes: 15 additions & 0 deletions Analytics/Classes/Crypto/SEGCrypto.h
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
1 change: 0 additions & 1 deletion Analytics/Classes/Internal/SEGAnalyticsUtils.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#import <Foundation/Foundation.h>

NSURL *SEGAnalyticsURLForFilename(NSString *filename);
NSString *GenerateUUIDString();

// Date Utils
Expand Down
18 changes: 0 additions & 18 deletions Analytics/Classes/Internal/SEGAnalyticsUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,6 @@

static BOOL kAnalyticsLoggerShowLogs = NO;

NSURL *SEGAnalyticsURLForFilename(NSString *filename)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *supportPath = [paths firstObject];
if (![[NSFileManager defaultManager] fileExistsAtPath:supportPath
isDirectory:NULL]) {
NSError *error = nil;
if (![[NSFileManager defaultManager] createDirectoryAtPath:supportPath
withIntermediateDirectories:YES
attributes:nil
error:&error]) {
SEGLog(@"error: %@", error.localizedDescription);
}
}
return [[NSURL alloc] initFileURLWithPath:[supportPath stringByAppendingPathComponent:filename]];
}

NSString *GenerateUUIDString()
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
Expand Down
22 changes: 22 additions & 0 deletions Analytics/Classes/Internal/SEGFileStorage.h
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
Loading

0 comments on commit ab7a7db

Please sign in to comment.