Security Embedded is 15+ years of experience in building secure systems. Learn more about how we can help you by exploring Phil's blog or contacting us.

Under the Hood of WebAuthn in Safari

Under the Hood of WebAuthn in Safari

unsplash-image-ktWur2xM1hs.jpg

At WWDC in 2020, Apple announced Touch ID and Face ID authentication for the Web as a new feature in Safari. In the break-out session, much of the content was an overview of what this meant for web developers. The break-out did make it clear that this new feature is exposed using WebAuthn in the browser. So how did Apple do it?

Before we dive into Apple’s implementation, let’s talk a bit about WebAuthn.

What is WebAuthn?

WebAuthn is a W3C standard API that provides access to hardware authenticators in the browser, enabling strong second factor authentication or even passwordless authentication for users. It is a feature offered by every contemporary browser. The specification is part of the next iteration of the FIDO Alliance’s suite of standards, Universal Second Factor or U2F.

There are four parties parties involved in any WebAuthn transaction:

  • the relying party, the service provider who is seeking to authenticate users (i.e. Google, Facebook, Github)
  • the authenticator, a secure storage device for cryptographic keys and requires some sort of physical gesture to authorize use of these keys (i.e. a button push, a tap, verification of a biometric factor)
  • the web browser, acting as a channel between the relying party and authenticator
  • the user, who has to affirmatively interact ("the gesture") with the authenticator to prove physical presence.

There are two distinct stages involved in the WebAuthn lifecycle: enrollment and assertion.

At enrollment, the authenticator generates a unique asymmetric key pair for a given relying party, then attests to possession of this new private key. This attestation is usually a form of cryptographic proof of possession, often embodied as an X.509 certificate chain that links the device back to the manufacturer. The manufacturer issues a certificate to the device in the factory, a sort of proof that the device is authentic and keeps keys safe and secure as the WebAuthn standard specifies. The relying party can check this certificate chain, check the certificate that the manufacturer issued to the device, and make sure it all ties back to the manufacturer’s root certificate authority (CA). If all the attestation checks line up, the relying party stores a key handle provided by the authenticator that is used to refer to the key later on.

Once enrolled, after receiving a valid username and password, the relying party can request from the user an assertion from their authenticator. The relying party submits a nonce as a challenge to the authenticator. After the user performs an authorization gesture, the device asserts the user’s identity by signing the nonce, proving possession of the private key generated during enrollment.

WebAuthn authenticators come in three flavours:

  • Roaming Authenticators: discrete devices that represent the user based on being in the user’s physical possession. A Yubikey or a Google Titan key would be an example of this. These devices expose U2F or CTAP2 over a USB, Bluetooth or RFID link, enabling a web browser to communicate with the device.
  • Virtual Authenticators: a pure software authenticator that stores private keys in a database that likely doesn’t reside in secure hardware. Useful for testing, and better than nothing under some circumstances. It is hard for a relying party to trust that the keys won’t be stolen, though, since they could be stored in plaintext on disk, making them easy to extract and copy.
  • Platform Authenticators: authentication devices that are built into a device. An example would be a TPM in a modern laptop. These authenticators are convenient: they are built into the device, making it harder to lose or for a user to forget their authenticator. However, the credentials are bound to a particular device, so users who lose or reset the device will need another way to authenticate, such as a roaming authenticator.

The Secure Enclave is a platform authenticator, and we’ll focus on this model going forward. But first, what is the Secure Enclave Processor, and how does this provide unique security characteristics to Apple devices?

The Secure Enclave

In July 2012, Apple finalized their acquisition of Authentec, at the time the largest capacitive fingerprint sensor manufacturer in the world. In September 2013, Apple introduced Touch ID as a flagship feature in the iPhone 5S. This was a convenient way to unlock your phone, enabled by the technology acquired from Authentec. Every iPhone 5S user now had the option to unlock their phone by presenting their finger, instead of tapping in their passcode, all thanks to the power of biometric authentication.

