Created
October 11, 2013 18:05
-
-
Save superwills/6939348 to your computer and use it in GitHub Desktop.
SecCRUD operations (SecCreate, SecRead, SecUpdate, SecDelete) for serialization of binary objects with primitive-typed data in them.
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
#ifndef KEYCHAIN_H | |
#define KEYCHAIN_H | |
/* | |
kSecAttrAccessible - A CFTypeRef (opaque) value that indicates when your app | |
needs access to the data in a keychain item. You should choose the | |
most restrictive option that meets your app’s needs so that iOS can protect | |
that item to the greatest extent possible. For a list of possible values, | |
see “Keychain Item Accessibility Constants.” | |
kSecAttrAccessGroup - The corresponding value is of type CFStringRef | |
and indicates which access group an item is in. Access groups can be | |
used to share keychain items among two or more applications. For | |
applications to share a keychain item, the applications must have a | |
common access group listed in their keychain-access-groups entitlement, | |
and the application adding the shared item to the keychain must specify | |
this shared access-group name as the value for this key in the dictionary | |
passed to the SecItemAdd function. | |
An application can be a member of any number of access groups. | |
By default, the SecItemUpdate, SecItemDelete, and SecItemCopyMatching | |
functions search all the access groups an application is a member | |
of. Include this key in the search dictionary for these functions | |
to specify which access group is searched. | |
kSecAttrCreationDate - The corresponding value is of type CFDateRef | |
and represents the date the item was created. Read only. | |
kSecAttrModificationDate - The corresponding value is of type CFDateRef | |
and represents the last time the item was updated. Read only. | |
kSecAttrDescription - The corresponding value is of type CFStringRef | |
and specifies a user-visible string describing this kind of item | |
(for example, "Disk image password"). | |
kSecAttrComment - The corresponding value is of type CFStringRef | |
and contains the user-editable comment for this item. | |
kSecAttrCreator - The corresponding value is of type CFNumberRef | |
and represents the item's creator. This number is the unsigned | |
integer representation of a four-character code (for example, 'aCrt'). | |
kSecAttrType - The corresponding value is of type CFNumberRef and | |
represents the item's type. This number is the unsigned integer | |
representation of a four-character code (for example, 'aTyp'). | |
kSecAttrLabel - The corresponding value is of type CFStringRef | |
and contains the user-visible label for this item. | |
kSecAttrIsInvisible - The corresponding value is of type CFBooleanRef | |
and is kCFBooleanTrue if the item is invisible (that is, should not be displayed). | |
kSecAttrIsNegative - The corresponding value is of type CFBooleanRef | |
and indicates whether there is a valid password associated with this | |
keychain item. This is useful if your application doesn't want a | |
password for some particular service to be stored in the keychain, | |
but prefers that it always be entered by the user. | |
kSecAttrAccount - The corresponding value is of type CFStringRef and | |
contains an account name. Items of class kSecClassGenericPassword | |
and kSecClassInternetPassword have this attribute. | |
kSecAttrService - The corresponding value is a string of type | |
CFStringRef that represents the service associated with this item. | |
Items of class kSecClassGenericPassword have this attribute. | |
kSecAttrGeneric - The corresponding value is of type CFDataRef and | |
contains a user-defined attribute. Items of class kSecClassGenericPassword | |
have this attribute. | |
*/ | |
bool SecCheck( OSStatus res, const char* msg ) | |
{ | |
if( res==errSecSuccess ) | |
{ | |
printf( "< %s okie dokie >\n", msg ) ; // COMMENT THIS OUT TO SILENCE OK's | |
} | |
else | |
{ | |
printf( "< NOT OK!! >: %s FAILED:\n >> ", msg ) ; | |
switch( res ) | |
{ | |
case errSecUnimplemented: | |
puts( "errSecUnimplemented: Function or operation not implemented." ) ; break; | |
case errSecParam: | |
puts( "errSecParam: One or more parameters passed to a function where not valid." ) ; break; | |
case errSecAllocate: | |
puts( "errSecAllocate: Failed to allocate memory." ) ; break; | |
case errSecNotAvailable: | |
puts( "errSecNotAvailable: No keychain is available. You may need to restart your computer." ) ; break; | |
case errSecDuplicateItem: | |
puts( "errSecDuplicateItem: The specified item already exists in the keychain." ) ; break; | |
case errSecItemNotFound: | |
puts( "errSecItemNotFound: The specified item could not be found in the keychain." ) ; break; | |
case errSecInteractionNotAllowed: | |
puts( "errSecInteractionNotAllowed: User interaction is not allowed." ) ; break; | |
case errSecDecode: | |
puts( "errSecDecode: Unable to decode the provided data." ) ; break; | |
case errSecAuthFailed: | |
puts( "errSecAuthFailed: The user name or passphrase you entered is not correct." ) ; break; | |
default: | |
puts( "UNDEFINED ERROR" ) ; break; | |
} | |
} | |
return res == errSecSuccess ; | |
} | |
// The base ctor is too large. | |
CFMutableDictionaryRef CFMutableDictionaryCreateEmpty() | |
{ | |
return CFDictionaryCreateMutable( 0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ) ; | |
} | |
CFMutableDictionaryRef getKeylookup( const char* keyname ) | |
{ | |
CFMutableDictionaryRef keyLookup = CFMutableDictionaryCreateEmpty() ; | |
CFDictionaryAddValue( keyLookup, kSecClass, kSecClassGenericPassword ) ; // "generic password" for arbitrary binary data | |
CFStringRef cfAccount = CFStringCreateWithCString( NULL, keyname, kCFStringEncodingMacRoman ) ; | |
CFDictionaryAddValue( keyLookup, kSecAttrAccount, cfAccount ) ; // uniquely identify the row. | |
CFRelease( cfAccount ) ; | |
return keyLookup ; | |
} | |
// provides serialization and deserialization for static types | |
template <typename T> | |
bool SecCreate( const char* keyname, const T* data ) | |
{ | |
CFMutableDictionaryRef keyLookup = getKeylookup( keyname ) ; | |
CFDataRef cfData = CFDataCreate( 0, (const UInt8*)data, sizeof(T) ) ; | |
// store binary data (any CFDataRef) in kSecAttrGeneric | |
// for a kSecClassGenericPassword type keychain entry | |
CFDictionaryAddValue( keyLookup, kSecAttrGeneric, cfData ) ;// the actual data we want to store. | |
bool success = SecCheck( SecItemAdd( keyLookup, NULL ), "SecItemAdd" ) ; | |
CFRelease( cfData ) ; | |
CFRelease( keyLookup ) ; | |
return success ; | |
} | |
// You should know the len, as sizeof(T) | |
template <typename T> | |
bool SecRead( const char* keyname, T* data ) | |
{ | |
CFMutableDictionaryRef keyLookup = getKeylookup( keyname ) ; | |
CFDictionaryAddValue( keyLookup, kSecReturnAttributes, kCFBooleanTrue ) ; // makes it return a DICTIONARY | |
CFMutableDictionaryRef dataFromKeychain ; | |
OSStatus res = SecItemCopyMatching( keyLookup, (CFTypeRef *)&dataFromKeychain ) ; | |
CFRelease( keyLookup ) ; | |
bool success = 0 ; | |
if( res == noErr ) | |
{ | |
CFDataRef cfData = (CFDataRef)CFDictionaryGetValue( dataFromKeychain, kSecAttrGeneric ) ; // the cfData doesn't need CFRELEASE | |
// because it is just GETVALUE, NOT CREATE or COPY. See http://stackoverflow.com/questions/10203990/ | |
const UInt8* datFromKC = CFDataGetBytePtr( cfData ) ; | |
if( cfData ) | |
{ | |
success = 1 ; // we succeeded in retrieving the data | |
memcpy( data, datFromKC, sizeof( T ) ) ; // copy sizeof(T) bytes. | |
// you're responsible to alloc `data`'s memory space. | |
} | |
else { puts( "ERR: kSecAttrGeneric field not set, no CFData" ) ; } // record found, but the kSecAttrGeneric field was not set | |
CFRelease( dataFromKeychain ) ; | |
return success ; // ok | |
} //else {} // OTHER ERROR, such as NOT FOUND | |
return success ; | |
} | |
// Attempts to update, fails if row didn't exist (so creates it) | |
template <typename T> | |
bool SecUpdate( const char* keyname, const T* data ) | |
{ | |
CFMutableDictionaryRef keyLookup = getKeylookup( keyname ) ; | |
// wrap the kvp to change in a dictionary | |
CFDataRef cfData = CFDataCreate( 0, (const UInt8*)data, sizeof(T) ) ; | |
CFMutableDictionaryRef dataAttrib = CFMutableDictionaryCreateEmpty() ; | |
CFDictionaryAddValue( dataAttrib, kSecAttrGeneric, cfData ) ; | |
bool success = SecCheck( SecItemUpdate( keyLookup, dataAttrib ), "SecItemUpdate" ) ; | |
CFRelease( keyLookup ) ; | |
CFRelease( dataAttrib ) ; | |
CFRelease( cfData ) ; | |
return success ; | |
} | |
bool SecDelete( const char* keyname ) | |
{ | |
CFMutableDictionaryRef keyLookup = getKeylookup( keyname ) ; | |
bool success = SecCheck( SecItemDelete( keyLookup ), "SecItemDelete" ) ; | |
CFRelease( keyLookup ) ; | |
return success ; | |
} | |
///////////////////// SAMPLE TEST (delete this part in your own project) | |
// Sample binary data structure, just for serialization example usage | |
struct BinaryData { | |
int v1,v2,v3 ; | |
double r1,r2,r3 ; | |
char data[80]; // NOTICE I ONLY USE PRIMITIVE TYPES | |
// FOR EASY SERIALIZATION (ie no std::string for the string data) | |
// just fill with sample data | |
BinaryData() { | |
v1=1,v2=2,v3=3,r1=70.5,r2=70.6,r3=2000.7 ; | |
sprintf( data, "Hi! I am sample data." ) ; | |
} | |
// unserialize by copying binary data directly | |
BinaryData( const UInt8* dat ) | |
{ | |
memcpy( this, dat, sizeof( BinaryData ) ) ; | |
} | |
void print() const | |
{ | |
printf( "%d,%d,%d,%f,%f,%f,%s\n", v1,v2,v3, r1,r2,r3, data ) ; | |
} | |
} ; | |
void testKeychainParameterized() | |
{ | |
BinaryData bd ; | |
bd.r1=892357.234,bd.r2=900,bd.r3=885.2 ; | |
SecDelete( "mydat" ) ; | |
SecCreate( "mydat", &bd ) ; | |
BinaryData unbd ; | |
SecRead( "mydat", &unbd ) ; | |
sprintf( unbd.data, "REWRITTEN DATA!!!" ) ; | |
SecUpdate( "mydat", &unbd ) ; | |
SecRead( "mydat", &unbd ) ; | |
unbd.print() ; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment