Skip to content

Commit f5940f0

Browse files
authored
add serialization type hinting (#5718)
* add serialization type hinting * reorganize to prevent circular dependency * review feedback * damn you black
1 parent 4372d3f commit f5940f0

File tree

6 files changed

+135
-49
lines changed

6 files changed

+135
-49
lines changed

src/cryptography/hazmat/_types.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# This file is dual licensed under the terms of the Apache License, Version
2+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3+
# for complete details.
4+
5+
import typing
6+
7+
from cryptography.hazmat.primitives.asymmetric import (
8+
dsa,
9+
ec,
10+
ed25519,
11+
ed448,
12+
rsa,
13+
)
14+
15+
16+
_PUBLIC_KEY_TYPES = typing.Union[
17+
dsa.DSAPublicKey,
18+
rsa.RSAPublicKey,
19+
ec.EllipticCurvePublicKey,
20+
ed25519.Ed25519PublicKey,
21+
ed448.Ed448PublicKey,
22+
]
23+
_PRIVATE_KEY_TYPES = typing.Union[
24+
ed25519.Ed25519PrivateKey,
25+
ed448.Ed448PrivateKey,
26+
rsa.RSAPrivateKey,
27+
dsa.DSAPrivateKey,
28+
ec.EllipticCurvePrivateKey,
29+
]

src/cryptography/hazmat/primitives/serialization/base.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,44 @@
44

55

66
import abc
7+
import typing
78
from enum import Enum
89

9-
from cryptography import utils
10+
from cryptography.hazmat._types import _PRIVATE_KEY_TYPES, _PUBLIC_KEY_TYPES
1011
from cryptography.hazmat.backends import _get_backend
12+
from cryptography.hazmat.primitives.asymmetric import dh
1113

1214

13-
def load_pem_private_key(data, password, backend=None):
15+
def load_pem_private_key(
16+
data: bytes, password: typing.Optional[bytes], backend=None
17+
) -> _PRIVATE_KEY_TYPES:
1418
backend = _get_backend(backend)
1519
return backend.load_pem_private_key(data, password)
1620

1721

18-
def load_pem_public_key(data, backend=None):
22+
def load_pem_public_key(data: bytes, backend=None) -> _PUBLIC_KEY_TYPES:
1923
backend = _get_backend(backend)
2024
return backend.load_pem_public_key(data)
2125

2226

23-
def load_pem_parameters(data, backend=None):
27+
def load_pem_parameters(data: bytes, backend=None) -> dh.DHParameters:
2428
backend = _get_backend(backend)
2529
return backend.load_pem_parameters(data)
2630

2731

28-
def load_der_private_key(data, password, backend=None):
32+
def load_der_private_key(
33+
data: bytes, password: typing.Optional[bytes], backend=None
34+
) -> _PRIVATE_KEY_TYPES:
2935
backend = _get_backend(backend)
3036
return backend.load_der_private_key(data, password)
3137

3238

33-
def load_der_public_key(data, backend=None):
39+
def load_der_public_key(data: bytes, backend=None) -> _PUBLIC_KEY_TYPES:
3440
backend = _get_backend(backend)
3541
return backend.load_der_public_key(data)
3642

3743

38-
def load_der_parameters(data, backend=None):
44+
def load_der_parameters(data: bytes, backend=None) -> dh.DHParameters:
3945
backend = _get_backend(backend)
4046
return backend.load_der_parameters(data)
4147

@@ -73,15 +79,13 @@ class KeySerializationEncryption(metaclass=abc.ABCMeta):
7379
pass
7480

7581

76-
@utils.register_interface(KeySerializationEncryption)
77-
class BestAvailableEncryption(object):
78-
def __init__(self, password):
82+
class BestAvailableEncryption(KeySerializationEncryption):
83+
def __init__(self, password: bytes):
7984
if not isinstance(password, bytes) or len(password) == 0:
8085
raise ValueError("Password must be 1 or more bytes.")
8186

8287
self.password = password
8388

8489

85-
@utils.register_interface(KeySerializationEncryption)
86-
class NoEncryption(object):
90+
class NoEncryption(KeySerializationEncryption):
8791
pass

src/cryptography/hazmat/primitives/serialization/pkcs12.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,35 @@
22
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
33
# for complete details.
44

5+
import typing
56

67
from cryptography import x509
78
from cryptography.hazmat.backends import _get_backend
89
from cryptography.hazmat.primitives import serialization
910
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
1011

1112

12-
def load_key_and_certificates(data, password, backend=None):
13+
def load_key_and_certificates(
14+
data: bytes, password: typing.Optional[bytes], backend=None
15+
):
1316
backend = _get_backend(backend)
1417
return backend.load_key_and_certificates_from_pkcs12(data, password)
1518

1619

17-
def serialize_key_and_certificates(name, key, cert, cas, encryption_algorithm):
20+
_ALLOWED_PKCS12_TYPES = typing.Union[
21+
rsa.RSAPrivateKeyWithSerialization,
22+
dsa.DSAPrivateKeyWithSerialization,
23+
ec.EllipticCurvePrivateKeyWithSerialization,
24+
]
25+
26+
27+
def serialize_key_and_certificates(
28+
name: bytes,
29+
key: typing.Optional[_ALLOWED_PKCS12_TYPES],
30+
cert: typing.Optional[x509.Certificate],
31+
cas: typing.Optional[typing.Iterable[x509.Certificate]],
32+
encryption_algorithm: serialization.KeySerializationEncryption,
33+
):
1834
if key is not None and not isinstance(
1935
key,
2036
(

src/cryptography/hazmat/primitives/serialization/pkcs7.py

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
33
# for complete details.
44

5-
5+
import typing
66
from enum import Enum
77

88
from cryptography import x509
@@ -12,30 +12,57 @@
1212
from cryptography.utils import _check_byteslike
1313

1414

15-
def load_pem_pkcs7_certificates(data):
15+
def load_pem_pkcs7_certificates(data: bytes) -> typing.List[x509.Certificate]:
1616
backend = _get_backend(None)
1717
return backend.load_pem_pkcs7_certificates(data)
1818

1919

20-
def load_der_pkcs7_certificates(data):
20+
def load_der_pkcs7_certificates(data: bytes) -> typing.List[x509.Certificate]:
2121
backend = _get_backend(None)
2222
return backend.load_der_pkcs7_certificates(data)
2323

2424

25+
_ALLOWED_PKCS7_HASH_TYPES = typing.Union[
26+
hashes.SHA1,
27+
hashes.SHA224,
28+
hashes.SHA256,
29+
hashes.SHA384,
30+
hashes.SHA512,
31+
]
32+
33+
_ALLOWED_PRIVATE_KEY_TYPES = typing.Union[
34+
rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey
35+
]
36+
37+
38+
class PKCS7Options(Enum):
39+
Text = "Add text/plain MIME type"
40+
Binary = "Don't translate input data into canonical MIME format"
41+
DetachedSignature = "Don't embed data in the PKCS7 structure"
42+
NoCapabilities = "Don't embed SMIME capabilities"
43+
NoAttributes = "Don't embed authenticatedAttributes"
44+
NoCerts = "Don't embed signer certificate"
45+
46+
2547
class PKCS7SignatureBuilder(object):
2648
def __init__(self, data=None, signers=[], additional_certs=[]):
2749
self._data = data
2850
self._signers = signers
2951
self._additional_certs = additional_certs
3052

31-
def set_data(self, data):
53+
def set_data(self, data: bytes) -> "PKCS7SignatureBuilder":
3254
_check_byteslike("data", data)
3355
if self._data is not None:
3456
raise ValueError("data may only be set once")
3557

3658
return PKCS7SignatureBuilder(data, self._signers)
3759

38-
def add_signer(self, certificate, private_key, hash_algorithm):
60+
def add_signer(
61+
self,
62+
certificate: x509.Certificate,
63+
private_key: _ALLOWED_PRIVATE_KEY_TYPES,
64+
hash_algorithm: _ALLOWED_PKCS7_HASH_TYPES,
65+
) -> "PKCS7SignatureBuilder":
3966
if not isinstance(
4067
hash_algorithm,
4168
(
@@ -63,15 +90,22 @@ def add_signer(self, certificate, private_key, hash_algorithm):
6390
self._signers + [(certificate, private_key, hash_algorithm)],
6491
)
6592

66-
def add_certificate(self, certificate):
93+
def add_certificate(
94+
self, certificate: x509.Certificate
95+
) -> "PKCS7SignatureBuilder":
6796
if not isinstance(certificate, x509.Certificate):
6897
raise TypeError("certificate must be a x509.Certificate")
6998

7099
return PKCS7SignatureBuilder(
71100
self._data, self._signers, self._additional_certs + [certificate]
72101
)
73102

74-
def sign(self, encoding, options, backend=None):
103+
def sign(
104+
self,
105+
encoding: serialization.Encoding,
106+
options: typing.Iterable[PKCS7Options],
107+
backend=None,
108+
) -> bytes:
75109
if len(self._signers) == 0:
76110
raise ValueError("Must have at least one signer")
77111
if self._data is None:
@@ -120,12 +154,3 @@ def sign(self, encoding, options, backend=None):
120154

121155
backend = _get_backend(backend)
122156
return backend.pkcs7_sign(self, encoding, options)
123-
124-
125-
class PKCS7Options(Enum):
126-
Text = "Add text/plain MIME type"
127-
Binary = "Don't translate input data into canonical MIME format"
128-
DetachedSignature = "Don't embed data in the PKCS7 structure"
129-
NoCapabilities = "Don't embed SMIME capabilities"
130-
NoAttributes = "Don't embed authenticatedAttributes"
131-
NoCerts = "Don't embed signer certificate"

src/cryptography/hazmat/primitives/serialization/ssh.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import re
99
import struct
10+
import typing
1011
from base64 import encodebytes as _base64_encode
1112

1213
from cryptography import utils
@@ -464,7 +465,17 @@ def _lookup_kformat(key_type):
464465
raise UnsupportedAlgorithm("Unsupported key type: %r" % key_type)
465466

466467

467-
def load_ssh_private_key(data, password, backend=None):
468+
_SSH_PRIVATE_KEY_TYPES = typing.Union[
469+
ec.EllipticCurvePrivateKey,
470+
rsa.RSAPrivateKey,
471+
dsa.DSAPrivateKey,
472+
ed25519.Ed25519PrivateKey,
473+
]
474+
475+
476+
def load_ssh_private_key(
477+
data: bytes, password: typing.Optional[bytes], backend=None
478+
) -> _SSH_PRIVATE_KEY_TYPES:
468479
"""Load private key from OpenSSH custom encoding."""
469480
utils._check_byteslike("data", data)
470481
backend = _get_backend(backend)
@@ -538,7 +549,10 @@ def load_ssh_private_key(data, password, backend=None):
538549
return private_key
539550

540551

541-
def serialize_ssh_private_key(private_key, password=None):
552+
def serialize_ssh_private_key(
553+
private_key: _SSH_PRIVATE_KEY_TYPES,
554+
password: typing.Optional[bytes] = None,
555+
):
542556
"""Serialize private key with OpenSSH custom encoding."""
543557
if password is not None:
544558
utils._check_bytes("password", password)
@@ -613,11 +627,22 @@ def serialize_ssh_private_key(private_key, password=None):
613627
ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:])
614628

615629
txt = _ssh_pem_encode(buf[:mlen])
616-
buf[ofs:mlen] = bytearray(slen)
630+
# Ignore the following type because mypy wants
631+
# Sequence[bytes] but what we're passing is fine.
632+
# https://github.com/python/mypy/issues/9999
633+
buf[ofs:mlen] = bytearray(slen) # type: ignore
617634
return txt
618635

619636

620-
def load_ssh_public_key(data, backend=None):
637+
_SSH_PUBLIC_KEY_TYPES = typing.Union[
638+
ec.EllipticCurvePublicKey,
639+
rsa.RSAPublicKey,
640+
dsa.DSAPublicKey,
641+
ed25519.Ed25519PublicKey,
642+
]
643+
644+
645+
def load_ssh_public_key(data: bytes, backend=None) -> _SSH_PUBLIC_KEY_TYPES:
621646
"""Load public key from OpenSSH one-line format."""
622647
backend = _get_backend(backend)
623648
utils._check_byteslike("data", data)
@@ -660,7 +685,7 @@ def load_ssh_public_key(data, backend=None):
660685
return public_key
661686

662687

663-
def serialize_ssh_public_key(public_key):
688+
def serialize_ssh_public_key(public_key: _SSH_PUBLIC_KEY_TYPES) -> bytes:
664689
"""One-line public key format for OpenSSH"""
665690
if isinstance(public_key, ec.EllipticCurvePublicKey):
666691
key_type = _ecdsa_key_type(public_key)

src/cryptography/x509/base.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import typing
1010
from enum import Enum
1111

12+
from cryptography.hazmat._types import _PRIVATE_KEY_TYPES, _PUBLIC_KEY_TYPES
1213
from cryptography.hazmat.backends import _get_backend
1314
from cryptography.hazmat.primitives import hashes, serialization
1415
from cryptography.hazmat.primitives.asymmetric import (
@@ -24,20 +25,6 @@
2425

2526

2627
_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1)
27-
_PUBLIC_KEY_TYPES = typing.Union[
28-
dsa.DSAPublicKey,
29-
rsa.RSAPublicKey,
30-
ec.EllipticCurvePublicKey,
31-
ed25519.Ed25519PublicKey,
32-
ed448.Ed448PublicKey,
33-
]
34-
_PRIVATE_KEY_TYPES = typing.Union[
35-
ed25519.Ed25519PrivateKey,
36-
ed448.Ed448PrivateKey,
37-
rsa.RSAPrivateKey,
38-
dsa.DSAPrivateKey,
39-
ec.EllipticCurvePrivateKey,
40-
]
4128

4229

4330
class AttributeNotFound(Exception):

0 commit comments

Comments
 (0)