trezor-agent icon indicating copy to clipboard operation
trezor-agent copied to clipboard

Add support for OnlyKey

Open onlykey opened this issue 4 years ago • 14 comments

We forked an old version of trezor-agent back when it just had SSH support and have been using this for OnlyKey: https://docs.crp.to/onlykey-agent.html https://github.com/trustcrypto/onlykey-agent

Thanks by the way, its been working really well. We are looking to update this and since trezor-agent has support for additional hardware now, Ledger and KeepKey, I was wondering if you would accept a PR to add support for OnlyKey as an additional agent in https://github.com/romanz/trezor-agent/tree/master/agents?

I wanted to ask before I do the work to make the changes required to do this.

Thanks!

onlykey avatar Oct 03 '19 19:10 onlykey

Sounds good, I will be happy to accept the PR :)

romanz avatar Oct 04 '19 03:10 romanz

@romanz I have implemented support for OnlyKey, including:

  • SSH (ed25519 and nist256p1)
  • GPG Sign (ed25519 and nist256p1)
  • GPG Decrypt (curve25519 and nist256p1)

Doesn't support the PIN functionality though so I am thinking I will remove those command args that aren't used, OnlyKey has on device PIN entry.

I still have to do more testing before the PR but just wanted to provide the update - https://github.com/romanz/trezor-agent/compare/master...onlykey:onlykey-agent-merge

If you are interested in trying it out I will gladly send over a free onlykey, just shoot an email to t at crp.to with shipping address and I will send one over. Thanks!

onlykey avatar Jul 03 '20 15:07 onlykey

@romanz I am looking into implementing RSA support as well for GPG and SSH as onlykey already supports RSA keys, any advice on where to start here? I expect I will need to find a way to parse the pkcs v1.5 blob (256 bytes for RSA 2048 and 512 for RSA 4096).

onlykey avatar Jul 03 '20 19:07 onlykey

@romanz I have implemented support for OnlyKey, including: * SSH (ed25519 and nist256p1) * GPG Sign (ed25519 and nist256p1) * GPG Decrypt (curve25519 and nist256p1)

Cool, thanks for the update!

Doesn't support the PIN functionality though so I am thinking I will remove those command args that aren't used, OnlyKey has on device PIN entry.

Sounds good.

I still have to do more testing before the PR but just wanted to provide the update - master...onlykey:onlykey-agent-merge

Looks good overall, will do a full review when you'll open the PR.

romanz avatar Jul 04 '20 08:07 romanz

@romanz I am looking into implementing RSA support as well for GPG and SSH as onlykey already supports RSA keys, any advice on where to start here?

Good question - since TREZOR et al. don't support RSA, I am not sure how such keys are represented in SSH/GPG...

romanz avatar Jul 04 '20 08:07 romanz

@romanz I have completed implementing the following features:

  • Add support for existing functionality for OnlyKey, derived keys for nist256p1, curve25519, ed25519 for GPG and SSH
  • Add support for static keys, some times we have had users ask about not using the derived keys and instead using local keys stored on device. This has been implemented and is used like this:

-dk arg is decryption key -sk arg is signing key

Note: OnlyKey supports 16 static ECC keys (101 -116), and 4 static RSA keys (1-4)

$ onlykey-gpg init "user [email protected]" -sk 102 -dk 101 $ onlykey-agent user@domain -sk 102 -dk 101 $ onlykey-agent user@domain -sk 102 -dk 101 -c

  • Add support for RSA keys for SSH. This is used like this: $ onlykey-agent "user [email protected]" -e rsa -sk 2 -dk 1 Note: since RSA keys are not derived -sk and -dk are required

The above is tested and working but I wanted to reach out to see if you have any ideas about what's not working below:

ISSUE#1 - Add support for RSA keys for GPG. Since RSA keys are not derived we just use -i for import existing public key. So import instead of creating pubkey. This works but I run into issue where when it runs gpg2--list-secret-keys none are found:

$ onlykey-gpg init "[email protected] [email protected]" -vv -sk 2 -dk 1 -i public.asc && GPG date | gpg2 --encrypt -r "user" | gpg2 --decrypt

