Skip to content

Instantly share code, notes, and snippets.

@superwills
Created October 11, 2013 18:05
Show Gist options
  • Save superwills/6939348 to your computer and use it in GitHub Desktop.
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.
#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