Closed lukaszfryc closed 5 years ago
We use react-native-keychain
library for saving passwords.
Next steps:
Some gotchas
When that is implemented, please ping me and @corpetty for additional security review.
@rasom Interested in taking this one?
We have iOS and Android parity except for this feature, and about half our users are on Android and they would love this.
I'm just curious, because i'm actually very interested in this feature.
You said you are using react-native-keychain
, and i see this library is actually supports Keychain.BIOMETRY_TYPE::FINGERPRINT
. Is it tough task to implement fingerprint usage on Android?
@cheatfate for biometrics there are https://github.com/status-im/status-react/issues/5556 and https://github.com/status-im/status-react/issues/5557.
It is not so hard to just use some feature of a lib, but it is much trickier to figure out if this library is actually as safe as it claims. The last thing you want in crypto is to have an adversary to be able to decrypt your private key. We need to make sure that it is safe first, on different devices with different OS versions and flavours from different vendors.
As a first step, I tried to use a background service to keep password in RAM only. That wouldn't have solved the whole issue, but it would have a much less of the attack surface than storing it somewhere on the device. (Afaik, there is an idea to do a similar thing with Desktop and Keycard).
Essentially, the idea was to create a service that will run even when the app has been killed by the OS, that would hold the password in a variable. When the app is re-started, it can re-connect to the service and request the password from there. It is pretty safe, because the operating system protects access to the service.
Unfortunately, I hit a roadblock. Any service that is bound to the app is stopped on force-quitting or due to the lack of RAM. There are certain flags like START_STICKY
, but they are only helping to restart the service after it was killed. Hence, the RAM contents would be wiped out anyways.
There are ways on keeping services "running forever" (like this one), but what they all mean is restarting the service when it is killed in a more or less intrusive ways.
Background services aren't suitable to keep the password in RAM.
Research key storage methods available on Android and jailbreak detection.
https://developer.android.com/training/articles/keystore
It looks like Android Keystore System is something we can use to store the password. It provides a tamper-proof (see Extraction Prevention).
We need to check for [KeyInfo.isInsideSecurityHardware()
](https://developer.android.com/reference/android/security/keystore/KeyInfo.html#isInsideSecureHardware()) for the keys and not allow using it otherwise.
We need to check for rooted Android and not allow to use this tech if it is rooted.
We shouldn't store plaintext password anywhere, but we can create a key inside the KeyStore that is hardware-backed and use it to encrypt the password, which then we can store to any local storage.
If possible, we should use StrongBox Security Module — a Secure Enclave's analogue with hardware separation.
The difference between isInsideSecurityHardware()
and isStrongBoxBacked()
are described in this article.
TL;DR is that
isInsideSecurityHardware()
is on the same chipset's Trusted Execution Environment (TEE), so it is protected agains OS kernel compromise;isStrongBoxBacked()
"...is a discrete separate hardware and allows guarantees such as Insider Attack Resistance."We can have this option to store user's password using this key. Security audit and threat analysis of the implementation is needed though.
StatusService.java
and avoid exposing password to JS at all). Might not be possible due to the DB encryption relying on password.Android Developers
As these are HSMs, I would further push to have the private keys stored this way as well if the user is not using the keycard.
Yep, 100%, I will add it to our backlog.
@corpetty there are some flowcharts of the proposal.
With the proposal like the least amount of changes are necessary to our codebase (because it works very similarly to what we do on iOS). Almost all of the changes will be in our fork of react-native-keychain
(we will have to fork it).
The major downside is that the plaintext password is exposed to JS. Though, it is exposed to JS right now anyway, when user types the password in. The attack on generic react-native-keychain
is more probable though, than on our own codebase.
Note: steps 9 and 10. The encryption key is never exposed to the app and never leaves the TEE/SE. Encryption happens inside TEE/SE.
Note: steps 3, 4 and 5. The encryption key is never exposed to the app and never leaves the TEE/SE. Decryption happens inside TEE/SE.
Today I started working to add an additional parameter to react-native-keychain
that allows a user to set a minimum encryption key guarantee (ANY/SECURE_SOFTWARE/TEE) to encryption operations. That way we can be sure that the app password is always encrypted with a key that is at least inside the security hardware (TEE guarantees).
Hashing of the password needs to be added.
https://github.com/status-im/react-native-keychain/pull/6 — minimum security guarantees https://github.com/status-im/react-native-keychain/releases/tag/v.3.0.0-status — our fork with this functionality https://github.com/status-im/status-react/pull/6616 — status-react PR
GitHub
:key: Keychain Access for React Native. Contribute to status-im/react-native-keychain development by creating an account on GitHub.
Android is done and released, further improvements will be done in: https://www.pivotaltracker.com/epic/show/4246970
Description
Type: Feature
Summary: Recently "Save password until logout" (https://github.com/status-im/status-react/pull/5617) was implemented for iOS. This issue is about doing the same for Android. More details can be found in https://github.com/status-im/status-react/pull/5617.
TODO
TEE
guarantee on Android