Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New input functionality for new KeyStoreAPI #161

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
290 changes: 239 additions & 51 deletions scripts/termux-keystore.in
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,88 @@ SCRIPTNAME=termux-keystore
show_usage () {
echo "Usage: $SCRIPTNAME command"
echo "These commands are supported:"
echo " list [-d]"
echo " delete <alias>"
echo " generate <alias> [-a alg] [-s size] [-u validity]"
echo " list [-sd]"
echo " delete <alias> [-s pref]"
echo " generate [alias] [-bcilq] [-a alg] [-s size] [-p purposes] [-u validity]"
echo " sign <alias> <algorithm>"
echo " verify <alias> <algorithm> <signature>"
echo " encrypt <alias> [-a alg] [-q] [-p path] [-s pref] [-t timeout]"
echo " decrypt <alias> [-a alg] [-q] [-p path] [-s pref] [-t timeout]"
echo
echo "list: List the keys stored inside the keystore."
echo " -d Detailed results (includes key parameters)."
echo "list: List either the keys stored inside the keystore or data in shared preferences."
echo " -s Shared preferences. Shows shared preferences. [Omit to show keys]"
echo " -d Detailed results. For keys, shows key parameters."
echo " For shared preferences, shows data of preferences."
echo
echo "delete: Permanently delete a given key from the keystore."
echo " alias Alias of the key to delete."
echo "delete: Delete either a given key from the keystore or a given shared preference."
echo " alias Alias of the key or shared preference-associated key to delete."
echo " -s pref Shared preference to delete. [Omit to delete key"
echo " and all shared preferences]"
echo
echo "generate: Create a new key inside the hardware keystore."
echo " alias Alias of the key."
echo " -a alg Algorithm to use (either 'RSA' or 'EC'). Defaults to RSA."
echo " -s size Key size to use. For RSA, the options are 2048, 3072"
echo " and 4096. For EC, the options are 256, 384 and 521."
echo " -u validity User validity duration in seconds. Omit to disable."
echo " When enabled, the key can only be used for the"
echo " duration specified after the device unlocks. After"
echo " the duration has passed, the user needs to re-lock"
echo " and unlock the device again to be able to use this key."
echo " alias Alias of the key. If not supplied then randomly generated."
echo " For generation, ensure /dev/urandom is accessible."
echo " -b Biometric flag, require e.g. fingerprint, iris, or face."
echo " -c Credentials flag, require e.g. PIN, pattern, or password. (≥Android11)"
echo " -i Invalidate flag, key invalidated by new biometric enrollments. (≥Android7)"
echo " (iff -u validity=-1)"
echo " -l Locked flag, allows key access while device is locked. (≥Android9)"
echo " -q Quiet flag, silences key specifications output."
echo " -a alg Algorithm. Asymmetric key pairs are 'RSA' or 'EC'."
echo " Symmetric key ciphers or cipher options specified as"
echo " 'ALG/MODE/PADDING'. ALG is 'RSA' or 'AES'."
echo " See: Android keystore#SupportedAlgorithms"
echo " [Asymmetric Default: 'RSA']"
echo " [Symmetric Default: 'AES/GCM/NoPadding']"
echo " -s size Key size. For RSA, the options are 2048, 3072, and 4096."
echo " For EC, the options are 256, 384, and 521."
echo " For AES, the options are 128, 192, and 256."
echo " [Defaults to smallest size]"
echo " -p purposes Key purposes. See: Android KeyProperties.PURPOSE flags"
echo " [Asymmetric Default is sign+verify: '4|8']"
echo " [Symmetric Default is encrypt+decrypt: '1|2']"
echo " -u validity User validity duration in seconds. Specifies duration the key can be"
echo " used for after authorization if -c or -b were supplied. Default"
echo " requires authorization for every key invocation. [Default: -1]"
echo
echo "sign: Sign using the given key, the data is read from stdin and the"
echo "signature is output to stdout."
echo " alias Alias of the key to use for signing."
echo " algorithm Algorithm to use, e.g. 'SHA256withRSA'. This should"
echo " match the algorithm of the key."
echo " match the algorithm of the key."
echo
echo "verify: Verify a signature. The data (original file) is read from stdin."
echo " alias Alias of the key to use for verify."
echo " algorithm Algorithm that was used to sign this data."
echo " signature Signature file to use in verification."
echo
echo "encrypt: Encrypt using the given key, the data is read from a file or stdin"
echo "(in that precedence) and the encrypted data is output to stdout and/or"
echo "shared preferences with a user-supplied name. Output is of the form"
echo "[IV.length][IV][Encrypted Data] (IV omitted if IV.length is 0)."
echo " alias Alias of the key to use for encrypting."
echo " -a alg Algorithm to use in the form 'ALG/MODE/PADDING'. This should match the"
echo " algorithm of the key. If unspecified, uses key's first instance."
echo " -q Quiet flag, silences encrypted data output."
echo " -p path Path of input file. Name of filepath containing data to be encrypted."
echo " -s pref Shared preference. Name of shared preference to store encrypted data."
echo " -t timeout Timeout for authentication in seconds. Specifies the valid timeframe for"
echo " authentication. Some devices continue showing the prompt or"
echo " automatically cancel. Default is system-dependent. [Default: -1]"
echo
echo "decrypt: Decrypt using the given key, the data is read from a file, shared preference, or"
echo "stdin (in that precedence) and the decrypted data can be output to stdout. Input is"
echo "expected in the form [IV.length][IV][Encrypted Data] (IV omitted if IV.length is 0)."
echo " alias Alias of the key to use for decrypting."
echo " -a alg Algorithm to use in the form 'ALG/MODE/PADDING'. This should match the"
echo " algorithm of the key. If unspecified, uses key's first instance."
echo " -q Quiet flag, silences decrypted data output."
echo " -p path Path of input file. Name of filepath containing data to be decrypted."
echo " -s pref Shared preference. Name of shared preference containing data to be"
echo " decrypted."
echo " -t timeout Timeout for authentication in seconds. Specifies the valid timeframe for"
echo " authentication. Some devices continue showing the prompt or"
echo " automatically cancel. Default is system-dependent. [Default: -1]"
}


