Over half a year ago I did a security audit of GrapheneOS, and posted the outcome here and in another thread I can no longer find:
https://discuss.grapheneos.org/d/13113-my-audit-of-grapheneos-is-complete-found-two-more-issues/
At that time, I found two serious VPN leaks, one multicast leak and one DNS leak. After this the Graphene OS developers found a few more VPN leaks of similar nature, but most of those including the two I reported should be fixed now since a few months back.
Today I did a new security audit of Graphene OS, on the 2024110700 release, to confirm the VPN leaks really are fixed, and to look into the security of how files are protected in the newly introduced Private Space. An issue worth knowing about was found, namely that locking the Private Space does not unload the encryption keys for it at all, only a device reboot does.
Summary:
I can confirm that both the VPN multicast leak and DNS leak have been fixed, as expected. I have not been able to detect any VPN leaks over either Wifi nor 4G when "block all traffic outside VPN" is enabled. I tested this both in real profiles and in the new Private Space functionality, no VPN leaks. For Wifi this was tested on the official 2024110700 build by connecting to a Wifi hotspot running Wireshark, and for 4G this was tested on a userdebug build with tcpdump.
Files in Private Space always seem to be stored with their own unique encryption key and their own weaver token, regardless whether you choose to use the same credentials or separate credentials when setting up the Private Space. That is to say, Private Space do not share encryption key or weaver token with the owner profile even if you choose to share unlock credentials when setting up the Private Space. Login attempts are also throttled for the Private Space the same way like it was any other user profile, that is, throttling is per profile, or per weaver slot to be more precise. This was tested by using "adb shell" on a userdebug build to print the encrypted keys in "/data/misc/vold/user_keys/ce/USERID/current", and the number of weaver slots enabled in "/metadata/password_slots/slot_map", and by manually trying out when throttling is hit for the various profiles. I did not test that the weaver slot for Private Space or any other profile is actually used for the key derivation, I just assume so.
The Private Space files are only available in unencrypted form in the file system after the password has been entered into the Private Space unlock screen. This is the same whether the Private Space was configured using same credentials as owner profile or separate. This means that even if the owner profile is fully unlocked, there should be no way for an attacker to access the files in the still locked Private Space unless they know the password. Not even if they can perform advanced forensics or disassemble the device. However, once the Private Space has been unlocked once, only a device reboot will make the files unavailable again. Locking the Private Space does nothing, see "Issues found" below. This was tested by using "adb shell" on a userdebug build to list the content of directories and files in "/data/media/USERID" and "/data/user/USERID".
Another thing I was curious about is how file sharing between owner profile and Private Space when using the Share function or File Picker is actually implemented. This was not obvious to me, as the files should be stored with entirely different owner IDs, and with permissions that forbids cross-profile access. After having poked around in the file system a bit, I don't think files are ever listable or readable to any apps, even those running in the same profile, as the files are owner by MediaProvider system app, and the only other permission is that "media_rw" can list, read, write and delete the files too. That is probably the broad file system permission you can grant apps, but shouldn't ever do. Each app has its own UID. Instead, according to internet, it seems when sending a file to another app, one has to obtain a handle to it and create an intent with that handle, to allow the receiving app to stream read the file from a content provider rather than the file system. Also the File Picker seem to allow stream reading the file from a content provider only, not from the file system itself. This means the content provider can enforce any permissions it wants, just like how it displays the File Picker UI on the apps behalf, and it should be safe. I am not certain of any of this, but it looks to be something like that. Basically a GVFS like system for Android, where files are read using app IPC instead, and virtual file systems can thus also be offered, like network or cloud storage providers and so.
Issues found:
- Once the Private Space has been unlocked once, all app data and file data will remain availabe in unencrypted form until next device reboot. Not even explicitly choosing to lock the profile will help, all that does is shutting down all apps, but the app data and file data will still remain available in unencrypted form. This may be unexpected, but is probably as designed, so that fingerprint unlock will work after having entered the password once. This issue appears for all credential modes (same or separate, PIN or password, fingerprint or no fingerprint). Rebooting the device is the only way to unload the file encryption key for the Private Space. Secondary user profiles do not have this problem. When they are shut down, their file encryption key is properly unloaded. This was tested by using "adb shell" on a userdebug build to list the content of directories and files in "/data/media/USERID" and "/data/user/USERID". It is not clear to me this is an actual bug, but I have reported the issue to the GrapheneOS developers so they are aware of it: https://github.com/GrapheneOS/os-issue-tracker/issues/4305
That is all for this time!