From 90ef6170d6288115de7adcb2112817b10d432bc0 Mon Sep 17 00:00:00 2001 From: ville rantanen Date: Wed, 9 Jan 2019 12:40:42 +0200 Subject: [PATCH] 2fa tool for viewing your 2FA codes --- bin/2fa-view | 1 + reporting/qolop | 4 +- shell/2fa-view | 284 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 287 insertions(+), 2 deletions(-) create mode 120000 bin/2fa-view create mode 100755 shell/2fa-view diff --git a/bin/2fa-view b/bin/2fa-view new file mode 120000 index 0000000..f263e72 --- /dev/null +++ b/bin/2fa-view @@ -0,0 +1 @@ +../shell/2fa-view \ No newline at end of file diff --git a/reporting/qolop b/reporting/qolop index 73d4ee3..8a3193f 100755 --- a/reporting/qolop +++ b/reporting/qolop @@ -32,7 +32,7 @@ Examples: Run qolop as script to print ANSI code table, or evaluate commands - qolop eval "_qCol G; printf hello; _qCol z" | qolop export + qolop eval "_qCol G; printf hello; _qCol z" | qolop escape The first command produces string with ANSI commands, the second will escape the escape characters, so you can copy/paste @@ -184,7 +184,7 @@ if [[ "$0" = "${BASH_SOURCE[0]}" ]]; then eval "$@" exit } - [[ "$1" = "export" ]] && { + [[ "$1" = "escape" ]] && { cat - | sed s/$'\e'/\\\\033/g echo '' exit diff --git a/shell/2fa-view b/shell/2fa-view new file mode 100755 index 0000000..fce844f --- /dev/null +++ b/shell/2fa-view @@ -0,0 +1,284 @@ +#!/bin/bash +_help() { + echo ' +2FA code viewer + +Usage: +- Create a (possible encrypted) folder for your service keys +- When the service presents you the QRCode, you typically can + view the actual code as text. It looks something like: + I5XXIY3IMEWCA3LBMRSSA6LPOUQGY33PNMQQU=== +- Store this piece of text in a file: "Service name".key +- Run 2fa-view command in the folder + +Viewer: +- Codes and service names are printed on the screen +- The upper timer shows the 30 second interval when + the code changes +- The lower timer shows time left before the program exits + +Options: + --light to disable animations + --color X to prefer a color for the skull. Choose color from: + k r g b c m y w K R G B C M Y W + ' + + + exit +} + +for (( i=1; i<=$#; i++ )); do + value=${!i} + j=$(( i + 1 )) + [[ "$value" = "--help" ]] && _help + [[ "$value" = "--light" ]] && { LIGHT=1; continue; } + [[ "$value" = "--color" ]] && { + TFA_COLOR_TITLE="${!j}" + ((i++)) + continue; + } + [[ "${value}" = "-"* ]] && { + [[ "$value" =~ -.*h ]] && { _help; } + continue + } +done + + +timeadjust=0 +update_frequency=30 +exit_timeout=120 +if [ "$1" = "light" ]; then LIGHT=1; fi +shopt -s nullglob +. qolop + +picture=" ..;;;::;:;::;;:;,. + .:::::;;;:;:::::::;;;;::;:;:. + .::::;;::;::;:;::::::::::;:;::;::. + ,::::;:::::::;;;::;:;;::::;::::::::. + ';:::::::::::::::;;;;;;;:::;;::;;;:.. + ';;::::',',',;:::;:;;;,'.;::::;;:;... + .'. . .,:,.. .. + ' @ .::;:;. @ .. + .:' .;: .::. .::. + ..,;;;:;:;;:' ,;,'';;,..,,' + . .;;. .. .,::, . + ';:;;;::;;:::..::: + ,:::...;:. . .,, + .. +" +picture_colors="kkKKwwbm$TFA_COLOR_TITLE$TFA_COLOR_TITLE$TFA_COLOR_TITLE$TFA_COLOR_TITLE$TFA_COLOR_TITLE$TFA_COLOR_TITLE" +picture_color_codes=() +for x in $( seq 0 $(( ${#picture_colors} - 1 )) ); do + picture_color_codes+=( $( _qCol ${picture_colors:$x:1} ) ) +done +picture_eye=$( _qCol R ) + +_skull() { + if [ "$LIGHT" = 1 ]; then return; fi + _qPos abs 4 1 + for x in $( seq 1 ${#picture} ); do + chr="${picture:$x:1}" + if [ "$chr" = " " ]; then _qPos right 1; continue; fi + color="${picture_color_codes[$(( $RANDOM % ${#picture_colors} ))]}" + if [ "$chr" = "@" ]; then color="$picture_eye"; chr="o"; fi + printf "%s%s" \ + "$color" \ + "$chr" + done + + _qCol z +} + +flame=( ) +for x in $( seq 1 $(( 44 * 8 )) ); do + flame+=( 0 ) +done +flame_mask_array=( \ + "11111000000000000000000000000000000000001111" \ + "11000000000000000000000000000000000000000001" \ + "00000000000000000000000000000000000000000000" \ + "00000000000000000000000000000000000000000000" \ + "10000000000001110110111111001110000000000001" \ + "11100000010111111111011110111101100000000111" \ + "11100000011111111111111111111111110000000111" \ + "11110001111111111111111111111111110100011111" \ +) +flame_mask=( ) +for y in {0..7}; do + for x in {0..43}; do + flame_mask+=( ${flame_mask_array[$y]:$x:1} ) + done +done +flame_chars=" .:##@" +flame_colors="zzzmryYY" +flame_color_codes=() +for x in $( seq 0 $(( ${#flame_colors} - 1 )) ); do + flame_color_codes+=( $( _qCol ${flame_colors:$x:1} ) ) +done +flame_next=$( _qPos right 1 ) +flame_corner=$( _qPos abs 1 1 ) + +_flames() { + if [ "$LIGHT" = 1 ]; then return; fi + width=43 + height=7 + seq_x=$( seq 0 $width ) + for x in $seq_x; do + pos=$(( ( $height ) * ( $width + 1 ) + $x )) + flame[$pos]=$(( $RANDOM % 256 )) + done + + for y in $( seq 0 $(( $height - 1 )) ); do + for x in $seq_x; do + if [ $x -eq $width ]; then continue; fi + if [ $x -eq 0 ]; then continue; fi + pos=$(( $y * ( $width + 1 ) + $x )) + pos_under=$(( ( $y + 1 ) * ( $width + 1 ) + $x )) + new_value=$(( ( ( \ + ${flame[$(( $pos_under ))]} + \ + ${flame[$(( $pos_under + 1 ))]} + \ + ${flame[$(( $pos_under - 1 ))]} \ + ) * 17 ) / 48 )) + + if [ $new_value -gt 255 ]; then + new_value=255 + fi + flame[$pos]=$new_value + done + done + flame_buffer="${flame_corner}${flame_next}${flame_next}${flame_next}" + for x in $( seq 0 $(( ( $width + 1 ) * ( $height + 1 ) - 1 )) ); do + if [ $(( $x % ( $width + 1 ) )) -eq $width ]; then flame_buffer+="\n${flame_next}${flame_next}${flame_next}"; fi + if [ ${flame_mask[$x]} -eq 1 ]; then flame_buffer+="${flame_next}"; continue; fi + print_value=$(( flame[$x] / 32 )) + flame_buffer+="${flame_color_codes[${print_value}]}${flame_chars:${print_value}:1}" + done + printf "$flame_buffer" + _qPos abs 1 1 +} + +_stars() { + if [ "$LIGHT" = 1 ]; then return; fi + wall_chars=" ,.'\`" + wall_colors="kkkKKKzzzr" + wall_color_codes=() + for x in $( seq 0 $(( ${#wall_colors} - 1 )) ); do + wall_color_codes+=( $( _qCol ${wall_colors:$x:1} ) ) + done + + lines=$(( 25 + $KEY_FILES )) + _qPos abs 1 1 + seq_x=$( seq 1 50 ) + for y in $( seq 1 $lines ); do + for x in $seq_x; do + chr="${wall_chars:$(( $RANDOM % ${#wall_chars} )):1}" + if [ "$chr" = " " ]; then _qPos right 1; continue; fi + color="${wall_color_codes[$(( $RANDOM % ${#wall_colors} ))]}" + printf "%s%s" \ + "$color" \ + "$chr" + done + printf "\n" + done + _qCol z +} + +_exit() { + clear + _skull + _qPos down 4 + exit +} +_get_code() { + oathtool -b --totp $( cat "$1" | tr -dc '[:print:]' ) +} +_print_codes() { + #~ printf 'L%.0s' $( seq -1 $update_frequency ) + if [ "$LIGHT" = 1 ]; then + _qPos abs 3 1; + else + _qPos abs 20 1 + fi + even=0 + for k in *key; do + if [ $even -eq 0 ]; then + _qCol z Y + else + _qCol z G + fi + name=${k%.key} + name=${name//_/ } + _qPos right 6 + printf "[ %s ]%s %s \n" \ + $( _get_code "$k" ) \ + $( _qCol s ) \ + "$name" + even=$(( 1 - $even )) + done + _qCol z +} + +_show_clock() { + for foo in {1..1}; do + i=$(( ( $( date +%s ) - $timeadjust ) % $update_frequency )) + l=$(( $update_frequency - $i )) + if [ "$LIGHT" = 1 ]; then + _qPos abs $(( $KEY_FILES + 5 )) 1 + else + _qPos abs $(( $KEY_FILES + 22 )) 1 + fi + _qCol CLREND z g;_qPos right 5; date + # seconds to code update + _qCol z g + printf "\r"; _qPos right 4; printf "["; + printf ' %.0s' $( seq 1 $update_frequency ) + printf ']\r'; _qPos right 5 + if [ $l -gt 8 ]; then _qCol z G; + elif [ $l -gt 3 ]; then _qCol z Y; + else _qCol z R; + fi + printf '=%.0s' $( seq 1 $l ) + _qCol z c + # seconds to this client shutdown + left=$(( $update_frequency * ( $exit_timeout - $SECONDS ) / $exit_timeout )) + printf "\n\r"; _qPos right 4; printf "["; + printf ' %.0s' $( seq 1 $update_frequency ) + printf ']\r'; _qPos right 5 + if [ $left -gt 8 ]; then _qCol z c; + elif [ $left -gt 3 ]; then _qCol z y; + else _qCol z r; + fi + printf '#%.0s' $( seq 1 $left ) + _qCol z; printf "\n" + _qCol CLREND + # die if key pressed, or client open for exit_timeout length + wait_for_second=$SECONDS + for i in {1..10}; do + read -t 0.1 foo + [[ "$?" -eq 0 ]] && _exit + [[ $SECONDS -gt $exit_timeout ]] && _exit + _flames + if [ $SECONDS -gt $wait_for_second ]; then break; fi + done + done +} +if [ -f "$1" ]; then _get_code "$1"; _get_code "$1" | xclip; exit; fi +for key in *key; do ((KEY_FILES++)); done +clear +_print_codes +_skull +_flames; _flames; _flames; _flames + +while :;do + set -e + new_codes=$( _print_codes ) + set +e + [[ ! "$new_codes" = "$codes" ]] && { + # print codes only if they change, this keeps selection in shell intact + _stars + codes=$new_codes + echo -e "$codes" + echo '' + } + _show_clock +done