Security

RSS for tag

Secure the data your app manages and control access to your app using the Security framework.

Security Documentation

Pinned Posts

Posts under Security tag

273 Posts
Sort by:
Post not yet marked as solved
5 Replies
3.3k Views
Hello, I'm working with Cryptokit and I'm currently unable to export a P256 ECDSA signature data to base64Encoding and convert it back to derRepresentation. Here is my code: let privateKey = P256.Signing.PrivateKey() //Generated successfully /* For example pemRepresentation: -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgK+lUhkqFo637XuvJ Q6YiqZx04c33UUyWnLnpwfTwrtChRANCAARy55pVFhjgq3jKnQTx7s1XDIaNm20m 29ZEiBJnvzYfQGUQ+4ldzJYm34z8W2X/waqTqXsY4/oSO1sDFSQ6XwrG -----END PRIVATE KEY----- */ let publicKey = privateKey.publicKey //Picked up successfully /* For example pemRepresentation: -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcueaVRYY4Kt4yp0E8e7NVwyGjZtt JtvWRIgSZ782H0BlEPuJXcyWJt+M/Ftl/8Gqk6l7GOP6EjtbAxUkOl8Kxg== -----END PUBLIC KEY----- */ //Basic message to test, "something" here let messageDigest = "something".data(using: .utf8)!                  let signature = try privateKey.signature(for: messageDigest) //Successfully generated                  //Now from the PublicKey, let's check the signature for the message let isValidSignature = publicKey.isValidSignature(signature, for: messageDigest) //Successfully Return true //So far everything works fine //Now lets try to export the derRepresentation of the signature we just created to a base64EncodedString and try to use it later let signDataDerRepresentation = signature.derRepresentation //Here the signDataDerRepresentation is 70 bytes length //Here we get the signature derRepresentation to base64EncodedString, perfect to export let signDataDerRepresentationBase64String = signature.derRepresentation.base64EncodedString() /* "MEQCIGVC/zOGKEauy9AetVViTZiMTtFIeNtW9xALMTu6aIjSAiB+QPz9nGwzy51k3p3osu9OY6oQXkuLHTPoSWxPorg8GA==" */ //Here the signDataDerRepresentationBase64String is 96 bytes length //Now when I try to create a signature from a derRepresentation it works fine when I use the original derRepresentation data: let signature1 = try! P256.Signing.ECDSASignature(derRepresentation: signDataDerRepresentation) //signature1 is created //But when I try to create exactly the same signature from the base64EncodedString, an exception is throw //Convert base64String to Data let signDataDerRepresentationBase64StringData = signDataDerRepresentationBase64String.data(using: .utf8)! do { //Try to create a signature from the base64EncodedData of de the derRepresentation let signature2 = try P256.Signing.ECDSASignature(derRepresentation: signDataDerRepresentationBase64StringData) //Here it fails, signature2 is not created }catch{ //It fails with error: invalidASN1Object print("Signature failed \(error)") /* Signature failed invalidASN1Object */ } So now, I would like to know how to convert de derRepresentation of the signature I created into a base64EncodedString to the derRepresentation the ECDSASignature(derRepresentation:) func is looking for to create a signature from derRepresentation? I don't understand how I can convert my base64 String into that derRepresentation this func is looking for, I can't find the doc : /// An ECDSA (Elliptic Curve Digital Signature Algorithm) Signature @available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) extension P256.Signing {     public struct ECDSASignature : ContiguousBytes {         /// Returns the raw signature.         /// The raw signature format for ECDSA is r || s         public var rawRepresentation: Data         /// Initializes ECDSASignature from the raw representation.         /// The raw signature format for ECDSA is r || s         /// As defined in https://tools.ietf.org/html/rfc4754         public init<D>(rawRepresentation: D) throws where D : DataProtocol         /// Initializes ECDSASignature from the DER representation.         public init<D>(derRepresentation: D) throws where D : DataProtocol         /// Calls the given closure with the contents of underlying storage.         ///         /// - note: Calling `withUnsafeBytes` multiple times does not guarantee that         ///         the same buffer pointer will be passed in every time.         /// - warning: The buffer argument to the body should not be stored or used         ///            outside of the lifetime of the call to the closure.         public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R         /// A DER-encoded representation of the signature         public var derRepresentation: Data { get }     } } Thank you for your help. Hélie
Posted
by
Post not yet marked as solved
0 Replies
2.1k Views
General: Apple Platform Security support document Security Overview Cryptography: DevForums tags: Security, Apple CryptoKit Security framework documentation Apple CryptoKit framework documentation Common Crypto man pages — For the full list of pages, run: % man -k 3cc For more information about man pages, see Reading UNIX Manual Pages. On Cryptographic Key Formats DevForums post SecItem attributes for keys DevForums post CryptoCompatibility sample code Keychain: DevForums tags: Security Security > Keychain Items documentation TN3137 On Mac keychain APIs and implementations SecItem Fundamentals DevForums post SecItem Pitfalls and Best Practices DevForums post Investigating hard-to-reproduce keychain problems DevForums post Smart cards and other secure tokens: DevForums tag: CryptoTokenKit CryptoTokenKit framework documentation Mac-specific frameworks: DevForums tags: Security Foundation, Security Interface Security Foundation framework documentation Security Interface framework documentation Related: Networking Resources — This covers high-level network security, including HTTPS and TLS. Network Extension Resources — This covers low-level network security, including VPN and content filters. Code Signing Resources Notarisation Resources Trusted Execution Resources — This includes Gatekeeper. App Sandbox Resources Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com"
Posted
by
Post marked as solved
5 Replies
2.4k Views
Mac users often ask whether they should install "anti-virus" software. The answer usually given on ASC is "no." The answer is right, but it may give the wrong impression that there is no threat from what are loosely called "viruses." There is a threat, and you need to educate yourself about it. This is a comment on what you should—and should not—do to protect yourself from malicious software ("malware") that circulates on the Internet and gets onto a computer as an unintended consequence of the user's actions. It does not apply to software, such as keystroke loggers, that may be installed deliberately by an intruder who has hands-on access to the computer, or who has been able to log in to it remotely. That threat is in a different category, and there's no easy way to defend against it. The comment is long because the issue is complex. The key points are in sections 5, 6, and 10. OS X now implements three layers of built-in protection specifically against malware, not counting runtime protections such as execute disable, sandboxing, system library randomization, and address space layout randomization that may also guard against other kinds of exploits. 2. All versions of OS X since 10.6.7 have been able to detect known Mac malware in downloaded files, and to block insecure web plugins. This feature is transparent to the user. Internally Apple calls it "XProtect." The malware recognition database used by XProtect is automatically updated; however, you shouldn't rely on it, because the attackers are always at least a day ahead of the defenders. The following caveats apply to XProtect: ☞ It can be bypassed by some third-party networking software, such as BitTorrent clients and Java applets. ☞ It only applies to software downloaded from the network. Software installed from a CD or other media is not checked. As new versions of OS X are released, it's not clear whether Apple will indefinitely continue to maintain the XProtect database of older versions such as 10.6. The security of obsolete system versions may eventually be degraded. Security updates to the code of obsolete systems will stop being released at some point, and that may leave them open to other kinds of attack besides malware. 3. Starting with OS X 10.7.5, there has been a second layer of built-in malware protection, designated "Gatekeeper" by Apple. By default, applications and Installer packages downloaded from the network will only run if they're digitally signed by a developer with a certificate issued by Apple. Software certified in this way hasn't necessarily been tested by Apple, but you can be reasonably sure that it hasn't been modified by anyone other than the developer. His identity is known to Apple, so he could be held legally responsible if he distributed malware. That may not mean much if the developer lives in a country with a weak legal system (see below.) Gatekeeper doesn't depend on a database of known malware. It has, however, the same limitations as XProtect, and in addition the following: ☞ It can easily be disabled or overridden by the user. ☞ A malware attacker could get control of a code-signing certificate under false pretenses, or could simply ignore the consequences of distributing codesigned malware. ☞ An App Store developer could find a way to bypass Apple's oversight, or the oversight could fail due to human error. Apple has so far failed to revoke the codesigning certificates of some known abusers, thereby diluting the value of Gatekeeper and the Developer ID program. These failures don't involve App Store products, however. For the reasons given, App Store products, and—to a lesser extent—other applications recognized by Gatekeeper as signed, are safer than others, but they can't be considered absolutely safe. "Sandboxed" applications may prompt for access to private data, such as your contacts, or for access to the network. Think before granting that access. Sandbox security is based on user input. Never click through any request for authorization without thinking. 4. Starting with OS X 10.8.3, a third layer of protection has been added: a "Malware Removal Tool" (MRT). MRT runs automatically in the background when you update the OS. It checks for, and removes, malware that may have evaded the other protections via a Java exploit (see below.) MRT also runs when you install or update the Apple-supplied Java runtime (but not the Oracle runtime.) Like XProtect, MRT is effective against known threats, but not against unknown ones. It notifies you if it finds malware, but otherwise there's no user interface to MRT. 5. The built-in security features of OS X reduce the risk of malware attack, but they are not, and never will be, complete protection. Malware is a problem of human behavior, and a technological fix is not going to solve it. Trusting software to protect you will only make you more vulnerable. The best defense is always going to be your own intelligence. With the possible exception of Java exploits, all known malware circulating on the Internet that affects a fully-updated installation of OS X 10.6 or later takes the form of so-called "****** horses," which can only have an effect if the victim is duped into running them. The threat therefore amounts to a battle of wits between you and the scam artists. If you're smarter than they think you are, you'll win. That means, in practice, that you always stay within a safe harbor of computing practices. Malware defence By Linc Davis - https://discussions.apple.com/thread/6460085
Posted
by
Post not yet marked as solved
0 Replies
2.3k Views
I spend way too much time interacting with the Security framework. Most Security framework APIs are kinda clunky to call from Swift, largely because they use Core Foundation conventions. However, I see a lot of folks working much harder than they should to call these APIs. This post contains two tips to make your life easier. Many Security framework APIs work in terms of CFDictionary. I regularly see folks create these dictionaries like so: let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecMatchLimit as String: kSecMatchLimitAll, kSecReturnRef as String: true, ] var copyResult: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &copyResult) That’s much too hard. Try this instead: let query = [ kSecClass: kSecClassKey, kSecMatchLimit: kSecMatchLimitAll, kSecReturnRef: true, ] as NSDictionary var copyResult: CFTypeRef? let status = SecItemCopyMatching(query, &copyResult) Much nicer. Security framework APIs have a wide variety of ways to indicate an error: Some routines return an OSStatus and that’s it. Some routines return an OSStatus and an ‘out’ value. Some routines return a pointer, where nil indicates an error. Some routines return a pointer, where nil indicates an error, with a CFError ‘out’ value. Some routines return a Boolean, where false indicates an error, with a CFError ‘out’ value. In Swift you really just want to call the API and have it throw. The code pasted in at the end of this post helps with that. It declares a bunch of overloaded secCall(…) functions, one for each of the cases outlined above. It takes code like this: let query = [ kSecClass: kSecClassKey, kSecMatchLimit: kSecMatchLimitAll, kSecReturnRef: true, ] as NSDictionary var copyResult: CFTypeRef? = nil let err = SecItemCopyMatching(query, &copyResult) guard err == errSecSuccess else { throw NSError(domain: NSOSStatusErrorDomain, code: Int(err)) } let keys = copyResult! as! [SecKey] and turns it into this: let query = [ kSecClass: kSecClassKey, kSecMatchLimit: kSecMatchLimitAll, kSecReturnRef: true, ] as NSDictionary let keys = try secCall { SecItemCopyMatching(query, $0) } as! [SecKey] Still not exactly pretty, but definitely an improvement. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Revision History 2023-11-27 Fixed a polarity bug with the Bool routine. As the saying goes “Real programmers can get the branch backwards in any language!” |-: /// Calls a Security framework function, throwing if it returns an error. /// /// For example, the `SecACLRemove` function has a signature like this: /// /// ``` /// func SecACLRemove(…) -> OSStatus /// ``` /// /// and so you call it like this: /// /// ``` /// try secCall { SecACLRemove(acl) } /// ``` /// /// - Parameter body: A function that returns an `OSStatus` value. /// - Throws: If `body` returns anything other than `errSecSuccess`. func secCall(_ body: () -> OSStatus) throws { let err = body() guard err == errSecSuccess else { throw NSError(domain: NSOSStatusErrorDomain, code: Int(err), userInfo: nil) } } /// Calls a Security framework function that returns an error and a value indirectly. /// /// For example, the `SecItemCopyMatching` function has a signature like this: /// /// ``` /// func SecItemCopyMatching(…, _ result: UnsafeMutablePointer<CFTypeRef?>?) -> OSStatus /// ``` /// /// and so you call it like this: /// /// ``` /// let keys = try secCall { SecItemCopyMatching([ /// kSecClass: kSecClassKey, /// kSecMatchLimit: kSecMatchLimitAll, /// kSecReturnRef: true, /// ] as NSDictionary, $0) } /// ``` /// /// - Parameter body: A function that returns an `OSStatus` value and takes a /// ‘out’ pointer to return the result indirectly. /// - Throws: If `body` returns anything other than `errSecSuccess`. /// - Returns: The value returned indirectly by the function. func secCall<Result>(_ body: (_ resultPtr: UnsafeMutablePointer<Result?>) -> OSStatus) throws -> Result { var result: Result? = nil let err = body(&result) guard err == errSecSuccess else { throw NSError(domain: NSOSStatusErrorDomain, code: Int(err), userInfo: nil) } return result! } /// Calls a Security framework function that returns `nil` on error. /// /// For example, the `SecKeyCopyPublicKey` function has a signature like this: /// /// ``` /// func SecKeyCopyPublicKey(…) -> SecKey? /// ``` /// /// and so you call it like this: /// /// ``` /// let publicKey = try secCall { SecKeyCopyPublicKey(privateKey) } /// ``` /// /// - Parameters: /// - code: An `OSStatus` value to throw if there’s an error; defaults to `errSecParam`. /// - body: A function that returns a value, or `nil` if there’s an error. /// - Throws: If `body` returns `nil`. /// - Returns: On success, the non-`nil` value returned by `body`. func secCall<Result>(_ code: Int = Int(errSecParam), _ body: () -> Result?) throws -> Result { guard let result = body() else { throw NSError(domain: NSOSStatusErrorDomain, code: code, userInfo: nil) } return result } /// Calls a Security framework function that returns `nil` on error along with a `CFError` indirectly. /// /// For example, the `SecKeyCreateDecryptedData` function has a signature like this: /// /// ``` /// func SecKeyCreateDecryptedData(…, _ error: UnsafeMutablePointer<Unmanaged<CFError>?>?) -> CFData? /// ``` /// /// and so you call it like this: /// /// ``` /// let plainText = try secCall { SecKeyCreateDecryptedData(privateKey, .rsaEncryptionPKCS1, cypherText, $0) } /// ``` /// /// - Parameter body: A function that returns a value, which returns `nil` if /// there’s an error and, in that case, places a `CFError` value in the ‘out’ parameter. /// - Throws: If `body` returns `nil`. /// - Returns: On success, the non-`nil` value returned by `body`. func secCall<Result>(_ body: (_ resultPtr: UnsafeMutablePointer<Unmanaged<CFError>?>) -> Result?) throws -> Result { var errorQ: Unmanaged<CFError>? = nil guard let result = body(&errorQ) else { throw errorQ!.takeRetainedValue() as Error } return result } /// Calls a Security framework function that returns false on error along with a `CFError` indirectly. /// /// For example, the `SecKeyVerifySignature` function has a signature like this: /// /// ``` /// func SecKeyVerifySignature(…, _ error: UnsafeMutablePointer<Unmanaged<CFError>?>?) -> Bool /// ``` /// /// and so you call it like this: /// /// ``` /// try secCall { SecKeyVerifySignature(publicKey, .ecdsaSignatureMessageX962SHA1, signedData, signature, $0) } /// ``` /// /// - Parameter body: A function that returns a false if there’s an error and, /// in that case, places a `CFError` value in the ‘out’ parameter. /// - Throws: If `body` returns false. func secCall(_ body: (_ resultPtr: UnsafeMutablePointer<Unmanaged<CFError>?>) -> Bool) throws { var errorQ: Unmanaged<CFError>? = nil guard body(&errorQ) else { throw errorQ!.takeRetainedValue() as Error } }
Posted
by
Post marked as solved
2 Replies
2.2k Views
We've enabled ATS restrictions in our app, and everything works fine, except sometimes, randomly, the CDN download resource fails. In most cases, it happens to users who on iOS 14.* and WiFI (VPN helps solve the problem :thinking_face:) Logs: (ExampleClientErrorLogServlet) :: Client error: {"arguments":["test_resource","Caught Error Domain%3DNSURLErrorDomain Code%3D-1200 \"An SSL error has occurred and a secure connection to the server cannot be made.\" UserInfo%3D{NSErrorFailingURLStringKey%3Dhttps://my-url/reource.bin, NSLocalizedRecoverySuggestion%3DWould you like to connect to the server anyway?, _kCFStreamErrorDomainKey%3D3, _NSURLErrorFailingURLSessionTaskErrorKey%3DLocalDownloadTask &lt;A50DCF0E-38F3-4454-A78A-B4552336561E&gt;.&lt;1&gt;, _NSURLErrorRelatedURLSessionTaskErrorKey%3D(\n \"LocalDownloadTask &lt;A50DCF0E-38F3-4454-A78A-B4552336561E&gt;.&lt;1&gt;\"\n), NSLocalizedDescription%3DAn SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey%3Dhttps://my-url/reource.bin, NSUnderlyingError%3D0x2882e1050 {Error Domain%3DkCFErrorDomainCFNetwork Code%3D-1200 \"(null)\" UserInfo%3D{_kCFStreamPropertySSLClientCertificateState%3D0, _kCFNetworkCFStreamSSLErrorOriginalValue%3D-9816, _kCFStreamErrorDomainKey%3D3, _kCFStreamErrorCodeKey%3D-9816, _NSURLErrorNWPathKey%3Dsatisfied (Path is satisfied), viable, interface: en0, ipv4, dns}}, _kCFStreamErrorCodeKey%3D-9816}"],"format":"Downloading {} file failed: {}","platform":"ios","version":"2.87.1"} 26.07.2022 01:39:55 [DEBUG][9] :: platform: ios, version: 2.87.1. Downloading test_resource file failed: Caught Error Domain%3DNSURLErrorDomain Code%3D-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo%3D{NSErrorFailingURLStringKey%3Dhttps://my-url/reource.bin, NSLocalizedRecoverySuggestion%3DWould you like to connect to the server anyway?, _kCFStreamErrorDomainKey%3D3, _NSURLErrorFailingURLSessionTaskErrorKey%3DLocalDownloadTask &lt;A50DCF0E-38F3-4454-A78A-B4552336561E&gt;.&lt;1&gt;, _NSURLErrorRelatedURLSessionTaskErrorKey%3D( ), NSLocalizedDescription%3DAn SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey%3Dhttps://my-url/reource.bin, NSUnderlyingError%3D0x2882e1050 {Error Domain%3DkCFErrorDomainCFNetwork Code%3D-1200 "(null)" UserInfo%3D{_kCFStreamPropertySSLClientCertificateState%3D0, _kCFNetworkCFStreamSSLErrorOriginalValue%3D-9816, _kCFStreamErrorDomainKey%3D3, _kCFStreamErrorCodeKey%3D-9816, _NSURLErrorNWPathKey%3Dsatisfied (Path is satisfied), viable, interface: en0, ipv4, dns}}, _kCFStreamErrorCodeKey%3D-9816} _kCFNetworkCFStreamSSLErrorOriginalValue=-9816 _kCFStreamErrorDomainKey=3 _kCFStreamErrorCodeKey=-9816 We've tried nscurl --ats-diagnostics on the URL: Configuring ATS Info.plist keys and displaying the result of HTTPS loads to https:/url-path. A test will "PASS" if URLSession:task:didCompleteWithError: returns a nil error. ============================================================== Default ATS Secure Connection --- ATS Default Connection ATS Dictionary: {} Result : PASS --- ============================================================== Allowing Arbitrary Loads --- Allow All Loads ATS Dictionary: {     NSAllowsArbitraryLoads = true; } Result : PASS --- ================================================================================ Configuring TLS exceptions for url --- TLSv1.3 ATS Dictionary: {     NSExceptionDomains =     {         "url" =         {             NSExceptionMinimumTLSVersion = "TLSv1.3";         };     }; } Result : FAIL Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSErrorFailingURLStringKey=url, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask &lt;250D7C7A-A090-41F1-8FED-E73FCB511F41&gt;.&lt;1&gt;, _NSURLErrorRelatedURLSessionTaskErrorKey=(     "LocalDataTask &lt;250D7C7A-A090-41F1-8FED-E73FCB511F41&gt;.&lt;1&gt;" ), NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=url, NSUnderlyingError=0x6000021318f0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9836, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9836, _NSURLErrorNWPathKey=satisfied (Path is satisfied), viable, interface: lo0}}, _kCFStreamErrorCodeKey=-9836} --- ====================================== nsurl --ats-diagnostic show me another error code -9836 and like I know TLSv1.3 not necessary yet Maybe someone can give some suggestions, any help !! :pray: Thx!
Posted
by
Post not yet marked as solved
7 Replies
3k Views
Hi, I am trying to implement encryption and decryption with EC signing keys. in the process I am getting the following error while creating a SecKey from the private key. Error in creating a secKey Optional(Swift.Unmanaged&lt;__C.CFErrorRef&gt;(_value: Error Domain=NSOSStatusErrorDomain Code=-50 "EC private key creation from data failed" (paramErr: error in user parameter list) UserInfo={numberOfErrorsDeep=0, NSDescription=EC private key creation from data failed})) Code snippet for decryption func decrypt(data: Data, key: SecureEnclave.P256.Signing.PrivateKey) throws -&gt; Data? {     var error: Unmanaged&lt;CFError&gt;?     let privateKeyData: CFData = key.dataRepresentation as CFData     let privateKeyAttributes = [kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,                                 kSecAttrKeyClass: kSecAttrKeyClassPrivate] as CFDictionary     guard let SecKey = SecKeyCreateWithData(privateKeyData, privateKeyAttributes as CFDictionary, &amp;error)     else {         print("Error in creating a secKey", error)         return nil     }          guard SecKeyIsAlgorithmSupported(SecKey, .decrypt, EncryptAndDecryptAlogrithm)     else {         print("Decryption algorithm is not supported", error)         return nil     }          guard let decryptedData = SecKeyCreateDecryptedData(SecKey, EncryptAndDecryptAlogrithm, data as CFData, &amp;error) else {         print("Error in decryption", error)         return nil     }     return decryptedData as Data } let data = Data(base64Encoded: "BNtHrb1cZuflSDZz+E3PnIkLtYUQuBDW+ONlzuAypZcQa+5oKv0L0wSIBMMseMr0roloexPwTaVV26ddewTP0+vRt9v6uLOg366cElMo6P5nh2K7xKi1PMcRyBVel+Kq9WQWT/EkRIuUkHdq2KLXy/Q=")! let alice = try SecureEnclave.P256.Signing.PrivateKey() let decryptedData = try decrypt(data: data, key:alice) Thank you in advance.
Posted
by
Post marked as solved
3 Replies
2.3k Views
Hi, I want to implement FIDO based biometric authentication in our app. I don't want to use passkeys because they are only compatible with iOS 16 and higher. Is there a way to use it through the SFSafariViewController, a web view, ASWebAuthenticationSession or any another method?
Posted
by
Post not yet marked as solved
4 Replies
1.2k Views
I am trying to use SecItemUpdate in order to change the kSecAttrAccessControl value on a private key protected by the Secure Enclave as well as an .applicationPassword - which I want to change. I have been unsuccessful getting the query and attributesToUpdate dictionaries right though, with SecItemUpdate returning either errSecParam, errSecNoSuchAttr or errSecAuthFailed. Am I on the right track here or am I trying to do something that is not possible?
Posted
by
Post not yet marked as solved
10 Replies
1.4k Views
I maintain a cross-platform client side network library for persistent TCP connections targeting Win32, Darwin and FreeBSD platforms. I recently upgraded to a Mac Studio w/ M1 Max (Ventura 13.1) from a late 2015 Intel Macbook Pro (Monterey 12.6.2) and I've encountered a discrepancy between the two. For secure TCP connections my lib uses WolfSSL across all platforms but also supports use of system provided Security libraries. On Darwin platforms this is SecureTransport. Yes I am aware SecureTransport is deprecated in favor of Network. I intend to attempt to integrate with Network later but for now my architecture dictates that I use similar C-style callbacks akin to WolfSSL, OpenSSL, MBedTLS etc. On the first call to SSLHandshake the SecureTransport write callback generates 151 bytes for my TLS 1.2 connection to example.com:443 on both platforms. However, while on Intel MBP I am able to continue with the full handshake I immediately receive 0 bytes with EOF. In Wireshark on the Intel MBP the 151 bytes are observed as a TLS 1.2 client hello while on M1 it is observed as an SSL continuation message and that is the last message observed.
Posted
by
Post not yet marked as solved
8 Replies
1.2k Views
We develop a custom authorization plugin for the macOS login screen. We use the SFAuthorizationPluginView class to display our plugin's UI overriding the default macOS login screen, as it is done in the NameAndPassword example (https://github.com/antoinebell/NameAndPassword). We experience a heavy lag - both when using our plugin or NameAndPassword plugin - on the UI when we click any of the user's avatar and there is an animation in which the avatar slides into the middle of the screen, and a custom password box is appearing. This lag also comes when we go back to where the user can select another users. (deactivation of the plugin). We use of course the "List of users" login window display mode. Here I want to emphasise the issue with the NameAndPassword example, because the source code is provided for it and it is a small example created originally by Apple. Are you aware of any bugs in the SFAuthorizationPluginView class which blocks the main/UI thread in the above mentioned cases? We use macOS Monterey 12.6, tried both on M1 and Intel based chipset. We tried the NameAndPassword plugin both on physical machine and Parallel's VM. Original behaviour: https://www.icloud.com/iclouddrive/0b2Z87L3GrTDouwg4pjj0vL2g#original_macOS_login_screen_animation The laggy behaviour with the NameAndPassword plugin: https://www.icloud.com/iclouddrive/0d2_GASYjDGaClboAxXjejH6g#NameAndPassword_lag Thanks for the help in advance!
Posted
by
Post not yet marked as solved
11 Replies
2.1k Views
software used: xcode: 14.1 swift: 4 openssl v3 when trying to import the p12 certificate with SecPKCS12Import, it is failing with the following error: Error Domain=NSOSStatusErrorDomain Code=-25293 "The user name or passphrase you entered is not correct." UserInfo={NSLocalizedDescription=The user name or passphrase you entered is not correct.})   -25293 The p12 certificate is created using AES-256-CBC. This p12 file is passed through the following method that returned Data and then sent to SecPKCS12Import API. BIO* createPKCS12fromPKCS12(const unsigned char* data, long dataLength, char* originalPassphrase, char* newPassphrase) {   OSSL_PROVIDER defaultProvider = OSSL_PROVIDER_try_load(NULL, "default", 1);       BIO bp = BIO_new_mem_buf(data, (int)dataLength);   PKCS12* originalPKCS12 = NULL;   d2i_PKCS12_bio(bp, &originalPKCS12);   BIO_free(bp);   EVP_PKEY* privateKey;   X509* x509;   STACK_OF(X509)* caChain = NULL;   PKCS12_parse(originalPKCS12, originalPassphrase, &privateKey, &x509, &caChain);   PKCS12_free(originalPKCS12);       PKCS12* newPKCS12 = PKCS12_create(newPassphrase, "SAP Identity", privateKey, x509, caChain, 149, 146, 0, 0, 0);   unsigned long a = ERR_get_error();   printf("%lu", a);   EVP_PKEY_free(privateKey);   X509_free(x509);   sk_X509_free(caChain);   BIO* mem = NULL;   if (newPKCS12 != NULL) {     mem = BIO_new(BIO_s_mem());     i2d_PKCS12_bio(mem, newPKCS12);     PKCS12_free(newPKCS12);         }       return mem; }      let err = SecPKCS12Import(pkcs12Data as CFData, query as CFDictionary, &items)     print("error in:: SecPKCS12Import", err.error, "\n", err) If required, we may share the p12 certificate and associate password with you to debug it further.
Posted
by
Post not yet marked as solved
0 Replies
1.7k Views
I regularly help developers with keychain problems, both here on DevForums and for my Day Job™ in DTS. Many of these problems are caused by a fundamental misunderstanding of how the keychain works. This post is my attempt to explain that. I wrote it primarily so that Future Quinn™ can direct folks here rather than explain everything from scratch (-: If you have questions or comments about any of this, put them in a new thread and apply the Security tag so that I see it. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" SecItem: Fundamentals or How I Learned to Stop Worrying and Love the SecItem API The SecItem API seems very simple. After all, it only has four function calls, how hard can it be? In reality, things are not that easy. Various factors contribute to making this API much trickier than it might seem at first glance. This post explains the fundamental underpinnings of the keychain. For information about specific issues, see its companion post, SecItem: Pitfalls and Best Practices. Keychain Documentation Your basic starting point should be Keychain Items. If your code runs on the Mac, also read TN3137 On Mac keychain APIs and implementations. Read the doc comments in <Security/SecItem.h>. In many cases those doc comments contain critical tidbits. When you read keychain documentation [1] and doc comments, keep in mind that statements specific to iOS typically apply to iPadOS, tvOS, and watchOS as well (r. 102786959). Also, they typically apply to macOS when you target the data protection keychain. Conversely, statements specific to macOS may not apply when you target the data protection keychain. [1] Except TN3137, which is very clear about this (-: Caveat Mac Developer macOS supports two different implementations: the original file-based keychain and the iOS-style data protection keychain. If you’re able to use the data protection keychain, do so. It’ll make your life easier. TN3137 On Mac keychain APIs and implementations explains this distinction in depth. The Four Freedoms^H^H^H^H^H^H^H^H Functions The SecItem API contains just four functions: SecItemAdd(_:_:) SecItemCopyMatching(_:_:) SecItemUpdate(_:_:) SecItemDelete(_:) These directly map to standard SQL database operations: SecItemAdd(_:_:) maps to INSERT. SecItemCopyMatching(_:_:) maps to SELECT. SecItemUpdate(_:_:) maps to UPDATE. SecItemDelete(_:) maps to DELETE. You can think of each keychain item class (generic password, certificate, and so on) as a separate SQL table within the database. The rows of that table are the individual keychain items for that class and the columns are the attributes of those items. Note Except for the digital identity class, kSecClassIdentity, where the values are split across the certificate and key tables. See Digital Identities Aren’t Real in SecItem: Pitfalls and Best Practices. This is not an accident. The data protection keychain is actually implemented as an SQLite database. If you’re curious about its structure, examine it on the Mac by pointing your favourite SQLite inspection tool — for example, the sqlite3 command-line tool — at the keychain database in ~/Library/Keychains/UUU/keychain-2.db, where UUU is a UUID. WARNING Do not depend on the location and structure of this file. These have changed in the past and are likely to change again in the future. If you embed knowledge of them into a shipping product, it’s likely that your product will have binary compatibility problems at some point in the future. The only reason I’m mentioning them here is because I find it helpful to poke around in the file to get a better understanding of how the API works. For information about which attributes are supported by each keychain item class — that is, what columns are in each table — see the Note box at the top of Item Attribute Keys and Values. Alternatively, look at the Attribute Key Constants doc comment in <Security/SecItem.h>. Uniqueness A critical part of the keychain model is uniqueness. How does the keychain determine if item A is the same as item B? It turns out that this is class dependent. For each keychain item class there is a set of attributes that form the uniqueness constraint for items of that class. That is, if you try to add item A where all of its attributes are the same as item B, the add fails with errSecDuplicateItem. For more information, see the errSecDuplicateItem page. It has lists of attributes that make up this uniqueness constraint, one for each class. These uniqueness constraints are a major source of confusion, as discussed in the Queries and the Uniqueness Constraints section of SecItem: Pitfalls and Best Practices. Parameter Blocks Understanding The SecItem API is a classic ‘parameter block’ API. All of its inputs are dictionaries, and you have to know which properties to set in each dictionary to achieve your desired result. Likewise for when you read properties in output dictionaries. There are five different property groups: The item class property, kSecClass, determines the class of item you’re operating on: kSecClassGenericPassword, kSecClassCertificate, and so on. The item attribute properties, like kSecAttrAccessGroup, map directly to keychain item attributes. The search properties, like kSecMatchLimit, control how the system runs a query. The return type properties, like kSecReturnAttributes, determine what values the query returns. The value type properties, like kSecValueRef perform multiple duties, as explained below. There are other properties that perform a variety of specific functions. For example, kSecUseDataProtectionKeychain tells macOS to use the data protection keychain instead of the file-based keychain. These properties are hard to describe in general; for the details, see the documentation for each such property. Inputs Each of the four SecItem functions take dictionary input parameters of the same type, CFDictionary, but these dictionaries are not the same. Different dictionaries support different property groups: The first parameter of SecItemAdd(_:_:) is an add dictionary. It supports all property groups except the search properties. The first parameter of SecItemCopyMatching(_:_:) is a query and return dictionary. It supports all property groups. The first parameter of SecItemUpdate(_:_:) is a pure query dictionary. It supports all property groups except the return type properties. Likewise for the only parameter of SecItemDelete(_:). The second parameter of SecItemUpdate(_:_:) is an update dictionary. It supports the item attribute and value type property groups. Outputs Two of the SecItem functions, SecItemAdd(_:_:) and SecItemCopyMatching(_:_:), return values. These output parameters are of type CFTypeRef because the type of value you get back depends on the return type properties you supply in the input dictionary: If you supply a single return type property, except kSecReturnAttributes, you get back a value appropriate for that return type. If you supply multiple return type properties or kSecReturnAttributes, you get back a dictionary. This supports the item attribute and value type property groups. To get a non-attribute value from this dictionary, use the value type property that corresponds to its return type property. For example, if you set kSecReturnPersistentRef in the input dictionary, use kSecValuePersistentRef to get the persistent reference from the output dictionary. In the single item case, the type of value you get back depends on the return type property and the keychain item class: For kSecReturnData you get back the keychain item’s data. This makes most sense for password items, where the data holds the password. It also works for certificate items, where you get back the DER-encoded certificate. Using this for key items is kinda sketchy. If you want to export a key, called SecKeyCopyExternalRepresentation. Using this for digital identity items is nonsensical. For kSecReturnRef you get back an object reference. This only works for keychain item classes that have an object representation, namely certificates, keys, and digital identities. You get back a SecCertificate, a SecKey, or a SecIdentity, respectively. For kSecReturnPersistentRef you get back a data value that holds the persistent reference. Value Type Subtleties There are three properties in the value type property group: kSecValueData kSecValueRef kSecValuePersistentRef Their semantics vary based on the dictionary type. For kSecValueData: In an add dictionary, this is the value of the item to add. For example, when adding a generic password item (kSecClassGenericPassword), the value of this key is a Data value containing the password. This is not supported in a query dictionary. In an update dictionary, this is the new value for the item. For kSecValueRef: In add and query dictionaries, the system infers the class property and attribute properties from the supplied object. For example, if you supply a certificate object (SecCertificate, created using SecCertificateCreateWithData), the system will infer a kSecClass value of kSecClassCertificate and various attribute values, like kSecAttrSerialNumber, from that certificate object. This is not supported in an update dictionary. For kSecValuePersistentRef: For query dictionaries, this uniquely identifies the item to operate on. This is not supported in add and update dictionaries. Revision History 2023-09-12 Fixed various bugs in the revision history. Added a paragraph explaining how to determine which attributes are supported by each keychain item class. 2023-02-22 Made minor editorial changes. 2023-01-28 First posted.
Posted
by
Post not yet marked as solved
0 Replies
1.5k Views
If you’re on macOS and targeting the file-based keychain, kSecMatchLimitAll always defaults to kSecMatchLimitOne I regularly help developers with keychain problems, both here on DevForums and for my Day Job™ in DTS. Over the years I’ve learnt a lot about the API, including many pitfalls and best practices. This post is my attempt to collect that experience in one place. If you have questions or comments about any of this, put them in a new thread and apply the Security tag so that I see it. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" SecItem: Pitfalls and Best Practices It’s just four functions, how hard can it be? The SecItem API seems very simple. After all, it only has four function calls, how hard can it be? In reality, things are not that easy. Various factors contribute to making this API much trickier than it might seem at first glance. This post explains some of the keychain’s pitfalls and then goes on to explain various best practices. Before reading this, make sure you understand the fundamentals by reading its companion post, SecItem: Fundamentals. Pitfalls Lets start with some common pitfalls. Queries and Uniqueness Constraints The relationship between query dictionaries and uniqueness constraints is a major source of problems with the keychain API. Consider code like this: var copyResult: CFTypeRef? = nil let query = [ kSecClass: kSecClassGenericPassword, kSecAttrService: "AYS", kSecAttrAccount: "mrgumby", kSecAttrGeneric: Data("SecItemHints".utf8), ] as NSMutableDictionary let err = SecItemCopyMatching(query, &copyResult) if err == errSecItemNotFound { query[kSecValueData] = Data("opendoor".utf8) let err2 = SecItemAdd(query, nil) if err2 == errSecDuplicateItem { fatalError("… can you get here? …") } } Can you get to the fatal error? At first glance this might not seem possible because you’ve run your query and it’s returned errSecItemNotFound. However, the fatal error is possible because the query contains an attribute, kSecAttrGeneric, that does not contribute to the uniqueness. If the keychain contains a generic password whose service (kSecAttrService) and account (kSecAttrAccount) attributes match those supplied but who’s generic (kSecAttrGeneric) attribute does not, the SecItemCopyMatching calls will return errSecItemNotFound. However, for a generic password item, of the attributes shown here, only the service and account attributes are included in the uniqueness constraint. If you try to add an item where those attributes match an existing item, the add will fail with errSecDuplicateItem even though the value of the generic attribute is different. The take-home point is that that you should study the attributes that contribute to uniqueness and use them in a way that’s aligned with your view of uniqueness. See the Uniqueness section of SecItem: Fundamentals for a link to the relevant documentation. Erroneous Attributes Each keychain item class supports its own specific set of attributes. For information about the attributes supported by a given class, see SecItem: Fundamentals. I regularly see folks use attributes that aren’t supported by the class they’re working with. For example, the kSecAttrApplicationTag attribute is only supported for key items (kSecClassKey). Using it with a certificate item (kSecClassCertificate) will cause, at best, a runtime error and, at worst, mysterious bugs. This is an easy mistake to make because: The ‘parameter block’ nature of the SecItem API means that the compiler won’t complain if you use an erroneous attribute. On macOS, the shim that connects to the file-based keychain ignores unsupported attributes. Imagine you want to store a certificate for a particular user. You might write code like this: let err = SecItemAdd([ kSecClass: kSecClassCertificate, kSecAttrApplicationTag: Data(name.utf8), kSecValueRef: cert, ] as NSDictionary, nil) The goal is to store the user’s name in the kSecAttrApplicationTag attribute so that you can get back their certificate with code like this: let err = SecItemCopyMatching([ kSecClass: kSecClassCertificate, kSecAttrApplicationTag: Data(name.utf8), kSecReturnRef: true, ] as NSDictionary, &copyResult) On iOS, and with the data protection keychain on macOS, both calls will fail with errSecNoSuchAttr. That makes sense, because the kSecAttrApplicationTag attribute is not supported for certificate items. Unfortunately, the macOS shim that connects the SecItem API to the file-based keychain ignores extraneous attributes. This results in some very bad behaviour: SecItemAdd works, ignoring kSecAttrApplicationTag. SecItemCopyMatching ignores kSecAttrApplicationTag, returning the first certificate that it finds. If you only test with a single user, everything seems to work. But, later on, when you try your code with multiple users, you might get back the wrong result depending on the which certificate the SecItemCopyMatching call happens to discover first. Ouch! Context Matters Some properties change behaviour based on the context. The value type properties are the biggest offender here, as discussed in the Value Type Subtleties section of SecItem: Fundamentals. However, there are others. The one that’s bitten me is kSecMatchLimit: In a query and return dictionary its default value is kSecMatchLimitOne. If you don’t supply a value for kSecMatchLimit, SecItemCopyMatching returns at most one item that matches your query. In a pure query dictionary its default value is kSecMatchLimitAll. For example, if you don’t supply a value for kSecMatchLimit, SecItemDelete will delete all items that match your query. This is a lesson that, once learnt, is never forgotten! Note Although this only applies to the data-protection keychain. If you’re on macOS and targeting the file-based keychain, kSecMatchLimit always defaults to kSecMatchLimitOne (r. 105800863). Fun times! Digital Identities Aren’t Real A digital identity is the combination of a certificate and the private key that matches the public key within that certificate. The SecItem API has a digital identity keychain item class, namely kSecClassIdentity. However, the keychain does not store digital identities. When you add a digital identity to the keychain, the system stores its components, the certificate and the private key, separately, using kSecClassCertificate and kSecClassKey respectively. This has a number of non-obvious effects: Adding a certificate can ‘add’ a digital identity. If the new certificate happens to match a private key that’s already in the keychain, the keychain treats that pair as a digital identity. Likewise when you add a private key. Similarly, removing a certificate or private key can ‘remove’ a digital identity. Adding a digital identity will either add a private key, or a certificate, or both, depending on what’s already in the keychain. Removing a digital identity removes its certificate. It might also remove the private key, depending on whether that private key is used by a different digital identity. Keys Aren’t Stored in the Secure Enclave Apple platforms let you protect a key with the Secure Enclave (SE). The key is then hardware bound. It can only be used by that specific SE [1]. Earlier versions of the Protecting keys with the Secure Enclave article implied that SE-protected keys were stored in the SE itself. This is not true, and it’s caused a lot of confusion. For example, I once asked the keychain team “How much space does the SE have available to store keys?”, a question that’s complete nonsense once you understand how this works. In reality, SE-protected keys are stored in the standard keychain database alongside all your other keychain items. The difference is that the key is wrapped in such a way that only the SE can use it. So, the key is protected by the SE, not stored in the SE. A while back we updated the docs to clarify this point but the confusion persists. [1] Technically it’s that specific iteration of that specific SE. If you erase the device then the key material needed to use the key is erased and so the key becomes permanently useless. This is the sort of thing you’ll find explained in Apple Platform Security. Careful With that Shim, Mac Developer As explained in TN3137 On Mac keychain APIs and implementations, macOS has a shim that connects the SecItem API to either the data protection keychain or the file-based keychain depending on the nature of the request. That shim has limitations. Some of those are architectural but others are simply bugs in the shim. For some great examples, see the Investigating Complex Attributes section below. The best way to avoid problems like this is to target the data protection keychain. If you can’t do that, try to avoid exploring the outer reaches of the SecItem API. If you encounter a case that doesn’t make sense, try that same case with the data protection keychain. If it works there but fails with the file-based keychain, please do file a bug against the shim. It’ll be in good company. Add-only Attributes Some attributes can only be set when you add an item. These attributes are usually associated with the scope of the item. For example, to protect an item with the Secure Enclave, supply the kSecAttrAccessControl attribute to the SecItemAdd call. Once you do that, however, you can’t change the attribute. Calling SecItemUpdate with a new kSecAttrAccessControl won’t work. Lost Keychain Items A common complaint from developers is that a seemingly minor update to their app has caused it to lose all of its keychain items. Usually this is caused by one of two problems: Entitlement changes Query dictionary confusion Access to keychain items is mediated by various entitlements, as described in Sharing access to keychain items among a collection of apps. If the two versions of your app have different entitlements, one version may not be able to ‘see’ items created by the other. Imagine you have an app with an App ID of SKMME9E2Y8.com.example.waffle-varnisher. Version 1 of your app is signed with the keychain-access-groups entitlement set to [ SKMME9E2Y8.groupA, SKMME9E2Y8.groupB ]. That makes its keychain access group list [ SKMME9E2Y8.groupA, SKMME9E2Y8.groupB, SKMME9E2Y8.com.example.waffle-varnisher ]. If this app creates a new keychain item without specifying kSecAttrAccessGroup, the system places the item into SKMME9E2Y8.groupA. If version 2 of your app removes SKMME9E2Y8.groupA from the keychain-access-groups, it’ll no longer be able to see the keychain items created by version 1. You’ll also see this problem if you change your App ID prefix, as described in App ID Prefix Change and Keychain Access. IMPORTANT When checking for this problem, don’t rely on your .entitlements file. There are many steps between it and your app’s actual entitlements. Rather, run codesign to dump the entitlements of your built app: % codesign -d --entitlements - /path/to/your.app Lost Keychain Items, Redux Another common cause of lost keychain items is confusion about query dictionaries, something discussed in detail in this post and SecItem: Fundamentals. If SecItemCopyMatching isn’t returning the expected item, add some test code to get all the items and their attributes. For example, to dump all the generic password items, run code like this: func dumpGenericPasswords() throws { let itemDicts = try secCall { SecItemCopyMatching([ kSecClass: kSecClassGenericPassword, kSecMatchLimit: kSecMatchLimitAll, kSecReturnAttributes: true, ] as NSDictionary, $0) } as! [[String: Any]] print(itemDicts) } Then compare each item’s attributes against the attributes you’re looking for to see why there was no match. Best Practices With the pitfalls out of the way, let’s talk about best practices. Less Painful Dictionaries I look at a lot of keychain code and it’s amazing how much of it is way more painful than it needs to be. The biggest offender here is the dictionaries. Here are two tips to minimise the pain. First, don’t use CFDictionary. It’s seriously ugly. While the SecItem API is defined in terms of CFDictionary, you don’t have to work with CFDictionary directly. Rather, use NSDictionary and take advantage of the toll-free bridge. For example, consider this CFDictionary code: CFTypeRef keys[4] = { kSecClass, kSecAttrService, kSecMatchLimit, kSecReturnAttributes, }; static const int kTen = 10; CFNumberRef ten = CFNumberCreate(NULL, kCFNumberIntType, &kTen); CFAutorelease(ten); CFTypeRef values[4] = { kSecClassGenericPassword, CFSTR("AYS"), ten, kCFBooleanTrue, }; CFDictionaryRef query = CFDictionaryCreate( NULL, keys, values, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); Note This might seem rather extreme but I’ve literally seen code like this, and worse, while helping developers. Contrast this to the equivalent NSDictionary code: NSDictionary * query = @{ (__bridge NSString *) kSecClass: (__bridge NSString *) kSecClassGenericPassword, (__bridge NSString *) kSecAttrService: @"AYS", (__bridge NSString *) kSecMatchLimit: @10, (__bridge NSString *) kSecReturnAttributes: @YES, }; Wow, that’s so much better. Second, if you’re working in Swift, take advantage of its awesome ability to create NSDictionary values from Swift dictionary literals. Here’s the equivalent code in Swift: let query = [ kSecClass: kSecClassGenericPassword, kSecAttrService: "AYS", kSecMatchLimit: 10, kSecReturnAttributes: true, ] as NSDictionary Nice! Avoid Reusing Dictionaries I regularly see folks reuse dictionaries for different SecItem calls. For example, they might have code like this: var copyResult: CFTypeRef? = nil let dict = [ kSecClass: kSecClassGenericPassword, kSecAttrService: "AYS", kSecAttrAccount: "mrgumby", kSecReturnData: true, ] as NSMutableDictionary var err = SecItemCopyMatching(dict, &copyResult) if err == errSecItemNotFound { dict[kSecValueData] = Data("opendoor".utf8) err = SecItemAdd(dict, nil) } This specific example will work, but it’s easy to spot the logic error. kSecReturnData is a return type property and it makes no sense to pass it to a SecItemAdd call whose second parameter is nil. I’m not sure why folks do this. I think it’s because they think that constructing dictionaries is expensive. Regardless, this pattern can lead to all sorts of weird problems. For example, it’s the leading cause of the issue described in the Queries and the Uniqueness Constraints section, above. My advice is that you use a new dictionary for each call. That prevents state from one call accidentally leaking into a subsequent call. For example, I’d rewrite the above as: var copyResult: CFTypeRef? = nil let query = [ kSecClass: kSecClassGenericPassword, kSecAttrService: "AYS", kSecAttrAccount: "mrgumby", kSecReturnData: true, ] as NSMutableDictionary var err = SecItemCopyMatching(query, &copyResult) if err == errSecItemNotFound { let add = [ kSecClass: kSecClassGenericPassword, kSecAttrService: "AYS", kSecAttrAccount: "mrgumby", kSecValueData: Data("opendoor".utf8), ] as NSMutableDictionary err = SecItemAdd(add, nil) } It’s a bit longer, but it’s much easier to track the flow. And if you want to eliminate the repetition, use a helper function: func makeDict() -> NSMutableDictionary { [ kSecClass: kSecClassGenericPassword, kSecAttrService: "AYS", kSecAttrAccount: "mrgumby", ] as NSMutableDictionary } var copyResult: CFTypeRef? = nil let query = makeDict() query[kSecReturnData] = true var err = SecItemCopyMatching(query, &copyResult) if err == errSecItemNotFound { let add = makeDict() query[kSecValueData] = Data("opendoor".utf8) err = SecItemAdd(add, nil) } Think Before Wrapping A lot of folks look at the SecItem API and immediately reach for a wrapper library. A keychain wrapper library might seem like a good idea but there are some serious downsides: It adds another dependency to your project. Different subsystems within your project may use different wrappers. The wrapper can obscure the underlying API. Indeed, its entire raison d’être is to obscure the underlying API. This is problematic if things go wrong. I regularly talk to folks with hard-to-debug keychain problems and the conversation goes something like this: Quinn: What attributes do you use in the query dictionary? J R Developer: What’s a query dictionary? Quinn: OK, so what error are you getting back? J R Developer: It throws WrapperKeychainFailedError. That’s not helpful )-: If you do use a wrapper, make sure it has diagnostic support that includes the values passed to and from the SecItem API. Also make sure that, when it fails, it returns an error that includes the underlying keychain error code. These benefits will be particularly useful if you encounter a keychain problem that only shows up in the field. Wrappers must choose whether to be general or specific. A general wrapper may be harder to understand than the equivalent SecItem calls, and it’ll certainly contain a lot of complex code. On the other hand, a specific wrapper may have a model of the keychain that doesn’t align with your requirements. I recommend that you think twice before using a keychain wrapper. Personally I find the SecItem API relatively easy to call, assuming that: I use the techniques shown in Less Painful Dictionaries, above, to avoid having to deal with CFDictionary. I use my secCall(…) helpers to simplify error handling. For the code, see Calling Security Framework from Swift. If you’re not prepared to take the SecItem API neat, consider writing your own wrapper, one that’s tightly focused on the requirements of your project. For example, in my VPN apps I use the wrapper from this post, which does exactly what I need in about 100 lines of code. Prefer to Update Of the four SecItem functions, SecItemUpdate is the most neglected. Rather than calling SecItemUpdate I regularly see folks delete and then re-add the item. This is a shame because SecItemUpdate has some important benefits: It preserves persistent references. If you delete and then re-add the item, you get a new item with a new persistent reference. It’s well aligned with the fundamental database nature of the keychain. It forces you to think about which attributes uniquely identify your item and which items can be updated without changing the item’s identity. Understand These Key Attributes Key items have a number of attributes that are similarly named, and it’s important to keep them straight. I created a cheat sheet for this, namely, SecItem attributes for keys. You wouldn’t believe how often I consult this! Investigating Complex Attributes Some attributes have values where the format is not obvious. For example, the kSecAttrIssuer attributed is documented as: The corresponding value is of type CFData and contains the X.500 issuer name of a certificate. What exactly does that mean? If I want to search the keychain for all certificates issued by a specific certificate authority, what value should I supply? One way to figure this out is to add a certificate to the keychain, read the attributes back, and then dump the kSecAttrIssuer value. For example: let cert: SecCertificate = … let attrs = try secCall { SecItemAdd([ kSecValueRef: cert, kSecReturnAttributes: true, ] as NSDictionary, $0) } as! [String: Any] let issuer = attrs[kSecAttrIssuer as String] as! NSData print((issuer as NSData).debugDescription) // prints: <3110300e 06035504 030c074d 6f757365 4341310b 30090603 55040613 024742> Those bytes represent the contents of a X.509 Name ASN.1 structure with DER encoding. This is without the outer SEQUENCE element, so if you dump it as ASN.1 you’ll get a nice dump of the first SET and then a warning about extra stuff at the end of the file: % xxd issuer.asn1 00000000: 3110 300e 0603 5504 030c 074d 6f75 7365 1.0...U....Mouse 00000010: 4341 310b 3009 0603 5504 0613 0247 42 CA1.0...U....GB % dumpasn1 -p issuer.asn1 SET { SEQUENCE { OBJECT IDENTIFIER commonName (2 5 4 3) UTF8String 'MouseCA' } } Warning: Further data follows ASN.1 data at position 18. Note For details on the Name structure, see section 4.1.2.4 of RFC 5280. Amusingly, if you run the same test against the file-based keychain you’ll… crash. OK, that’s not amusing. It turns out that the code above doesn’t work when targeting the file-based keychain because SecItemAdd doesn’t return a dictionary but rather an array of dictionaries (r. 21111543). Once you get past that, however, you’ll see it print: <301f3110 300e0603 5504030c 074d6f75 73654341 310b3009 06035504 06130247 42> Which is different! Dumping it as ASN.1 shows that it’s the full Name structure, including the outer SEQUENCE element: % xxd issuer-file-based.asn1 00000000: 301f 3110 300e 0603 5504 030c 074d 6f75 0.1.0...U....Mou 00000010: 7365 4341 310b 3009 0603 5504 0613 0247 seCA1.0...U....G 00000020: 42 B % dumpasn1 -p issuer-file-based.asn1 SEQUENCE { SET { SEQUENCE { OBJECT IDENTIFIER commonName (2 5 4 3) UTF8String 'MouseCA' } } SET { SEQUENCE { OBJECT IDENTIFIER countryName (2 5 4 6) PrintableString 'GB' } } } This difference in behaviour between the data protection and file-based keychains is a known bug (r. 26391756) but in this case it’s handy because the file-based keychain behaviour makes it easier to understand the data protection keychain behaviour. App Groups on the Mac Sharing access to keychain items among a collection of apps explains that three entitlements determine your keychain access: keychain-access-groups application-identifier (com.apple.application-identifier on macOS) com.apple.security.application-groups In the discussion of com.apple.security.application-groups it says: Starting in iOS 8, the array of strings given by this entitlement also extends the list of keychain access groups. That’s true, but it’s also potentially misleading. This affordance only works on iOS and its child platforms. It doesn’t work on macOS. That’s because app groups work very differently on macOS than they do on iOS. For all the details, see App Groups: macOS vs iOS: Fight!. However, the take-home point is that, when you use the data protection keychain on macOS, your keychain access group list is built from keychain-access-groups and com.apple.application-identifier. Revision History 2024-04-11 Added the App Groups on the Mac section. 2023-10-25 Added the Lost Keychain Items and Lost Keychain Items, Redux sections. 2023-09-22 Made minor editorial changes. 2023-09-12 Fixed various bugs in the revision history. Added the Erroneous Attributes section. 2023-02-22 Fixed the link to the VPNKeychain post. Corrected the name of the Context Matters section. Added the Investigating Complex Attributes section. 2023-01-28 First posted.
Posted
by
Post not yet marked as solved
5 Replies
626 Views
We are building an Authorization Plugin with a custom GUI where we prompt the user for additional information after they have entered their username/password. Right now, we are doing this using an NSWindowController and an NSWindow. The problem is that the login "spinner" shows up in front of the window as seen here: Is there a way to avoid this? Or do we have to subclass SFAuthorizationPluginView to get this to work? Thanks!
Posted
by
Post not yet marked as solved
3 Replies
1.8k Views
After static analysis of IPA binary, We found below warnings • @rpath: It is recommended to reconfigure the application binary to ensure it has disabled the Runpath Search Path(@rpath) How can i fix this?
Posted
by
Post marked as solved
11 Replies
2.1k Views
My end goal is to use eciesEncryptionCofactorX963SHA256AESGCM with a key generated on the Secure Enclave using CryptoKit, that requires Biometric Authentication. CryptoKit does not implement the ECIES encryption algorithms, so my goal was to fall back to the Security framework. The public key can be easily converted to a SecKey because it implements x963Representation which can then be imported as follows: let enclaveSecKey: SecKey = SecKeyCreateWithData(enclaveKey.x963Representation as CFData, [ kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClass: kSecAttrKeyClassPublic, kSecAttrKeySizeInBits: 256 ] as [String: Any] as CFDictionary, nil), I have everything working except the code to decrypt with the private key. Naturally, the Secure Enclave does not expose the private key - as is its design - rather some kind of token? I did read the Keychain documentation which notes that it is not possible to simply obtain an x963Representation of the private key (as it's a custom representation returned by the Secure Enclave). However, my ultimate question is this: can one convert the Secure Enclave representation into something that can be used as a SecKey for encryption/decryption (without necessarily being stored in the Keychain - i.e., 'correct') as it seems both CryptoKit and Security have a means of representing the private key token returned by the Secure Enclave? (Or is one's only recourse to use the Security framework for generating and storing the keys too?) I have also tried this code to create a SecKey representation, having retrieved the GenericPasswordConvertible out of the keychain (note the use of kSecAttrTokenID: kSecAttrTokenIDSecureEnclave) with the aforementioned goal of loading the Secure Enclave's private token as a SecKey: let enclaveSecKey: SecKey = SecKeyCreateWithData(enclaveKey.rawRepresentation as CFData, [ kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClass: kSecAttrKeyClassPrivate, kSecAttrTokenID: kSecAttrTokenIDSecureEnclave, kSecAttrAccessible: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecUseAuthenticationContext: try await createAuthContext( reason: "Decrypt data", fallbackTitle: "Enter your device password to decrypt data", mustEvaluate: true ), kSecAttrIsPermanent: true, kSecAttrIsExtractable: false, kSecAttrSynchronizable: false, kSecAttrKeySizeInBits: 256, kSecAttrAccessControl: SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, [.biometryAny, .privateKeyUsage], &cfSecKeyCreateError )! ] as [String: Any] as CFDictionary, nil) This works, in and of itself, (i.e., it loads without error and cfSecKeyCreateError is nil, however when I try SecSecKeyCopyPublicKey I get a different, incorrect public key and - naturally, I suppose - if I attempt to decrypt data with the private key that fails with: Optional(Swift.Unmanaged<__C.CFErrorRef>(_value: Error Domain=NSOSStatusErrorDomain Code=-50 "ECIES: Failed to aes-gcm decrypt data (err -69)" UserInfo={numberOfErrorsDeep=0, NSDescription=ECIES: Failed to aes-gcm decrypt data (err -69)}))
Posted
by
Post marked as solved
2 Replies
910 Views
Looking at the certificate chains for various binaries (using Apple's APIs or codesign --vvd) shows several patterns for the common names. I am wondering why some code has the structure Apple Root CA Developer ID Certification Authority Developer ID Application: Google LLC (EQHXZ8M8AV) while others have the pattern Apple Root CA Apple Worldwide Developer Relations Certification Authority Apple Mac OS Application Signing Note, the second pattern does not include an organizational name. Why is there a difference? Is the second pattern an older pattern and the first (with the organization name) the new pattern? (There are other certificate patterns like for Apple's binaries and development code I am testing)
Posted
by
Post marked as solved
2 Replies
410 Views
SecCopyErrorMessageString suggests that the return value should be called with CFRelease to release/free the object. Though upon attempting to do so, XCode says "CFRelease is unavailable: Core Foundation objects are automatically memory managed". I have verified with Instruments in XCode that the follow code increases in memory usage without bound, from ~3MB to 20MB+ over 100000 iterations. public func simpleLeak(){ var secReq: SecRequirement? let err = SecRequirementCreateWithString("blah" as CFString, SecCSFlags(rawValue: 0), &amp;secReq) guard err == errSecSuccess else { var errorString = SecCopyErrorMessageString(err, nil) print(errorString) return } } What's the proper usage of SecCopyErrorMessageString that doesn't leak memory in Swift?
Posted
by