Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TOTP support #14

Closed
colemickens opened this issue Dec 13, 2022 · 22 comments
Closed

TOTP support #14

colemickens opened this issue Dec 13, 2022 · 22 comments
Labels
enhancement New feature or request

Comments

@colemickens
Copy link

I was very excited to finally get rid of gopass but it seems prs is missing totp functionality. It would be great if it could be supported.

@timvisee
Copy link
Owner

Great to see you're using prs. I did not initially plan to integrate TOTP, because adding it in the very same password store usually defeats the purpose 2FA.

However, if this really is something you're missing, I'd be happy to take a look into implementing it.

Does gopasss implementation function well, because then I can use that as a baseline?

Please note that you can use gopass alongside prs with the same store in the mean time for TOTP functionally.

@timvisee timvisee added the enhancement New feature or request label Dec 13, 2022
@colemickens
Copy link
Author

@timvisee I do understand some folks are of that opinion. I keep my GPG on a yubikey with its own PIN so it clears my bar for "enough like a second factor".

I do think that gopass has implemented the TOTP support well, and I'd be more than happy to test a prs implementation against my various TOTP-enabled store entries that I traditionally use with gopass.

Thanks for prs and being open to this feature request!

@timvisee
Copy link
Owner

timvisee commented Dec 17, 2022

Do you have a suggestion for how this could be configured?

I'm thinking of putting the secret token in a regular pass file like:

pass

Site: example.org
User: example
Totp: 1234567890abcdef

because gopass seems to do it that way.

And the following commands to work with this:

prs totp show myfile
prs totp copy myfile
prs totp live/watch myfile
prs totp add/edit myfile
prs totp help

# or their shorts
prs otp s
prs otp c
prs otp w
prs otp a

Where watch and copy would simply stdout the current token. Watch would keep showing the token with a timer. Add would prompt you for the secret token to merge into a file (or create a new file).

And maybe a prs show myfile --totp flag to show file contents like normal but with the secret token replaced with the current TOTP token.

Note that I haven't worked with TOTP on a CLI before, so I might be missing useful stuff.

@colemickens
Copy link
Author

The UX that I'm going from with gopass is roughly:

> gopass show "websites/schwab.com/colemickens"

[password redacted]
comment:
url: schwab.com
username: colemickens
otpauth://totp/schwab?secret=XXredactXX
❯ /nix/store/z8pv90b2ysynalwzq5pvhi2smg1hknfx-gopass-1.14.9/bin/gopass totp schwab
Entry "schwab" not found. Starting search...
[ 0] websites/developer.schwab.com/cole.mickens@gmail.com
[ 1] websites/schwab.com/colemickens

Found secrets - Please select an entry (q to abort) [0]: 1
1
websites/schwab.com/colemickens
xxxxxx
⚠ ([q] to stop. -o flag to avoid.) This OTP password still lasts for:
]  1 / 25 [Gpass                                                                                                                          ]   4.00%

