Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
rustle committed Mar 13, 2012
0 parents commit deffe41
Show file tree
Hide file tree
Showing 3 changed files with 316 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
@@ -0,0 +1,8 @@
build
xcuserdata
*.mode*
*pbxuser
*.perspectivev3
project.xcworkspace
xcuserdata
.DS_Store
46 changes: 46 additions & 0 deletions BPXLUUIDHandler.h
@@ -0,0 +1,46 @@
//
// BPXLUUIDHandler.h
// UUIDHandler
//
// Created by Doug Russell on 2/29/12.
// Copyright (c) 2012 Black Pixel. All rights reserved.
//
// Created by Doug Russell on 2/29/12.
// Copyright (c) 2012 Black Pixel. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#import <Foundation/Foundation.h>

@interface BPXLUUIDHandler : NSObject

/**
* Retrieve UUID from keychain, if one does not exist, generate one and store it in the keychain.
* UUIDs stored in the keychain will perisist across application installs
* but not across device restores.
*/
+ (NSString *)UUID;
/**
* Remove stored UUID from keychain
*/
+ (void)reset;
/**
* Getter/setter for access group used for reading/writing from keychain.
* Useful for shared keychain access across applications with the
* same bundle seed (requires properly configured provisioning and entitlements)
*/
+ (NSString *)accessGroup;
+ (void)setAccessGroup:(NSString *)accessGroup;

@end
262 changes: 262 additions & 0 deletions BPXLUUIDHandler.m
@@ -0,0 +1,262 @@
//
// BPXLUUIDHandler.m
// UUIDHandler
//
// Created by Doug Russell on 2/29/12.
// Copyright (c) 2012 Black Pixel. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#import "BPXLUUIDHandler.h"

#ifndef ARCLOGIC
#define ARCLOGIC

#ifdef HASARC
#undef HASARC
#endif
#ifdef HASWEAK
#undef HASWEAK
#endif
#ifdef STRONG
#undef STRONG
#endif
#ifdef __STRONG
#undef __STRONG
#endif
#ifdef WEAK
#undef WEAK
#endif
#ifdef __WEAK
#undef __WEAK
#endif

#define HASARC __has_feature(objc_arc)

#define HASWEAK __has_feature(objc_arc_weak)

#if HASARC
#define IF_ARC(ARCBlock, NOARCBlock) ARCBlock
#define NO_ARC(NoARCBlock)
#define STRONG strong
#define __STRONG __strong
#if HASWEAK
#define __WEAK __weak
#define WEAK weak
#define NO_WEAK(NoWeakBlock)
#else
#define WEAK assign
#define __WEAK __unsafe_unretained
#define NO_WEAK(NoWeakBlock) NoWeakBlock
#endif
#else
#define IF_ARC(ARCBlock, NOARCBlock) NOARCBlock
#define NO_ARC(NoARCBlock) NoARCBlock
#define STRONG retain
#define __STRONG
#define WEAK assign
#define __WEAK
#define NO_WEAK(NoWeakBlock) NoWeakBlock
#endif

#endif

@implementation BPXLUUIDHandler

static CFStringRef account = CFSTR("bpxl_uuid_account");
static CFStringRef service = CFSTR("bpxl_uuid_service");

static CFMutableDictionaryRef CreateKeychainQueryDictionary(void)
{
CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword);
CFDictionarySetValue(query, kSecAttrAccount, account);
CFDictionarySetValue(query, kSecAttrService, service);
#if !TARGET_IPHONE_SIMULATOR
if ([BPXLUUIDHandler accessGroup])
{
CFDictionarySetValue(query, kSecAttrAccessGroup, (__bridge CFTypeRef)[BPXLUUIDHandler accessGroup]);
}
#endif
return query;
}

+ (NSString *)generateUUID
{
#if TARGET_IPHONE_SIMULATOR
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [[UIDevice currentDevice] uniqueIdentifier];
#pragma clang diagnostic pop
#endif
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
CFRelease(uuidRef);
NSString *uuid;
IF_ARC(
uuid = objc_retainedObject(uuidStringRef);
,
uuid = [(NSString *)uuidStringRef autorelease];
)
return uuid;
}