Note: The Authentec acquisition was the second time Apple bought a critical vendor for a design I was working on. At the time I was working on a biometric token that integrated an Authentec fingerprint sensor, leading to a break-neck pace redesign to include a Fingerprint Cards sensor instead. Apple immediately discontinued all Authentec parts, and cancelled all relationships they could.

The first time I experienced this was when Apple bought PA-Semi, but that’s another story.

Under the covers, Apple quietly introduced another technology in the iPhone 5S’s new A7 System on a Chip (SoC): the Secure Enclave Processor, or SEP. The SEP is a separate ARM Cortex-A CPU, with isolation from the ARM CPU cores running iOS. Its security critical tasks include managing communication with the biometric sensor, performing biometric template extraction and matching, handling user identity keys and managing data encryption keys and hardware. There have been many great overviews of the SEP from a reverse engineer’s perspective. Apple also details how the SEP fits into their devices’ overall security model.

Every Apple SoC since the A7 includes a SEP, and the SEP is even built-in to the T1 and T2 security chips of Intel CPU-based Macintosh computers. Over the years, the SEP has accreted more responsibilities in the iOS and macOS security model. For example, the Apple M1 SoC integrates the SEP in its secure boot process. However, the core goal of the SEP remains unchanged: to provide a secure and trusted (by Apple) store for keys, identity secrets and biometric templates.

SEP Key Attestation

The SEP is the gatekeeper for a multitude of identities associated with an Apple device. Some identities are generated for a particular purpose, to be used by an App to encrypt data. Other identities, such as the Silicon Identity Key (SIK), are unique to a particular device. When an Apple device is activated (personalized with a fresh iOS or macOS install), the SEP generates a new symmetric key, the UID. The UIK is a P-256 key pair derived from the UID, resulting in an asymmetric key pair that can be traced back to an individual activation of a device by a user. The UIK is only accessible to public key accelerator hardware in the SEP -- not even sepOS can access the private key.

The UIK is crucial for key attestation: Apple uses this key to uniquely identify a phone and user at a point in time. Apple’s apps can ask the SEP generate and attest a new key, the UIK signs an attestation ticket to be submitted to one of Apple’s attestation authorities. This ticket serves as proof the SEP generated, manages and protects this new key.

Apple’s attestation authorities are central to the SEP’s attestation workflow. There are two authorities used by Apple devices today: the Basic Attestation Authority (BAA) and the Anonymous Attestation Authority (AAA). These attestation authorities take metadata collected by macOS/iOS and match it up with data in the encrypted attestation ticket generated by the SEP and device metadata collected in the factory and stored by Apple. Apple verifies that the signature on the attestation ticket matches a known device, and checks the status of the device. If the device has not been reported stolen or lost and Apple recognizes the device as being manufactured in their factories, the attestation authority returns an X.509 certificate chain containing proof that Apple checked and validated the attestation metadata from the device. The end entity certificate of this X.509 chain is for the key pair that corresponds to the attestation ticket, and includes metadata about the key and the attestation attempt.

Attestation is just one step in a larger cryptographic protocol. When transferring encrypted or signed data across an insecure channel, you need some assurance that a previous communication isn’t being replayed by an attacker. Nonces achieve this in any such protocol, ensuring uniqueness of every communication. As a part of its attestation, the SEP allows the app to include a nonce in the attestation ticket, specified by the caller requesting the attestation. This nonce is normally included in the end entity X.509 attestation certificate the attestation authority produces.

WebAuthn in Safari

With the SEP, Apple’s hardware platforms have all the raw components needed to implement WebAuthn: a secure place to manage keys and a mechanism to attest to possessing keys (using the UIK and the AAA).

Safari is a part of the WebKit project. WebKit is open source software. Safari releases shipped with macOS and iOS are even built from the upstream open source WebKit repository. This makes it easy for us to verify how Safari uses various public frameworks. We can also use these as hints to help us find relevant entry points to any private frameworks we may need to reverse engineer.

Apple-Style WebAuthn Attestation

