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.exceptions import UnsupportedAlgorithm, _Reasons
10from cryptography.hazmat.bindings._rust import openssl as rust_openssl
11from cryptography.hazmat.primitives import _serialization
12from cryptography.utils import Buffer
13
14
15class MLDSA44PublicKey(metaclass=abc.ABCMeta):
16 @classmethod
17 def from_public_bytes(cls, data: bytes) -> MLDSA44PublicKey:
18 from cryptography.hazmat.backends.openssl.backend import backend
19
20 if not backend.mldsa_supported():
21 raise UnsupportedAlgorithm(
22 "ML-DSA-44 is not supported by this backend.",
23 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
24 )
25
26 return rust_openssl.mldsa.from_mldsa44_public_bytes(data)
27
28 @abc.abstractmethod
29 def public_bytes(
30 self,
31 encoding: _serialization.Encoding,
32 format: _serialization.PublicFormat,
33 ) -> bytes:
34 """
35 The serialized bytes of the public key.
36 """
37
38 @abc.abstractmethod
39 def public_bytes_raw(self) -> bytes:
40 """
41 The raw bytes of the public key.
42 Equivalent to public_bytes(Raw, Raw).
43
44 The public key is 1,312 bytes for MLDSA-44.
45 """
46
47 @abc.abstractmethod
48 def verify(
49 self,
50 signature: Buffer,
51 data: Buffer,
52 context: Buffer | None = None,
53 ) -> None:
54 """
55 Verify the signature.
56 """
57
58 @abc.abstractmethod
59 def verify_mu(
60 self,
61 signature: Buffer,
62 mu: Buffer,
63 ) -> None:
64 """
65 Verify the signature over a precomputed mu (message representative).
66
67 mu must be 64 bytes.
68 """
69
70 @abc.abstractmethod
71 def __eq__(self, other: object) -> bool:
72 """
73 Checks equality.
74 """
75
76 @abc.abstractmethod
77 def __copy__(self) -> MLDSA44PublicKey:
78 """
79 Returns a copy.
80 """
81
82 @abc.abstractmethod
83 def __deepcopy__(self, memo: dict) -> MLDSA44PublicKey:
84 """
85 Returns a deep copy.
86 """
87
88
89if hasattr(rust_openssl, "mldsa"):
90 MLDSA44PublicKey.register(rust_openssl.mldsa.MLDSA44PublicKey)
91
92
93class MLDSA44PrivateKey(metaclass=abc.ABCMeta):
94 @classmethod
95 def generate(cls) -> MLDSA44PrivateKey:
96 from cryptography.hazmat.backends.openssl.backend import backend
97
98 if not backend.mldsa_supported():
99 raise UnsupportedAlgorithm(
100 "ML-DSA-44 is not supported by this backend.",
101 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
102 )
103
104 return rust_openssl.mldsa.generate_mldsa44_key()
105
106 @classmethod
107 def from_seed_bytes(cls, data: Buffer) -> MLDSA44PrivateKey:
108 from cryptography.hazmat.backends.openssl.backend import backend
109
110 if not backend.mldsa_supported():
111 raise UnsupportedAlgorithm(
112 "ML-DSA-44 is not supported by this backend.",
113 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
114 )
115
116 return rust_openssl.mldsa.from_mldsa44_seed_bytes(data)
117
118 @abc.abstractmethod
119 def public_key(self) -> MLDSA44PublicKey:
120 """
121 The MLDSA44PublicKey derived from the private key.
122 """
123
124 @abc.abstractmethod
125 def private_bytes(
126 self,
127 encoding: _serialization.Encoding,
128 format: _serialization.PrivateFormat,
129 encryption_algorithm: _serialization.KeySerializationEncryption,
130 ) -> bytes:
131 """
132 The serialized bytes of the private key.
133
134 This method only returns the serialization of the seed form of the
135 private key, never the expanded one.
136 """
137
138 @abc.abstractmethod
139 def private_bytes_raw(self) -> bytes:
140 """
141 The raw bytes of the private key.
142 Equivalent to private_bytes(Raw, Raw, NoEncryption()).
143
144 This method only returns the seed form of the private key (32 bytes).
145 """
146
147 @abc.abstractmethod
148 def sign(self, data: Buffer, context: Buffer | None = None) -> bytes:
149 """
150 Signs the data.
151 """
152
153 @abc.abstractmethod
154 def sign_mu(self, mu: Buffer) -> bytes:
155 """
156 Signs a precomputed mu (message representative).
157
158 mu must be 64 bytes and already incorporates the context, so no
159 context is accepted here.
160 """
161
162 @abc.abstractmethod
163 def __copy__(self) -> MLDSA44PrivateKey:
164 """
165 Returns a copy.
166 """
167
168 @abc.abstractmethod
169 def __deepcopy__(self, memo: dict) -> MLDSA44PrivateKey:
170 """
171 Returns a deep copy.
172 """
173
174
175if hasattr(rust_openssl, "mldsa"):
176 MLDSA44PrivateKey.register(rust_openssl.mldsa.MLDSA44PrivateKey)
177
178
179class MLDSA65PublicKey(metaclass=abc.ABCMeta):
180 @classmethod
181 def from_public_bytes(cls, data: bytes) -> MLDSA65PublicKey:
182 from cryptography.hazmat.backends.openssl.backend import backend
183
184 if not backend.mldsa_supported():
185 raise UnsupportedAlgorithm(
186 "ML-DSA-65 is not supported by this backend.",
187 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
188 )
189
190 return rust_openssl.mldsa.from_mldsa65_public_bytes(data)
191
192 @abc.abstractmethod
193 def public_bytes(
194 self,
195 encoding: _serialization.Encoding,
196 format: _serialization.PublicFormat,
197 ) -> bytes:
198 """
199 The serialized bytes of the public key.
200 """
201
202 @abc.abstractmethod
203 def public_bytes_raw(self) -> bytes:
204 """
205 The raw bytes of the public key.
206 Equivalent to public_bytes(Raw, Raw).
207
208 The public key is 1,952 bytes for MLDSA-65.
209 """
210
211 @abc.abstractmethod
212 def verify(
213 self,
214 signature: Buffer,
215 data: Buffer,
216 context: Buffer | None = None,
217 ) -> None:
218 """
219 Verify the signature.
220 """
221
222 @abc.abstractmethod
223 def verify_mu(
224 self,
225 signature: Buffer,
226 mu: Buffer,
227 ) -> None:
228 """
229 Verify the signature over a precomputed mu (message representative).
230
231 mu must be 64 bytes.
232 """
233
234 @abc.abstractmethod
235 def __eq__(self, other: object) -> bool:
236 """
237 Checks equality.
238 """
239
240 @abc.abstractmethod
241 def __copy__(self) -> MLDSA65PublicKey:
242 """
243 Returns a copy.
244 """
245
246 @abc.abstractmethod
247 def __deepcopy__(self, memo: dict) -> MLDSA65PublicKey:
248 """
249 Returns a deep copy.
250 """
251
252
253if hasattr(rust_openssl, "mldsa"):
254 MLDSA65PublicKey.register(rust_openssl.mldsa.MLDSA65PublicKey)
255
256
257class MLDSA65PrivateKey(metaclass=abc.ABCMeta):
258 @classmethod
259 def generate(cls) -> MLDSA65PrivateKey:
260 from cryptography.hazmat.backends.openssl.backend import backend
261
262 if not backend.mldsa_supported():
263 raise UnsupportedAlgorithm(
264 "ML-DSA-65 is not supported by this backend.",
265 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
266 )
267
268 return rust_openssl.mldsa.generate_mldsa65_key()
269
270 @classmethod
271 def from_seed_bytes(cls, data: Buffer) -> MLDSA65PrivateKey:
272 from cryptography.hazmat.backends.openssl.backend import backend
273
274 if not backend.mldsa_supported():
275 raise UnsupportedAlgorithm(
276 "ML-DSA-65 is not supported by this backend.",
277 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
278 )
279
280 return rust_openssl.mldsa.from_mldsa65_seed_bytes(data)
281
282 @abc.abstractmethod
283 def public_key(self) -> MLDSA65PublicKey:
284 """
285 The MLDSA65PublicKey derived from the private key.
286 """
287
288 @abc.abstractmethod
289 def private_bytes(
290 self,
291 encoding: _serialization.Encoding,
292 format: _serialization.PrivateFormat,
293 encryption_algorithm: _serialization.KeySerializationEncryption,
294 ) -> bytes:
295 """
296 The serialized bytes of the private key.
297
298 This method only returns the serialization of the seed form of the
299 private key, never the expanded one.
300 """
301
302 @abc.abstractmethod
303 def private_bytes_raw(self) -> bytes:
304 """
305 The raw bytes of the private key.
306 Equivalent to private_bytes(Raw, Raw, NoEncryption()).
307
308 This method only returns the seed form of the private key (32 bytes).
309 """
310
311 @abc.abstractmethod
312 def sign(self, data: Buffer, context: Buffer | None = None) -> bytes:
313 """
314 Signs the data.
315 """
316
317 @abc.abstractmethod
318 def sign_mu(self, mu: Buffer) -> bytes:
319 """
320 Signs a precomputed mu (message representative).
321
322 mu must be 64 bytes and already incorporates the context, so no
323 context is accepted here.
324 """
325
326 @abc.abstractmethod
327 def __copy__(self) -> MLDSA65PrivateKey:
328 """
329 Returns a copy.
330 """
331
332 @abc.abstractmethod
333 def __deepcopy__(self, memo: dict) -> MLDSA65PrivateKey:
334 """
335 Returns a deep copy.
336 """
337
338
339if hasattr(rust_openssl, "mldsa"):
340 MLDSA65PrivateKey.register(rust_openssl.mldsa.MLDSA65PrivateKey)
341
342
343class MLDSA87PublicKey(metaclass=abc.ABCMeta):
344 @classmethod
345 def from_public_bytes(cls, data: bytes) -> MLDSA87PublicKey:
346 from cryptography.hazmat.backends.openssl.backend import backend
347
348 if not backend.mldsa_supported():
349 raise UnsupportedAlgorithm(
350 "ML-DSA-87 is not supported by this backend.",
351 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
352 )
353
354 return rust_openssl.mldsa.from_mldsa87_public_bytes(data)
355
356 @abc.abstractmethod
357 def public_bytes(
358 self,
359 encoding: _serialization.Encoding,
360 format: _serialization.PublicFormat,
361 ) -> bytes:
362 """
363 The serialized bytes of the public key.
364 """
365
366 @abc.abstractmethod
367 def public_bytes_raw(self) -> bytes:
368 """
369 The raw bytes of the public key.
370 Equivalent to public_bytes(Raw, Raw).
371
372 The public key is 2,592 bytes for MLDSA-87.
373 """
374
375 @abc.abstractmethod
376 def verify(
377 self,
378 signature: Buffer,
379 data: Buffer,
380 context: Buffer | None = None,
381 ) -> None:
382 """
383 Verify the signature.
384 """
385
386 @abc.abstractmethod
387 def verify_mu(
388 self,
389 signature: Buffer,
390 mu: Buffer,
391 ) -> None:
392 """
393 Verify the signature over a precomputed mu (message representative).
394
395 mu must be 64 bytes.
396 """
397
398 @abc.abstractmethod
399 def __eq__(self, other: object) -> bool:
400 """
401 Checks equality.
402 """
403
404 @abc.abstractmethod
405 def __copy__(self) -> MLDSA87PublicKey:
406 """
407 Returns a copy.
408 """
409
410 @abc.abstractmethod
411 def __deepcopy__(self, memo: dict) -> MLDSA87PublicKey:
412 """
413 Returns a deep copy.
414 """
415
416
417if hasattr(rust_openssl, "mldsa"):
418 MLDSA87PublicKey.register(rust_openssl.mldsa.MLDSA87PublicKey)
419
420
421class MLDSA87PrivateKey(metaclass=abc.ABCMeta):
422 @classmethod
423 def generate(cls) -> MLDSA87PrivateKey:
424 from cryptography.hazmat.backends.openssl.backend import backend
425
426 if not backend.mldsa_supported():
427 raise UnsupportedAlgorithm(
428 "ML-DSA-87 is not supported by this backend.",
429 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
430 )
431
432 return rust_openssl.mldsa.generate_mldsa87_key()
433
434 @classmethod
435 def from_seed_bytes(cls, data: Buffer) -> MLDSA87PrivateKey:
436 from cryptography.hazmat.backends.openssl.backend import backend
437
438 if not backend.mldsa_supported():
439 raise UnsupportedAlgorithm(
440 "ML-DSA-87 is not supported by this backend.",
441 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
442 )
443
444 return rust_openssl.mldsa.from_mldsa87_seed_bytes(data)
445
446 @abc.abstractmethod
447 def public_key(self) -> MLDSA87PublicKey:
448 """
449 The MLDSA87PublicKey derived from the private key.
450 """
451
452 @abc.abstractmethod
453 def private_bytes(
454 self,
455 encoding: _serialization.Encoding,
456 format: _serialization.PrivateFormat,
457 encryption_algorithm: _serialization.KeySerializationEncryption,
458 ) -> bytes:
459 """
460 The serialized bytes of the private key.
461
462 This method only returns the serialization of the seed form of the
463 private key, never the expanded one.
464 """
465
466 @abc.abstractmethod
467 def private_bytes_raw(self) -> bytes:
468 """
469 The raw bytes of the private key.
470 Equivalent to private_bytes(Raw, Raw, NoEncryption()).
471
472 This method only returns the seed form of the private key (32 bytes).
473 """
474
475 @abc.abstractmethod
476 def sign(self, data: Buffer, context: Buffer | None = None) -> bytes:
477 """
478 Signs the data.
479 """
480
481 @abc.abstractmethod
482 def sign_mu(self, mu: Buffer) -> bytes:
483 """
484 Signs a precomputed mu (message representative).
485
486 mu must be 64 bytes and already incorporates the context, so no
487 context is accepted here.
488 """
489
490 @abc.abstractmethod
491 def __copy__(self) -> MLDSA87PrivateKey:
492 """
493 Returns a copy.
494 """
495
496 @abc.abstractmethod
497 def __deepcopy__(self, memo: dict) -> MLDSA87PrivateKey:
498 """
499 Returns a deep copy.
500 """
501
502
503if hasattr(rust_openssl, "mldsa"):
504 MLDSA87PrivateKey.register(rust_openssl.mldsa.MLDSA87PrivateKey)