#!/usr/bin/env sh

# termux-ssh-askpass v0.3
# usage:
#    termux-ssh-askpass [ prompt text (optional) ] [ /path/to/private.key ]
# simple drop-in replacement for ssh-askpass that acts as a wrapper script over
# android's hardware-backed keystore functionality and the fingerprint lock to
# unlock passphrase-protected OpenSSH keys, often used by ssh-agent and friends

# specifically, a hardware-backed inaccessible private key is stored in the
# security chip on the device which is only available during a validity period
# after successful fingerprint unlocks and this is used to sign the matching
# pubkey file for the portable OpenSSH private key being unlocked by ssh-agent
# to produce a passphrase from the signed nonce value produced by the security
# chip

# USAGE NOTES
# 1. termux-api package and the companion Termux:API app are required
# 2. this script assumes you have already created an android keystore
#    RSA key with alias 'default'
#    eg. termux-keystore generate 'default' -a RSA -s 4096 -u 10
# 3. your SSH key should be have a passphrase generated from this script before
#    using with ssh-agent
#    eg. ssh-keygen -p -f ~/.ssh/id_rsa \
#            -N "$(termux-ssh-askpass ~/.ssh/id_rsa)" -F 'old passphrase'

NAME="${0##*/}"
abort() { echo "[$NAME] $@" 1>&2; exit 1; }

! is-termux && exit 1

# OpenSSH only provides path to private key via first arg only:
# eg. $SSH_ASKPASS "Enter passphrase for '/path/to/.ssh/id_rsa' (blah blah): "
# paths provided by OpenSSH with spaces may or may not have quotes
# therefore, DO NOT use spaces in private key paths
[ ! -z "$1" ] || abort "Must be run by OpenSSH with arguments."
f="${1%: }" # strip garbage
f="${f% (*)}" # strip garbage emitted by ssh-add -c
f="${f##* }" # strip preceding prompt
f="${f%\'}"; f="${f#\'}" # strip quotes (as seen with git)
f="${f%\"}"; PRIVKEY="${f#\"}" # strip quotes (just in case)

# pubkey also required for nonce signing so don't allow running standalone
PUBKEY="$PRIVKEY.pub"
[ -f "$PUBKEY" ] || abort "Expected pubkey located in '$PUBKEY'."

# hardware-backed signing keys with a validity duration set are only made
# available by the security chip after successful fingerprint unlock attempt
SIGNING_KEY='default'
termux-fingerprint \
	-t "$NAME" -s 'Authenticate to unlock hardware keystore' \
	-d "Generating signed passphrase for ${PRIVKEY}" -c 'Abort' \
	| jq -r '.auth_result' | fgrep -q 'AUTH_RESULT_SUCCESS' || exit 1
termux-keystore sign "$SIGNING_KEY" SHA256withRSA < "$PUBKEY" | tr -cd 'A-Za-z0-9'