When using the SEP-based platform authenticator, Apple exposes its own unique attestation format for WebAuthn. Any WebAuthn authenticator returns two important blobs of data to the relying party:

  • clientDataJSON, a JSON blob that contains information about what is being authenticated, including the relying party URL and the attestation nonce;
  • attestationObject, a CBOR-encoded blob containing the metadata about the attestation operation performed.

The attestationObject for SEP-based attestation consists of 3 fields:

  • authData, the data around the authenticator state at time of attestation,
  • fmt, an indicator of what type of attestation is to be used (in this case “apple”),
  • attStmt, containing a single element, an array of X.509 certificates forming a chain up to Apple’s WebAuthn Root CA, DER-encoded.

The attStmt array of certificates starts from the certificate subordinate to Apple’s WebAuthn Root CA, down to an end entity certificate that represents the WebAuthn credential itself. The end entity certificate also includes a 32 byte attestation nonce, stored in an extension field This nonce is not the nonce provided by the relying party at enrollment time, but rather is calculated using several fields in the enrollment payload:

SHA-256(authData || SHA-256(clientDataJSON))

The clientDataJSON object contains and includes the origin of the relying party (the URL scheme, hostname, port), as well as a URI-safe base64 encoding of the nonce from the relying party. This ensures that the nonce from the relying party is included in generating the end entity certificate’s nonce. By sealing the hash of these blobs of data in the end entity certificate, we can verify that these structures received in the attestation payload have not been tampered with, showing we can have some degree of trust in this metadata originating from an Apple device not being replayed nor tampered with.

Interested in seeing the specifics of how to verify WebAuthn key attestations from Safari? Have a look at this sample project.

Key Generation with the Security Framework

The Security framework manages keychains for apps on both macOS and iOS. This public framework is the primary means of interaction with the OS’s keychain for third-party applications. Any app that generates, uses and needs to store various kinds of cryptographic keys will use this framework. The Security Framework does include several private APIs (sometimes called System Private Interface or SPIs), including SecKeyCreateAttestation. This SPI is protected by an entitlement that Apple will not grant to third-party apps, but is critical to SEP key attestation.

While third-party developers cannot directly request key attestation, they can use the SecKeyCreateRandomKey API to generate keys managed by the SEP. This is achieved by setting kSecAttrTokenID to kSecAttrTokenIDSecureEnclave. Of course, third-party apps have no way to verify these keys are protected by the SEP.

Another part of the key generation process includes specifying labels and other attributes used to retrieve the key from the keychain. Safari stores alongside these keys the relying party’s identifier (the host, scheme and optionally port of the relying party), and uses this identifier to later check the relying party matches when requesting a key, preventing other websites from using this key. The relying party is given an opaque handle with which to reference this key pair in the future when requesting authentication assertions, as well.

Note: Apple used to require kSecAttrKeyType be set to kSecAttrKeyTypeECSECPrimeRandomPKA (Public Key Accelerator); it seems this is no longer the requirement to use key attestation in the SEP. It’s been a while since I looked at the key storage service (sks) in sepOS, but it might be able to attest non-PKA keys, or perhaps all keys generated in the SEP are now PKA keys.