2020-07-16 16:03:54,317 DEBUG        run: ['/usr/local/Cellar/gnupg/2.2.20/bin/gpg', '--homedir', '/Users/x/.gnupg/trezor', '--import-ownertrust', '/Users/t/.gnupg/trezor/ownertrust.txt'] [__init__.py:115]
gpg: inserting ownertrust of 6
2020-07-16 16:03:54,326 DEBUG        run: ['/usr/local/Cellar/gnupg/2.2.20/bin/gpg', '--homedir', '/Users/x/.gnupg/trezor', '--list-secret-keys', '[email protected] <[email protected]>'] [__init__.py:115]
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: error reading key: No secret key
Traceback (most recent call last):
  File "/Users/x/opt/anaconda3/bin/onlykey-gpg", line 8, in <module>
    sys.exit(gpg_tool())
  File "/Users/x/opt/anaconda3/bin/onlykey_agent.py", line 6, in <lambda>
    gpg_tool = lambda: libagent.gpg.main(DeviceType)
  File "/Users/x/opt/anaconda3/lib/python3.7/site-packages/libagent/gpg/__init__.py", line 378, in main
    return args.func(device_type=device_type, args=args)
  File "/Users/x/opt/anaconda3/lib/python3.7/site-packages/libagent/gpg/__init__.py", line 226, in run_init
    check_call(keyring.gpg_command(['--homedir', homedir, '--list-secret-keys', args.user_id]))
  File "/Users/x/opt/anaconda3/lib/python3.7/site-packages/libagent/gpg/__init__.py", line 116, in check_call
    subprocess.check_call(args=args, stdin=stdin, env=env)
  File "/Users/x/opt/anaconda3/lib/python3.7/subprocess.py", line 363, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['/usr/local/Cellar/gnupg/2.2.20/bin/gpg', '--homedir', '/Users/x/.gnupg/trezor', '--list-secret-keys', '[email protected] <[email protected]>']' returned non-zero exit status 2.

ISSUE#2 - Lets say we have a private key set in OnlyKey ECC slot 101 and 102, the following will work:

$ onlykey-gpg init "[email protected] [email protected]" -vv -sk 102 -dk 101 && GPG date | gpg2 --encrypt -r "crptest" | gpg2 --decrypt

2020-07-16 16:11:33,927 DEBUG        out: 'tru::1:1594930292:0:3:1:5\npub:-:256:22:345940EB83C97E97:0:::-:::scESC:::::ed25519:::0:\nfpr:::::::::C9A19C19C78B5D2041EE28C9345940EB83C97E97:\nuid:-::::::F94409DBA3D6194724C58F73630C3740E10B7310::[email protected] <[email protected]>::::::::::0:\nsub:-:256:18:D2831D730E2264B5:0::::::e:::::cv25519::\nfpr:::::::::B91801058E7F937EADB00597D2831D730E2264B5:\n' [__init__.py:109]
2020-07-16 16:11:33,928 DEBUG        setting /Users/t/.gnupg/trezor/ownertrust.txt contents:
C9A19C19C78B5D2041EE28C9345940EB83C97E97:6
  [__init__.py:122]
2020-07-16 16:11:33,928 DEBUG        run: ['/usr/local/Cellar/gnupg/2.2.20/bin/gpg', '--homedir', '/Users/t/.gnupg/trezor', '--import-ownertrust', '/Users/t/.gnupg/trezor/ownertrust.txt'] [__init__.py:115]
gpg: inserting ownertrust of 6
2020-07-16 16:11:33,936 DEBUG        run: ['/usr/local/Cellar/gnupg/2.2.20/bin/gpg', '--homedir', '/Users/t/.gnupg/trezor', '--list-secret-keys', '[email protected] <[email protected]>'] [__init__.py:115]
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
sec   ed25519 1970-01-01 [SC]
      C9A19C19C78B5D2041EE28C9345940EB83C97E97
uid           [ultimate] [email protected] <[email protected]>
ssb   cv25519 1970-01-01 [E]

gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: can't open 'date'
gpg: WARNING: cipher algorithm AES256 not found in recipient preferences
gpg: encrypted with 256-bit ECDH key, ID D2831D730E2264B5, created 1970-01-01
      "[email protected] <[email protected]>"

So then without changing anything we try and import the pubkey instead of creating it, everything is the same except the PGP key id/date. It gets this error:

$ onlykey-gpg init "[email protected] [email protected]" -vv -sk 102 -dk 101 -i [email protected] && GPG date | gpg2 --encrypt -r "crptest" | gpg2 --decrypt

