From 87ab008deb2bd7701da6e91f3998e51e68ec2bcf Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 30 Sep 2020 18:21:05 +0200 Subject: [PATCH 1/2] Automatically unlock keystore using fingerprint, if possible --- src/bridge/mod.rs | 51 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/src/bridge/mod.rs b/src/bridge/mod.rs index 4b00164..9e6a8bc 100644 --- a/src/bridge/mod.rs +++ b/src/bridge/mod.rs @@ -7,12 +7,15 @@ use std::error::Error; use std::io::{Read, Write}; use std::process::{Command, Stdio}; +use serde_json; +use serde_json::{Value}; + use base64; /// Send a request to `termux-api` to list all the keys. /// Returns a string that contains a JSON object. pub fn list_keys() -> Result> { - Ok(communicate(&["list", "--ez", "detailed", "true"], &[0; 0])?) + Ok(communicate(&"Keystore", &["-e", "command", "list", "--ez", "detailed", "true"], &[0; 0])?) } /// Send some data to `termux-api` to be signed. @@ -23,16 +26,48 @@ pub fn list_keys() -> Result> { /// /// [Signature algorithms]: /// https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature +fn sign_internal(alias: &str, algorithm: &str, data: &[u8]) -> Result, Box> { + let args = [ + "-e", "command", "sign", + "-e", "alias", alias, + "-e", "algorithm", algorithm + ]; + let output = communicate(&"Keystore", &args, data)?; + let res = base64::decode(output)?; + if res.len() == 0 { + return Err("Could not sign!".into()) + } + Ok(res) +} + +pub fn unlock() -> Result<(), Box> { + let args = [ + "--es", "title", "Tergent", + "--es", "description", "Use your fingerprint to unlock the keystore", + ]; + let json = communicate(&"Fingerprint", &args, &[0; 0])?; + let decoded: Value = serde_json::from_str::(&json)?; + let res = decoded["auth_result"].as_str().ok_or("Invalid result")?; + if res == "AUTH_RESULT_FAILURE" { + return Err("Fingerprint authentication failed".into()) + } + Ok(()) +} + pub fn sign(alias: &str, algorithm: &str, data: &[u8]) -> Result, Box> { - let args = ["sign", "-e", "alias", alias, "-e", "algorithm", algorithm]; - let output = communicate(&args, data)?; - return Ok(base64::decode(output)?); + match sign_internal(&alias, &algorithm, &data) { + Ok(res) => Ok(res), + Err(_) => match unlock() { + Ok(_) => Ok(sign_internal(&alias, &algorithm, &data)?), + Err(e) => Err(e) + } + } } /// Performs a generic call to `termux-api`, providing `args` to its receiver. /// Sets up proper sockets so that the `input` is provided to `termux-api` and /// its output is returned from this function. -fn communicate(args: &[&str], input: &[u8]) -> Result> { +fn communicate(method: &str, args: &[&str], input: &[u8]) -> Result> { let mut input_socket = socket::Socket::new()?; let mut output_socket = socket::Socket::new()?; @@ -43,7 +78,7 @@ fn communicate(args: &[&str], input: &[u8]) -> Result> { .args(&["-n", "com.termux.api/.TermuxApiReceiver"]) .args(&["--es", "socket_input", &output_socket.address()]) .args(&["--es", "socket_output", &input_socket.address()]) - .args(&["--es", "api_method", "Keystore", "-e", "command"]) + .args(&["--es", "api_method", method]) .args(args) .stdin(Stdio::null()) .stdout(Stdio::null()) @@ -65,6 +100,8 @@ fn communicate(args: &[&str], input: &[u8]) -> Result> { input_socket.close()?; // We need to reap our children otherwise they will stay as zombies. - command.wait()?; + // Ignore result, since this may error if the process has already closed + command.wait(); + Ok(output) } From 71b9f37bb10a092681f75b33ccaeb186a0e5624f Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 30 Sep 2020 18:22:45 +0200 Subject: [PATCH 2/2] Update readme --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1e5280a..9f9853b 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,11 @@ You will need to configure cargo with the correct locations for "ar" and "linker [https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-21-rust-on-android.html](https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-21-rust-on-android.html) Then this project can be compiled with the command `cargo build --target=aarch64-linux-android` (or any other Android target). -Alternatively, you can download a precompiled deb package from the releases page. +Alternatively, you can download a precompiled deb package from the releases page, or install it from the termux package repo: + +``` +pkg i tergent +``` Upgrading from 0.1 ------------------ @@ -77,4 +81,4 @@ Auto-locking tergent does not provide password protected sessions yet. However, Android [provides a mechanism](https://developer.android.com/training/articles/keystore#UserAuthentication) to automatically lock the keys after a specified time has passed since the last device unlock. To take advantage of this feature, use the flag while generating the keys, e.g. `--ei validity 10` for a 10-second lock. In this case, the keys are usable only for 10 seconds after the phone is unlocked. To unlock the keys after this time has passed, simply re-lock and unlock your device again. -Alternatively, you can invoke a biometric prompt (fingerprint or face unlock) which might also reset this timer depending on your device. termux includes the `termux-fingerprint` command which can be used for this purpose. +Alternatively, you can invoke a biometric prompt (fingerprint or face unlock) which might also reset this timer depending on your device. Tergent will automatically try invoking the termux Fingerprint API to prompt biometric authentication.