+ (NSString *)storeUUID:(BOOL)itemExists
{
// Build a query
CFMutableDictionaryRef query = CreateKeychainQueryDictionary();

NSString *uuid = [[self class] generateUUID];

CFDataRef dataRef;
IF_ARC(
dataRef = CFBridgingRetain([uuid dataUsingEncoding:NSUTF8StringEncoding]);
,
dataRef = CFRetain([uuid dataUsingEncoding:NSUTF8StringEncoding]);
)
OSStatus status;
if (itemExists)
{
CFMutableDictionaryRef passwordDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(passwordDictionaryRef, kSecValueData, dataRef);
status = SecItemUpdate(query, passwordDictionaryRef);
CFRelease(passwordDictionaryRef);
}
else
{
CFDictionarySetValue(query, kSecValueData, dataRef);
status = SecItemAdd(query, NULL);
}

if (status != noErr)
{
NSLog(@"BPXLUUIDHandler Keychain Save Error: %ld", status);
uuid = nil;
}

CFRelease(dataRef);
CFRelease(query);

return uuid;
}

static NSString *_uuid = nil;
+ (NSString *)UUID
{
if (_uuid != nil)
return _uuid;

// Build a query
CFMutableDictionaryRef query = CreateKeychainQueryDictionary();

// See if the attribute exists
CFTypeRef attributeResult = NULL;
OSStatus status = SecItemCopyMatching(query, (CFTypeRef *)&attributeResult);
if (attributeResult != NULL)
CFRelease(attributeResult);

if (status != noErr)
{
CFRelease(query);
if (status == errSecItemNotFound) // If there's no entry, store one
{
return [[self class] storeUUID:NO];
}
else // Any other error, log it and return nil
{
NSLog(@"BPXLUUIDHandler Unhandled Keychain Error %ld", status);
return nil;
}
}

// Fetch stored attribute
CFDictionaryRemoveValue(query, kSecReturnAttributes);
CFDictionarySetValue(query, kSecReturnData, (id)kCFBooleanTrue);
CFTypeRef resultData = NULL;
status = SecItemCopyMatching(query, &resultData);

if (status != noErr)
{
CFRelease(query);
if (status == errSecItemNotFound) // If there's no entry, store one
{
return [[self class] storeUUID:NO];
}
else // Any other error, log it and return nil
{
NSLog(@"BPXLUUIDHandler Unhandled Keychain Error %ld", status);
return nil;
}
}

if (resultData != NULL)
{
IF_ARC(
_uuid = [[NSString alloc] initWithData:objc_retainedObject(resultData) encoding:NSUTF8StringEncoding];
,
_uuid = [[NSString alloc] initWithData:(NSData *)resultData encoding:NSUTF8StringEncoding];
CFRelease(resultData);
)
}

CFRelease(query);

return _uuid;
}

+ (void)reset
{
NO_ARC([_uuid release];)
_uuid = nil;

// Build a query
CFMutableDictionaryRef query = CreateKeychainQueryDictionary();

// See if the attribute exists
CFTypeRef attributeResult = NULL;
CFDictionarySetValue(query, kSecReturnAttributes, (id)kCFBooleanTrue);
OSStatus status = SecItemCopyMatching(query, (CFTypeRef *)&attributeResult);
if (attributeResult != NULL)
CFRelease(attributeResult);

if (status == errSecItemNotFound)
{
CFRelease(query);
return;
}

status = SecItemDelete(query);
if (status != noErr)
{
NSLog(@"BPXLUUIDHandler Keychain Delete Error: %ld", status);
}
CFRelease(query);
}

static NSString *_accessGroup = nil;
+ (NSString *)accessGroup
{
return _accessGroup;
}

+ (void)setAccessGroup:(NSString *)accessGroup
{
NO_ARC(
[accessGroup retain];
[_accessGroup release];
)
_accessGroup = accessGroup;
}

@end

0 comments on commit deffe41

Please sign in to comment.