Expand All @@ -50,17 +99,36 @@ check_args () {
fi
}

list_keys () {
if [ "$#" -gt 0 ] && [ "$1" = "-d" ]; then
$CMD_BASE -e command list --ez detailed true
else
$CMD_BASE -e command list
fi
list_Data () {
DETAILED=0; PREF=0;
while getopts ds NAME; do
case "$NAME" in
d) DETAILED=1 ;;
s) PREF=1 ;;
?) ;;
esac
done

$CMD_BASE -e command list --ei detailed "$DETAILED" --ei pref "$PREF"
}

delete_key () {
check_args delete 1 $#
$CMD_BASE -e command delete -e alias "$1"
delete_data () {
if [ $# -lt 1 ]; then
echo "$SCRIPTNAME delete: alias required"
exit 1
fi
ALIAS=$1; shift
PREF=-1;
while getopts s: NAME; do
case "$NAME" in
s) PREF=$OPTARG ;;
?) ;;
esac
done
shift $((OPTIND-1))
if [ "$#" -gt 0 ]; then echo "Unmatched argument \"$1\" ignoring: \"$*\""; fi

$CMD_BASE -e command delete -e alias "$ALIAS" -e pref "$PREF"
}

sign_data () {
Expand All @@ -74,53 +142,173 @@ verify_data () {
-e signature "$(realpath "$3")"
}

generate_key () {
encrypt_data () {
if [ $# -lt 1 ]; then
echo "$SCRIPTNAME generate: alias argument is required"
echo "$SCRIPTNAME encrypt: alias required"
exit 1
fi
ALIAS=$1; shift
ALGORITHM=RSA; SIZE=-1; CURVE=secp256r1; VALIDITY=0
while getopts a:s:c:u: NAME; do
ALIAS=$1; shift 1
ALGORITHM=-1; INFILE=-1; STORE=-1; TIMEOUT=-1; QUIET=0;
while getopts a:p:s:t:q NAME; do
case "$NAME" in
a) ALGORITHM=$OPTARG ;;
s) SIZE=$OPTARG ;;
u) VALIDITY=$OPTARG ;;
p) INFILE=$(realpath "$OPTARG") ;;
s) STORE=$OPTARG ;;
t) TIMEOUT=$OPTARG ;;
q) QUIET=1 ;;
?) ;;
esac
done
shift $((OPTIND-1))
if [ "$#" -gt 0 ]; then echo "Unmatched argument \"$1\" ignoring: \"$*\""; fi

