1
0
mirror of https://github.com/danog/tergent.git synced 2024-11-30 04:29:03 +01:00

Automatically unlock keystore using fingerprint, if possible

This commit is contained in:
Daniil Gentili 2020-09-30 18:21:05 +02:00
parent d4b25663d7
commit 87ab008deb
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7

View File

@ -7,12 +7,15 @@ use std::error::Error;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use serde_json;
use serde_json::{Value};
use base64; use base64;
/// Send a request to `termux-api` to list all the keys. /// Send a request to `termux-api` to list all the keys.
/// Returns a string that contains a JSON object. /// Returns a string that contains a JSON object.
pub fn list_keys() -> Result<String, Box<dyn Error>> { pub fn list_keys() -> Result<String, Box<dyn Error>> {
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. /// Send some data to `termux-api` to be signed.
@ -23,16 +26,48 @@ pub fn list_keys() -> Result<String, Box<dyn Error>> {
/// ///
/// [Signature algorithms]: /// [Signature algorithms]:
/// https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature /// https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature
fn sign_internal(alias: &str, algorithm: &str, data: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
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<dyn Error>> {
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::<serde_json::Value>(&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<Vec<u8>, Box<dyn Error>> { pub fn sign(alias: &str, algorithm: &str, data: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
let args = ["sign", "-e", "alias", alias, "-e", "algorithm", algorithm]; match sign_internal(&alias, &algorithm, &data) {
let output = communicate(&args, data)?; Ok(res) => Ok(res),
return Ok(base64::decode(output)?); 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. /// 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 /// Sets up proper sockets so that the `input` is provided to `termux-api` and
/// its output is returned from this function. /// its output is returned from this function.
fn communicate(args: &[&str], input: &[u8]) -> Result<String, Box<dyn Error>> { fn communicate(method: &str, args: &[&str], input: &[u8]) -> Result<String, Box<dyn Error>> {
let mut input_socket = socket::Socket::new()?; let mut input_socket = socket::Socket::new()?;
let mut output_socket = socket::Socket::new()?; let mut output_socket = socket::Socket::new()?;
@ -43,7 +78,7 @@ fn communicate(args: &[&str], input: &[u8]) -> Result<String, Box<dyn Error>> {
.args(&["-n", "com.termux.api/.TermuxApiReceiver"]) .args(&["-n", "com.termux.api/.TermuxApiReceiver"])
.args(&["--es", "socket_input", &output_socket.address()]) .args(&["--es", "socket_input", &output_socket.address()])
.args(&["--es", "socket_output", &input_socket.address()]) .args(&["--es", "socket_output", &input_socket.address()])
.args(&["--es", "api_method", "Keystore", "-e", "command"]) .args(&["--es", "api_method", method])
.args(args) .args(args)
.stdin(Stdio::null()) .stdin(Stdio::null())
.stdout(Stdio::null()) .stdout(Stdio::null())
@ -65,6 +100,8 @@ fn communicate(args: &[&str], input: &[u8]) -> Result<String, Box<dyn Error>> {
input_socket.close()?; input_socket.close()?;
// We need to reap our children otherwise they will stay as zombies. // 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) Ok(output)
} }