¿Qué hace que un elemento de llavero sea único (en iOS)?

Mi pregunta se refiere a los llaveros en iOS (iPhone, iPad, …). Creo (pero no estoy seguro) que la implementación de llaveros bajo Mac OS X plantea la misma pregunta con la misma respuesta.


iOS proporciona cinco tipos (clases) de elementos de llavero. Debe elegir uno de esos cinco valores para la clave kSecClass para determinar el tipo:

 kSecClassGenericPassword used to store a generic password kSecClassInternetPassword used to store an internet password kSecClassCertificate used to store a certificate kSecClassKey used to store a kryptographic key kSecClassIdentity used to store an identity (certificate + private key) 

Después de mucho tiempo de leer documentación sobre manzanas, blogs y entradas al foro, descubrí que un artículo de llavero de tipo kSecClassGenericPassword obtiene su singularidad de los atributos kSecAttrAccessGroup , kSecAttrAccount y kSecAttrService .

Si esos tres atributos en la solicitud 1 son los mismos que en la solicitud 2, recibirá el mismo elemento de clave de llave genérica, independientemente de cualquier otro atributo. Si uno (o dos o todos) de estos atributos cambia su valor, entonces obtiene diferentes elementos.

Pero kSecAttrService solo está disponible para elementos del tipo kSecClassGenericPassword , por lo que no puede formar parte de la “clave única” de un elemento de ningún otro tipo, y parece que no hay documentación que indique claramente qué atributos determinan de manera única un elemento de llavero. .

El código de ejemplo en la clase “KeychainItemWrapper” de “GenericKeychain” usa el atributo kSecAttrGeneric para hacer que un elemento sea único, pero esto es un error. Las dos entradas en este ejemplo solo se almacenan como dos entradas distintas, porque su kSecAttrAccessGroup es diferente (una tiene el grupo de acceso establecido, la otra lo deja libre). Si intenta agregar una segunda contraseña sin un grupo de acceso, utilizando KeychainItemWrapper de Apple, fallará.

Entonces, por favor, responde mis preguntas:

  • ¿Es cierto que la combinación de kSecAttrAccessGroup , kSecAttrAccount y kSecAttrService es la “clave única” de un elemento de llavero cuya kSecClass es kSecClassGenericPassword ?
  • ¿Qué atributos hacen que un elemento de llavero sea único si kSecClass no es kSecClassGenericPassword ?

Las claves principales son las siguientes (derivadas de los archivos de código abierto de Apple, consulte Schema.m4 , KeySchema.m4 y SecItem.cpp ):

  • Para un elemento de llavero de la clase kSecClassGenericPassword , la clave primaria es la combinación de kSecAttrAccount y kSecAttrService .
  • Para un elemento de llavero de la clase kSecClassInternetPassword , la clave primaria es la combinación de kSecAttrAccount , kSecAttrSecurityDomain , kSecAttrServer , kSecAttrProtocol , kSecAttrAuthenticationType , kSecAttrPort y kSecAttrPath .
  • Para un elemento de llavero de la clase kSecClassCertificate , la clave principal es la combinación de kSecAttrCertificateType , kSecAttrIssuer y kSecAttrSerialNumber .
  • Para un elemento de llavero de la clase kSecClassKey , la clave primaria es la combinación de kSecAttrApplicationLabel , kSecAttrApplicationTag , kSecAttrKeyType , kSecAttrKeySizeInBits , kSecAttrEffectiveKeySize y el creador, fecha de inicio y fecha de finalización que aún no están expuestos por SecItem.
  • Para un elemento de llavero de la clase kSecClassIdentity no he encontrado información en los campos de clave principal en los archivos de código abierto, pero como una identidad es la combinación de una clave privada y un certificado, asumo que la clave primaria es la combinación de la primaria campos clave para kSecClassKey y kSecClassCertificate .

Como cada elemento de llavero pertenece a un grupo de acceso de llavero, parece que el grupo de acceso de llavero (campo kSecAttrAccessGroup ) es un campo adicional para todas estas llaves primarias.

Estaba atacando un error el otro día (en iOS 7.1) que está relacionado con esta pregunta. Estaba usando SecItemCopyMatching para leer un elemento kSecClassGenericPassword y seguía devolviendo errSecItemNotFound (-25300) aunque kSecAttrAccessGroup , kSecAttrAccount y kSecAttrService coincidían con el elemento en el llavero.

Eventualmente descubrí que kSecAttrAccessible no coincidía. El valor en el llavero contenía pdmn = dk ( kSecAttrAccessibleAlways ), pero yo estaba usando kSecAttrAccessibleWhenUnlocked .

Por supuesto, este valor no es necesario en primer lugar para SecItemCopyMatching , pero OSStatus no era errSecParam ni errSecBadReq sino simplemente errSecItemNotFound (-25300), lo que dificultaba su búsqueda.

Para SecItemUpdate he experimentado el mismo problema, pero en este método, incluso el uso de la misma kSecAttrAccessible en el parámetro de query no funcionó. Solo eliminar completamente este atributo lo solucionó.

Espero que este comentario ahorre algunos momentos preciosos de depuración para algunos de ustedes.

La respuesta dada por @Tammo Freese parece ser correcta (pero sin mencionar todas las claves primarias). Estaba buscando alguna prueba en la documentación. Finalmente encontrado:

Documentación de Apple que menciona las claves principales para cada clase de secreto (cita a continuación):

El sistema considera que un elemento es un duplicado para un llavero dado cuando ese llavero ya tiene un elemento de la misma clase con el mismo conjunto de claves primarias compuestas. Cada clase de artículo de llavero tiene un conjunto diferente de llaves primarias, aunque algunos atributos se usan en común en todas las clases. En particular, cuando corresponda, kSecAttrSynchronizable y kSecAttrAccessGroup son parte del conjunto de claves principales . Las claves principales adicionales por clase se enumeran a continuación:

  • Para contraseñas genéricas, las claves principales incluyen kSecAttrAccount y kSecAttrService.
  • Para las contraseñas de Internet, las claves principales incluyen kSecAttrAccount, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrProtocol, kSecAttrAuthenticationType, kSecAttrPort y kSecAttrPath.
  • Para los certificados, las claves principales incluyen kSecAttrCertificateType, kSecAttrIssuer y kSecAttrSerialNumber.
  • Para los elementos clave, las claves principales incluyen kSecAttrKeyClass, kSecAttrKeyType, kSecAttrApplicationLabel, kSecAttrApplicationTag, kSecAttrKeySizeInBits y kSecAttrEffectiveKeySize.
  • Para los elementos de identidad, que son un certificado y una clave privada agrupados, las claves principales son las mismas que para un certificado. Debido a que una clave privada puede certificarse más de una vez, la exclusividad del certificado determina la identidad.