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
5from __future__ import annotations
6
7import abc
8
9from cryptography import utils
10from cryptography.hazmat.primitives.hashes import HashAlgorithm
11
12# This exists to break an import cycle. These classes are normally accessible
13# from the serialization module.
14
15
16class PBES(utils.Enum):
17 PBESv1SHA1And3KeyTripleDESCBC = "PBESv1 using SHA1 and 3-Key TripleDES"
18 PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC"
19
20
21class Encoding(utils.Enum):
22 PEM = "PEM"
23 DER = "DER"
24 OpenSSH = "OpenSSH"
25 Raw = "Raw"
26 X962 = "ANSI X9.62"
27 SMIME = "S/MIME"
28
29
30class PrivateFormat(utils.Enum):
31 PKCS8 = "PKCS8"
32 TraditionalOpenSSL = "TraditionalOpenSSL"
33 Raw = "Raw"
34 OpenSSH = "OpenSSH"
35 PKCS12 = "PKCS12"
36
37 def encryption_builder(self) -> KeySerializationEncryptionBuilder:
38 if self not in (PrivateFormat.OpenSSH, PrivateFormat.PKCS12):
39 raise ValueError(
40 "encryption_builder only supported with PrivateFormat.OpenSSH"
41 " and PrivateFormat.PKCS12"
42 )
43 return KeySerializationEncryptionBuilder(self)
44
45
46class PublicFormat(utils.Enum):
47 SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1"
48 PKCS1 = "Raw PKCS#1"
49 OpenSSH = "OpenSSH"
50 Raw = "Raw"
51 CompressedPoint = "X9.62 Compressed Point"
52 UncompressedPoint = "X9.62 Uncompressed Point"
53
54
55class ParameterFormat(utils.Enum):
56 PKCS3 = "PKCS3"
57
58
59class KeySerializationEncryption(metaclass=abc.ABCMeta):
60 pass
61
62
63class BestAvailableEncryption(KeySerializationEncryption):
64 def __init__(self, password: bytes):
65 if not isinstance(password, bytes) or len(password) == 0:
66 raise ValueError("Password must be 1 or more bytes.")
67
68 self.password = password
69
70
71class NoEncryption(KeySerializationEncryption):
72 pass
73
74
75class KeySerializationEncryptionBuilder:
76 def __init__(
77 self,
78 format: PrivateFormat,
79 *,
80 _kdf_rounds: int | None = None,
81 _hmac_hash: HashAlgorithm | None = None,
82 _key_cert_algorithm: PBES | None = None,
83 ) -> None:
84 self._format = format
85
86 self._kdf_rounds = _kdf_rounds
87 self._hmac_hash = _hmac_hash
88 self._key_cert_algorithm = _key_cert_algorithm
89
90 def kdf_rounds(self, rounds: int) -> KeySerializationEncryptionBuilder:
91 if self._kdf_rounds is not None:
92 raise ValueError("kdf_rounds already set")
93
94 if not isinstance(rounds, int):
95 raise TypeError("kdf_rounds must be an integer")
96
97 if rounds < 1:
98 raise ValueError("kdf_rounds must be a positive integer")
99
100 return KeySerializationEncryptionBuilder(
101 self._format,
102 _kdf_rounds=rounds,
103 _hmac_hash=self._hmac_hash,
104 _key_cert_algorithm=self._key_cert_algorithm,
105 )
106
107 def hmac_hash(
108 self, algorithm: HashAlgorithm
109 ) -> KeySerializationEncryptionBuilder:
110 if self._format is not PrivateFormat.PKCS12:
111 raise TypeError(
112 "hmac_hash only supported with PrivateFormat.PKCS12"
113 )
114
115 if self._hmac_hash is not None:
116 raise ValueError("hmac_hash already set")
117 return KeySerializationEncryptionBuilder(
118 self._format,
119 _kdf_rounds=self._kdf_rounds,
120 _hmac_hash=algorithm,
121 _key_cert_algorithm=self._key_cert_algorithm,
122 )
123
124 def key_cert_algorithm(
125 self, algorithm: PBES
126 ) -> KeySerializationEncryptionBuilder:
127 if self._format is not PrivateFormat.PKCS12:
128 raise TypeError(
129 "key_cert_algorithm only supported with PrivateFormat.PKCS12"
130 )
131 if self._key_cert_algorithm is not None:
132 raise ValueError("key_cert_algorithm already set")
133 return KeySerializationEncryptionBuilder(
134 self._format,
135 _kdf_rounds=self._kdf_rounds,
136 _hmac_hash=self._hmac_hash,
137 _key_cert_algorithm=algorithm,
138 )
139
140 def build(self, password: bytes) -> KeySerializationEncryption:
141 if not isinstance(password, bytes) or len(password) == 0:
142 raise ValueError("Password must be 1 or more bytes.")
143
144 return _KeySerializationEncryption(
145 self._format,
146 password,
147 kdf_rounds=self._kdf_rounds,
148 hmac_hash=self._hmac_hash,
149 key_cert_algorithm=self._key_cert_algorithm,
150 )
151
152
153class _KeySerializationEncryption(KeySerializationEncryption):
154 def __init__(
155 self,
156 format: PrivateFormat,
157 password: bytes,
158 *,
159 kdf_rounds: int | None,
160 hmac_hash: HashAlgorithm | None,
161 key_cert_algorithm: PBES | None,
162 ):
163 self._format = format
164 self.password = password
165
166 self._kdf_rounds = kdf_rounds
167 self._hmac_hash = hmac_hash
168 self._key_cert_algorithm = key_cert_algorithm