TOTP codes within the terminal
The demise of Authy desktop has caused a variety of modifications, in all probability the certainly one of which I most recognize is a cellular app which lets me edit codes: with 2FAS I can edit labels on codes, and I’ve lastly been in a position to affiliate a code which I couldn’t establish (it had neither model brand nor description apart from an electronic mail deal with) to the platform it’s for. A cleanup was due and is welcome, and a migration to 2FAS was simple with export/import after I patched authy-migration to provide JSON.
Nevertheless, Authy killing off its desktop providing additionally means when on the desktop I’ve to make use of a cellular system and sort the 6 digits right into a Net kind. Not an enormous drawback, however copy/paste was snug. Whereas each Open Supply TOTP apps I studied extra intently, 2FAS and Raivo OTP, have a desktop app which interacts in a roundabout way with the cellular, they didn’t persuade me. I’m positive different individuals are proud of how they work.
I believe I’ve discovered an answer for myself to the desktop drawback.
I all the time have a terminal open, so copy paste from a program I launch there right into a Net browser area is straightforward.
$ otp ali
872348 13 Alibaba JP
For lack of creativeness the jumble of utilities is known as otp and it expects a common expression to seek for in 2FAS’ title and account fields. Straightforward. The output is the TOTP code, the remaining time (TTL) for the code, adopted by title and account, and a double-click on the 6 digits adopted by a CMD-V within the Net browser area completes a login process.
I create a 2FAS backup which accommodates JSON of all the codes, switch that to Unix/Linux and simplify it utilizing gron. (I later simplify this by reading the encrypted backup and directly producing the JSON I would like.)
$ gron Backup_2024-02-22.2fas |
egrep '.(title|otp.account|secret)' |
sed -e 's/otp.//' |
gron -u
{
"companies": [
{
"account": "JP",
"name": "Aliaba",
"secret": "MFRHEYLDMFSGCYTSMEYDAMBR"
},
...
I then store this blob in a SOPS store using one or more age identities (private keys). This combination suits me well: I very much appreciate the ease and simplicity of age, and having interacted with SOPS a bit I like its utility.
So, easy enough, but how to do this without leaving private keys (age calls these identities contrary to their public keys which are recipients) lying around?
I work on macOS and decide to store the age identity in Keychain Access. I’m sure there’s something adequate for Linux and possibly Windows.
A key is generated and written directly as a new password to macOS Keychain. Note that the clear text of the key doesn’t touch the disk. age shows me the public key (recipient) which I need once only to initialize the SOPS file.
$ security add-generic-password -a jpmens -j "age key for SOPS" -s sopsk1 -w "$(age-keygen | grep AGE-SECRET-KEY )"
Public key: age1w5g63f708smrguhujaxn72q2vs0wppfwgj3uy88cqd7e6zn5sczsjuadau
I verify that the identity has correctly been stored; it’ll be displayed on stdout. I can also search for sopsk1
in the Keychain Access app:
$ security find-generic-password -a jpmens -s sopsk1 -w
AGE-SECRET-KEY-1MQ955QVPDNL5LAYPYAFY5GRDFE2XQ3Q24TXZZ4KQ0CL9YHKCXJKQWK9TP3
At any time I need age’s public key (recipient), I can derive it from the identity; it matches what was shown us above.
$ security find-generic-password -a jpmens -s sopsk1 -w | age-keygen -y
age1w5g63f708smrguhujaxn72q2vs0wppfwgj3uy88cqd7e6zn5sczsjuadau
So far we’ve got an age private key (or identity) stashed away safely (safely enough for me anyway), and we can derive the public key (recipient) from it.
Using SOPS I now create the encrypted JSON container file. The sops
utility expects a comma-separated list of age recipients on the command line and will create the named file and encrypt the values of JSON elements therein to the specified recipients. The following command drops me into $EDITOR where I will add the JSON obtained above after gronning and ungronning it. (Those are verbs now. 🙂
$ sops --age age1w5g63f708smrguhujaxn72q2vs0wppfwgj3uy88cqd7e6zn5sczsjuadau t.json
The result is a file containing SOPS data. (The more recipients I specify, the larger the file gets; recipients’ keys are encrypted into SOPS output so that each can decrypt.)
Now I have a file with all TOTP codes securely encrypted into it.
What’s missing is a wrapper around decrypting the TOTP storage file (here: t.json
), selecting the entries I need with grep(1)
, and producing the actual TOTP code from its seed (secret
) in the JSON dict.
#!/usr/bin/env bash
[ $# -ne 1 ] && { echo "Utilization: $0 sample" >&2; exit 2; }
SOPS_AGE_KEY_FILE=<( safety find-generic-password -a jpmens -s sopsk1 -w) sops --decrypt t.json |
jq -r '.companies[] | [.secret, .name, .account] | @tsv' |
grep -i -- "$@" |
whereas learn secret title account; do
echo "$(echo $secret | genotp) $title $account"
carried out
The age identification is learn from Keychain and by way of a FIFO right into a variable and offered to sops
which decrypts our JSON retailer to stdout from whence we reformat with jq(1)
into TAB-separated traces and grep(1)
our means by. Strains which match are handed to genotp
, a small Golang program which does the work of calculating the TOTP code from the key. (I stole the hard bits.) The outcome are TOTP codes and their TTL immediately within the shell.
$ otp bab
872348 13 Alibaba JP
I’ll must backup 2FAS TOTP checklist periodically, however I’m not including codes as regularly as I used to; au contraire – I’m really deleting accounts: Xing, Linked-In, and foremost Twitter are a few of the ones I’ve given up.
As such the handbook interplay is ok for my use case.
I might have favored to have the ability to learn by 2FAS’ encrypted export which, from a look on the supply code of their iOS app, makes use of generated RSA keys, however I’ll depart that to certainly one of you to implement for me. Till then, there’s a temporary window of time throughout which the checklist of unencrypted TOTP seeds hits the disk right here, however that’s a threat I’m prepared to take. Seconds earlier than hitting “publish” on this publish, I assumed I’d ask the internets, and there are utilities which do that. I ran the Ruby instrument beneath on my encrypted backup and bought clear textual content.
Additional studying