ios-application
ios-application copied to clipboard
Data protection - On iCloud data encryption / On device data encryption
This is about discussing the current state of the code. I would like to discuss about the current implementation of the code and, if possible, have a better understanding of what's going on. I may be wrong or have a wrong understanding of the code, this is why I'm opening this.
:cloud: On iCloud data encryption
Context:
The data that is sent to CloudKit is AES-256 encrypted using your encryption key. Your encryption key (that was defined during setup) is stored in Secure Enclave.
The Security Policy is stating that when using Apple iCloud the data that is sent is encrypted using our encryption key. From my logic, if the data sent is encrypted using our encryption key (exactly like our local data), it should not be decrypted when it's leaving the app.
Observation: If I'm looking at the code, it seems like the data that is sent to CloudKit is decrypted.
Line of code where the secret is re-encrypted retrieved from the cloud: https://github.com/raivo-otp/ios-application/blob/a4c7811f1cdcad80f3fac01d2c8fb75a117cb348/Raivo/Syncers/CloudKit/ModelConverters/CloudKitPasswordConverter.swift#L81
Line of code where the secret is decrypted before beign sent to the cloud: https://github.com/raivo-otp/ios-application/blob/a4c7811f1cdcad80f3fac01d2c8fb75a117cb348/Raivo/Syncers/CloudKit/ModelConverters/CloudKitPasswordConverter.swift#L48
Do I have a misunderstanding of how CloudKit works? Why our local data, which is already encrypted in it's current state is decrypted before being sent to CloudKit? I have deduced that the data is decrypted before being re-encrypted by CloudKit and not by the app. In this case, what's the Situation used?
:iphone: On device data encryption
Context: This is more about what Raivo-OTP should use for best security. For more information on these protections and what they offer, I encourage reading this report from security researchers at Johns Hopkins University that details limitations of encryption and the type of encryption available for applications to use. It explains how the decryption keys of the application is handled and when it's decrypted.
Observation: Without stating the entire report, it made me aware that the vast majority of apps available are using "Protected Until First User Authentication (a.k.a. After First Unlock) (AFU)" where the encryption keys are decrypted into memory when the user first enters the device passcode after a reboot, and remain in memory even if the device is locked. This practice used by Raivo OTP is sensible to attacks since everybody, I assume, reboot their phones only rarely and the phones are carried in a locked-but-authenticated state. Since Raivo OTP does not select a different protection class, it uses the default class (Protected Until First User Authentication).
Question: For this type of protection, which Data Protection class Raivo-OTP app is currently using?
Is it possible to use the best protection available (Class A: Complete Protection, data are evicted shortly after device lock - 10 seconds)? Since the app does not need to be running in the background (the sync with icloud could only happen when the app is actively open), this level of security should not affect user experience and would give the maximum protection for the user.
Thanks for all your good work! :thumbsup:
Hi @kevinstsauveur,
Thank you for sponsoring Raivo OTP! It feels good to know that you care so much about the app.
Lets first talk about the local database, as this always exists, even when you use Apple iCloud synchronisation.
Local database
The local data(base) is AES-256 encrypted with a key that is derived using PBKDF2 based on a combination of your encryption key and PIN code. Your encryption key (that was defined during setup) is stored in Secure Enclave. Your PIN code is not stored on the device. source
This means you need both your PIN code and your encryption key to decrypt the local database and to be able to view the OTP seeds/tokens. The encryption key is stored in Secure Enclave, but you must enter the PIN code yourself, as it's not stored on the device. Using both of these details a key is derived that is used as seed for the Realm database (https://realm.io/).
While the app is unlocked, this derived key is stored in the AppDelegate. After locking the app, closing the app or after a certain amount of inactivity (set by user), this variable will be cleared. This will also trigger that the app will automatically show the unlock screen again, as it cannot access the data in the database.
Apple iCloud
Actually this is just CloudKit (your app specific private database in iCloud). It has many great features. For example, apps can subscribe for changes in the CloudKit database, in order to show data in the app in real time.
Let's say you have an iPad and an iPhone with Raivo OTP installed and you are signed in using your own iCloud Account on both devices. If you add a one-time password on the iPhone, it will be pushed in real time to the iPad, as Raivo OTP on the iPad is subscribed to the CloudKit database and listens for changes. However, as the data is encrypted, both of the devices must have the same encryption key. Otherwise the iPad is not able to read the data.
If we would just push the encrypted data from the local database to CloudKit, it would mean you need both the same encryption key and PIN code on the other devices. This is inconvenient in multiple ways. First of all it would mean that you cannot have different PIN codes on different devices. Second of all it could introduce problems if we would like to replace the PIN code feature by something else in the future.
Therefore, I decided to encrypt the data in iCloud using only your encryption key.
If you choose "Apple iCloud" as synchronization method, the statements of the "Offline (none)" synchronization method apply to the local database, with in addition that the data in the local database is sent to CloudKit (a database in Apple iCloud). The data that is sent to CloudKit is AES-256 encrypted using your encryption key. Your encryption key (that was defined during setup) is stored in Secure Enclave. This allows you to have different PIN codes on different instances of the Service. source
Source code
I can imagine that it's hard to read the source code, as I only documented around 50% of if yet. The following example shows what happens when you tap the "save" button, after editing the seed of a one-time password.
-
The
onSavefunction in theMainEditPasswordViewControllerclass will be called. https://github.com/raivo-otp/ios-application/blob/master/Raivo/Controllers/Main/MainEditPasswordViewController.swift#L49 -
The
onSavefunction updates thesecretof a row in the Realm database https://github.com/raivo-otp/ios-application/blob/master/Raivo/Controllers/Main/MainEditPasswordViewController.swift#L64 -
The update arrives in the
onLocalChangefunction (which accepts aRealmCollectionChange) of theCloudKitPasswordSyncerclass. https://github.com/raivo-otp/ios-application/blob/a4c7811f1cdcad80f3fac01d2c8fb75a117cb348/Raivo/Syncers/CloudKit/ModelSyncers/CloudKitPasswordSyncer.swift#L140 -
After that, it arrives in the
onLocalChangefunction that accepts aPassword. https://github.com/raivo-otp/ios-application/blob/a4c7811f1cdcad80f3fac01d2c8fb75a117cb348/Raivo/Syncers/CloudKit/ModelSyncers/CloudKitPasswordSyncer.swift#L157 -
The
onLocalChangefunction converts the localPasswordto aCKRecord(CloudKit Record that can be pushed to CloudKit) using thegetRemotefunction of theCloudKitPasswordConverterclass. This is the piece of code that you are referring to. The piece of code encrypts the secret from the password (from the unlocked local database) using your encryption key, in preparation for it to be stored in CloudKit. https://github.com/raivo-otp/ios-application/blob/a4c7811f1cdcad80f3fac01d2c8fb75a117cb348/Raivo/Syncers/CloudKit/ModelConverters/CloudKitPasswordConverter.swift#L74 -
At last, the
CloudKitPasswordSyncerpushes theCKRecordto CloudKit. https://github.com/raivo-otp/ios-application/blob/a4c7811f1cdcad80f3fac01d2c8fb75a117cb348/Raivo/Syncers/CloudKit/ModelSyncers/CloudKitPasswordSyncer.swift#L185
So the getRemote function you are referring to simply converts a Password model to a CKRecord. The getLocalCopy does the reverse. It converts a CKRecord to a Password model. So basically it's the reverse of what you stated. Both of these methods do not send or recieve anything from CloudKit, that happens in other functions.
I was not aware of Apple's Data Protection Classes, and it's something I need the time for to look into. Currently I can only say that If Raivo OTP is locked (by default after 5 minutes), all data becomes inaccessible. This can be set to 20 seconds if preferred by the user.
I hope this gives you a better understanding of how Raivo OTP handles encryption. In the future I hope to write more easy to understand code, that is also documented using docblocks.
Thanks for this detailed explanation about the source code and how it's handling the data. That's really helpful!
I was not aware of Data Protection classes before reading an article about "How Law Enforcement Gets Around Your Smartphone's Encryption" (yes that's a pretty click bait title). This article is based on the report of the researchers about the critical lack in coverage due to under-utilization of the powerful and compelling security and privacy control tools.
Without stating the entire report, it made me aware that the vast majority of apps available are using "Protected Until First User Authentication (a.k.a. After First Unlock) (AFU)" where the encryption keys are decrypted into memory when the user first enters the device passcode after a reboot, and remain in memory even if the device is locked. This practice used by Raivo OTP is sensible to attacks since everybody, I assume, reboot their phones only rarely and the phones are carried in a locked-but-authenticated state. Since Raivo OTP does not select a different protection class, it uses the default class (Protected Until First User Authentication).
I'm looking forward to improve the app in the best way possible.
Without stating the entire report, it made me aware that the vast majority of apps available are using "Protected Until First User Authentication (a.k.a. After First Unlock) (AFU)" where the encryption keys are decrypted into memory when the user first enters the device passcode after a reboot, and remain in memory even if the device is locked. This practice used by Raivo OTP is sensible to attacks since everybody, I assume, reboot their phones only rarely and the phones are carried in a locked-but-authenticated state. Since Raivo OTP does not select a different protection class, it uses the default class (Protected Until First User Authentication).
Thanks for shining a light on this. To make sure I understand the implications for security correctly: does this only pose a risk in scenarios where someone running forensic software has physical access to my device, or does it mean that the method used by Raivo OTP is generally not secure (e.g. one slightly fishy app from the app store could compromise it)? The former I could live with, because coming from Android and being used to Aegis, Raivo checks all the boxes for me.
Hi @t-hoffmann,
iOS uses a sandboxing process that isolates each application. This makes each apps completely independent and inaccessible from each other.
To be clear, no matter which Data Protection Class Raivo-OTP uses, no app could theorically be able to access it's data.
I was talking about when the decryption keys to access Raivo-OTP's data are stored. Actually, I believe (because it's the default state) Raivo-OTP uses "Protected Until First User Authentication (a.k.a. After First Unlock) (AFU)". So once your iOS device is unlocked for the first time (after a reboot), the decryption keys are stored in memory and those keys are never evicted. As you pointed out, this is an issue in the scenario of someone with physical access and running forensic software 1.
Since Raivo-OTP does not need to store these keys permanently (in fact, it is only mandatory when the application is open), I strongly recommend changing which Data Protection Class the application uses to a more secure state.
To better understand those classes, I encourage reading this report from security researchers at Johns Hopkins University.
@tijme thanks a lot for a great app! can you please update on the status of this one? As it was previously mentioned, data protection class would significantly improve the app security (and yes, forensic software can do much more if there is public jailbreak available for current iOS version - not the case AFAIK).
Having this one sorted out is the only bit I'm looking forward to, in order to ditch Authy for your tool!
If you are out of capacity, I'd appreciate reporting that in oder for someone from this thread (or beyond) to pick it up!
the vast majority of apps available are using "Protected Until First User Authentication (a.k.a. After First Unlock) (AFU)
Does it include Google Authenticator ? Just to understand if I’m sacrificing some security for some convenience, as i’m hesitating to switch from it to Raivo. (I ask mostly out of curiosity, because if someone have physical access to my phone, he also have physical access to me, and i’m pretty sure it will take more time hacking my phone than punching me in the face to get my PIN)
@tijme
Thank you for creating and continuing to enhance and develop Raivo.
Can you please comment with an update about this topic?
In simple words, can you explain if Raivo's TOTP codes could be compromised if a bad actor gained access to the device and ran forensic software to exploit the decryption keys stored in memory, as described above, regarding the Data Protection Classes? Please explain how the device passcode/Face ID, Raivo encryption key, and app pin apply in this scenario. thank you.