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 "
130 "PrivateFormat.PKCS12"
131 )
132 if self._key_cert_algorithm is not None:
133 raise ValueError("key_cert_algorithm already set")
134 return KeySerializationEncryptionBuilder(
135 self._format,
136 _kdf_rounds=self._kdf_rounds,
137 _hmac_hash=self._hmac_hash,
138 _key_cert_algorithm=algorithm,
139 )
140
141 def build(self, password: bytes) -> KeySerializationEncryption:
142 if not isinstance(password, bytes) or len(password) == 0:
143 raise ValueError("Password must be 1 or more bytes.")
144
145 return _KeySerializationEncryption(
146 self._format,
147 password,
148 kdf_rounds=self._kdf_rounds,
149 hmac_hash=self._hmac_hash,
150 key_cert_algorithm=self._key_cert_algorithm,
151 )
152
153
154class _KeySerializationEncryption(KeySerializationEncryption):
155 def __init__(
156 self,
157 format: PrivateFormat,
158 password: bytes,
159 *,
160 kdf_rounds: int | None,
161 hmac_hash: HashAlgorithm | None,
162 key_cert_algorithm: PBES | None,
163 ):
164 self._format = format
165 self.password = password
166
167 self._kdf_rounds = kdf_rounds
168 self._hmac_hash = hmac_hash
169 self._key_cert_algorithm = key_cert_algorithm