#!/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