One-time passwords
T = (Current Unix time - T0) / X
. Each generated OTP is valid for X seconds, by default 30. TOTP is used by Google Authenticator and the Yubico OATH applet which we will use in our demo.YubiKey Neo
The Android lockscreen
INTERNAL_SYSTEM_WINDOW
signature permission, which is available only to system applications.For a long time the keyguard was an implementation detail of Android's window system and was not separated into a dedicated component. With the introduction of lockscreen widgets, dreams (i.e., screensavers) and support for multiple users, the keyguard gained quite a lot of functionality and was eventually extracted in a dedicated system application, Keyguard, in Android 4.4. The Keyguard app lives in the com.android.systemui process, along with the core Android UI implementation. Most importantly for our purposes, the Keyguard app includes a service with a remote interface,
IKeyguardService
. This service allows its clients to check the current state of the keyguard, set the current user, launch the camera and hide or disable the keyguard. As can be expected, operations that change the state of the keyguard are protected by a system signature permission, CONTROL_KEYGUARD
.Unlocking the keyguard
/data/system/gesture.key
as an unsalted SHA-1 value. The hash of the PIN/password is a combination of the SHA-1 and MD5 hash values of the user input, salted with a random value. It is stored in the /data/misc/password.key
file. The Face Unlock implementation is proprietary and no details are available about the format of the stored data. Normally not visible to the user are the Google account password unlock method (used when the device is locked after too many incorrect unlock attempts) and the unlock method that uses the PIN or PUK of the SIM card. The Google unlock method uses the proprietary Google Login Service to verify the entered password, and the PIN/PUK method simply sends commands to the SIM card via the RIL interface.As you can see, all unlock methods are based on a fixed PIN, password or pattern. Except in the case of a long and complex password, which is rather hard to input on a touchscreen keyboard, all unlock secrets usually have low entropy and can easily be guessed or bruteforced. Android partially protects against such attacks by permanently locking the device after too many unsuccessful attempts. Additionally security polices introduced by a device administrator application can enforce PIN/password complexity rules and even wipe the device after too many unsuccessful attempts.
One approach to improve the security of the keyguard is to use an OTP in order to unlock the device. While this is not directly supported by Android, it can be implemented on production devices by using a device administrator application that periodically changes the unlock PIN or password using the
DevicePolicyManager
API. One such application is TimePIN (which this post was in part inspired by) which sets the unlock password based on the current time. TimePIN allows you to set different modifiers that are applied when calculating the current PIN. Modifiers can be stacked, so the transformation can become complex, but still easy to remember. A secret component, called an offset can be mixed in for added security.Unlocking via NFC
An alternative way to use something you have to unlock the device is to use an NFC tag. This is not supported by stock Android, but is implemented in some devices, for example the Motorola X (marketed as Motorola Skip). While the Motorola Skip is a proprietary solution and no implementation details are available, apps that offer similar functionality such as NFC LockScreenOff Enabler compare the UID of the read tag to a list of stored values and unlock the device if the UID is in the list. While this is fairly secure as the UID of most NFC tags is read-only, cards that allow for UID modification are available, and a programmable NFC card emulator can emit any UID.
One problem with implementing NFC unlock is that by default Android does not scan for NFC devices when the screen is turned off or locked. This is intended as a security measure, because if the device reads NFC tags while the screen is off, vulnerabilities can be triggered without physical access to the device or the owner noticing, as has been demonstrated. NFC LockScreenOff Enabler and similar applications can get around this limitation when running on rooted devices by installing hooks into system methods, thus allowing the NFC system service configuration to be modified at runtime.
Unlocking using the YubiKey Neo
While unlocking the keyguard certainly doesn't need the full functionality of the Google Authenticator app, displaying the current OTP is useful for debugging and initializing with a QR code is quite convenient. That's why for our demo we will simply modify the Authenticator app slightly, instead of writing another OTP source. As we need to provide the OTP to the system NFC service, which runs in a different process, we add a remote AIDL service with a single method that returns the current OTP:
interface IRemoteOtpSource {
String getNextCode(String accountName);
}
The NFC service can then bind to the OTP service that implements this interface and retrieve the current OTP. Of course, providing the OTP to everyone is not a great idea, so we protect the service with a signature permission that can only be granted to system apps by signing our RemoteAuthenticator app with the platform certificate:
<manifest ...>
...
<permission
android:name="com.google.android.apps.remoteauthenticator.GET_OTP_CODE"
android:protectionlevel="signature"/>
...
<application ...>
...
<service android:enabled="true" android:exported="true"
android:name="com.google.android.apps.authenticator.OtpService"
android:permission="com.google.android.apps.remoteauthenticator.GET_OTP_CODE">
</service>
</application>
</manifest>
The full source code of the RemoteAuthenticator app is available on Github. Once installed, the app needs to be initialized with the same key and account name as the OATH applet on the YubiKey Neo. Our sample NFC unlock implementations looks for an account named 'lockscreen' when it detects the OATH applet. The interface of the modified app is identical to that of Google Authenticator:
Before we can use an NFC tag to unlock the keyguard, we need to make sure the system NFC service can detect NFC tags even when the keyguard is locked. As we mentioned earlier, that is not the case in stock Android, so we change the default polling mode from
SCREEN_STATE_ON_UNLOCKED
to SCREEN_STATE_ON_LOCKED
in NfcService.java
:package com.android.nfc;
...
public class NfcService implements DeviceHostListener {
...
/** minimum screen state that enables NFC polling (discovery) */
static final int POLLING_MODE = SCREEN_STATE_ON_LOCKED;
...
}
With this done, we can hook into the NFC service tag dispatch sequence, and, borrowing some code from the Yubico Authenticator app, check whether the scanned tag includes an OATH applet. If so, we read out the current OTP and compare it with the OTP returned by the RemoteAuthenticator app installed on the device. If the OTPs match, we dismiss the keyguard and let the dispatch continue. If the tag doesn't contain an OTP applet, or the OTPs don't match, we do not dispatch the tag. To unlock the keyguard we simply call the
keyguardDone()
method of the system KeyguardService
. The unlock process might look something like this:Full source code for the modified NFC service is available on Github (in the 'otp-unlock' branch). Note that while this demo implementation handles basic error cases like OATH applet not found or connection with tag lost, it is not particularly robust. It only tries to connect to remote services once, and if either of them is unavailable, NFC unlock is disabled altogether. It doesn't provide any visual indication that NFC unlock is happening either, the keyguard simply disappears as seen in the video above. Another missing piece is multi-user support: in order to support multiple users, the code needs to look for the current users's account on the NFC device, and not for a hardcoded name. Finally, the NFC unlock as currently implemented is not a full unlock method: it cannot be selected in the Screen security settings, but simply supplements the currently selected unlock method.
Summary
CONTROL_KEYGUARD
permission. This makes it easy to implement alternative unlock mechanisms, such as NFC unlock. However, NFC tag polling is disabled by default when the screen is locked, so adding an NFC unlock mechanism requires modifying the system NFC service. For added security, NFC unlock methods should rely not only on the UID of the scanned tag, but on some secret information that is securely stored inside the tag. This could be a private key for use in some sort of signature-based authentication scheme, or an OTP seed. An easy way to implement OTP-based NFC unlock is to use the Yubico OATH applet, pre-installed on the YubiKey Neo, along with a modified Google Authenticator app that offers a remote interface to read the current OTP.