RetainPtr<SecKeyRef> LocalConnection::createCredentialPrivateKey(LAContext *context, SecAccessControlRef accessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const
{
    NSDictionary *attributes = @{
        (id)kSecAttrTokenID: (id)kSecAttrTokenIDSecureEnclave,
        (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
        (id)kSecAttrKeySizeInBits: @256,
        (id)kSecPrivateKeyAttrs: @{
            (id)kSecUseAuthenticationContext: context,
            (id)kSecAttrAccessControl: (id)accessControlRef,
            (id)kSecAttrIsPermanent: @YES,
            (id)kSecAttrAccessGroup: (id)String(LocalAuthenticatiorAccessGroup),
            (id)kSecAttrLabel: secAttrLabel,
            (id)kSecAttrApplicationTag: secAttrApplicationTag,
        }};
    LOCAL_CONNECTION_ADDITIONS
    CFErrorRef errorRef = nullptr;
    auto credentialPrivateKey = adoptCF(SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes, &errorRef));
    auto retainError = adoptCF(errorRef);
    if (errorRef) {
        LOG_ERROR("Couldn't create private key: %@", (NSError *)errorRef);
        return nullptr;
    }
    return credentialPrivateKey;
}

Safari doesn’t do anything special when generating keys for use in WebAuthn, beyond protecting a key from being used by a different origin. But we’ll see there are roadblocks around attestation that prevent third-party browsers, like Chrome and Firefox, from implementing the same functionality.

WebAuthn Key Policy

Keys generated by the SEP can have biometric or access control policies attached to them in order to force certain actions by the user before a key can be used. This is accomplished by setting appropriate parameters in SecAccessControl when generating the key. WebAuthn keys in Safari have the kSecAccessControlUserPresence flag set. These keys can be unlocked using either a biometric factor or a PIN (thus proving user presence), and do not necessarily require that any biometric factor be enrolled to unlock the key. This is in line with the typical WebAuthn/U2F token model, where possession of the unique device is proof that you’re authorized to use the credentials.

For those of you considering WebAuthn as an authentication control, remember that anyone who possesses a device or has access to the device remotely would be able to use these credentials. These keys are nevertheless very difficult for an attacker to clone, raising the bar against phishing attacks and other remote compromise risks.

Don’t be too Entitled!

Apps must state up-front what capabilities they need in order to run for Apple to sign them. This is the principle of least privilege - when the OS grants an app access to only what it needs to run, the app exposes a smaller attack surface. The OS can also gate all kinds of sensitive functions or capabilities behind entitlements, locking these functions away from most applications. Apple goes one step further, and seals this set of entitlements into the app, under the app bundle signature. This signed seal gives Apple the power to deny certain entitlements to third-party applications -- if an app is not signed by Apple, it won’t launch in most cases. Before signing an app, Apple vets the list of entitlements the app requests -- pick an entitlement Apple doesn’t want you to have, and they won’t sign your app. This is a very powerful control to protect the integrity of their ecosystem.

All keychain items are tagged with an access group, identifying which app or family of apps are allowed to use that key. Each app that uses the keychain must specify a keychain access group in its entitlements -- this identifier could be shared among apps developed by the same company. Apple also has a few special keychain access groups, like lockdown-identities, representing activation-related keys. Apple will not notarize apps that include an entitlement to access this keychain. On a Mac you can turn off System Integrity Protection (SIP) to bypass all these controls -- a useful feature for security researchers.

If you spend enough time reverse engineering Apple’s DeviceIdentity private framework, you’ll see several other entitlements related to key attestation. These include:

  • com.apple.keystore.sik.access - to get access to use the SIK to attest to keys
  • com.apple.security.attestation.access - to be able to call SecKeyCreateAttestation successfully

The Webkit repository includes all the tools and build system hooks required to build the internal version of Safari. In the repo is a shell script that prepares the entitlements PLIST for a Safari build. This shell script has a function, conveniently named mac_process_webauthn_entitlements. WebAuthn on Safari has its keys tagged with the com.apple.webkit.webauthn access group.

function mac_process_webauthn_entitlements()
{
    if [[ "${WK_USE_RESTRICTED_ENTITLEMENTS}" == YES ]]
    then
        plistbuddy Add :com.apple.security.device.usb bool YES

        plistbuddy Add :keychain-access-groups array
        plistbuddy Add :keychain-access-groups:0 string com.apple.webkit.webauthn
        plistbuddy Add :keychain-access-groups:1 string lockdown-identities

        plistbuddy Add :com.apple.security.attestation.access bool YES
        plistbuddy Add :com.apple.keystore.sik.access bool YES
        plistbuddy Add :com.apple.private.RemoteServiceDiscovery.allow-sandbox bool YES
        plistbuddy Add :com.apple.private.RemoteServiceDiscovery.device-admin bool YES
        plistbuddy Add :com.apple.appattest.spi bool YES
        plistbuddy Add :com.apple.mobileactivationd.spi bool YES
        plistbuddy Add :com.apple.mobileactivationd.bridge bool YES
        plistbuddy Add :com.apple.private.security.bootpolicy bool YES
    fi
}

At a glance, it looks like Safari depends on attestation capabilities provided by the DeviceIdentity framework, which calls SecKeyCreateAttestation. The com.apple.appattest.spi entitlement looks to be something new; we’ll have to look into, as well. There are other interesting entitlements, like the mobileactivationd entitlements, also used to retrieve metadata needed to request attestation from AAA.

WebAuthn Key Attestation

Apple has been reticent to expose SEP key attestation to third party applications. Device Check App Attestation and WebAuthn key attestation are the only way third-parties can use these features. These mechanisms are use case specific, and don’t provide a general-purpose form of attestation with which apps could build their own authentication protocols. Nonetheless, these are very useful checks for app developers to protect their apps and certain types of users.

Apple hid the WebAuthn implementation of key attestation behind a SPI in the new (as of Big Sur and iOS 14) AppAttest framework, AppAttest_WebAuthentication_AttestKey. AppAttest’s public APIs include the new App Attestation feature, so it would make sense to reuse some of the same code.

void LocalConnection::getAttestation(SecKeyRef privateKey, NSData *authData, NSData *hash, AttestationCallback&& completionHandler) const
{
#if HAVE(APPLE_ATTESTATION)
    AppAttest_WebAuthentication_AttestKey(privateKey, authData, hash, makeBlockPtr([completionHandler = WTFMove(completionHandler)] (NSArray *certificates, NSError *error) mutable {
        completionHandler(certificates, error);
    }).get());
#endif
}

The easy code reading exercise has ended. The sequel will be a straightforward exercise in analyzing private frameworks included in macOS’s dyld_shared_cache using IDA. We know the SPI’s entry point and can guess at the framework, IDA just needs to analyze the AppAttest framework and its dependencies. Loading and analyzing the dyld_shared_cache from macOS Big Sur on x86-64 took about 7 hours on my Linux VM, so if you plan to go down this path you had better start this in the morning and have a full day of other work planned.

The SPI AppAttest_WebAuthentication_AttestKey calls AppAttest_Common_Attest, after performing some basic sanity checks. AppAttest_Common_ValidateEntitlements checks for the following entitlements:

  • com.apple.appattest.spi
  • com.apple.security.attestation.access
/*!
 @function SecKeyCreateAttestation
 @abstract Attests a key with another key.

 @param key The attesting key.
 @param keyToAttest The key which is to be attested.
 @param error An optional pointer to a CFErrorRef. This value is set if an error occurred.

 @result On success a CFDataRef containing the attestation data is returned, on failure it returns NULL.

 @discussion Key attestation only works for CTK SEP keys, i.e. keys created with kSecAttrTokenID=kSecAttrTokenIDSecureEnclave.
*/
CFDataRef SecKeyCreateAttestation(SecKeyRef key, SecKeyRef keyToAttest, CFErrorRef *error)

This confirms why Safari added these entitlements: they’re required to call the AppAttest SPI. The AppAttest SPI relies on DeviceIdentity’s attestation functions, which ultimately rely on SecKeyCreateAttestation, hence the check for com.apple.security.attestation.access.

To kick off the attestation process, AppAttest_WebAuthentication_AttestKey does the following:

  • Call the AppAttest_Common_AttestKey function, collecting parameters to support the attestation, including:
    • The nonce to be passed to AAA
    • The length of time the certificate should be valid
    • A list of X.509 certificate extension OIDs to include in the end entity certificate, specifically the attestation nonce.
  • Hand that information to the private function createAnonymousAttestationRequest - which under the covers calls DeviceIdentityCreateClientCertificateRequest, and which collects metadata about the host to prepare an attestation request. This function also requests that the new WebAuthn key is attested to using the UIK (create_uik_attestation_with_ref_key), which returns an encrypted attestation ticket on success.
  • Generate a payload for AAA with all the information collected. The result is a JSON object that comprises:
    • The encrypted attestation ticket for the unique WebAuthn key, attested to by the UIK
    • The device’s ChipID/ECID
    • The authenticator data for this request (a CBOR object, base64-encoded)
    • The hash of the client data for this request
  • Make an API call to https://register.appattest.apple.com/v1/attest with the payload, which returns a PEM-encoded X.509 certificate chain. This chain is converted to an array of DER-encoded certificates that are finally passed to the completion block argument of AppAttest_WebAuthentication_AttestKey.

At this point, the X.509 certificate chain can then be relayed to the relying party from JavaScript code running in Safari. It’s up to the relying party to verify the attestation, and decide whether or not to accept the WebAuthn enrollment attempt. If the attestation checks out, the relying party will store the key handle for future authentication attempts, such as the next time the enrolled user logs in.

Anonymous to Who?

Apple has gone to great lengths to make it hard to track users on their platform. Recent Safari releases make sure to obfuscate parameters often used by advertisers to identify and track users. Other recent developments include requiring iOS apps to disclose how they use a user’s information. Apple has all but declared corporate war on Facebook’s designs to track people online. These are all great steps for user privacy, at least from companies other than Apple.

In private conversations, it is clear that privacy is a priority for Apple’s security team when it designs new security technology. The idea behind AAA is that no recipient of a particular attestation will be able to track this back to an exact physical phone. While this is an excellent outcome, one has to ask how anonymous a user is with respect to one particular company: Apple itself.

When a device requests an anonymous AAA attestation, the request includes several key pieces of information:

  • An attestation ticket from the SEP, a signed and encrypted blob that contains information about the key, the device that generated the key, the version of sepOS the device is running, and other relevant metadata;
  • The ChipID and ECID of the device, likely used to determine which parameters to use to decrypt and verify the attestation ticket;
  • The authenticator data, a CBOR object that contains a hash of the relying party ID, among other things;
  • A hash of the client data JSON object.

This gives AAA the ability to reconstruct most of the data that went into producing the nonce that is wrapped in the SEP attestation ticket, without disclosing the client data portion of the attestation. This avoids Apple ever possessing the plaintext relying party ID, and makes it hard for Apple to determine the relying party without going to some effort.

One consideration though: Apple will get a hash of the relying party ID, the rpIdHash field of the authenticator data. If they so desired, Apple could log and track this field, along with activation information about the device requesting the attestation. The rpIdHash can be determined for common sites (i.e. Facebook, Google, Github), and with a large enough sample Apple could also determine the mapping from rpIdHash to relying party URL for other sites.

To avoid these accusations, Apple could take measures to better protect user privacy, such as hashing the rpIdHash with the relying party's nonce and transmitting that to Apple instead. This would still provide enough protection to prevent an attacker from being able to trick AAA into signing an attestation for a different relying party altogether. The relying party would check if the expected URL hashed, then hashed with the nonce, matches up with the rpIdHash field. This does create a bit more complexity for the relying party when it verifies the integrity of the attestation metadata, but means every rpIdHash would be unique.

What about Third Party Apps?

After all this, it seems it will be a while before third parties can integrate SEP-based WebAuthn into their applications. Most of the functionality involved is private or requires calling SPIs. Apple will not notarize applications that request the entitlements to call these SPIs or use the activation-specific identities, meaning that major browsers like Chrome or Firefox won’t be able to use this functionality. Until Apple exposes this functionality as some daemon that can be called by third-party apps, this really nice feature will be a Safari exclusive.

From a security perspective, Apple opening up the WebAuthn SPIs for attestation would only improve their platform, enabling developers to build more secure authentication into their apps. Having more general key attestation capabilities open to third-party apps would be an exciting development. This would cement the position of Apple’s platform as a leader in user and enterprise identity management capabilities, enabling sophisticated identity management applications.

We are not at the Precipice of a GPS Disaster

We are not at the Precipice of a GPS Disaster