System frameworks encrypt both data at rest and data in transit in a transparent way for you. This functionality is available by simply setting an attribute. However you may want to do more to protect your users' data. CryptoKit is a new Swift framework that makes it easier and safer than ever to perform cryptographic operations, whether you simply need to compute a hash or are implementing a more advanced authentication protocol.
Welcome to the bitcoin session . Kidding. Welcome to Cryptography and Your Apps session. Today, my colleague, Frederic, and I are here to show you that using cryptography on Apple products has never been easier. I am Yannick Sierra. I manage the Cryptographic Engineering Team at Apple. So today on one hand we have a set of great system features which solve a set of common security problems. On the other hand, we have an exciting new Swift API for cryptographic operation. We're really excited about it. If you here, it's probably because there's some information you want to protect, right.
We know that you as developers and as users find it really important to protect user's information, your information.
There are different kind of information, which are all sensitive. It starts with profile information such as phone numbers and email addresses. Your applications may also be handling user information such as photos or music by using an application to process and improve.
Your application may also be handling business assets.
This could be paid content your app is making available to users. Or account information like in the mobile banking scenarios.
For all of this information, it is critical to keep them secure, and you need to have great tools for that. At Apple, security and privacy are core values. We built it in all of the features that we work on.
Cryptography is a big part of it. For example, with device integrity we use cryptography to validate the integrity of the software running on the platform to make sure it has not been tampered with and guarantee its security properties are not changed.
We use cryptography to protect data at rest.
We use cryptography across a wide variety of applications users love, use extensively for capturing very personal moments and sharing them. This is a case of Messages, Safari, and iCloud.
With over a billion device, we operation cryptography at a huge scale.
The stakes, as you can imagine, are really high too. Performance is critical for a great user experience. Energy is fundamental for that full-day battery life, and security, it's a cornerstone here.
Now, let's have a closer look at cryptography.
Cryptography is really a tool. It provides solution to a set of basic properties.
It starts with authentication as an example, knowing who sent you a message.
Encryption, knowing that that message only you can decrypt it. And Integrity, knowing that no one else could have modified the content of that message before it got to you. To solve broader security problem, we combine cryptographic functions into cryptographic protocol, and that's no easy task.
Actually, many of you, most of you have probably heard the saying, "don't roll your own crypto", right. There are good reasons for that. A lot of things can go wrong.
It starts with maybe not picking the right primitives and therefore not getting the security you want to achieve. It may also be adding the right primitives but not using them the right way, and the lowest level, the implementation may give away the secrets or the keys. That could be the case of timing attacks or padding oracle attacks.
This is why cryptography is incredibly difficult. There are endless examples of failures of cryptographic protocols in the news, and the worst thing is, security vulnerabilities may take a while to be discovered. It may take years before someone reports finding an issue.
This is why the risks are really high, and so it's an effort to get it right.
Designing a robust protocol takes a lot of time, skills, and expertise you need to be a cryptographer.
Even when you got the protocol, the work is not done yet. There is a continuous amount of work to monitor new attacks, see what is applicable to your own code, and then respond to it, but sometimes it's not even that easy. It can be dependencies. Those libraries that you picked to integrate in your app. And in some situation, you also need to revise and carry the protocol and manage all kind of interpretability scenarios. All of this is a lot of work, and it's taking time and energy away of the big ideas you may have or the next cool features.
This is why you can truly benefit from native features.
Our philosophy has been to provide full solutions for a set of common problems so that we do the hard work and you don't have to.
For you, it raises the risk and the effort, and we can even achieve better security through the assistance of delegated hardware features we have on our platforms.
Now, we actually look through a set of very common scenarios many of you may be familiar with. We're going to start with how to protect data on a device. Then we can look at protecting credentials and keys. After that, sharing data across devices and users.
Then, securing network connections, and finally, verifying remote parties with certificates.
Now, I'm going to look into the first example. Like I'm sure many of you have found that scenario and find yourself trying to protect data on device. Well, the best advice is not to just copy and paste what you find on some of those websites. You're better off using data protection.
Data protection combines the strengths of the passcode with the hardware technologies in the Secure Enclave to make sure the keys are well secured, and the encrypted data cannot be used to brute-force the passcode. But there's even more. Data protection provides post compromise recovery.
Post compromise recovery, also known as forward secrecy, guarantees that if adversary gets his hand on the keys on the device and the users change the passcode, well, all the data encrypted after that cannot be recovered from those old keys.
This is important, critical, and if you want to learn more, there's even more data protection does, and you can look at the iOS security white paper to get more information. The one thing you need to know is that there are different protection attributes you can set when your app fails. It starts from until first authentication. Even the data is protected until the first time the user unlocks the phone after boot.
Today is a lot better you can do. There is the complete file protection. Complete file protection means the data is only available after the user unlocks his phone. As soon as the user locks his phone back, put it back in the pocket or in a bag, the data is secure again. And it's really easy to use. Here's an example. The only thing you need to use is this option, completeFileProtection. It can't be easier than that, right. If you're not using this option yet, please go ahead and look in your application where you could use it to get that best level of security.
Now, in addition to files, there may be more sensitive information that you may want to protect.
There could be credentials like authentication tokens used to authenticate on services or cryptography keys. But for those, please don't write them in defaults that you could see on all the platforms. Instead, there is Keychain.
Keychain in the Secitem APIs allows you to write those items as in the local Keychain, which means they stay on that one device or on iCloud Keychain where they synchronize across all of the devices for a seamless experience in the user account. This requires user accounts with two-factor authentication enabled. For Keychain, you find very similar protection as for the files that I mentioned before and a few extra ones. Here, again, I will recommend you to look at the documentation to learn more.
In addition to when a key is cryptographically available, sometimes important for you to know how the user authenticated it and when.
This is why we have LocalAuthentication.
LocalAuthentication allows you to put restrictions as to when an operation can be executed. For example, you could define that it is required for the user to authenticate with biometrics with Face ID. It can be very handy, and in this world, on macOS we have some exciting news. On macOS, you're used to device authentication properties, and it gives two way for the user to authenticate, the password or biometrics for Macs which support biometrics with Touch ID. This year we're really excited to announce that now without any change in your application, the user can authenticate with Apple Watch and a double-click on Apple Watch. It's a great new experience, which is going to reduce the use of the password and reduce the friction, improves user experience.
Now, with these new capabilities we added two new policies. The first one, to reduce the authentication to biometrics only and Watch. And the second one to the Watch alone.
We are looking forward to see how you're going to use those in your application on macOS.
Now, I mentioned the Keychain, there is an easy way to synchronize items across all of the devices of an account. What if you have data? Well, for data we have a great solution too.
CloudKit. CloudKit allows you to encrypt assets in the Private CloudKit Database, and the data will be available across all of the devices without the user having to sign in into the application. It is great, and even more, you can also use it to share to all the users.
The way it works is Apple as a trusted party help manage the iCloud identities and the control to the data. And I have an example here again, to show it's easy to set up.
From the file, we create an asset.
With this asset, we created a record. And then we upload this record into the Private CloudKit Database. And this is it, and if you want to add sharing to other users, it is possible for a set of defined APIs. If you haven't looked into CloudKit before, it's great time to go have a look at the documentation and see how powerful it can be for you. Now, with this, we talked about protecting data inside of devices, share it across devices, even devices from another user, what when you want it done on data from your own server for example.
Where for that you want to establish a secure connection.
A secure connection is really, really important because it helps guaranteeing the confidentiality of all of the information that the user is accessing through your application as well as guarantee the trustworthiness of that information, make sure it does not be tampered with.
And the first thing you should not do here again, you're just trying to use your custom own protocol. There is a great solution. It's a standard solution.
Transport Layer Security. And it's really easy to use on our platforms. We have two great APIs that make that really easy. The first one is network framework, which you may have discovered last year with a great talk at WWDC. The second one is URL Sessions, which you may know about as part of the App Transport Security.
For URL Session, the only thing you need to do is use an https endpoint. In either of those cases, it relies on our implementation of TLS with great defaults underneath. Defaults ensures cypher suites are chosen to provide strong security including perfect forward secrecy as well as the algorithm that would be the most efficient on Apple products. Or implementation of TLS also use TLS 1.3 by default. If you're not familiar with TLS 1.3, it has a lot of improvements from the previous revisions, a lot better security and even better efficiency by reducing some of those round trips.
If you are not using TLS 1.3, please go ahead and try to deploy that on your servers and without any change of the Code of your application, you will have the benefit of it in your app.
And now, to show you it's really easy, I have two examples to show you with Network Framework and URL Session, but before jumping into those, I want to highlight that Secure Transport is not the recommended way anymore, and we ask you to migrate to Network Framework and URL Sessions if you're already using Secure Transport. With that, here's a couple of examples. With Network Framework, when you establish a connection, the only thing you need to do is using :.tls, and here we go, you add that TLS connection, providing great security and performance.
For URL Session, the only thing here, it's https endpoint, and this is it. In some situation, you may not have the possibility to use TLS. For example, if you have trying to secure data through a content delivery network, for example. In this case, you manage those certificates, and for certificates, one of the things to avoid is using it on parser or trying to take a parser you find somewhere on a website. Parsers are incredibly vulnerable by providing a great attack exposure.
On a platform, we have SecTrust for that. SecTrust provides you an easy way to validate the certificate against a trust policy. The trust policy may contain information about expiration, revocation, and until certificate has been validated, you know that you can use a key. Note that if using TLS you don't have a need to worry about certificate. It's all handled for you underneath. Now, in this release, we're happy to introduce a new function in SecTrust. This new function combines two things you were able to do before. On one hand, it's a function, which allows to asynchronously through an explicit action, do a certificate validation so that your application can do something else while the certificate is being validated. But also this function gives you a richer handling. So, as a developer, you know what is going on in case of failure, and you can debug it. But also, you can build logic into your application to respond to different error scenarios. So this new function is going to make things even more convenient and easier for you to debug. We're looking forward to you adopting it. So, with this, I just walked you through a set of five different scenarios for which we have really easy to use solutions, which provide great security by defaults or you're only a few attributes away from great security. And here again, we benefit from deep integration with the hardware to provide security features which otherwise are very challenging to provide. So, in any of the scenarios, when you encounter them, go ahead and use the system frameworks, and if you already do, go ahead and make sure you're using those best attributes so that you can protect users and business assets the best possible way. And we understand that in some situations it may not be practical for you to use the system features. For example, you may need to have interoperability outside of the Apple ecosystem. You may to authenticate with your service or simply you're given a specification you will need to implement. For all of these cases, we now have a great solution for you.
Apple CryptoKit. Apple CryptoKit is new Swift cryptographic API which has been designed to follow the same philosophy as for those features we talked about and give you an easy access to cryptographic functions and hard to misuse functions. We're really excited to give that in this release.
And now, I'm going to hand over to Frederic who is going to tell you all about this exciting new API. So please join me in welcoming Frederic on stage. Thanks Yannick. Hello everyone. My name is Frederic Jacobs, and I'm working on the Cryptographic Engineering Team here at Apple. I'm excited to tell you more today about Apple CryptoKit. We think you're really going to like implementing cryptographic protocols in Swift. There are four aspects of Apple CryptoKit I would like to share with you this afternoon. First, we are going to talk about CryptoKit and Swift.
Second, we will talk about the curated set of algorithms that is available in CryptoKit.
Third, we are going to discuss about the Secure Enclave and biometric authentication, and finally, we'll talk about performance. Let's dive right in.
In the past, you might have had to write code that looks like this when you were calling into C crypto API from Swift. You had to allocate buffers, figure out for each of the buffers what size it had to be, and there were values such as the nonce that you might have to search what the safe value for that value was. And then you have all of these nested calls into the encryption function where you're passing pointers, and there are no argument labels that help you figure out which pointer needs to go into which argument. In contrast, with Apple CryptoKit, you can do exactly the same operation with a single line of code.
In this-- isn't this cool? So, in order to do so, you're passing the data you want to encrypt to cipher. In this case we're using AES.GCM, and you're using the key you want to encrypt the data with and that's it.
Something else you might have had to do is generating your own cryptographic key when you were calling into a C crypto API from Swift. And that involved first converting a number of bits and bytes because cryptographic keys are commonly expressed in bits.
Then you have to allocate a buffer for that amount of bytes and use the system, random number generator to fill that buffer with cryptographically secure random numbers.
Then, if an error occurred you had to handle that.
You're then finally ready to use that key, and when you're done with that key, you don't have to forget to zeroize it to remove it from memory. In contrast the CryptoKit, generating a key is just a single line of code. You call the SymmetricKey initializer, and you pass the number of bits you want the key to hold.
We're able to zeroize this key on release for you because knowing about the auto reference counting of this value whenever it is getting deallocated, we're zeroizing that buffer for you. So, CryptoKit and Swift. We have a strongly typed API, and whenever you're initializing a value, we are verifying that the data you're passing is a strong value for that argument.
Thanks to Swift's memory management model, we're able to zeroize on release all of the secret values allocated within CryptoKit.
We perform equitable conformances for values like message authentication code if you're familiar with that content so that you're able to verify two-message authentication codes in constant time by just using the equals equals operator.
CryptoKit also defines protocols so that you can write generic code over a hash function H, and if in multiple places of your code base you have to pass different hash functions, you can write the code once and then parametrize the function call on a different hash function.
So, we talked about CryptoKit and Swift, and now I want to dive into the curated set of algorithms that is supported in CryptoKit.
All algorithms are standard and peer-reviewed algorithms. Let's see what's available in CryptoKit.
We have support for hash functions, message authentication codes, authenticated encryption, key agreement, and signatures.
Some of these algorithms are algorithms we have supported before in frameworks such as Common Crypto or SecKey, but some of these algorithms are algorithms we're supporting for the first time. Those are highlighted in green on the slide. Note that we also have an insecure module.
This module provides algorithms such as MD5 and SHA1 so that you can adopt CryptoKit even if you need to be using some algorithms that no longer meet the best security standards.
Let's focus on a few examples to see how CryptoKit works. Let's start with hash functions. Hash functions produced deterministic fixed-size output that is called a digest, and unlike hashable in Swift, hash functions in CryptoKit provide cryptographic properties such as collision resistance. This means that it is hard to find two inputs that will hash to the same digest. In order to compute the hash, it is as easy as calling the hash method on the hash function you want to use, passing the data you want to hash.
Here, I have some audio data I want to hash, and I use the SHA256 hash function to compute a digest.
If you're streaming a file, you might be reading it from an input stream.
In that case, you want to compute the digest incrementally.
You start by initializing a hasher by calling the MT initializer on the hash function. You then pass the data you want to hash, and you call the update method one or multiple times in order to do so. And when you want the digest to be computed, you just call the finalize method on the hash for instance, and that will return the digest. Next, let's talk about authenticated encryption. This is something you've long been asking for. As its name suggests, it provides both authentication and encryption.
The lack of authentication in cryptographic protocols can lead to a variety of attacks, and manually combining authentication with encryption can lead to things like padding oracle attacks. So, instead we're really happy to provide it to you as a single API call.
I'm working on the site project. It's a hiking app. Most of the content on the app is free, but there's additional content you can purchase through the in-app purchase. Once you purchase the content, you can retrieve the additional content.
I'm using a content delivery network in order to keep the downloads fast, as my user base grows.
In order to obtain the content, I first check with my server if the in-app purchase is valid. Then, the key is retrieved from the server and passed on the phone.
I download the encrypted data from the content delivery network, and I use the key in order to decrypt the encrypted content. And if the key is right, and the data wasn't tampered with on the content delivery network, I'm able to retrieve the map data. Let's see how this translates to code. First, we start by initializing a SymmetricKey from the key data that was retrieved from the server. Then we are initializing a sealed box.
In this case, it doesn't really matter what's in the sealed box, but know that if you're working on implementing a specification that requires you to combine the nonce, the cipher text and the tag in a specific way, you can use the sealed box to do that. If you're implementing a protocol that requires you to pass specific nonce values we're also able to support that. But, in this example, we'll focus on the simple case.
So, we're passing the downloaded data as a combined representation to the sealed box, and now that we have a box, we can simply open it by calling the open method on the cipher. And we're passing the key. It's that easy.
Next, let's talk about signatures.
Signatures are used to authenticate data using a private key, and using the signature, we are able to verify data using the associated public key. Let's take an example where we want to use the signature in order to authorize and operation. The operation here can be a two-factor log-in or some sensitive transaction such as wiring money to another user of the application.
In order to do so, we start by generating a private key on the device.
We then retrieve the associated public key. The associated public key is registered with your service, and once we want to perform that operation, we're using the private key to generate the signature on the transaction data and then sending over the transaction data and the signature to the server.
The server will verify that the signature is correct, and if the signature is right, it will proceed to doing the operation. Let's see what this code looks like. In order to generate a cryptographically strong private key, we call the PrivateKey initializer.
Then we want to register some public key data on the server. We start by retrieving the public key by just calling public key on the private key, and then we want to say what representation we want to get from the public key. Multiple representations of public keys exist, and we have support in CryptoKit for a lot of them.
In this case, we're taking the compact representation. As always, we're storing the private key in the Keychain.
Sample code in order to do that will be available on the developer portal. In order to produce the signature, we are calling the signature method on the private key, and we're passing the transaction data we wish to sign, and this returns the signature.
Now, this key that protects these operations is of high value, so we want to give it the best security it can. That's where the Secure Enclave comes in.
The Secure Enclave is s hardware-based key manager that's isolated from the main processor to provide an extra layer of security. It is used as part of critical system features, such as Touch ID or Face ID.
Let's see how to improve on or code that uses signatures to take advantage of the Secure Enclave. We start by checking if a Secure Enclave is available on the device. We do so by calling isAvailable on SecureEnclave. And from there, we can use exactly the same code as before in order to produce, to generate the key and produce the signature within the Secure Enclave. Note that in order to do so, we simply prefix or call to the PrivateKey initializer call that generates the key, and we prefix it with Secure Enclave dot.
It's that easy to take advantage of the Secure Enclave.
An advantage of using the Secure Enclave is that you can constrain key usage. In this case, we want to say that the key that we're generating on the Secure Enclave is only accessible when the device is unlocked and that this key will be available on this device only.
We can further constrain key usage by saying that when we're performing an operation with the private key, we want to require user presence.
Requiring user presence means that the user will either be prompted for biometric authentication or they will be requested for their device password. Now that we have composed an access control policy, we are simply passing it to the initializer of the key we're generating, and that policy will be enforced.
You might want to give some additional context to your users about why they're required to authenticate. In order to do so, you can pass a LocalAuthentication context. In this case, we say that we want the LocalAuthentication context to be valid for ten seconds, so the user will not be required to reauthenticate during that amount of time, and we want to tell the user that they're required to authenticate because they're authorizing a $10 transfer to Bob. In order to use that authentication context, you simply pass it to the key's initializer.
Finally, let's talk about performance.
CryptoKit is built on top of corecrypto. Corecrypto is Apple's native cryptographic library that features all of the system frameworks that Yannick has described before. With the help of the team that brought you the Accelerate framework and the CPU design team, corecrypto has hand-tuned assembly code, squeezing every cycle out of each of the support micro architectures.
In addition to that, because it uses corecrypto, it takes advantage of security mitigations in corecrypto such as side-channel resistance, and because corecrypto is FIPS validated, that means that you can use CryptoKit in the FIPS compliant way. Yannick opened with this slide showing large categories of bug classes that are available in cryptographic protocols and implementations.
With CryptoKit, we are starting to eliminate some of these bug classes, but CryptoKit remains a low-level crypto API that's powerful to let you implement a wide range of protocols, even broken ones. Not all cryptographic weaknesses can be addressed at the level of the cryptographic library. Therefore, we strongly recommend you to rely on higher level system frameworks when you can. This is only the beginning, and we want to continue helping you ship the safest apps as possible by delivering on APIs that help you prevent secure legal vulnerabilities in your applications. We can't wait for you to start adopting CryptoKit in your apps. On the page of this session, you'll find a link to the CryptoKit documentation, and it comes with Xcode Playground.
Download it, play with it for an hour, and see what's available. See how you could take advantage of CryptoKit in your apps.
We will be at the labs tomorrow to answer any of your questions, and we're looking forward to your feedback. Thank you. [ Applause ]
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.