|
20 | 20 | from cryptography.hazmat.backends import default_backend |
21 | 21 | from cryptography.hazmat.primitives.asymmetric.dsa import DSAParameterNumbers, DSAPublicNumbers |
22 | 22 | from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers |
| 23 | +from cryptography.hazmat.primitives.asymmetric.utils import Prehashed |
| 24 | +from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat |
| 25 | +from cryptography.hazmat.primitives.asymmetric import ec |
| 26 | +from cryptography.hazmat.primitives import hashes |
23 | 27 | from urllib.parse import urlparse |
24 | 28 |
|
25 | 29 | import base64 |
26 | 30 | import binascii |
27 | | -import ecdsa |
28 | 31 | import hashlib |
29 | 32 | import re |
30 | 33 | import struct |
|
34 | 37 | __all__ = ["AuthorizedKeysFile", "SSHKey"] |
35 | 38 |
|
36 | 39 |
|
| 40 | +class _ECVerifyingKey: |
| 41 | + """ecdsa.key.VerifyingKey reimplementation |
| 42 | + """ |
| 43 | + def __init__(self, pubkey, default_hashfunc): |
| 44 | + self.pubkey = pubkey |
| 45 | + self.default_hashfunc = default_hashfunc |
| 46 | + |
| 47 | + @property |
| 48 | + def curve(self): |
| 49 | + """Curve instance""" |
| 50 | + return self.pubkey.curve |
| 51 | + |
| 52 | + def __repr__(self): |
| 53 | + pub_key = self.to_string("compressed") |
| 54 | + self.to_string("raw") |
| 55 | + return "VerifyingKey({0!r}, {1!r}, {2})".format( |
| 56 | + pub_key, self.curve.name, self.default_hashfunc.name |
| 57 | + ) |
| 58 | + |
| 59 | + def to_string(self, encoding="raw"): |
| 60 | + """Pub key as bytes string""" |
| 61 | + if encoding == "raw": |
| 62 | + return self.pubkey.public_numbers().encode_point()[1:] |
| 63 | + elif encoding == "uncompressed": |
| 64 | + return self.pubkey.public_numbers().encode_point() |
| 65 | + elif encoding == "compressed": |
| 66 | + return self.pubkey.public_bytes(Encoding.X962, PublicFormat.CompressedPoint) |
| 67 | + else: |
| 68 | + raise ValueError(encoding) |
| 69 | + |
| 70 | + def to_pem(self, point_encoding="uncompressed"): |
| 71 | + """Pub key as PEM""" |
| 72 | + if point_encoding != "uncompressed": |
| 73 | + raise ValueError(point_encoding) |
| 74 | + return self.pubkey.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo) |
| 75 | + |
| 76 | + def to_der(self, point_encoding="uncompressed"): |
| 77 | + """Pub key as ASN.1/DER""" |
| 78 | + if point_encoding != "uncompressed": |
| 79 | + raise ValueError(point_encoding) |
| 80 | + return self.pubkey.public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo) |
| 81 | + |
| 82 | + def verify(self, signature, data): |
| 83 | + """Verify signature of provided data""" |
| 84 | + return self.pubkey.verify(signature, data, ec.ECDSA(self.default_hashfunc)) |
| 85 | + |
| 86 | + def verify_digest(self, signature, digest): |
| 87 | + """Verify signature over prehashed digest""" |
| 88 | + return self.pubkey.verify(signature, data, ec.ECDSA(Prehashed(digest))) |
| 89 | + |
| 90 | + |
37 | 91 | class AuthorizedKeysFile: # pylint:disable=too-few-public-methods |
38 | 92 | """Represents a full authorized_keys file. |
39 | 93 |
|
@@ -73,11 +127,11 @@ class SSHKey: # pylint:disable=too-many-instance-attributes |
73 | 127 | DSA_N_LENGTH = 160 |
74 | 128 |
|
75 | 129 | ECDSA_CURVE_DATA = { |
76 | | - b"nistp256": (ecdsa.curves.NIST256p, hashlib.sha256), |
77 | | - b"nistp192": (ecdsa.curves.NIST192p, hashlib.sha256), |
78 | | - b"nistp224": (ecdsa.curves.NIST224p, hashlib.sha256), |
79 | | - b"nistp384": (ecdsa.curves.NIST384p, hashlib.sha384), |
80 | | - b"nistp521": (ecdsa.curves.NIST521p, hashlib.sha512), |
| 130 | + b"nistp256": (ec.SECP256R1(), hashes.SHA256()), |
| 131 | + b"nistp192": (ec.SECP192R1(), hashes.SHA256()), |
| 132 | + b"nistp224": (ec.SECP224R1(), hashes.SHA256()), |
| 133 | + b"nistp384": (ec.SECP384R1(), hashes.SHA384()), |
| 134 | + b"nistp521": (ec.SECP521R1(), hashes.SHA512()) |
81 | 135 | } |
82 | 136 |
|
83 | 137 | RSA_MIN_LENGTH_STRICT = 1024 |
@@ -368,12 +422,13 @@ def _process_ecdsa_sha(self, data): |
368 | 422 |
|
369 | 423 | current_position, key_data = self._unpack_by_int(data, current_position) |
370 | 424 | try: |
371 | | - # data starts with \x04, which should be discarded. |
372 | | - ecdsa_key = ecdsa.VerifyingKey.from_string(key_data[1:], curve, hash_algorithm) |
373 | | - except AssertionError as ex: |
| 425 | + ecdsa_pubkey = ec.EllipticCurvePublicKey.from_encoded_point( |
| 426 | + curve, key_data |
| 427 | + ) |
| 428 | + except ValueError as ex: |
374 | 429 | raise InvalidKeyError("Invalid ecdsa key") from ex |
375 | | - self.bits = int(curve_information.replace(b"nistp", b"")) |
376 | | - self.ecdsa = ecdsa_key |
| 430 | + self.bits = curve.key_size |
| 431 | + self.ecdsa = _ECVerifyingKey(ecdsa_pubkey, hash_algorithm) |
377 | 432 | return current_position |
378 | 433 |
|
379 | 434 | def _process_ed25516(self, data): |
|
0 commit comments