Why? I need the text SECRET encoded in previously saved QR images, screenshots, and Google Authenticator Export images for import into other apps KeePassXC, Proton Pass
The motivation for this project was initiated by password housekeeping. I'm content with the tools I use for password management such as KeePassXC, pass, iTerm2 Password Manager (can't live without now), but I lacked visibility and portability of my TOTP parameters and passwords.
This tool uses:
- The excellent rqrr crate for digging out otpauth data from most image types
- The reverse engineered protobuf
- The file-format crate for classifying stdin
- Shout out to totp-rs for its succinct byte slicing and Algorithm Enum; derivations of both were used. MIT LICENSE
- Waiting for resolution on SIGPIPE for general CLI Unix tools to avoid "broken pipe".
- The remedy is to modify stdout and use writeln!(stdout)? instead of println!() as shown below
pub fn reset_sigpipe() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(target_family = "unix")]
{
use nix::sys::signal;
unsafe {
signal::signal(signal::Signal::SIGPIPE, signal::SigHandler::SigDfl)?;
}
}
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// behave like a typical unix utility
general::reset_sigpipe()?;
let mut stdout = io::stdout().lock();
...
- Install
totp-qr
e.g.cargo install totp-qr
or buildcargo install --path .
- Gather your QR-images into a directory
- Run
scripts/mk-totp-func.sh directory
- Inspect, copy, and add to your ~/.bashrc
- otpauth-totp-qr.jpg holds 1 account: "otpauth://totp/..."
- otpauth-migration-qr.jpg holds 3 accounts: "otpauth-migration://offline?data=..."
$> totp-qr --uri images/otpauth-totp-qr.jpg
otpauth://totp/Example:[email protected]?issuer=Example&period=30&secret=JBSWY3DPEHPK3PXP
$> totp-qr --uri images/otpauth-migration-qr.jpg
otpauth-migration://offline?data=Ci0KCkhlbGxvId6tvu8SEnRlc3QxQGV4YW1wbGUxLmNvbRoFVGVzdDEgASgBMAIKLQoKSGVsbG8h3q2%2B8BISdGVzdDJAZXhhbXBsZTIuY29tGgVUZXN0MiABKAEwAgotCgpIZWxsbyHerb7xEhJ0ZXN0M0BleGFtcGxlMy5jb20aBVRlc3QzIAEoATACEAIYASAA
otpauth data should be kept PRIVATE, OpenSSL can be used to encrypt the data (password: foo)
$> totp-qr --uri images/* | openssl aes-256-cbc -e -pbkdf2 -a
enter AES-256-CBC encryption password:
Verifying - enter AES-256-CBC encryption password:
U2FsdGVkX18lfKZ20uQn/AcAWa85hUmcJzQ8mvS9JX0BJb7qVDddrCjbjPxagIw6
hwHeLBPWx1U0GbA7zszAYKNa6FB2I53ldNET/tnutUBNmQeuxqbiVH8A0or9Ni8+
Lj8onivfmaGzcBGGGMtz3wliD/LL+iUhkG+A2FZpIE2mIf9QdwofI9jSAhDhAW3y
d+AXZWWsHRRVs5MvIA++CcchKLG+FOza3fcBIt7RtqkdISQYDw+TgMGLN8NS5/ak
tk8PcuO+QfjmtNXh0/96mn5jYCGdD1NvioeDkwBu7883q2ChHXcOLRuPqqlJAR/2
T+DwgtEyCO5ZhQPn3nj9E1Gy1xXAm+4Yt8CueXvuBS5SJJLQd94Q+HT1SsyMhYB0
FGVb6YifAjV3Snsk3UO/60quJ8cfQxjDW5Pef/a0LjtMZL2d+jaYImFcLEMUrnlI
FRGDcbHR1oAmdonyuSNJBQ==
scripts/mk-totp-func.sh encrypts URI's and outputs a Bash function named totp()
$> ./scripts/mk-totp-func.sh images/
enter AES-256-CBC encryption password:
Verifying - enter AES-256-CBC encryption password:
totp() {
openssl aes-256-cbc -d -pbkdf2 -a << EOF | totp-qr $1 | sort -t, -k2
U2FsdGVkX1+gVAFEnnQVFQVmzDUU47Sl6NIqFOAQaM85dspvn8gt2hueK272RRi4
vdWDBLsFeKM4qp7Jq2TSV2Lca2/29cwPcZtAVnaz02VbxO2m/e3b4RjB9AxjRk1R
iTPdTzG+BO2GYHjdz515Dc/N4+HD5UMVJr7yAsypdJ/ThRN3CWCjUYd3mAGx9/g7
0GCwTJ6psw4CtwbgL3hg66cZq9w43Wwj0P+S3eL87ueZRHHfr10hEtLTsJQLuRDl
479WZpzFFPTyrr3jVQFMqmhgEXKXf2VnFp4aLvCk6OKP93iQU3fE5aRWTEpQytYF
+F/AvpAQUnEOvAAivFFa2SBXZPHDscENzG16P0O8i3hWoyJizoAJIOMOPsA3HgMZ
1kxCFBnME1Pd1dlrSTsfhFNpjfbaURWxI5pwMS/fAKMIoRLWydeGJOukNIv+zPmI
PPJXNNa5fQP647srICuCnw==
EOF
}
$> ./scripts/mk-totp-func.sh images/ >> ~/.bashrc
$> . ~/.bashrc
$> type -a totp
totp is a function
totp ()
{
openssl aes-256-cbc -d -pbkdf2 -a <<EOF |
...
EOF
totp-qr $1 | sort -t, -k2
}
Tip: If you're on a Mac using iTerm2 check out password manager (shortcut: ⌥ ⌘ F) for supplying passwords
$> totp
enter AES-256-CBC decryption password:
757676, Example
757676, Test1
255080, Test2
476239, Test3
$> totp -e | jq
enter AES-256-CBC decryption password:
[
{
"secret": "JBSWY3DPEHPK3PXP",
"issuer": "Test1",
"sha": "SHA1",
"digits": 6,
"period": 30
},
{
"secret": "JBSWY3DPEHPK3PXQ",
"issuer": "Test2",
"sha": "SHA1",
"digits": 6,
"period": 30
},
{
"secret": "JBSWY3DPEHPK3PXR",
"issuer": "Test3",
"sha": "SHA1",
"digits": 6,
"period": 30
},
{
"secret": "JBSWY3DPEHPK3PXP",
"issuer": "Example",
"sha": "SHA1",
"digits": 6,
"period": 30
}
]
$> totp -u
enter AES-256-CBC decryption password:
otpauth-migration://offline?data=Ci0KCkhlbGxvId6tvu8SEnRlc3QxQGV4YW1wbGUxLmNvbRoFVGVzdDEgASgBMAIKLQoKSGVsbG8h3q2%2B8BISdGVzdDJAZXhhbXBsZTIuY29tGgVUZXN0MiABKAEwAgotCgpIZWxsbyHerb7xEhJ0ZXN0M0BleGFtcGxlMy5jb20aBVRlc3QzIAEoATACEAIYASAA
otpauth://totp/Example:[email protected]?issuer=Example&period=30&secret=JBSWY3DPEHPK3PXP
Usage: totp-qr [OPTIONS] [FILES]...
Arguments:
[FILES]... image-file|stdin, filename of "-" implies stdin
Options:
-a, --auth <AUTH> "otpauth-migration://offline?data=..." or "otpauth://totp/...?secret=SECRET"
-v, --verbose Verbose output
-e, --export Export account information as JSON
-i, --import Import JSON accounts
-u, --uri Output extracted URI's
-h, --help Print help
-V, --version Print version
$> totp-qr -v images/*.jpg
otpauth = otpauth-migration://offline?data=Ci0KCkhlbGxvId6tvu8SEnRlc3QxQGV4YW1wbGUxLmNvbRoFVGVzdDEgASgBMAIKLQoKSGVsbG8h3q2%2B8BISdGVzdDJAZXhhbXBsZTIuY29tGgVUZXN0MiABKAEwAgotCgpIZWxsbyHerb7xEhJ0ZXN0M0BleGFtcGxlMy5jb20aBVRlc3QzIAEoATACEAIYASAA
237769, Account { secret: "JBSWY3DPEHPK3PXP", issuer: "Test1", sha: "SHA1", digits: 6, period: 30 }
734660, Account { secret: "JBSWY3DPEHPK3PXQ", issuer: "Test2", sha: "SHA1", digits: 6, period: 30 }
021109, Account { secret: "JBSWY3DPEHPK3PXR", issuer: "Test3", sha: "SHA1", digits: 6, period: 30 }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
otpauth = otpauth://totp/Example:[email protected]?issuer=Example&period=30&secret=JBSWY3DPEHPK3PXP
237769, Account { secret: "JBSWY3DPEHPK3PXP", issuer: "Example", sha: "SHA1", digits: 6, period: 30 }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$> totp-qr --auth="otpauth://totp/ACME%20Co:[email protected]?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30"
970700, ACME Co
$> echo 'secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ' | totp-qr
970700,
$> totp-qr < images/otpauth-migration-qr.jpg
237769, Test1
734660, Test2
021109, Test3
$> totp-qr -e images/*.jpg | totp-qr -iv
939954, Account { secret: "JBSWY3DPEHPK3PXP", issuer: "Test1", sha: "SHA1", digits: 6, period: 30 }
561818, Account { secret: "JBSWY3DPEHPK3PXQ", issuer: "Test2", sha: "SHA1", digits: 6, period: 30 }
787732, Account { secret: "JBSWY3DPEHPK3PXR", issuer: "Test3", sha: "SHA1", digits: 6, period: 30 }
939954, Account { secret: "JBSWY3DPEHPK3PXP", issuer: "Example", sha: "SHA1", digits: 6, period: 30 }