if [ "$ALGORITHM" = "RSA" ]; then
case "$SIZE" in
-1) SIZE=2048 ;;
2048|3072|4096) ;;
*) echo "$SCRIPTNAME: invalid RSA key size $SIZE"; exit 1 ;;
esac
elif [ "$ALGORITHM" = "EC" ]; then
case "$SIZE" in
-1|256) CURVE=secp256r1 ;;
384) CURVE=secp384r1 ;;
521) CURVE=secp521r1 ;;
*) echo "$SCRIPTNAME: invalid EC key size $SIZE"; exit 1 ;;
$CMD_BASE -e command encrypt -e alias "$ALIAS" -e algorithm "$ALGORITHM" \
-e filepath "$INFILE" -e store "$STORE" --ei quiet "$QUIET" \
--ei authenticationTimeout "$TIMEOUT" | base64 -d
}

decrypt_data () {
if [ $# -lt 1 ]; then
echo "$SCRIPTNAME decrypt: alias required"
exit 1
fi
ALIAS=$1; shift 1
ALGORITHM=-1; INFILE=-1; STORE=-1; TIMEOUT=-1; QUIET=0;
while getopts a:p:s:t:q NAME; do
case "$NAME" in
a) ALGORITHM=$OPTARG ;;
p) INFILE=$(realpath "$OPTARG") ;;
s) STORE=$OPTARG ;;
t) TIMEOUT=$OPTARG ;;
q) QUIET=1 ;;
?) ;;
esac
done
shift $((OPTIND-1))
if [ "$#" -gt 0 ]; then echo "Unmatched argument \"$1\" ignoring: \"$*\""; fi

$CMD_BASE -e command decrypt -e alias "$ALIAS" -e algorithm "$ALGORITHM" \
-e filepath "$INFILE" -e store "$STORE" --ei quiet "$QUIET" \
--ei authenticationTimeout "$TIMEOUT" | base64 -d
}

generate_key () {
#If noargs or args starts with flags
if [ "$#" -eq 0 ] || [ -n "${1%%[!-]*}" ]; then
ALIAS="$(head -c 5 /dev/urandom | base32)"
echo "Generated Alias: \"${ALIAS?"unable to generate, please provide as argument"}\""
else
echo "$SCRIPTNAME: invalid algorithm $ALGORITHM"; exit 1
ALIAS=$1
shift
fi
# Defaults
_ALGORITHM="RSA"; MODE=-1; PADDING=-1; SIZE=-1; PURPOSES=-1
UNLOCKED=1; VALIDITY=-1; INVALIDATE=0; AUTH=0;
# Args handler
while getopts a:s:p:u:qlicb NAME; do
case "$NAME" in
q) QUIET=1 ;;
l) UNLOCKED=0 ;;
i) INVALIDATE=1 ;;
c) AUTH=$((AUTH|1)) ;;
b) AUTH=$((AUTH|2)) ;;
a) _ALGORITHM=$OPTARG ;;
s) SIZE=$OPTARG ;;
p) PURPOSES=$OPTARG ;;
u) VALIDITY=$OPTARG ;;
?) ;;
esac
done
shift $((OPTIND-1))
if [ "$#" -gt 0 ]; then echo "Unmatched argument \"$1\" ignoring: \"$*\""; fi

ALGORITHM=${_ALGORITHM%%/*}
# Begin splice by /
case "$ALGORITHM" in
"$_ALGORITHM") ;;
*) MODE=${_ALGORITHM#*/}
MODE=${MODE%/*}
if [ "$ALGORITHM" = "${_ALGORITHM%/*/*}" ]; then PADDING=${_ALGORITHM##*/}; fi ;;
esac
# End splice by /

