Swift, can't create a dictionary to hold key values? - ios

I want to create a dictionary to hold these values but I get the following error
import Security
var keychainQuery: Dictionary =
[
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: userAccount,
dataFromString: kSecValueData
]
Cannot convert the expression's type 'Dictionary' to type '#lvalue Unmanaged<AnyObject>!'

In most cases with Swift, I've found it much less painful to declare the types of the dictionary. In your case:
var keychainQuery: Dictionary<String, AnyObject> = [
...
]
Should work.

As far as I know for now, building a Keychain query with a Swift Dictionary is not possible.
Keys like kSecClass are of a non-hashable type and so can't be put in a Dictionary (that strictly needs items that share the same protocol btw.).
One of the ways I have seen to circumvent this problem is to use an NSDictionary and pass keys and values as Arrays. This code only seems to work in Xcode 6 Beta 1.
var query = NSMutableDictionary(objects: [kSecClassGenericPassword, service, account, secret], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecValueData])
Another approach can be seen on this Stackoverflow answer.
Based on that approach you would convert all the key and value constants to Strings using the \() syntax.
The query would then look like this:
var query = ["\(kSecClass)": "\(kSecClassGenericPassword)", "\(kSecAttrService)": service, "\(kSecAttrAccount)": account, "\(kSecValueData)": secretData]
I tested this solution and got an error saying that I'm trying to add already existing keys. So this doesn't seem to work either.

Related

A NSOperationQueue error I'm trying to figure out from Crashlytics

I got this error on Crashlytics this morning and I can't firgure out what the problem is. It would be awesome to get your opinions about it. I thInk it's most likely a multi threading issue. But I'm not able to pin point exactly what it is.
EDIT: I dug a little deeper and here's the code that's failing:
Also, I've figured out that the error is:
Could not cast value of type '__NSSingleObjectArrayI' (0x1aa60bca0) to 'NSMutableArray' (0x1aa60bd90).
2016-09-22 08:29:34.136764 GrabbnGo[4204:822290] Could not cast value of type '__NSSingleObjectArrayI' (0x1aa60bca0) to 'NSMutableArray' (0x1aa60bd90).
This was working perfectly all this while and it's suddenly causing problems and the app is already on the store :/
json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as? [String: AnyObject]
let str = NSString(data: data!, encoding: NSUTF8StringEncoding)
print(str)
let OrderDictionary = json as NSDictionary
let result = OrderDictionary.objectForKey("result") as! NSMutableArray
OK, this sort of has a unique answer. Basically, you're misusing NSJSONSerialization, and it's a time-bomb bug that eventually bit you.
According to the documentation:
https://developer.apple.com/reference/foundation/jsonserialization
All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
This is a very simple and clear sentence. You should respect it. It says nothing about NSMutableArray, only NSArray. The JSON parser is using whatever compatible (subclass) object for the NSArray that it so chooses. If there's only one item in the array, it appears that the internal type __NSSingleObjectArray is a lot more efficient, probably both in speed and memory.
There is almost certainly a change on the server (or customer behavior) such that result now (often? sometimes? always?) has only 1 item in it, so the JSON parser made a different choice that you're not supposed to care about.
All you need to do is change it to NSArray and construct an NSMutableArray from it if you really need:
let myJSONParsedArray: NSArray = ...
let myMutableArray = NSMutableArray(myJSONParsedArray)
But first learn about why force unwrapping is so dangerous, especially in a network code environment, when you can't trust any data at all, regarding type and value and maliciousness, ever!!

compare two secKey (public keys) in ios Swift

I want to ssl public key pinning in swift, I read lot of examples how to do that, last think who I can't find is How to compare two public keys in SecKey object format.
Example:
let serverPublicKey = SecTrustCopyPublicKey(secTrust) /*return SecKey object from actual SecTrust*/
let clientPublicKey = getLocalPublicKeyFromDer() /*return SecKey from .der local*/
how to compare them? At now I do that and it works:
if(serverPublicKey! as AnyObject).isEqual(clientPublicKey){
/*Key is the same, pinning OK!*/
}
find it way on gitHub: https://github.com/teamcarma/IOS-AlamofireDomain/blob/master/Source/ServerTrustPolicy.swift
but is cast to AnyObject a good idea? How to work isEqual on casted SecKey? Can any explain me?
ps.
Another idea is getting base64 from SecKey - I try and it also works, but it require a KeyChain temp operations and look no profesional.
Cited from the headers:
"Most SecKeychainItem functions will work on an SecKeyRef."*
You may cast SecKeyRef to a SecKeychainItem. If this is a valid operation (that is, the key is a keychain item), you may apply function
SecKeychainItemCreatePersistentReference
and get a CFData object, filled with attributes and data. Then check with memcpyon the bytes or cast it to a NSData object and check with isEqualToData. Don't forget to release the CFData object.
Edit
On iOS, as far as I known, the only reliable approach is to copy the data (or secret) into the keychain, using a temporary key, so that you can find it again, and then extract the data. It's cumbersome, but if you just implement it in a minimalistic way, it should not take more than 30 lines of code. I have a working example.
I The usual disclaimer: Use this at your own risk, and always be careful with security stuff.
iOS10 added:
CFDataRef _Nullable SecKeyCopyExternalRepresentation(SecKeyRef key, CFErrorRef *error)
so you can now create two Data (NSData) objects, then compare those.
Have a look at this answer for just getting the NSData: Can I get the modulus or exponent from a SecKeyRef object in Swift?
You can then compare the two NSData instances using isEqualToData:
I don't have expereince in the domain, but if they are two strings (irrespectiveof their content), you would basically do a simple check:
if(string1 == string2)
{
//condition logic
}

Building a dictionary at runtime in Swift 2.0

I am trying to build a simple dictionary at runtime in Swift. I am fairly new to Swift, but experienced in Obj-C (and missing it).
I am gathering some JSON data via a web service and looping through its elements. During this loop I need to build the dictionary. Here is the dictionary I need to generate
"gauge": {
"gaugeID" : "03185"
"name" : "SOME GAUGE NAME"
"cfs" : 8410
"stage" : 7.05
}
Since values for cfs and flow may not be present, I need to add these values to the dictionary conditionally.
I have declared the following dictionary
var dictEntry:[String:AnyObject]
Then as I loop through the dictionary I need to build each key-value and add it to the dictEntry dictionary. Every attempt I've made to do this fails. In Obj-C I could do the following:
[entryDict setValue:someValue forKey:#"cfs"];
How is this possible in Swift? Thanks!
Here you go:
dictEntry["cfs"] = someValue

Which attribute do you use to store passwords in Keychain (using iOS)?

(I'm using Swift.)
I have the following Dictionary to pass to SecItemAdd().
var attributes = [kSecClass as String: kSecClassInternetPassword,
kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecAttrDescription: "Alexander's Application Account Password",
kSecAttrAccount: self.usernameField.text, /* This is an outlet to a UITextField */
kSecAttrServer: "http://www.hausofalexander.tk/", /* This is my servers' domain */
kSecAttrAuthenticationType: kSecAttrAuthenticationTypeHTTPBasic,
kSecValueData: self.passwordField.text]; /* This is also and outlet to a UITextField */
Then I call SecItemAdd(attributes as CFDictionaryRef, nil)
I'm receiving errSecParam (which is error -50) because I'm passing an invalid parameter (attributes in this case) to SecItemAdd(). I'm assuming this is because I've a pair for kSecValueData in my attributes variable which isn't applicable to kSecClassInternetPassword for iOS according to the reference: Make sure you scroll down a bit to the kSecClassInternetPassword item to see the applicable attributes for a kSecClass of this type.
However, I'm completely able to use kSecValueData as an attribute and set a value for it with Cocoa (as in Mac development). I've made a Mac application to test this then looked for the item with Keychain Access to find that it was indeed there. However, this was using a kSecClass of kSecClassGenericPassword (I don't know if this is relevant, but this test application I'd written in Objective-C). At this point I'd decided to check which attributes are available if I were to use kSecClassGenericPassword in my iOS application. Sadly, kSecValueData isn't available for that kSecClass, either.
So where in the world do I set the password if kSecValueData isn't an applicable attribute to kSecClassGenericPassword, kSecClassInternetPassword, nor any other kSecClass items?
I've noticed that I'm allowed to use kSecAttrGeneric with these kSecClass items (namely kSecClassGenericPassword and kSecClassInternetPassword) and it takes a value of CFDataRef. I'm assuming that means I'm expected to pass NSData (as CFDataRef) to this attribute, but what do I pass as the NSData? Should that be a Dictionary? Should that be an NSASCIIStringEncoding value of the password String? Does this mean I can make any kSecAttr* value myself somehow?
Thanks for reading. If I've confused you, do tell and I'll happily explain in further detail.

Trouble using NSDictionary convenience init in Swift

I seem to be having a problem trying to create an NSDictionary in Swift using one of the convenience intializers. My confusion though lies in the fact that the error says my function signature is wrong, yet I'm using the function signature that XCode autocompleted for me.
My Code:
var query = NSDictionary(objects: [kSecClass, kSecAttrService, kSecAttrAccount, kSecReturnData], forKeys: [kSecClassGenericPassword, "healthBIT.lastSync", key, true])
The XCode provided signature:
var query = NSDictionary(objects: <#[AnyObject]#>, forKeys: <#[AnyObject]#>)
The error when compiling is: Extra argument 'forKeys' in call
What am I missing here? Am I just too sleep deprived to see the obvious? Or is it just a stupid mistake derived from my relative inexperience with Swift?
PS: I am trying to use NSDictionary here instead of a normal Swift dict because Swift dicts can't store mixed types, and I need to pass this to the underlying C based Keychain API.
After a discussion in chat, it turned out the problem is about the key variable referenced from the code sample provided in the question, which comes from a for loop:
for key in lastSync {
...
}
the error is that key is a (key, value) tuple, and that's causing issues when using it in a NSDictionary (objc types cannot handle swift specific features, such as generics, tuples, etc.).
The problem is solved by expanding the tuple accordingly:
for (key, value) in lastSync {
...
}
or, if value is not used:
for (key, _) in lastSync {
...
}

Resources