Skip to content

Commit

Permalink
Add the ability to instantiate your own Lockbox instance, specificall…
Browse files Browse the repository at this point in the history
…y so

you can provide your own key prefix. Thanks to Dave Barker for the original
idea and implementation.
  • Loading branch information
granoff committed Oct 31, 2014
1 parent f5c257d commit 2cf2771
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 43 deletions.
2 changes: 0 additions & 2 deletions LockBox/LockBox.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@
4D6801EF16B478CB000CED0E /* UnitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "UnitTests-Info.plist"; sourceTree = "<group>"; };
4D6801F116B478CB000CED0E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
4D6801F616B478CB000CED0E /* UnitTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UnitTests-Prefix.pch"; sourceTree = "<group>"; };
4D6801FB16B478F7000CED0E /* LockboxTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LockboxTests.h; sourceTree = "<group>"; };
4D6801FC16B478F7000CED0E /* LockboxTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LockboxTests.m; sourceTree = "<group>"; };
4D68020D16B489AB000CED0E /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
4D68020E16B489AB000CED0E /* Lockbox.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Lockbox.podspec; path = ../Lockbox.podspec; sourceTree = "<group>"; };
Expand Down Expand Up @@ -152,7 +151,6 @@
isa = PBXGroup;
children = (
4D6801EE16B478CB000CED0E /* Supporting Files */,
4D6801FB16B478F7000CED0E /* LockboxTests.h */,
4D6801FC16B478F7000CED0E /* LockboxTests.m */,
);
path = UnitTests;
Expand Down
13 changes: 0 additions & 13 deletions LockBox/UnitTests/LockboxTests.h

This file was deleted.

8 changes: 8 additions & 0 deletions LockBox/UnitTests/LockboxTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,12 @@ - (void)testNilArray

}

- (void)testLocalInstance
{
NSString *customKeyPrefix = @"my.custom.keyPrefix";
Lockbox *lb = [[Lockbox alloc] initWithKeyPrefix:customKeyPrefix];

XCTAssertEqualObjects(customKeyPrefix, lb.keyPrefix, @"Custom key prefix should equal '%@'", customKeyPrefix);
}

@end
37 changes: 37 additions & 0 deletions Lockbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,43 @@

@interface Lockbox : NSObject

#ifdef DEBUG
// For unit tests
@property (strong, nonatomic, readonly) NSString *keyPrefix;
#endif

// When the default key prefix (your app's bundle id) is not sufficient, instantiate your own
// instance of Lockbox specifying your own key prefix, and use the appropriate instance methods
// to store and retreive keychain data. You can also instantiate your own instance and use the
// default key prefix simply by calling [[Lockbox alloc] init];
-(instancetype)initWithKeyPrefix:(NSString *)keyPrefix;

// When adding instance methods, remember to add a corresponding class method.

