iOS keychain, store more kSecValueData items - objective-c

I am using a keychainwrapper which works with ARC, and with this you can store a password into the keychain with the following code:
[keychainItem setObject:InputField.text forKey:(__bridge id)(kSecValueData)];
And get it out with:
NSString *loc_pwd1 = [keychainItem objectForKey:(__bridge NSString*)kSecValueData];
As I understand the kSecValueData means that the value will be encrypted, but what if I wanna store more encrypted values, can you store with some kind of identifier, so you can have several kSecValueData elements.
Thanks

Your key is associated to one entry in the keychain. You could store an NSDictionary or NSArray (depending of your situation) instead of directly storing the inputField.text. So if you need to add more values to the object of that key, you would simply have to add it to the dictionary/array.
I also recently used the iOS keychain, and I picked up SSKeychain, it looks a bit more straightforward than yours, worth a look! (I'm not saying the one you use is bad, just letting you know what worked for me :P)

Related

KeychainWrapper Objective C Identifier and Key questions

I’m new to both in app purchases and using the keychain. I am using Objective C and KeychainItemWrapper.
For now I want to simply store a number in the keychain. I don’t want anyone outside the app to be able to change it, and while I don’t much care if anyone can see it, I would like to understand what’s encrypted and what’s not as I may want to store private data later.
I have successfully stored a number and retrieved, even after deleting and reinstalling the app, but I’m not at all sure I’m doing it right. How does one choose an identifier and which keys are appropriate?
As you’ll see, I’m pretty unclear on the concepts.
First question: What is the “identifier” and why can’t I use more than one?
Here’s the code:
#property (nonatomic, strong) KeychainItemWrapper *myChain;
. . .
if (myChain == nil)
{
// first question: what identifier should I use?
myChain = [[KeychainItemWrapper alloc] initWithIdentifier:#"test" accessGroup:nil];
}
So once I’ve used the identifier “test”, it appears that I’m stuck with it on this phone. If I use any other value then KeychainItemWrapper fails “unable to add item”. I don’t understand this at all. How do I create different identifiers? Do I want to or need to? What exactly is this identifying?
Moving on to storing. Per the code below, I can store a number, description and comment.
// storing:
// let’s start with the number 10
NSString *testNum = #"10";
NSString *testDescr = #"this is a line of text";
[myChain setObject:testDescr forKey:(__bridge id)(kSecAttrDescription)];
[myChain setObject:testNum forKey:(__bridge id)(kSecValueData)];
[myChain setObject:#"This is a comment" forKey:(__bridge id)(kSecAttrComment)];
// retrieving:
NSString *descOut = [myChain objectForKey: (__bridge id)kSecAttrDescription];
NSData *numberOut = [myChain objectForKey: (__bridge id)(kSecValueData)];
And this all works fine.
But what are the appropriate keys for this usage? I just picked a few at random for testing.
Thank you, I know I’m asking a lot, but I’ve been through the docs and stack overflow and various tutorials and I haven’t really got a good understanding of this.

MCNearbyServiceAdvertiser Multi-Object DiscoveryInfo

I do not understand how this makes sense. I put two objects in the discoveryinfo dictionary inside the MCNearbyServiceAdvertiser object that I create and the browser doesn't see the advertiser, yet when I move the second object out of the dictionary and comment it out at the end of the line, the browser sees the advertiser. Does the discoveryinfo dictionary only accept one object to work? I have a string as the first object and an array as the second. Here is what it looks like:
advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:myPeerID discoveryInfo:#{#"Name": [[NSString alloc] initWithString:myUniqueID], #"Peers": [[NSArray alloc] initWithArray:connectedPeersAry]} serviceType:#"Blahblah"];
And before that line, I tried this simpler format (but went to the above just in case the syntax was the problem):
advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:myPeerID discoveryInfo:#{#"Name": myUniqueID, #"Peers": connectedPeersAry} serviceType:#"FRCSCOUT"];
I guess I can put a dictionary or array inside the discoveryinfo dictionary, but I feel it's a pretty dumb way of doing things because a dictionary shouldn't ever be limited to one object for any case.
I'll go ahead and put my objects in another layer to "conserve space" inside the discoveryinfo dictionary, but if any of you find a better way of doing things or if you are seeing the same problem, please let me know.
You can have multiple objects in the discoveryInfo dictionary, but keep in mind that the dictionary will be encoded in a Bonjour TXT record. This imposes a few restrictions on what can be put into that dictionary.
As stated in the documentation for [MCNearbyServiceAdvertiser initWithPeer:discoveryInfo:serviceType:]:
This data is advertised using a Bonjour TXT record, encoded according to RFC 6763 (section 6). As a result:
The key-value pair must be no longer than 255 bytes (total) when encoded in UTF-8 format with an equals sign (=) between the key and the value.
Keys cannot contain an equals sign.
For optimal performance, the total size of the keys and values in this dictionary should be no more than about 400 bytes so that the entire advertisement can fit within a single Bluetooth data packet.
Well, found my problem. As quoted by Apple in their class reference for MCNearbyServiceAdvertiser:
"The content of discoveryInfo will be advertised within Bonjour TXT records, so you should keep the dictionary small for better discovery performance."
So, looks like I'll have to use a comma separated string of some sort...
EDIT
I misunderstood the Multipeer Connectivity API. I thought the roles were reversed and the Advertiser was basically the public host for a Multipeer Session, but it should be the Browser that invites Advertisers. I now just have the Unique ID generated as the discovery info.
Thank you all for your help and sorry for the API confusion on my part.

How to zeroize an objective-c object under ARC [duplicate]

On iOS, I was wondering, say if I read user provided password value as such:
NSString* strPwd = UITextField.text;
//Check 'strPwd'
...
//How to clear out 'strPwd' from RAM?
I just don't like leaving sensitive data "dangling" in the RAM. Any idea how to zero it out?
Basically you really can't. There are bugs filed with Apple over this exact issue. Additionally there are problems with UITextField and NSString at a minimum.
To reiterate the comment in a now deleted answer by #Leo Natan:
Releasing the enclosing NSString object does not guarantee the string
bytes are zeroes in memory. Also, if a device is jailbroken, all the
sandboxing Apple promises will be of no use. However, in this case,
there is little one can do, as it is possible to swap the entire
runtime in the middle of the application running, this getting the
password from the memory.
Please file another bug with apple requesting this, the more the better.
Apple Bug Reporter
While NSString doesn't have this capability (for reasons of encapsulation mentioned elsewhere), it shouldn't be too hard to have your app use regular old C-strings, which are just pointers to memory. Once you have that pointer, it's fairly easy to clear things out when you're done.
This won't help with user-entered text-fields (which use NSString-s and we can't change them), but you can certainly keep all of your app's sensitive data in pointer-based memory.
I haven't experimented with it (I don't have a current jailbroken device), but it also might be interesting to experiment with NSMutableString -- something like:
// Code typed in browser; may need adjusting
// keep "password" in an NSMutableString
NSInteger passLength = password.length;
NSString *dummy = #"-";
while (dummy.length < passLength)
{
dummy = [dummy stringByAppendingString: #"-"];
}
NSRange fullPass = NSMakeRange(0, passLength);
[password replaceOccurancesOfString: password
withString: dummy
options: 0
range: fullPass];
NOTE: I have no idea if this does what you want; it's just something I thought of while typing my earlier answer. Even if it does work now, I guess it depends on the implementation, which is fragile (meaning: subject to breaking in the future), so shouldn't be used.
Still, might be an interesting exercise! :)

Using NSDictionary for HTTP POST requests with identical keys

I have an app which shows the user a number of messages. I need to inform the server that the user has read the messages. To do this, I need to send an HTTP POST request to /api/mark_as_read/ with the IDs of the messages to mark as read, like:
ID: 1234
ID: 5678
ID: 90AB
Note that the HTTP spec allows possibly duplicate keys, which the API uses in this case.
I am currently using AFHTTPClient (part of the AFNetworking library). POST parameters are passed into AFHTTPClient as an NSDictionary, which of course has unique keys, meaning I can only pass in one value called "ID".
I've searched for a while for a solution and haven't been able to dig anything up. But I'm sure lots of people have encountered the issue before. How do I pass my HTTP client a dictionary with non-unique keys in Objective-C?
Update - I was able to resolve the issue by updating the AFNetworking library and putting an array of IDs in one NSDictionary value. (See below.)
Try passing an array for values:
NSArray *values = [NSArray arrayWithObjects:#"1234", #"5678", #"90AB", nil];
NSDictionary *params = [NSDictionary dictionaryWithObjectAndKey:values, #"ID"];
a dictionary is a set and keys are unique by 'definition'
no way this can be done with standard dict
BUT one could inherit set and just use keys array and dups array.
afnetwork would use it as a dictionary and it would provide the output you want.
they might have such a class OR you can easily write one kinda similiar
http://cocoaheads.byu.edu/code/CHDataStructures
** this is inefficient and should only be used for THIS purpose **

How do I store date using NSUserDefaults after I have the number in a variable using Xcode

I'm new and stuck any help would be great! I am using Xcode to build my app.
I have an IBAction "button pressed", this generates random numbers, then I have a bunch of if statements. Ultimately if things match up my long long variable "coins" has a numeric value. Right now I just change that value as in coins = coins + 10 and then display it in a UILabel. I now need to store that value, after all the calculations, whenever the user presses the button. So that their total of "coins" is being added to or subtracted from. Additionally I will want to add and In App Purchase that ads to the "coins" variable. So I know I need a way to store the value, even when the app is closed and re-opened.
Is there a way to use NSUserDefaults to store that value, either within the original IBAction buttonPressed? Also Or any help at all or suggestions would be great! Thanks and sorry if this sounds lame, but I'm on my first app :)
To store a number in NSUserDefaults:
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithUnsignedLongLong:coins] forKey:#"coins"];
To retrieve from NSUserDefaults and store it into coins:
coins = [[[NSUserDefaults standardUserDefaults] objectForKey:#"coins"] unsignedLongLongValue];
This is assuming you've declared coins as being an unsigned long long, and not a signed one. Also, there are convenience methods for storing NSIntegers if you want to go through those.
EDIT: Just realized you want to have IAPs, should've read more carefully. I recommend against storing anything of value (passwords, usernames, purchasable currency, highscores that can be submitted to an online leaderboard, etc) in NSUserDefaults. It's stored as a simple .plist (XML) which could be edited relatively easily as far as I understand.