From 669028690f0ba589bb900e24670afbf08142abd1 Mon Sep 17 00:00:00 2001 From: Federico Angelilli Date: Sun, 14 Jan 2024 17:10:39 +0100 Subject: Add otp action extension --- crypt.sh | 3 -- extension/otp.bash | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++ extension/prompt.bash | 0 3 files changed, 95 insertions(+), 3 deletions(-) create mode 100755 extension/otp.bash mode change 100644 => 100755 extension/prompt.bash diff --git a/crypt.sh b/crypt.sh index cd4fe47..61ddfcd 100755 --- a/crypt.sh +++ b/crypt.sh @@ -562,9 +562,6 @@ cmd_show() { error "$1 not found in the crypt" elif [[ -f "$CRYPT_PATH/$path.gpg" ]]; then _cmd_action_file "$path" show "$@" - elif [[ -f "$CRYPT_PATH/$path" ]]; then - #TODO: IS THIS CASE NEEDED? - _cmd_action_file "$path" show "$@" else error "Try to initialize the crypt." fi diff --git a/extension/otp.bash b/extension/otp.bash new file mode 100755 index 0000000..6eb8266 --- /dev/null +++ b/extension/otp.bash @@ -0,0 +1,95 @@ +_otp_parse() { + urldecode() { + local url_encoded="${1//+/ }" + printf '%b' "${url_encoded//%/\\x}" + } + + local uri="$1" + + uri="${uri//\`/%60}" + uri="${uri//\"/%22}" + + local pattern='^otpauth:\/\/(totp|hotp)(\/(([^:?]+)?(:([^:?]*))?)(:([0-9]+))?)?\?(.+)$' + [[ "$uri" =~ $pattern ]] || error "Cannot parse OTP key URI: $uri" + + otp_uri=${BASH_REMATCH[0]} + otp_type=${BASH_REMATCH[1]} + otp_label=${BASH_REMATCH[3]} + + otp_accountname=$(urldecode "${BASH_REMATCH[6]}") + [[ -z $otp_accountname ]] && otp_accountname=$(urldecode "${BASH_REMATCH[4]}") || otp_issuer=$(urldecode "${BASH_REMATCH[4]}") + [[ -z $otp_accountname ]] && error "Invalid key URI (missing accountname): $otp_uri" + + local p=${BASH_REMATCH[9]} + local params + local IFS=\&; read -r -a params < <(echo "$p") ; unset IFS + + pattern='^([^=]+)=(.+)$' + for param in "${params[@]}"; do + if [[ "$param" =~ $pattern ]]; then + case ${BASH_REMATCH[1]} in + secret) otp_secret=${BASH_REMATCH[2]} ;; + digits) otp_digits=${BASH_REMATCH[2]} ;; + algorithm) otp_algorithm=${BASH_REMATCH[2]} ;; + period) otp_period=${BASH_REMATCH[2]} ;; + counter) otp_counter=${BASH_REMATCH[2]} ;; + issuer) otp_issuer=$(urldecode "${BASH_REMATCH[2]}") ;; + *) ;; + esac + fi + done + + [[ -z "$otp_secret" ]] && error "Invalid key URI (missing secret): $otp_uri" + + pattern='^[0-9]+$' + [[ "$otp_type" == 'hotp' ]] && [[ ! "$otp_counter" =~ $pattern ]] && \ + error "Invalid key URI (missing counter): $otp_uri" +} + +BASE64="base64" +OATH=$(which oathtool) + +otp_code() { + [[ -z "$OATH" ]] && error "oathtool is required by otp_code action." + + while read -r line; do + if [[ "$line" == otpauth://* ]]; then + uri="$line" + _otp_parse "$line" + break + fi + done < "$FILE" + + local cmd + case "$otp_type" in + totp) cmd="$OATH -b --totp" + [[ -n "$otp_algorithm" ]] && cmd+=$(echo "=${otp_algorithm}" | tr "[:upper:]" "[:lower:]") + [[ -n "$otp_period" ]] && cmd+=" --time-step-size=$otp_period"s + [[ -n "$otp_digits" ]] && cmd+=" --digits=$otp_digits" + cmd+=" $otp_secret" + ;; + + hotp) + counter=$((otp_counter + 1)) + cmd="$OATH -b --hotp --counter=$counter" + [[ -n "$otp_digits" ]] && cmd+=" --digits=$otp_digits" + cmd+=" $otp_secret" + ;; + + *) error "OTP secret not found." ;; + esac + + eval "$cmd" || error "Failed to generate OTP code." + + if [[ "$otp_type" == "hotp" ]]; then + local line replaced uri=${otp_uri/&counter=$otp_counter/&counter=$counter} + while IFS= read -r line; do + [[ "$line" == otpauth://* ]] && line="$uri" + [[ -n "$replaced" ]] && replaced+=$'\n' + replaced+="$line" + done < "$FILE" + + echo "$replaced" > "$FILE" + MESSAGE="Increment HOTP counter for $NAME." + fi +} diff --git a/extension/prompt.bash b/extension/prompt.bash old mode 100644 new mode 100755 -- cgit v1.2.3