Pass by reference and objc_unretainedPointer from iOS5 - ios

I'm attempting to use the SFHF keychain classes (from here) with an IOS 5 project. I've successfully converted most of the class over to abide by the new ARC rules.
I'm having some trouble with one small section of the code as follows
OSStatus status = SecItemCopyMatching((CFDictionaryRef) objc_unretainedPointer(attributeQuery), (CFTypeRef *) objc_unretainedPointer(&attributeResult)
This gives the following syntax issue:
warning: Semantic Issue: Incompatible pointer types passing 'NSDictionary *__strong *' to parameter of type 'id'
I'm rather new to iOS development and this has me pretty much stumped right now. Any help is greatly appreciated.

This is the declaration of the API:
OSStatus SecItemCopyMatching (
CFDictionaryRef query,
CFTypeRef *result
);
The result is a pass-by-reference return value.
Declare a local variable of type CFTypeRef, call the function and pass the address of said local as per the API, then do any ARC specific shenanigans after the function call.
Yes -- the error is correct. You aren't passing a CFTypeRef, you are passing a CFTypeRef* and objc_unretainedPointer() has no clue what to do with that.
Do something like:
CFTypeRef localResult
SecItemCopyMatching(query, &localResult);
if (... no error ...) {
result = objc_retainedObject(localResult);
}

Had trouble with this call, this is the code I got to work:
NSMutableDictionary *queryDictionary = [[NSMutableDictionary alloc] init];
// Set some properties.
[queryDictionary setObject:[key dataUsingEncoding:NSUTF8StringEncoding] forKey:(__bridge id)kSecAttrGeneric];
[queryDictionary setObject:(id) kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];
[queryDictionary setObject:(__bridge id) kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
[queryDictionary setObject:(id) kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
[queryDictionary setObject:(__bridge id) kSecClassGenericPassword forKey:(__bridge id)kSecClass];
CFTypeRef attributes;
OSStatus keychainError = SecItemCopyMatching((__bridge CFDictionaryRef)(queryDictionary), &attributes);
if (keychainError == errSecSuccess)
{
NSDictionary *returnedDictionary = (__bridge_transfer NSDictionary *)attributes;
NSData *rawData = [returnedDictionary objectForKey:(__bridge id)kSecValueData];
return [[NSString alloc] initWithBytes:[rawData bytes] length:[rawData length] encoding:NSUTF8StringEncoding];
}

Related

SecKeyRef causes EXC_BAD_ACCESS (code=1) error when using SecItemCopyMatching

I'm fairly new to crypto on iOS, and I've been running into an error that I haven't been able to find a solution for:
Whenever I try to get a SecKeyRef to a public key in the iOS keychain and use it, I end up with a EXC_BAD_ACCESS error. The SecKeyRef (called "publicKeyReference" in my code below is initially set to NULL, but it should have a value after the SecItemCopyMatching method is called, which can be seen from the memory address in the debugger window.
Here's my code:
SecKeyRef publicKeyReference = NULL;
NSData* publicTag = [publicKeyIdentifier dataUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init];
// Set the public key query dictionary.
[queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnPersistentRef];
// Get the key.
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyReference);
// Encrypt using the public.
sanityCheck = SecKeyEncrypt( publicKeyReference,
PADDING,
plainBuffer,
plainBufferSize,
&cipherBuffer[0],
&cipherBufferSize
);
And Here's some screenshots of the error and the debug window:
It seems that something is being assigned to the SecKeyRef, since the value of the address isn't "0x0", but I've been continually getting the EXC_BAD_ACCESS error regardless of what I've tried. Any and all help is greatly appreciated on the issue.
I got the same error with SecKeyCreateEncryptedData function (which is intended for replacing the usage of SecKeyEncrypt on iOS 10+ ), it is not caused by the SecKeyRef, but the CFDataRef which is the encrypted data. So I suggest to check the encrypted data like plainBuffer, plainBufferSize, etc.

updating kSecValueData in iOS using secItemUpdate Error Code -50

I am at a loss here, I create a keychain query, add the item if it does not already exist, then I try and update kSecValueData with a test string and it returns error code -50, which means there was an error with one or more parameters I entered...
NSString *initial = #"";
NSData *initData = [initial dataUsingEncoding:NSUTF8StringEncoding];
//Create Search Dictionary For Phone Number...
NSDictionary *secPhoneItem = #{ (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecReturnData : (__bridge id)kCFBooleanTrue,
(__bridge id)kSecValueData : initData
};
//Check to see if keychain already exists by using secItemCopyMatching and associated status code
OSStatus PhoneCheckStatus = SecItemCopyMatching((__bridge CFDictionaryRef)secPhoneItem, NULL);
//Check Status Code Phone
if (PhoneCheckStatus == errSecItemNotFound) //If Phone Keychain Item Does Not already Exist
{
//Add Phone Number To Keychain
SecItemAdd((__bridge CFDictionaryRef)secPhoneItem, NULL);
}
//Update Phone Number to String
NSString *string = #"Test String";
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *attributesForUpdate = #{
(__bridge id)kSecValueData : data
};
OSStatus news = SecItemUpdate((__bridge CFDictionaryRef)secPhoneItem, (__bridge CFDictionaryRef)attributesForUpdate);
NSLog(#"Update Status Code: %ld", news);
If anyone knows why or can shed some info, the only lead I have right now from Apples Documentation is that you can only pass real attributes into secItemUpdate(), not "meta" attributes.
So after rereading the documentation, I found out that the key-value pair (__bridge id)kSecReturnData : (__bridge id)kCFBooleanTrue cannot be used in the secItemUpdate()' query parameter. To fix my problem and help better refine the search, I added the key-value pair(__bridge id)kSecAttrDescription : someUniqueData` to the search query along with the class item specification then making my attributes dictionary returned status 0: SUCCESS!!!
Key and certificate Attribute Update-----------
for the normal SecItemUpdate code gave two error:-25300(item not found),-50(trying to update more than one item in one update method)
here is the code for update method:
//Search Dictionary.....
NSMutableDictionary *searchDict = [[NSMutableDictionary alloc] init];
NSData *privateKeyTag = [NSData dataWithBytes:[keyIdentifier UTF8String] length:keyIdentifier.length];
[searchDict setObject:privateKeyTag forKey:(__bridge id)kSecAttrApplicationTag];
[searchDict setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[searchDict setObject:(__bridge id)(kSecAttrKeyTypeRSA) forKey:(__bridge id<NSCopying>)(kSecAttrKeyType)];
[searchDict setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
[searchDict setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id<NSCopying>)(kSecReturnData)];
status = SecItemCopyMatching((__bridge CFDictionaryRef)searchDict, (CFTypeRef*)&item);
if (status != errSecSuccess)
{//your code for error handling }
//dictionary for the attribute that are going to update in key of certificate
//if youwant to update your passward the add the passward attribute
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,kSecAttrAccessible, nil];
/*removing the some of the from the searching dictionary*/
[searchDict removeObjectForKey:(__bridge id)kSecReturnData];
[searchDict removeObjectForKey:(__bridge id)kSecMatchLimit];
//Creating the Array for every item in the keychain that is cert of key as Search Dictionary
NSArray *secItemclasses= #[(__bridge id)kSecClassKey,(__bridge id)kSecClassCertificate];
for (id secitemclass in secItemclasses) {
//updating the key as well as certificate attribute....//
status = SecItemUpdate((__bridge CFDictionaryRef)searchDict,(__bridge CFDictionaryRef)dict);
}
if(status != errSecSuccess)

iOS SecKeyRef to NSData

I have two keys, public and private, that are both stored in SecKeyRef-variables. For simplicity's sake, let's start with the public one. What I wanna do is export it to an NSData object. For that, there is an almost famous code snippet provide by Apple, which is here:
- (NSData *)getPublicKeyBits {
OSStatus sanityCheck = noErr;
NSData * publicKeyBits = nil;
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
// Set the public key query dictionary.
[queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(id)kSecAttrApplicationTag];
[queryPublicKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];
// Get the key bits.
sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyBits);
if (sanityCheck != noErr)
{
publicKeyBits = nil;
}
[queryPublicKey release];
return publicKeyBits;
}
I have Xcode 4.6.2, however, and the code appears erroneous ("__bridge" is added before each conversion to id). The new version looks like this:
- (NSData *)getPublicKeyBitsFromKey:(SecKeyRef)givenKey {
OSStatus sanityCheck = noErr;
NSData * publicKeyBits = nil;
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
// Set the public key query dictionary.
[queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnData];
// Get the key bits.
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyBits);
if (sanityCheck != noErr)
{
publicKeyBits = nil;
}
return publicKeyBits;
}
There are still two errors, though:
use of undeclared identifier 'publicTag'
Cast of an indirect pointer to an Objective-C pointer to 'CFTypeRef ' (aka 'const void *') is disallowed with ARC
Now, I hope that after your help, the first issue will no longer be a problem, for I do not want to build a query or whatnot to extract the key from the keychain. I have it in a variable and I wish to extract it from there. The variable's name is givenPublicKey, and that's the key I wish to convert to NSData.
So, how would I go about doing this and solving this ARC-issue?
Follow-up: How can I export a private key to NSData, since I've read several time that the function I'm trying to work with only works for public keys.
use of undeclared identifier 'publicTag'
The publicTag is just some unique identifier added to the Keychain items. In the CryptoExercise sample project it is defined as
#define kPublicKeyTag "com.apple.sample.publickey"
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag;
NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
Cast of an indirect pointer to an Objective-C pointer to 'CFTypeRef ' (aka 'const void *') is disallowed with ARC
This can be solved by using a temporary CFTypeRef variable:
CFTypeRef result;
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, &result);
if (sanityCheck == errSecSuccess) {
publicKeyBits = CFBridgingRelease(result);
}
I do not want to build a query or whatnot to extract the key from the keychain. I have it in a variable and I wish to extract it from there ...
As far as I know, you have to store the SecKeyRef to the Keychain temporarily. SecItemAdd
has the option to return the added item as data. From the documentation:
To obtain the data of the added item as an object of type CFDataRef,
specify the return type key kSecReturnData with a value of
kCFBooleanTrue.
Putting all that together, the following code should do what you want:
- (NSData *)getPublicKeyBitsFromKey:(SecKeyRef)givenKey {
static const uint8_t publicKeyIdentifier[] = "com.your.company.publickey";
NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
OSStatus sanityCheck = noErr;
NSData * publicKeyBits = nil;
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
[queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
// Temporarily add key to the Keychain, return as data:
NSMutableDictionary * attributes = [queryPublicKey mutableCopy];
[attributes setObject:(__bridge id)givenKey forKey:(__bridge id)kSecValueRef];
[attributes setObject:#YES forKey:(__bridge id)kSecReturnData];
CFTypeRef result;
sanityCheck = SecItemAdd((__bridge CFDictionaryRef) attributes, &result);
if (sanityCheck == errSecSuccess) {
publicKeyBits = CFBridgingRelease(result);
// Remove from Keychain again:
(void)SecItemDelete((__bridge CFDictionaryRef) queryPublicKey);
}
return publicKeyBits;
}
I hope that this works, I cannot test it at the moment.
Follow-up: How can I export a private key to NSData, since I've read several time that the function I'm trying to work with only works for public keys.
I don't know.

How to use ECC in iOS

Is there any example for use ECC in iOS?
I noticed that the kSecAttrKeyTypeEC in Apple Developer Documents, but I can't use it to generic Key pair.
Below code is modified from the example CryptoExercise
// Container dictionaries.
NSMutableDictionary * privateKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary * publicKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary * keyPairAttr = [[NSMutableDictionary alloc] init];
// Set top level dictionary for the keypair.
[keyPairAttr setObject:(id)kSecAttrKeyTypeEC forKey:(id)kSecAttrKeyType];
[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(id)kSecAttrKeySizeInBits];
// Set the private key dictionary.
[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent];
[privateKeyAttr setObject:privateTag forKey:(id)kSecAttrApplicationTag];
// See SecKey.h to set other flag values.
// Set the public key dictionary.
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent];
[publicKeyAttr setObject:publicTag forKey:(id)kSecAttrApplicationTag];
// See SecKey.h to set other flag values.
// Set attributes to top level dictionary.
[keyPairAttr setObject:privateKeyAttr forKey:(id)kSecPrivateKeyAttrs];
[keyPairAttr setObject:publicKeyAttr forKey:(id)kSecPublicKeyAttrs];
// SecKeyGeneratePair returns the SecKeyRefs just for educational purposes.
sanityCheck = SecKeyGeneratePair((CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef);
LOGGING_FACILITY( sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL, #"Something really bad went wrong with generating the key pair." );
The sanityCheck always return -50 which means 'errSecParam'.
I really don't know how to use it, thank you for read this.
NSDictionary *parameters = #{
(__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeEC,
(__bridge id)kSecAttrKeySizeInBits: #256,
(__bridge id)kSecPrivateKeyAttrs: #{
(__bridge id)kSecAttrIsPermanent: #YES,
(__bridge id)kSecAttrApplicationTag: [#"my.key.tag" dataUsingEncoding:NSUTF8StringEncoding],
},
(__bridge id)kSecPublicKeyAttrs: #{
(__bridge id)kSecAttrIsPermanent: #YES,
(__bridge id)kSecAttrApplicationTag: [#"my.key.pubtag" dataUsingEncoding:NSUTF8StringEncoding],
}
};
SecKeyRef publicKey, privateKey;
OSStatus status = SecKeyGeneratePair((__bridge CFDictionaryRef)parameters, &publicKey, &privateKey);
This works, double check your key size parameter.
Just a note, currently EC keys can only be used for signing/verifying data. Encryption/decryption returns errSecUnimplemented = -4.
CryptoKit now supports Ed25519 in iOS13+
https://developer.apple.com/documentation/cryptokit/curve25519/signing

Getting error using CFTypeRef with ARC

I basically followed this tutorial, and soon realized the project wouldn't compile because I was using ARC. I managed to suppress all the errors using __bridge (>.>) but I am still getting one error message, and I managed to read this stack question, but didn't understand how to apply the resolution to my problem.
Basically the method that is giving me the problem looks like this:
+ (NSString*)getPasswordForKey:(NSString*)aKey
{
NSString *password = nil;
NSMutableDictionary *searchDictionary = [self dictionaryForKey:aKey];
[searchDictionary setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
[searchDictionary setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
NSData *result = nil;
SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, (CFTypeRef *)&result);
if (result)
{
password = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}
return password;
}
I think you are making unnecessarily complex type casts by trying to cast the pointer-to-pointer argument. How about this:
CFTypeRef result = NULL;
BOOL statusCode = SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, &result);
if (statusCode == errSecSuccess) {
NSData *resultData = CFBridgingRelease(result);
password = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
}

Resources