-(BOOL)setString:(NSString *)value forKey:(NSString *)key;
-(BOOL)setString:(NSString *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility;
-(NSString *)stringForKey:(NSString *)key;

-(BOOL)setArray:(NSArray *)value forKey:(NSString *)key;
-(BOOL)setArray:(NSArray *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility;
-(NSArray *)arrayForKey:(NSString *)key;

-(BOOL)setSet:(NSSet *)value forKey:(NSString *)key;
-(BOOL)setSet:(NSSet *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility;
-(NSSet *)setForKey:(NSString *)key;

-(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key;
-(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility;
-(NSDictionary *)dictionaryForKey:(NSString *)key;

-(BOOL)setDate:(NSDate *)value forKey:(NSString *)key;
-(BOOL)setDate:(NSDate *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility;
-(NSDate *)dateForKey:(NSString *)key;

// Class methods that maintain the convenience of storing and retrieving data from the keychain
// using class-level methods. An internal instance of Lockbox is instantiated for the class that
// uses the instance methods above, and a key prefix equal to your app's bundle id.

+(BOOL)setString:(NSString *)value forKey:(NSString *)key;
+(BOOL)setString:(NSString *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility;
+(NSString *)stringForKey:(NSString *)key;
Expand Down
151 changes: 128 additions & 23 deletions Lockbox.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,44 @@
#define LOCKBOX_ID __bridge id
#define LOCKBOX_DICTREF __bridge CFDictionaryRef

static NSString *_bundleId = nil;
static Lockbox *_lockBox = nil;
static NSString *_defaultKeyPrefix = nil;

@interface Lockbox()
@property (strong, nonatomic, readwrite) NSString *keyPrefix;
@end

@implementation Lockbox

+(void)initialize
{
_bundleId = [[[NSBundle bundleForClass:[self class]] infoDictionary] objectForKey:(NSString*)kCFBundleIdentifierKey];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_defaultKeyPrefix = [[[NSBundle bundleForClass:[self class]] infoDictionary] objectForKey:(NSString*)kCFBundleIdentifierKey];
_lockBox = [[Lockbox alloc] init];
});
}

-(instancetype)init
{
self = [super init];
if (self) {
self.keyPrefix = _defaultKeyPrefix;
}
return self;
}

-(instancetype)initWithKeyPrefix:(NSString *)keyPrefix
{
self = [self init];
if (self) {
if (keyPrefix)
self.keyPrefix = keyPrefix;
}
return self;
}

+(NSMutableDictionary *)_service
-(NSMutableDictionary *)_service
{
NSMutableDictionary* dict = [NSMutableDictionary dictionary];

Expand All @@ -41,7 +69,7 @@ +(NSMutableDictionary *)_service
return dict;
}

+(NSMutableDictionary *)_query
-(NSMutableDictionary *)_query
{
NSMutableDictionary* query = [NSMutableDictionary dictionary];

Expand All @@ -53,12 +81,12 @@ +(NSMutableDictionary *)_query

// Prefix a bare key like "MySecureKey" with the bundle id, so the actual key stored
// is unique to this app, e.g. "com.mycompany.myapp.MySecretKey"
+(NSString *)_hierarchicalKey:(NSString *)key
-(NSString *)_hierarchicalKey:(NSString *)key
{
return [_bundleId stringByAppendingFormat:@".%@", key];
return [_keyPrefix stringByAppendingFormat:@".%@", key];
}

+(BOOL)setObject:(NSString *)obj forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
-(BOOL)setObject:(NSString *)obj forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
{
OSStatus status;

Expand Down Expand Up @@ -91,7 +119,7 @@ +(BOOL)setObject:(NSString *)obj forKey:(NSString *)key accessibility:(CFTypeRef
return (status == errSecSuccess);
}

+(NSString *)objectForKey:(NSString *)key
-(NSString *)objectForKey:(NSString *)key
{
NSString *hierKey = [self _hierarchicalKey:key];

Expand All @@ -112,27 +140,27 @@ +(NSString *)objectForKey:(NSString *)key
return s;
}

+(BOOL)setString:(NSString *)value forKey:(NSString *)key
-(BOOL)setString:(NSString *)value forKey:(NSString *)key
{
return [self setString:value forKey:key accessibility:DEFAULT_ACCESSIBILITY];
}

+(BOOL)setString:(NSString *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
-(BOOL)setString:(NSString *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
{
return [self setObject:value forKey:key accessibility:accessibility];
}

+(NSString *)stringForKey:(NSString *)key
-(NSString *)stringForKey:(NSString *)key
{
return [self objectForKey:key];
}

+(BOOL)setArray:(NSArray *)value forKey:(NSString *)key
-(BOOL)setArray:(NSArray *)value forKey:(NSString *)key
{
return [self setArray:value forKey:key accessibility:DEFAULT_ACCESSIBILITY];
}

+(BOOL)setArray:(NSArray *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
-(BOOL)setArray:(NSArray *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
{
NSString *components = nil;
if (value != nil && value.count > 0) {
Expand All @@ -141,7 +169,7 @@ +(BOOL)setArray:(NSArray *)value forKey:(NSString *)key accessibility:(CFTypeRef
return [self setObject:components forKey:key accessibility:accessibility];
}

+(NSArray *)arrayForKey:(NSString *)key
-(NSArray *)arrayForKey:(NSString *)key
{
NSArray *array = nil;
NSString *components = [self objectForKey:key];
Expand All @@ -151,17 +179,17 @@ +(NSArray *)arrayForKey:(NSString *)key
return array;
}

+(BOOL)setSet:(NSSet *)value forKey:(NSString *)key
-(BOOL)setSet:(NSSet *)value forKey:(NSString *)key
{
return [self setSet:value forKey:key accessibility:DEFAULT_ACCESSIBILITY];
}

+(BOOL)setSet:(NSSet *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
-(BOOL)setSet:(NSSet *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
{
return [self setArray:[value allObjects] forKey:key accessibility:accessibility];
}

+(NSSet *)setForKey:(NSString *)key
-(NSSet *)setForKey:(NSString *)key
{
NSSet *set = nil;
NSArray *array = [self arrayForKey:key];
Expand All @@ -171,20 +199,20 @@ +(NSSet *)setForKey:(NSString *)key
return set;
}

+ (BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key
-(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key
{
return [self setDictionary:value forKey:key accessibility:DEFAULT_ACCESSIBILITY];
}

+ (BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
-(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
{
NSMutableArray * keysAndValues = [NSMutableArray arrayWithArray:value.allKeys];
[keysAndValues addObjectsFromArray:value.allValues];

return [self setArray:keysAndValues forKey:key accessibility:accessibility];
}

+ (NSDictionary *)dictionaryForKey:(NSString *)key
-(NSDictionary *)dictionaryForKey:(NSString *)key
{
NSArray * keysAndValues = [self arrayForKey:key];

Expand All @@ -204,25 +232,102 @@ + (NSDictionary *)dictionaryForKey:(NSString *)key
forKeys:[keysAndValues subarrayWithRange:keys]];
}

+(BOOL)setDate:(NSDate *)value forKey:(NSString *)key
-(BOOL)setDate:(NSDate *)value forKey:(NSString *)key
{
return [self setDate:value forKey:key accessibility:DEFAULT_ACCESSIBILITY];
}

+(BOOL)setDate:(NSDate *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
-(BOOL)setDate:(NSDate *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
{
if (!value)
return [self setObject:nil forKey:key accessibility:accessibility];
NSNumber *rti = [NSNumber numberWithDouble:[value timeIntervalSinceReferenceDate]];
return [self setObject:[rti stringValue] forKey:key accessibility:accessibility];
}

+(NSDate *)dateForKey:(NSString *)key
-(NSDate *)dateForKey:(NSString *)key
{
NSString *dateString = [self objectForKey:key];
if (dateString)
return [NSDate dateWithTimeIntervalSinceReferenceDate:[dateString doubleValue]];
return nil;
}

#pragma mark - Class methods

+(BOOL)setString:(NSString *)value forKey:(NSString *)key
{
return [_lockBox setString:value forKey:key];
}

+(BOOL)setString:(NSString *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
{
return [_lockBox setString:value forKey:key accessibility:accessibility];
}

+(NSString *)stringForKey:(NSString *)key
{
return [_lockBox stringForKey:key];
}

+(BOOL)setArray:(NSArray *)value forKey:(NSString *)key
{
return [_lockBox setArray:value forKey:key];
}

+(BOOL)setArray:(NSArray *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility;
{
return [_lockBox setArray:value forKey:key accessibility:accessibility];
}

+(NSArray *)arrayForKey:(NSString *)key
{
return [_lockBox arrayForKey:key];
}

+(BOOL)setSet:(NSSet *)value forKey:(NSString *)key
{
return [_lockBox setSet:value forKey:key];
}

+(BOOL)setSet:(NSSet *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
{
return [_lockBox setSet:value forKey:key accessibility:accessibility];
}

+(NSSet *)setForKey:(NSString *)key
{
return [_lockBox setForKey:key];
}

+(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key
{
return [_lockBox setDictionary:value forKey:key];
}

+(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
{
return [_lockBox setDictionary:value forKey:key accessibility:accessibility];
}

+(NSDictionary *)dictionaryForKey:(NSString *)key
{
return [_lockBox dictionaryForKey:key];
}

+(BOOL)setDate:(NSDate *)value forKey:(NSString *)key
{
return [_lockBox setDate:value forKey:key];
}

+(BOOL)setDate:(NSDate *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility
{
return [_lockBox setDate:value forKey:key accessibility:accessibility];
}

+(NSDate *)dateForKey:(NSString *)key
{
return [_lockBox dateForKey:key];
}

@end
4 changes: 2 additions & 2 deletions Lockbox.podspec
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
Pod::Spec.new do |s|
s.name = 'Lockbox'
s.version = '2.0.0'
s.version = '2.1.0'
s.license = 'MIT'
s.summary = 'Lockbox is an Objective-C utility class for storing data securely in the keychain. Use it to store small, sensitive bits of data securely.'
s.homepage = 'https://github.com/granoff/Lockbox'
s.author = 'Mark H. Granoff'

s.source = { :git => 'https://github.com/granoff/Lockbox.git', :tag => '2.0.0' }
s.source = { :git => 'https://github.com/granoff/Lockbox.git', :tag => '2.1.0' }

s.platform = :ios
s.source_files = 'Lockbox.{h,m}'
Expand Down
Loading

0 comments on commit 2cf2771

Please sign in to comment.