Compare commits

...

13 Commits

Author SHA1 Message Date
Roman Zeyde
28cbb941f1 Bump version: 0.14.7 → 0.14.8 2023-08-19 15:42:03 +03:00
Roman Zeyde
0acc6cd2ef Update email in setup.py 2023-08-19 15:36:21 +03:00
Roman Zeyde
a247e877fc No need to install Cython & hidapi 2023-08-12 11:32:33 +03:00
Roman Zeyde
8cb323c550 Update docs to reference trezor-agent instead of trezor_agent (#342) 2023-08-12 11:28:35 +03:00
Roman Zeyde
d915d8594c Merge pull request #385 from Senjuu/ssh-certificates
Add Support for ED25519 ssh-certificates
2023-07-31 20:41:43 +03:00
Senjuu
473a565fc6 Add Support for ED25519 ssh-certificates 2023-07-31 13:06:05 +02:00
Roman Zeyde
2b49eacc01 Run CI also on PRs 2023-06-03 16:40:00 +03:00
Roman Zeyde
ba8a1ba8c8 Merge pull request #423 from jsmith-dev/patch-1
Update INSTALL.md
2023-05-12 09:41:08 +03:00
Julian Smith, Main Street Ventures
bf7324ca89 Update INSTALL.md
Fix install step instruction
2023-05-11 15:45:14 +10:00
Roman Zeyde
890fd0bdfd Merge pull request #421 from romanz/stable
Mark 'libagent' package as stable
2023-04-25 19:34:31 +03:00
Roman Zeyde
1518b7bde0 Mark 'libagent' package as stable 2023-04-25 17:59:42 +03:00
Roman Zeyde
23a48e41f7 Merge pull request #420 from romanz/fixup
Remove unused imports and fix a small lint issue
2023-04-25 17:47:46 +03:00
Roman Zeyde
97416308ed Remove unused imports and fix a small lint issue 2023-04-25 17:43:40 +03:00
13 changed files with 132 additions and 76 deletions

View File

@@ -1,6 +1,6 @@
[bumpversion]
commit = True
tag = True
current_version = 0.14.7
current_version = 0.14.8
[bumpversion:file:setup.py]

View File

@@ -1,6 +1,6 @@
name: Build
on: [push]
on: [push, pull_request]
jobs:
build:

View File

@@ -7,9 +7,9 @@ print('ONLY FOR DEBUGGING AND TESTING!!!')
setup(
name='fake_device_agent',
version='0.9.0',
description='Testing trezor_agent with a fake device - NOT SAFE!!!',
description='Testing SSH/GPG agent with a fake device - NOT SAFE!!!',
author='Roman Zeyde',
author_email='roman.zeyde@gmail.com',
author_email='dev@romanzey.de',
url='http://github.com/romanz/trezor-agent',
scripts=['fake_device_agent.py'],
install_requires=[

View File

@@ -6,7 +6,7 @@ setup(
version='0.9.0',
description='Using KeepKey as hardware SSH/GPG agent',
author='Roman Zeyde',
author_email='roman.zeyde@gmail.com',
author_email='dev@romanzey.de',
url='http://github.com/romanz/trezor-agent',
scripts=['keepkey_agent.py'],
install_requires=[

View File

@@ -6,7 +6,7 @@ setup(
version='0.9.0',
description='Using Ledger as hardware SSH/GPG agent',
author='Roman Zeyde',
author_email='roman.zeyde@gmail.com',
author_email='dev@romanzey.de',
url='http://github.com/romanz/trezor-agent',
scripts=['ledger_agent.py'],
install_requires=[

View File

@@ -6,7 +6,7 @@ setup(
version='0.12.0',
description='Using Trezor as hardware SSH/GPG agent',
author='Roman Zeyde',
author_email='roman.zeyde@gmail.com',
author_email='dev@romanzey.de',
url='http://github.com/romanz/trezor-agent',
scripts=['trezor_agent.py'],
install_requires=[

View File

@@ -63,11 +63,10 @@ gpg (GnuPG) 2.1.15
2. Make sure that your `udev` rules are configured [correctly](https://wiki.trezor.io/Udev_rules).
3. Then, install the latest [trezor_agent](https://pypi.python.org/pypi/trezor_agent) package:
3. Then, install the latest [trezor-agent](https://pypi.python.org/pypi/trezor-agent) package:
```
$ pip3 install Cython hidapi
$ pip3 install trezor_agent
$ pip3 install trezor-agent
```
Or, directly from the latest source code:
@@ -91,7 +90,7 @@ gpg (GnuPG) 2.1.15
* [KeepKey firmware releases](https://github.com/keepkey/keepkey-firmware/releases): `3.0.17+`
2. Make sure that your `udev` rules are configured [correctly](https://support.keepkey.com/support/solutions/articles/6000037796-keepkey-wallet-is-not-being-recognized-by-linux).
Then, install the latest [keepkey_agent](https://pypi.python.org/pypi/keepkey_agent) package:
Then, install the latest [keepkey-agent](https://pypi.python.org/pypi/keepkey-agent) package:
```
$ pip3 install keepkey_agent
@@ -100,7 +99,7 @@ Then, install the latest [keepkey_agent](https://pypi.python.org/pypi/keepkey_ag
Or, on Mac using Homebrew:
```
$ homebrew install keepkey-agent
$ brew install keepkey-agent
```
Or, directly from the latest source code:
@@ -117,10 +116,10 @@ Then, install the latest [keepkey_agent](https://pypi.python.org/pypi/keepkey_ag
* [Ledger Nano S firmware releases](https://github.com/LedgerHQ/blue-app-ssh-agent): `0.0.3+` (install [SSH/PGP Agent](https://www.ledgerwallet.com/images/apps/chrome-mngr-apps.png) app)
2. Make sure that your `udev` rules are configured [correctly](https://ledger.zendesk.com/hc/en-us/articles/115005165269-What-if-Ledger-Wallet-is-not-recognized-on-Linux-).
3. Then, install the latest [ledger_agent](https://pypi.python.org/pypi/ledger_agent) package:
3. Then, install the latest [ledger-agent](https://pypi.python.org/pypi/ledger-agent) package:
```
$ pip3 install ledger_agent
$ pip3 install ledger-agent
```
Or, directly from the latest source code:

View File

@@ -9,21 +9,17 @@ See these links for more details:
import argparse
import base64
import contextlib
import datetime
import io
import logging
import os
import sys
import traceback
import bech32
import pkg_resources
import semver
from cryptography.exceptions import InvalidTag
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
from .. import device, server, util
from .. import device, util
from . import client
log = logging.getLogger(__name__)

View File

@@ -159,35 +159,35 @@ class OnlyKey(interface.Device):
else:
vk = ecdsa.VerifyingKey.from_string(ok_pubkey, curve=ecdsa.SECP256k1)
return vk
else:
ok_pubkey = []
while time.time() < t_end:
try:
ok_pub_part = self.ok.read_bytes(timeout_ms=100)
if len(ok_pub_part) == 64 and len(set(ok_pub_part[0:63])) != 1:
log.info('received part= %s', repr(ok_pub_part))
ok_pubkey += ok_pub_part
# Todo know RSA type to know how many packets
except Exception as e:
raise interface.DeviceError(e)
log.info('received= %s', repr(ok_pubkey))
if len(ok_pubkey) == 256:
# https://security.stackexchange.com/questions/42268/how-do-i-get-the-rsa-bit-length-with-the-pubkey-and-openssl
ok_pubkey = b'\x00\x00\x00\x07' + b'\x73\x73\x68\x2d\x72\x73\x61' + \
b'\x00\x00\x00\x03' + b'\x01\x00\x01' + \
b'\x00\x00\x01\x01' + b'\x00' + bytes(ok_pubkey)
# ok_pubkey = b'\x00\x00\x00\x07' + b'\x72\x73\x61\x2d\x73\x68\x61\x32\x2d\x32\x35\x
# 36' + b'\x00\x00\x00\x03' + b'\x01\x00\x01' + b'\x00\x00\x01\x01' + b'\x00' + byte
# s(ok_pubkey)
elif len(ok_pubkey) == 512:
ok_pubkey = b'\x00\x00\x00\x07' + b'\x73\x73\x68\x2d\x72\x73\x61' + \
b'\x00\x00\x00\x03' + b'\x01\x00\x01' + \
b'\x00\x00\x02\x01' + b'\x00' + bytes(ok_pubkey)
else:
raise interface.DeviceError("Error response length is not a valid public key")
log.info('pubkey len = %s', len(ok_pubkey))
return ok_pubkey
ok_pubkey = []
while time.time() < t_end:
try:
ok_pub_part = self.ok.read_bytes(timeout_ms=100)
if len(ok_pub_part) == 64 and len(set(ok_pub_part[0:63])) != 1:
log.info('received part= %s', repr(ok_pub_part))
ok_pubkey += ok_pub_part
# Todo know RSA type to know how many packets
except Exception as e:
raise interface.DeviceError(e)
log.info('received= %s', repr(ok_pubkey))
if len(ok_pubkey) == 256:
# https://security.stackexchange.com/questions/42268/how-do-i-get-the-rsa-bit-length-with-the-pubkey-and-openssl
ok_pubkey = b'\x00\x00\x00\x07' + b'\x73\x73\x68\x2d\x72\x73\x61' + \
b'\x00\x00\x00\x03' + b'\x01\x00\x01' + \
b'\x00\x00\x01\x01' + b'\x00' + bytes(ok_pubkey)
# ok_pubkey = b'\x00\x00\x00\x07' + b'\x72\x73\x61\x2d\x73\x68\x61\x32\x2d\x32\x35\x
# 36' + b'\x00\x00\x00\x03' + b'\x01\x00\x01' + b'\x00\x00\x01\x01' + b'\x00' + byte
# s(ok_pubkey)
elif len(ok_pubkey) == 512:
ok_pubkey = b'\x00\x00\x00\x07' + b'\x73\x73\x68\x2d\x72\x73\x61' + \
b'\x00\x00\x00\x03' + b'\x01\x00\x01' + \
b'\x00\x00\x02\x01' + b'\x00' + bytes(ok_pubkey)
else:
raise interface.DeviceError("Error response length is not a valid public key")
log.info('pubkey len = %s', len(ok_pubkey))
return ok_pubkey
def sign(self, identity, blob):
"""Sign given blob and return the signature (as bytes)."""

View File

@@ -21,14 +21,16 @@ ECDH_NIST256 = 'nist256p1'
ECDH_CURVE25519 = 'curve25519'
# SSH key types
SSH_CERT_POSTFIX = b'-cert-v01@openssh.com'
SSH_NIST256_DER_OCTET = b'\x04'
SSH_NIST256_KEY_PREFIX = b'ecdsa-sha2-'
SSH_NIST256_CURVE_NAME = b'nistp256'
SSH_NIST256_KEY_TYPE = SSH_NIST256_KEY_PREFIX + SSH_NIST256_CURVE_NAME
SSH_NIST256_CERT_POSTFIX = b'-cert-v01@openssh.com'
SSH_NIST256_CERT_TYPE = SSH_NIST256_KEY_TYPE + SSH_NIST256_CERT_POSTFIX
SSH_NIST256_CERT_TYPE = SSH_NIST256_KEY_TYPE + SSH_CERT_POSTFIX
SSH_ED25519_KEY_TYPE = b'ssh-ed25519'
SUPPORTED_KEY_TYPES = {SSH_NIST256_KEY_TYPE, SSH_NIST256_CERT_TYPE, SSH_ED25519_KEY_TYPE}
SSH_ED25519_CERT_TYPE = SSH_ED25519_KEY_TYPE + SSH_CERT_POSTFIX
SUPPORTED_KEY_TYPES = {SSH_NIST256_KEY_TYPE, SSH_NIST256_CERT_TYPE,
SSH_ED25519_KEY_TYPE, SSH_ED25519_CERT_TYPE}
hashfunc = hashlib.sha256
@@ -43,6 +45,20 @@ def fingerprint(blob):
return ':'.join('{:02x}'.format(c) for c in bytearray(digest))
def __skip_certificate_fields(s):
_serial_number = util.recv(s, '>Q')
_type = util.recv(s, '>L')
_key_id = util.read_frame(s)
_valid_principals = util.read_frame(s)
_valid_after = util.recv(s, '>Q')
_valid_before = util.recv(s, '>Q')
_critical_options = util.read_frame(s)
_extensions = util.read_frame(s)
_reserved = util.read_frame(s)
_signature_key = util.read_frame(s)
_signature = util.read_frame(s)
def parse_pubkey(blob):
"""
Parse SSH public key from given blob.
@@ -69,18 +85,7 @@ def parse_pubkey(blob):
point = util.read_frame(s)
if key_type == SSH_NIST256_CERT_TYPE:
_serial_number = util.recv(s, '>Q')
_type = util.recv(s, '>L')
_key_id = util.read_frame(s)
_valid_principals = util.read_frame(s)
_valid_after = util.recv(s, '>Q')
_valid_before = util.recv(s, '>Q')
_critical_options = util.read_frame(s)
_extensions = util.read_frame(s)
_reserved = util.read_frame(s)
_signature_key = util.read_frame(s)
_signature = util.read_frame(s)
__skip_certificate_fields(s)
assert s.read() == b''
_type, point = point[:1], point[1:]
assert _type == SSH_NIST256_DER_OCTET
@@ -102,8 +107,12 @@ def parse_pubkey(blob):
result.update(point=coords, curve=CURVE_NIST256,
verifier=ecdsa_verifier)
if key_type == SSH_ED25519_KEY_TYPE:
if key_type in (SSH_ED25519_KEY_TYPE, SSH_ED25519_CERT_TYPE):
if key_type == SSH_ED25519_CERT_TYPE:
_nonce = util.read_frame(s)
pubkey = util.read_frame(s)
if key_type == SSH_ED25519_CERT_TYPE:
__skip_certificate_fields(s)
assert s.read() == b''
def ed25519_verify(sig, msg):

View File

@@ -2,21 +2,12 @@
import argparse
import binascii
import contextlib
import functools
import hashlib
import logging
import os
import re
import struct
import subprocess
import sys
import time
import pkg_resources
import semver
from .. import formats, server, util
from .. import util
from ..device import interface, ui
log = logging.getLogger(__name__)

View File

@@ -43,6 +43,57 @@ _public_key_cert = (
'home\n'
)
_public_key_ed25519_cert = (
'ssh-ed25519-cert-v01@openssh.com '
'AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29'
'tAAAAIK5TMdCnuxxy4rr0CTHLekAsnL4DAhFyksK5romkuw'
'xgAAAAIFBdF2tjfSO8nLIi736is+f0erq28RTc7CkM11NZt'
'TKRAAAAAAAAAAAAAAABAAAACXVuaXQtdGVzdAAAAA0AAAAJ'
'dW5pdC10ZXN0AAAAAAAAAAD//////////wAAAAAAAACCAAA'
'AFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybW'
'l0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb'
'3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAA'
'AAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAABoAAAAE2V'
'jZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBC'
'HF5pUcZLVlTUBzos8ojyN34KrS7TnGAZINhRsCoNuRV4NFN'
'IlEYpEvSwlumQuDx6B1y4Va+3pYzBbZInm6vwgAAABjAAAA'
'E2VjZHNhLXNoYTItbmlzdHAyNTYAAABIAAAAICUMX1taTy6'
'y+1Aa1m7kXHI/Qv7ZZIeNp7ndmCRLFCSuAAAAIBaX43k0Ye'
'Bk8a5zp6FyFCBYVOtis/DUbGm07d7miPnE '
'hello\n'
)
_public_key_ed25519_cert_BLOB = (
b'\x00\x00\x00 ssh-ed25519-cert-v01@openssh.com'
b'\x00\x00\x00 \xaeS1\xd0\xa7\xbb\x1cr\xe2\xba'
b'\xf4\t1\xcbz@,\x9c\xbe\x03\x02\x11r\x92\xc2\xb9'
b'\xae\x89\xa4\xbb\x0c`\x00\x00\x00 P]\x17kc}#'
b'\xbc\x9c\xb2"\xef~\xa2\xb3\xe7\xf4z\xba\xb6\xf1'
b'\x14\xdc\xec)\x0c\xd7SY\xb52\x91\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00'
b'\x00\tunit-test\x00\x00\x00\r\x00\x00\x00\tun'
b'it-test\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff'
b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00'
b'\x00\x00\x82\x00\x00\x00\x15permit-X11-forwar'
b'ding\x00\x00\x00\x00\x00\x00\x00\x17permit-ag'
b'ent-forwarding\x00\x00\x00\x00\x00\x00\x00\x16'
b'permit-port-forwarding\x00\x00\x00\x00\x00\x00'
b'\x00\npermit-pty\x00\x00\x00\x00\x00\x00\x00'
b'\x0epermit-user-rc\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00h\x00\x00\x00\x13ecdsa-sha2-n'
b'istp256\x00\x00\x00\x08nistp256\x00\x00\x00A'
b'\x04!\xc5\xe6\x95\x1cd\xb5eM@s\xa2\xcf(\x8f#w'
b'\xe0\xaa\xd2\xed9\xc6\x01\x92\r\x85\x1b\x02\xa0'
b'\xdb\x91W\x83E4\x89Db\x91/K\tn\x99\x0b\x83\xc7'
b'\xa0u\xcb\x85Z\xfbzX\xcc\x16\xd9"y\xba\xbf\x08'
b'\x00\x00\x00c\x00\x00\x00\x13ecdsa-sha2-nistp'
b'256\x00\x00\x00H\x00\x00\x00 %\x0c_[ZO.\xb2\xfb'
b'P\x1a\xd6n\xe4\\r?B\xfe\xd9d\x87\x8d\xa7\xb9'
b'\xdd\x98$K\x14$\xae\x00\x00\x00 \x16\x97\xe3y'
b'4a\xe0d\xf1\xaes\xa7\xa1r\x14 XT\xebb\xb3\xf0'
b'\xd4li\xb4\xed\xde\xe6\x88\xf9\xc4'
)
def test_parse_public_key():
key = formats.import_public_key(_public_key)
@@ -86,6 +137,16 @@ def test_parse_ed25519():
assert p['type'] == b'ssh-ed25519'
def test_parse_ed25519_cert():
p = formats.import_public_key(_public_key_ed25519_cert)
assert p['name'] == b'hello'
assert p['curve'] == 'ed25519'
assert p['blob'] == _public_key_ed25519_cert_BLOB
assert p['fingerprint'] == '86:b6:17:3e:e1:5c:ba:e0:dc:86:80:b2:47:b4:ad:50' # nopep8
assert p['type'] == b'ssh-ed25519-cert-v01@openssh.com'
def test_export_ed25519():
pub = (b'\x00P]\x17kc}#\xbc\x9c\xb2"\xef~\xa2\xb3\xe7\xf4'
b'z\xba\xb6\xf1\x14\xdc\xec)\x0c\xd7SY\xb52\x91')

View File

@@ -3,10 +3,10 @@ from setuptools import setup
setup(
name='libagent',
version='0.14.7',
version='0.14.8',
description='Using hardware wallets as SSH/GPG agent',
author='Roman Zeyde',
author_email='roman.zeyde@gmail.com',
author_email='dev@romanzey.de',
url='http://github.com/romanz/trezor-agent',
packages=[
'libagent',
@@ -35,7 +35,7 @@ setup(
platforms=['POSIX'],
classifiers=[
'Environment :: Console',
'Development Status :: 4 - Beta',
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Intended Audience :: Information Technology',
'Intended Audience :: System Administrators',