mirror of
https://github.com/romanz/amodem.git
synced 2026-05-03 08:27:26 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28cbb941f1 | ||
|
|
0acc6cd2ef | ||
|
|
a247e877fc | ||
|
|
8cb323c550 | ||
|
|
d915d8594c | ||
|
|
473a565fc6 | ||
|
|
2b49eacc01 | ||
|
|
ba8a1ba8c8 | ||
|
|
bf7324ca89 | ||
|
|
890fd0bdfd | ||
|
|
1518b7bde0 | ||
|
|
23a48e41f7 | ||
|
|
97416308ed |
@@ -1,6 +1,6 @@
|
||||
[bumpversion]
|
||||
commit = True
|
||||
tag = True
|
||||
current_version = 0.14.7
|
||||
current_version = 0.14.8
|
||||
|
||||
[bumpversion:file:setup.py]
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: Build
|
||||
|
||||
on: [push]
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -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=[
|
||||
|
||||
@@ -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=[
|
||||
|
||||
@@ -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=[
|
||||
|
||||
@@ -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=[
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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)."""
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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')
|
||||
|
||||
6
setup.py
6
setup.py
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user