(it's a little much, imo, with the progress bar, but it loops and spits out totp codes til killed)

❯ /nix/store/z8pv90b2ysynalwzq5pvhi2smg1hknfx-gopass-1.14.9/bin/gopass totp --clip schwab
Entry "schwab" not found. Starting search...
[ 0] websites/developer.schwab.com/cole.mickens@gmail.com
[ 1] websites/schwab.com/colemickens

Found secrets - Please select an entry (q to abort) [0]: 1
1
websites/schwab.com/colemickens
✔ Copied token for websites/schwab.com/colemickens to clipboard. Will clear in 45 seconds.

from the gopass manpage:

This requires that the password contain either a full otpauth:// URI or a TOTP secret prefixed by '2fa:'.

The otpauth url seems to support extra parameters in the URL too, I think one or two of mine generated 8 digit codes, and that appears in the otpauth url.

If you need specifics I can gopass grep to try to find all my otpauth examples.

I don't know if Password Store for Android supports both. I think I basically use otpauth:// everywhere.

@timvisee
Copy link
Owner

timvisee commented Dec 21, 2022

Thanks for your elaborate answer.

Yes, could you query your store for any other formats you may be using? For example: do you always use otpauth://? Is it ever prefixed with a proper like 2FA: or TOTP:? Do you ever have multiple tokens in a single file?
Are there any other differences?

I have a basic TOTP token show command working now on a separate branch. The digits parameter (for 8) is already supported.

I'm asking this how because knowing this might speed up implementing this.

The workflow will likely be a little bit different, to eliminate ambiguity and improve usability. It'll definitely be less noisy.

Do you have any suggestions thus far?

@colemickens
Copy link
Author

  • gopass grep 'totp:' = two results:
    ❯ gopass show colemickens/epicgames.com
    Secret: colemickens/epicgames.com
    
    pw-here-redact
    login: cole.redact
    url: http://epicgames.com
    totp: KFHEG--------------redact----------------------------------------GQ3Q
    2fa_backup_codes: KWMredact redact redact
    
    ❯ gopass show colemickens/app.netlify.com
    Secret: colemickens/app.netlify.com
    
    pw-here2
    login: redact
    totp: KFHEG--------------redact----------------------------------------GQ3Q
    recovery: KWMredact redact redact
    
  • gopass grep otpauth = many results where there is just a line with otpauth://totp/SECRET?digits=x format.

So totp: seems to be the raw key.

Any time that I have the otpauth url entry, it is on its own line. Granted, gopass has been more and more aggressive about trying to parse out the extra data, and recently affected some 2fa codes (hence why I opened this issue, actually).

I can confirm the Android app is able to generate TOTP codes for both formats.

@timvisee
Copy link
Owner

timvisee commented Dec 22, 2022

I've implemented the first TOTP bits. Would you mind to test it out?

I'd love to hear about your findings and opinions on the following:

  1. Does this work on your TOTP (otpauth) tokens?
  2. Does this work on your other TOTP (raw key) tokens?
  3. Should prs totp show show the time to live (-q does not)?
  4. The prs totp copy (without --no-recopy) tries to be smart, and waits to recopy the token when it changes within the clipboard timeout, what do you think of this?
    (for example, if the current token has a time to live of 2 seconds and you invoke the command, it copies the token, waits for 2 seconds, and copies the updated token)
  5. Do you currently add otpauth URLs to your store manually with prs edit?

You can find a binary here.
Or clone and build yourself from the 44-add-totp-support branch over on GitLab (not GitHub) here.

This adds:

  • prs totp show [query]: show formatted token
  • prs totp copy [query]: copy token
  • prs totp live [query]: keep watching live updating token
  • prs totp help: TOTP help

And variations:

  • prs totp show -q [query]: show token without formatting (quiet)
  • prs totp copy --no-recopy [query]: copy token (without automatic recopy)
  • prs totp live -q [query]: keep watching live updating token without formatting (quiet)
  • prs totp live --follow [query]: follow live updating token, each new token appears on a new line
$ prs totp show test
109 152 (valid for 12s)

$ prs totp show test -q
109152

$ prs totp copy test
Token copied to clipboard. Recopying changing token after 12 seconds...
Token copied to clipboard. Clearing after 8 seconds...

$ prs totp copy test --no-recopy
Token copied to clipboard. Clearing after 20 seconds...

$ prs totp live test
109 152 (valid for 12s)
# updates live

$ prs totp live test -q
109152
# updates live

$ prs totp live test --follow
109 152 (valid for 12s)
123 456 (valid for 30s)
654 321 (valid for 30s)
# updates live

You can have multiple TOTP secrets in a single file if you'd like. By default the totp commands will select the first one. Prefix each with a different property name to make them selectable, for example:

mypassword
totp1: otpauth://totp/SECRET
totp2: otpauth://totp/SECRET
other: RAWTOTPSECRETHERE

and use

prs totp show -p totp1
prs totp show -p totp2
prs totp show -p other

Please see prs totp help for further details on these commands.

@colemickens
Copy link
Author

Interestingly I circled back to this when I noticed that gopass didn't support Steam, and it seems totp-rs doesn't either. Understandable given the text of the RFC but all the same I opened this: constantoine/totp-rs#45

Initial testing otherwise seems to work but I'd like to test further before saying I properly vetted it.

Thanks! It might be my imagination but it felt very snappy already.

@colemickens
Copy link
Author

Here's a failure with prs that works with gopass:

~
❯ prs totp clip discord --verbose
Secret: colemickens/discord.com
error: failed to generate TOTP token
caused by: invalid TOTP secret URL
caused by: The length of the shared secret MUST be at least 128 bits. 80 bits is not enough

For a detailed log add '--verbose'
For more information add '--help'

~
❯ gopass totp --clip discord
Entry "discord" not found. Starting search...
✅ Found exact match in "colemickens/discord.com"
✔ Copied token for colemickens/discord.com to clipboard. Will clear in 45 seconds.

~
❯ gopass show discord
⚠ Entry "discord" not found. Starting search...
✅ Found exact match in "colemickens/discord.com"
Secret: colemickens/discord.com

pw-redacted
comments: These are your Discord backup codes for account cole.mickens@gmail.com.  Keep them safe!
login: cole.mickens@gmail.com
otpauth://totp/Discord%3Acole.mickens%40gmail.com?secret=GXXXL3YYSFZZZQ6R&issuer=discord&digits=6

@Pantamis
Copy link

Pantamis commented Jan 2, 2023

Hi !

I am really excited by this project as it makes a CLI for pass on windows possible ! (and I love Rust)

Just to say that I have the same issue with PayPal TOTP too: it is only 80 bits.

This can be a stong argument for totp-rs to support such TOTP !

@timvisee
Copy link
Owner

timvisee commented Jan 2, 2023

@colemickens Thanks for getting back to me. The 128-bit size is enforced by totp-rs. I've opened an issue to remove this limitation, which would fix the above errors: constantoine/totp-rs#46

If totp-rs isn't open to support this, I'll implement this separately.

@Pantamis Awesome to hear, thanks! Thank you for noting PayPal also uses a smaller amount of bits, that's strengthens the argument to support < 128-bits.

@timvisee
Copy link
Owner

timvisee commented Jan 2, 2023

I've implemented support for Steam tokens and short TOTP secrets. This is on a totp-rs downstream development branch, and I'm unsure if this will be fully implemented upstream. Anyway, the important thing is: Steam tokens and short secrets work now.

@colemickens @Pantamis Would you both mind to give it another spin (for Steam and Discord/PayPal)?

You can find the latest binary here: https://gitlab.com/timvisee/prs/-/jobs/3544324842/artifacts/browse (source)

@colemickens
Copy link
Author

colemickens commented Jan 2, 2023

  • Battle.net (6 digit code) = OK (verified with full login)
  • Paypal (short secret) = OK (verified with full login)
  • Discord (short secret) = OK (verified with full login)
  • Steam (5 digit code) = FAIL

I'll need to reboot to Windows later to troubleshoot against the desktop authenticator to see if I've made a mistake or if there's a bug.

edit: I spot checked maybe 10-12 other entries, they all generated codes, though I didn't test those.

@timvisee
Copy link
Owner

timvisee commented Jan 2, 2023

@colemickens Thanks for testing. I assume your Steam entry to have a different format then what I got, causing parsing issues. Could you share its URI (with the secret blacked-out)?

@Pantamis
Copy link

Pantamis commented Jan 3, 2023

I tested PayPal and Discord, it works for me too now ! (and the generated code is correct)

I compiled 44-add-totp-support branch myself directly to test it.

I don't have Steam account so I can't test for this setting.

@colemickens
Copy link
Author

Wine is truly an amazing thing. See screenshot.

Of note, the Steam 5-"digit" codes, aren't actually all digits, they include letters too?

screenshot-1672962967

@colemickens
Copy link
Author

colemickens commented Jan 5, 2023

And for good measure:

╭ zeph  ~ 0.90s
╰─▶ prs show steam | grep otpauth
Secret: colemickens/steamcommunity.com
otpauth://totp/Steam-redact?secret=ZMIyyyEMDEaaaTNbbbbUcccNBNKHxxxN&digits=5&period=60

edit: oops, so much for censoring the steam username :P

@timvisee
Copy link
Owner

timvisee commented Jan 6, 2023

@colemickens Thanks for giving this a spin!

I've recently made some changes that should improve things. The latest binary can be found here: https://gitlab.com/timvisee/prs/-/jobs/3562589842/artifacts/browse

Your otpauth URL for Steam doesn't have enough information for it to be properly detected. The Steam desktop authenticator tool probably doesn't care because Steam is the only type it supports. You should set the issuer to Steam to make this work (with the latest update), this is also what other authenticators output for Steam.

Besides, I see that you've set the period to 60. I believe this must be 30 for Steam.

Changing your URL to this should do the trick:
otpauth://totp/Steam-redact?secret=ZMIyyyEMDEaaaTNbbbbUcccNBNKHxxxN&digits=5&period=30&issuer=Steam

Ps: to view live output use prs totp watch steam

@colemickens
Copy link
Author

With the new build, and the otpauth changes you suggested:

screenshot-1672963896

(aka, it works - I tested a full login on steamcommunity.com as well)

So cool! :) Thank you!

@timvisee
Copy link
Owner

timvisee commented Jan 6, 2023

Awesome to see it works now! I wonder what could be done to improve parsing, but since it isn't standardized this is probably very hard.

I'll leave this open until a new prs version is released that includes this.

@colemickens
Copy link
Author

The only thing I can think of is some documentation, either on prs or on totp-rs (and linked to from prs) that indicates specially supported issuer= query params.

@timvisee
Copy link
Owner

timvisee commented Jan 6, 2023

prs v0.4.0 has now been released which includes this.

Changelog

@timvisee timvisee closed this as completed Jan 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants