3 Replies
      Latest reply on May 10, 2016 1:13 AM by eskimo
      catlan Level 1 Level 1 (0 points)

        Hi,

         

        I'm trying to fix an issue in RMStore and reviewing its keychain part. Based on best practise posted here and WWDC 2013 Session 709 "Protecting Secrets with the Keychain".

         

        The issue in short: for a very small percentage of customers, the IAPs stored in the keychain are not accessible and restore of these IAPs also fails. No developer was able to reproduce the issue so far.

         

        The following question will only focus on kSecClassGenericPassword and OS X.

         

        When querying for an item it's best to query for just the keys that are required to make the item unique. For a generic password that's kSecClass, kSecAttrService and kSecAttrAccount. (https://devforums.apple.com/message/959334#959334)

         

        RMStore also stores and searches for kSecAttrGeneric, see here.

         

        It seems to be no where document how important the right corresponding value per attribute is. A lot of sample code on the internet and the Apple GenericKeychain sample doesn't follow the documentation.

         

        kSecAttrGeneric - Generic attribute key.
        The corresponding value is of type CFDataRef and contains a user-defined attribute. Items of class kSecClassGenericPassword have this attribute.

         

        kSecAttrAccount - Account attribute key.
        The corresponding value is of type CFStringRef and contains an account name. Items of class kSecClassGenericPassword and kSecClassInternetPassword have this attribute.

         

        Yet in GenericKeychain sample for kSecAttrGeneric a CFStringRef is used. And RMStore sets CFDataRef for kSecAttrAccount, see here.

         

        Update: In the debugger if I query kSecReturnAttributes the attributes don't contain kSecAttrAccount. Only cdat, class, gena, labl, mdat, svce. So I assume because of the wrong type it doesn't get stored. Or is it because one of the two is choosen for uniques and the other is disregard?

         

         

        Question 1: Is for kSecClassGenericPassword the attributes kSecClass, kSecAttrService, kSecAttrGeneric enough? Or does it require kSecAttrAccount?

         

        Question 2: How is the query behaviour for attributes that are not required to make the item unique. Right now the code searchs for kSecClass, kSecAttrService, kSecAttrGeneric and kSecAttrAccount. See here.  But in the store we only have kSecClass, kSecAttrService, kSecAttrGeneric. Yet the search works most of the times.

         

        Recap of the issue: unable to access IAP and restore fails.

         

        This suggest to me that the search for kSecClass, kSecAttrService, kSecAttrGeneric and kSecAttrAccount works most of the times. But can break for a currently unknown reason. The restore fails because it uses the same search and if nothing is found it trys do add the item. See here. But the add fails because an item with the unique part of kSecClass, kSecAttrService and kSecAttrGeneric already exists.

         

        Would appreciate any insight into this!

        • Re: SecItemCopyMatching, types and uniques
          eskimo Apple Staff Apple Staff (12,295 points)

          To address your question about types, I’m going to have to start with some background.  There are two implementations of the SecItem API:

          • the iOS implementation, which is also used for iCloud Keychain on OS X

          • the OS X implementation, which is a compatibility shim that bridges over to the traditional keychain

          Note Both are available in Darwin.  Search the Security project for SecItemUpdate_ios and SecItemUpdate_osx to see how this works under the covers.

          The iOS implementation is based on SQLite.  If you’re familiar with SQLite you’ll know that it is, at its core, untyped, and thus the iOS implementation has to do its own type conversion.  This is why that implementation is somewhat forgiving on the types front.  However…

          IMPORTANT  I strongly recommend that you use the types specified in the header.  Other types do work but that’s an accident of the implementation rather than a designed in feature.  Moreover, as there are two implementations, it’s not always the case that these accidents line up.

          I realise that this rule is broken by various bits of Apple sample code.  I’d be more than happy if you filed a bug about any cases where you notice that.


          With regards the uniqueness constraints of various keychain items, the only definitive doc I know of for this is my post on the old DevForums.  I took a quick look at the current state of affairs for kSecClassGenericPassword and it has not change substantially since then. this is officially documented in the reference docs for errSecDuplicateItem.

          Uniqueness is only relevant for calls that update the database (SecItemAdd and SecItemUpdate).  The database enforces that each update maintain the uniqueness constraints.  For example, if the database contains the following generic passwords:

          svce        acct
          ----        ----
          door        gumby
          door        pokey
          

          here’s what you should expect:

          • add {"svce": "door", "acct": "gumby"} ? errSecDuplicateItem

          • add {"svce": "door", "acct": "minga"} ? errSecSuccess

          • add {"svce": "window", "acct": "gumby"} ? errSecSuccess

          • update {"svce": "door", "acct": "gumby"} to {"svce": "door", "acct": "pokey"} ? errSecDuplicateItem

          • update {"svce": "door", "acct": "gumby"} to {"svce": "door", "acct": "minga"} ? errSecSuccess

          • update {"svce": "door", "acct": "gumby"} to {"svce": "window", "acct": "gumby"} ? errSecSuccess

          In contrast, uniqueness criteria are irrelevant to database queries.  Database queries a very simple:

          • if you specify an attribute, you only get items where that attribute matches

          • if you leave out an attribute, you get all items

          The most common cause of problems with this design is a mismatch between your query and the uniqueness criteria.  For example, assuming the same database shown above, consider this sequence:

          1. copy {"svce": "door", "acct": "gumby", "gena": "foo"} ? errSecItemNotFound

          2. add {"svce": "door", "acct": "gumby", "gena": "foo"} ? errSecDuplicateItem

          Argh!

          However, if you understand the uniqueness criteria the reason for this is obvious: the query fails because you’ve included the kSecAttrGeneric attribute and that doesn’t match what’s in the database, while the add fails because that combination of kSecAttrAccount and kSecAttrService already exists in the database.

          So, to come back to your specific questions:

          Question 1: Is for kSecClassGenericPassword the attributes kSecClass, kSecAttrService, kSecAttrGeneric enough? Or does it require kSecAttrAccount?

          That depends.  The kSecAttrAccount attribute is not required, in that if you don’t supply it when you create the item, you effectively get an empty value.  However, that empty value can cause confusion later on, so…

          IMPORTANT I strongly recommend that you include values for all of the attributes included in the uniqueness constraint.  That makes things simpler later on.  If you don’t have a meaningful value, use a fixed value that’s not going to collide with anything (like a reverse DNS name).

          Question 2: How is the query behaviour for attributes that are not required to make the item unique. Right now the code searchs for kSecClass, kSecAttrService, kSecAttrGeneric and kSecAttrAccount. See here.  But in the store we only have kSecClass, kSecAttrService, kSecAttrGeneric. Yet the search works most of the times.

          I believe I’ve addressed this point via the discussion above.

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

            • Re: SecItemCopyMatching, types and uniques
              catlan Level 1 Level 1 (0 points)

              Hi Quinn,

               

              Thank you for your detailed answer.

               

              A bug for the wrong type on GenericKeychain is filled under 26167065.

               

              My test code shows that setting the wrong type on kSecAttrAccount results in the iCloud Keychain on OS X ignoring this attribute. I assume in the background it will create empty value like you suggest.

               

              Running some test code, on a customer system which is effected with the issue, showed that the kSecAttrGeneric attribute was missing in the keychain item. Which explains why SecItemCopyMatching returns errSecItemNotFound.

               

              So the question now is, how got this attribute missing? I don't see any code in RMStore that could be responsible for this. One idea is if a migration from traditional keychain to iCloud Keychain could be responible. Or the otherway around.

               

              Cheers,

              Christopher

                • Re: SecItemCopyMatching, types and uniques
                  eskimo Apple Staff Apple Staff (12,295 points)

                  So the question now is, how got this attribute missing?

                  I don’t have any good theories on that one, alas.

                  Share and Enjoy

                  Quinn “The Eskimo!”
                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                  let myEmail = "eskimo" + "1" + "@apple.com"