2020-07-16 16:12:31,557 DEBUG        out: 'tru::1:1594930350:0:3:1:5\npub:-:256:22:FDCABBD34C763B10:1594041052:::-:::scESC:::::ed25519:::0:\nfpr:::::::::BA28DF0123F2A9AA72255790FDCABBD34C763B10:\nuid:-::::1594041052::F94409DBA3D6194724C58F73630C3740E10B7310::[email protected] <[email protected]>::::::::::0:\nsub:-:256:18:BE3EA7B2ED208930:1594041052::::::e:::::cv25519::\nfpr:::::::::F83A3D33BEBD6E6C9C57E5A4BE3EA7B2ED208930:\n' [__init__.py:109]
2020-07-16 16:12:31,558 DEBUG        setting /Users/t/.gnupg/trezor/ownertrust.txt contents:
BA28DF0123F2A9AA72255790FDCABBD34C763B10:6
  [__init__.py:122]
2020-07-16 16:12:31,559 DEBUG        run: ['/usr/local/Cellar/gnupg/2.2.20/bin/gpg', '--homedir', '/Users/t/.gnupg/trezor', '--import-ownertrust', '/Users/t/.gnupg/trezor/ownertrust.txt'] [__init__.py:115]
gpg: inserting ownertrust of 6
2020-07-16 16:12:31,570 DEBUG        run: ['/usr/local/Cellar/gnupg/2.2.20/bin/gpg', '--homedir', '/Users/t/.gnupg/trezor', '--list-secret-keys', '[email protected] <[email protected]>'] [__init__.py:115]
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
sec   ed25519 2020-07-06 [SC]
      BA28DF0123F2A9AA72255790FDCABBD34C763B10
uid           [ultimate] [email protected] <[email protected]>
ssb   cv25519 2020-07-06 [E]

gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: can't open 'date'
gpg: ecdh failed in gcry_cipher_decrypt: Checksum error
gpg: encrypted with 256-bit ECDH key, ID BE3EA7B2ED208930, created 2020-07-06
      "[email protected] <[email protected]>"
gpg: public key decryption failed: Checksum error
gpg: decryption failed: No secret key

Seems similar to the RSA issue, but here it prompts for and receives the ECDH value, its just not matching what it expected (Checksum error). Any ideas on what needs to change here? The reason I would like to support this is so that users can bring and use existing keys from services like protonmail and it will work with the agent and generate valid signatures that you could then use with other services.

Any guidance would be greatly appreciated. https://github.com/onlykey/trezor-agent/tree/onlykey-agent-merge

onlykey avatar Jul 16 '20 20:07 onlykey

@romanz Also I replaced the ed25519 library with pynacl as ed25519 library has to compile from source and doesn't work on windows without dev tools and they say this here - https://pypi.org/project/ed25519/ "Not Recommended For New Applications: Use pynacl Instead"

onlykey avatar Jul 16 '20 20:07 onlykey

FYI, GPG created curve25519 keys are flipped backwards pretty sure that will fix my ISSUE2 - http://gnupg.10057.n7.nabble.com/Correct-method-to-generate-a-Curve25519-keypair-td56013.html

onlykey avatar Jul 17 '20 23:07 onlykey

@romanz Also I replaced the ed25519 library with pynacl as ed25519 library has to compile from source and doesn't work on windows without dev tools and they say this here - https://pypi.org/project/ed25519/ "Not Recommended For New Applications: Use pynacl Instead"

Many thanks! Would it be possible to split this into a separate PR please?

romanz avatar Jul 19 '20 07:07 romanz

I would be happy to review and merge first the nist256p1, curve25519, ed25519 SSH and GPG support (without the RSA and static keys support for now). Would it possible to split those into a separate PR (rebased over the pynacl PR)?

romanz avatar Jul 19 '20 07:07 romanz

@romanz sorry for delay, I just saw your response here. I have everything working now (except import RSA keys for GPG) and the full PR ready, I will break this up into multiple PRs as you suggested.

onlykey avatar Jul 31 '20 14:07 onlykey

@romanz Just wanted to check on the PR. Does everything look good and do you know when you would be able to merge this?

Thanks!

onlykey avatar Aug 04 '20 15:08 onlykey

Sorry, I missed the notification - will take a look tomorrow.

romanz avatar Aug 04 '20 15:08 romanz

I don't think the full support has landed yet, has it? I don't see implementation for -sk in #337.

kalbasit avatar Aug 21 '21 07:08 kalbasit