diff --git a/scripts/termux-keystore.in b/scripts/termux-keystore.in index d6d7ccc..2599c48 100644 --- a/scripts/termux-keystore.in +++ b/scripts/termux-keystore.in @@ -7,39 +7,88 @@ SCRIPTNAME=termux-keystore show_usage () { echo "Usage: $SCRIPTNAME command" echo "These commands are supported:" - echo " list [-d]" - echo " delete " - echo " generate [-a alg] [-s size] [-u validity]" + echo " list [-sd]" + echo " delete [-s pref]" + echo " generate [alias] [-bcilq] [-a alg] [-s size] [-p purposes] [-u validity]" echo " sign " echo " verify " + echo " encrypt [-a alg] [-q] [-p path] [-s pref] [-t timeout]" + echo " decrypt [-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]" } @@ -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 () { @@ -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