case "$ALGORITHM" in
"RSA") case "$SIZE" in
-1) SIZE=2048 ;;
2048|3072|4096) ;;
*) echo "$SCRIPTNAME: invalid RSA key size $SIZE"; exit 1 ;;
esac
case "$MODE" in
-1) ;;
"ECB") case "$PADDING" in
-1) PADDING="OAEPPadding" ;;
"NoPadding"|"PKCS1Padding"|"OAEPPadding") ;;
*) echo "$SCRIPTNAME: invalid padding RSA/$MODE/$PADDING"; exit 1 ;;
esac ;;
*) echo "$SCRIPTNAME: invalid RSA mode $MODE"; exit 1 ;;
esac ;;
"EC") case "$SIZE" in
-1) SIZE=256 ;;
256|384|521) ;;
*) echo "$SCRIPTNAME: invalid EC key size $SIZE"; exit 1 ;;
esac
MODE=-1
PADDING=-1 ;;
"AES") case "$SIZE" in
-1) SIZE=128 ;;
128|192|256) ;;
*) echo "$SCRIPTNAME: invalid AES key size $SIZE"; exit 1 ;;
esac
case "$MODE" in
-1) MODE="GCM"
PADDING="NoPadding";;
"CTR"|"GCM") case "$PADDING" in
-1|"NoPadding") PADDING="NoPadding" ;;
*) echo "$SCRIPTNAME: invalid padding AES/$MODE/$PADDING"
exit 1 ;;
esac ;;
"CBC"|"ECB") case "$PADDING" in
-1|"PKCS7Padding") PADDING="PKCS7Padding" ;;
*) echo "$SCRIPTNAME: invalid padding AES/$MODE/$PADDING"
exit 1 ;;
esac ;;
*) echo "$SCRIPTNAME: invalid AES mode $MODE"; exit 1 ;;
esac ;;
*) echo "$SCRIPTNAME: invalid algorithm $ALGORITHM"; exit 1 ;;
esac
if [ "$PURPOSES" -eq -1 ]; then
case "$MODE" in
"-1") PURPOSES="4|8" ;; # Sign+Verify for Pairs
*) case "$ALGORITHM" in
"AES") PURPOSES="1|2" ;; # Encrypt+Decrypt for Ciphers
"RSA") PURPOSES="1|2|4|8" ;;
esac ;;
esac
fi

if [ -z "${QUIET:+1}" ]; then
_ALGORITHM="$ALGORITHM/$MODE/$PADDING"
echo "Algorithm: ${_ALGORITHM%%/-1*}"
echo "Size: $SIZE"
echo "Purposes: $PURPOSES"
fi

# purpose 12 is SIGN+VERIFY
$CMD_BASE -e command generate -e alias "$ALIAS" -e algorithm "$ALGORITHM" \
--ei purposes 12 --esa digests NONE,SHA-1,SHA-256,SHA-384,SHA-512 \
--ei size "$SIZE" -e curve "$CURVE" --ei validity "$VALIDITY"
--ei purposes "$(($PURPOSES))" --esa digests NONE,SHA-1,SHA-256,SHA-384,SHA-512 \
--ei size "$SIZE" -e mode "$MODE" -e padding "$PADDING" --ei unlocked "$UNLOCKED" \
--ei validity "$VALIDITY" --ei invalidate "$INVALIDATE" --ei auth "$AUTH"
}

ACTION="${1-}"
if [ "$#" -gt 0 ]; then shift; fi

case "$ACTION" in
list) list_keys "$@" ;;
list) list_Data "$@" ;;
generate) generate_key "$@" ;;
delete) delete_key "$@" ;;
delete) delete_data "$@" ;;
sign) sign_data "$@" ;;
verify) verify_data "$@" ;;
encrypt) encrypt_data "$@" ;;
decrypt) decrypt_data "$@" ;;
*) show_usage ;;
esac