Coverage for pki_tools/types/key_pair.py: 98%
321 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-11-02 22:00 +0000
« prev ^ index » next coverage.py v7.14.3, created at 2026-11-02 22:00 +0000
1import abc
2import importlib
3import re
4from enum import Enum
5from typing import get_args
7import yaml
8from cryptography.hazmat.primitives import serialization
9from cryptography.hazmat.primitives._serialization import (
10 Encoding,
11 PrivateFormat,
12 PublicFormat,
13)
14from cryptography.hazmat.primitives.asymmetric import (
15 dsa,
16 ec,
17 ed448,
18 ed25519,
19 rsa,
20)
21from cryptography.hazmat.primitives.asymmetric.types import (
22 CertificateIssuerPrivateKeyTypes,
23 CertificateIssuerPublicKeyTypes,
24)
25from pydantic import BaseModel
27from pki_tools.types.crypto_parser import (
28 CryptoConfig,
29 HelperFunc,
30 InitCryptoParser,
31)
32from pki_tools.types.signature_algorithm import PKCS1v15Padding
33from pki_tools.types.utils import _byte_to_hex, _hex_to_byte
35PUBLIC_KEY_REGEXP = re.compile(
36 r"\s*-+BEGIN PUBLIC KEY-+[\w+/\s=]*-+END PUBLIC KEY-+\s*"
37)
38PRIVATE_KEY_REGEXP = re.compile(
39 r"\s*-+BEGIN.+PRIVATE KEY-+[\w+/\s=]*-+END.+PRIVATE KEY-+\s*"
40)
43class CryptoPrivateKey(InitCryptoParser, abc.ABC):
44 """
45 Represents a cryptographic private key.
46 """
48 @classmethod
49 def from_cryptography(
50 cls: type["CryptoPrivateKey"],
51 key: CertificateIssuerPrivateKeyTypes,
52 ) -> "CryptoPrivateKey":
53 """
54 Create a CryptoPrivateKey from a cryptography private key.
56 Args:
57 key: The cryptography private key.
59 Returns:
60 The CryptoPrivateKey object.
61 """
62 types = get_args(CertificateIssuerPrivateKeyTypes)
64 name = None
65 for check_type in types:
66 if isinstance(key, check_type):
67 name = check_type.__name__
68 break
70 return globals()[name].from_cryptography(key)
72 @property
73 def der_bytes(self) -> bytes:
74 """
75 Property to get the DER encoding of the public key.
77 Returns:
78 bytes: The DER encoded public key.
79 """
80 return self._crypto_object.private_bytes(
81 encoding=Encoding.DER,
82 format=PrivateFormat.PKCS8,
83 encryption_algorithm=serialization.NoEncryption(),
84 )
86 @property
87 def pem_bytes(self) -> bytes:
88 """
89 Property to get the PEM encoding of the private key.
91 Returns:
92 bytes: The PEM encoded private key.
93 """
94 kwargs = {
95 "encoding": serialization.Encoding.PEM,
96 "encryption_algorithm": serialization.NoEncryption(),
97 "format": serialization.PrivateFormat.TraditionalOpenSSL,
98 }
100 if "Ed" in self.__class__.__name__:
101 kwargs["format"] = serialization.PrivateFormat.PKCS8
103 return self._crypto_object.private_bytes(**kwargs)
105 @classmethod
106 def _crypto_config(cls) -> CryptoConfig:
107 return CryptoConfig(
108 load_pem=HelperFunc(
109 func=serialization.load_pem_private_key,
110 kwargs={"password": None},
111 ),
112 load_der=HelperFunc(
113 func=serialization.load_der_private_key,
114 kwargs={"password": None},
115 ),
116 pem_regexp=PRIVATE_KEY_REGEXP,
117 )
120class CryptoPublicKey(InitCryptoParser, abc.ABC):
121 """
122 Represents a cryptographic public key.
123 """
125 @classmethod
126 def from_cryptography(
127 cls: type["CryptoPublicKey"],
128 key: CertificateIssuerPublicKeyTypes,
129 ) -> "CryptoPublicKey":
130 """
131 Create a CryptoPublicKey from a cryptography public key.
133 Args:
134 key: The cryptography public key.
136 Returns:
137 The created CryptoPublicKey.
138 """
139 types = get_args(CertificateIssuerPublicKeyTypes)
141 name = None
142 for check_type in types:
143 if isinstance(key, check_type):
144 name = check_type.__name__
145 break
147 return globals()[name].from_cryptography(key)
149 @property
150 def der_bytes(self) -> bytes:
151 """
152 Property to get the DER encoding of the public key.
154 Returns:
155 bytes: The DER encoded public key.
156 """
157 return self._crypto_object.public_bytes(
158 encoding=Encoding.DER,
159 format=PublicFormat.SubjectPublicKeyInfo,
160 )
162 @property
163 def ocsp_bytes(self) -> bytes:
164 """
165 The bytes used for the OCSP Response hash
167 Returns:
168 bytes: The bytes used for the OCSP Response hash
169 """
170 return self.der_bytes
172 @property
173 def pem_bytes(self) -> bytes:
174 """
175 Property to get the PEM encoding of the public key.
177 Returns:
178 bytes: The PEM encoded public key.
179 """
180 return self._crypto_object.public_bytes(
181 encoding=serialization.Encoding.PEM,
182 format=serialization.PublicFormat.SubjectPublicKeyInfo,
183 )
185 @abc.abstractmethod
186 def verify(self, signed: InitCryptoParser) -> None:
187 """
188 Verifies the signature of a signed object.
190 Args:
191 signed: the signed object to verify.
192 """
194 @classmethod
195 def _crypto_config(cls) -> CryptoConfig:
196 return CryptoConfig(
197 load_pem=HelperFunc(func=serialization.load_pem_public_key),
198 load_der=HelperFunc(func=serialization.load_der_public_key),
199 pem_regexp=PUBLIC_KEY_REGEXP,
200 )
203class CryptoKeyPair(BaseModel):
204 """
205 Represents a cryptographic key pair.
207 Attributes:
208 private_key: The private key
209 public_key: The public key
210 """
212 private_key: CryptoPrivateKey
213 public_key: CryptoPublicKey
215 _init_func = "create"
217 @classmethod
218 @abc.abstractmethod
219 def generate(cls: type["CryptoKeyPair"], *args) -> "CryptoKeyPair":
220 """
221 Abstract method to generate a cryptographic key pair.
223 Returns:
224 The generated cryptographic key pair.
225 """
226 raise NotImplementedError
228 def __str__(self):
229 name = self.__class__.__name__
230 d = {name: {}}
231 if self.private_key is not None:
232 d[name] = self.private_key._string_dict()
233 else:
234 d[name] = self.public_key._string_dict()
235 return yaml.dump(d, indent=2)
238class DSAPublicKey(CryptoPublicKey):
239 """
240 Represents a DSA cryptographic key pair.
241 """
243 key_size: int
245 y: int
246 p: int
247 q: int
248 g: int
250 @classmethod
251 def from_cryptography(
252 cls: type["DSAPublicKey"],
253 key: dsa.DSAPublicKey,
254 ) -> "DSAPublicKey":
255 """
256 Create a DSAKeyPair from a cryptography key.
258 Args:
259 key: The `cryptography.hazmat.primitives.asymmetric.dsa`
260 public key.
262 Returns:
263 DSAKeyPair: The DSA key pair.
266 """
267 public_numbers = key.public_numbers()
268 param_numbers = key.parameters().parameter_numbers()
270 ret = cls(
271 y=public_numbers.y,
272 p=param_numbers.p,
273 q=param_numbers.q,
274 g=param_numbers.g,
275 key_size=key.key_size,
276 _x509_obj=key,
277 )
278 ret._x509_obj = key
279 return ret
281 @property
282 def ocsp_bytes(self) -> bytes:
283 """
284 The bytes used for the OCSP Response hash
286 Returns:
287 bytes: The bytes used for the OCSP Response hash
288 """
289 return self._crypto_object.public_bytes(
290 encoding=Encoding.DER,
291 format=PublicFormat.SubjectPublicKeyInfo,
292 )
294 def verify(self, signed: InitCryptoParser):
295 self._crypto_object.verify(
296 signed._crypto_object.signature,
297 signed.tbs_bytes,
298 signed._crypto_object.signature_hash_algorithm,
299 )
301 def _to_cryptography(self) -> dsa.DSAPublicKey:
302 public_numbers = dsa.DSAPublicNumbers(
303 y=self.y,
304 parameter_numbers=dsa.DSAParameterNumbers(
305 p=self.p, q=self.q, g=self.g
306 ),
307 )
308 return public_numbers.public_key()
310 def _string_dict(self) -> dict[str, str]:
311 return {
312 "key_size": str(self.key_size),
313 "public_key_y": str(self.y),
314 "prime_p": str(self.p),
315 "subprime_q": str(self.q),
316 "generator_g": str(self.g),
317 }
320class DSAPrivateKey(CryptoPrivateKey):
321 """
322 Represents a DSA cryptographic private key.
323 """
325 key_size: int
326 x: int
328 y: int
329 p: int
330 q: int
331 g: int
333 @classmethod
334 def from_cryptography(
335 cls: type["DSAPrivateKey"],
336 key: dsa.DSAPrivateKey,
337 ) -> "DSAPrivateKey":
338 """
339 Create a DSAPrivateKey from a cryptography key.
341 Args:
342 key: The `cryptography.hazmat.primitives.asymmetric.dsa`
343 private key.
345 Returns:
346 DSAKeyPair: The DSA private key.
347 """
348 x = key.private_numbers().x
350 public_numbers = key.public_key().public_numbers()
351 param_numbers = key.parameters().parameter_numbers()
353 ret = cls(
354 x=x,
355 y=public_numbers.y,
356 p=param_numbers.p,
357 q=param_numbers.q,
358 g=param_numbers.g,
359 key_size=key.key_size,
360 _x509_obj=key,
361 )
362 ret._x509_obj = key
363 return ret
365 def _to_cryptography(self) -> dsa.DSAPrivateKey:
366 public_numbers = dsa.DSAPublicNumbers(
367 y=self.y,
368 parameter_numbers=dsa.DSAParameterNumbers(
369 p=self.p, q=self.q, g=self.g
370 ),
371 )
373 private_numbers = dsa.DSAPrivateNumbers(
374 x=self.x, public_numbers=public_numbers
375 )
376 return private_numbers.private_key()
378 def _string_dict(self) -> dict[str, str]:
379 return {
380 "key_size": str(self.key_size),
381 "public_key_y": str(self.y),
382 "prime_p": str(self.p),
383 "subprime_q": str(self.q),
384 "generator_g": str(self.g),
385 }
388class DSAKeyPair(CryptoKeyPair):
389 """
390 Describes a DSA Key Pair including the public and private DSA keys.
392 --8<-- "docs/examples/key_pair_dsa.md"
393 """
395 @classmethod
396 def generate(cls: type["DSAKeyPair"], key_size: int) -> "DSAKeyPair":
397 """
398 Generate a DSA cryptographic key pair.
400 Args:
401 key_size: The size of the key.
403 Returns:
404 The generated DSA key pair.
405 """
406 crypto_private = dsa.generate_private_key(key_size=key_size)
407 crypto_public = crypto_private.public_key()
409 return cls(
410 private_key=DSAPrivateKey.from_cryptography(crypto_private),
411 public_key=DSAPublicKey.from_cryptography(crypto_public),
412 )
415class RSAPrivateKey(CryptoPrivateKey):
416 """
417 Represents an RSA cryptographic key pair.
418 """
420 key_size: int
422 e: int
423 n: int
425 d: int | None
426 p: int | None
427 q: int | None
428 dmp1: int | None
429 dmq1: int | None
430 iqmp: int | None
432 @classmethod
433 def from_cryptography(
434 cls: type["RSAPrivateKey"],
435 key: rsa.RSAPrivateKey,
436 ) -> "RSAPrivateKey":
437 """
438 Create an RSA private key from a cryptography key.
440 Args:
441 key: The cryptography private key.
443 Returns:
444 An RSAPrivateKey object
445 """
447 private_numbers = key.private_numbers()
448 public_numbers = private_numbers.public_numbers
449 d = private_numbers.d
450 p = private_numbers.p
451 q = private_numbers.q
452 dmp1 = private_numbers.dmp1
453 dmq1 = private_numbers.dmq1
454 iqmp = private_numbers.iqmp
456 ret = cls(
457 e=public_numbers.e,
458 n=public_numbers.n,
459 d=d,
460 p=p,
461 q=q,
462 dmp1=dmp1,
463 dmq1=dmq1,
464 iqmp=iqmp,
465 key_size=key.key_size,
466 _x509_obj=key,
467 )
468 ret._x509_obj = key
469 return ret
471 def _to_cryptography(self) -> rsa.RSAPrivateKey:
472 public_numbers = rsa.RSAPublicNumbers(e=self.e, n=self.n)
474 private_numbers = rsa.RSAPrivateNumbers(
475 p=self.p,
476 q=self.q,
477 d=self.d,
478 dmp1=self.dmp1,
479 dmq1=self.dmq1,
480 iqmp=self.iqmp,
481 public_numbers=public_numbers,
482 )
483 return private_numbers.private_key()
485 def _string_dict(self) -> dict[str, str]:
486 return {
487 "key_size": str(self.key_size),
488 "public_exponent_e": str(self.e),
489 "modulus_n": str(self.n),
490 "private_exponent_d": str(self.d),
491 "prime_p": str(self.p),
492 "prime_q": str(self.q),
493 "dmp1": str(self.dmp1),
494 "dmq1": str(self.dmq1),
495 "iqmp": str(self.iqmp),
496 }
499class RSAPublicKey(CryptoPublicKey):
500 """
501 Represents an RSA cryptographic key pair.
502 """
504 key_size: int
506 e: int
507 n: int
509 @classmethod
510 def from_cryptography(
511 cls: type["RSAPublicKey"],
512 key: rsa.RSAPublicKey,
513 ) -> "RSAPublicKey":
514 """
515 Create an RSA public key from a cryptography public key.
517 Args:
518 key: The public cryptography key.
520 Returns:
521 The RSA public key
522 """
523 public_numbers = key.public_numbers()
525 ret = cls(
526 e=public_numbers.e,
527 n=public_numbers.n,
528 key_size=key.key_size,
529 _x509_obj=key,
530 )
531 ret._x509_obj = key
532 return ret
534 @property
535 def der_bytes(self) -> bytes:
536 """
537 Property to get the DER encoding of the public key.
539 Returns:
540 bytes: The DER encoded public key.
541 """
542 return self._crypto_object.public_bytes(
543 encoding=Encoding.DER,
544 format=PublicFormat.PKCS1,
545 )
547 def verify(self, signed: InitCryptoParser) -> None:
548 return self._crypto_object.verify(
549 signed._crypto_object.signature,
550 signed.tbs_bytes,
551 PKCS1v15Padding()._to_cryptography(),
552 signed._crypto_object.signature_hash_algorithm,
553 )
555 def _to_cryptography(self) -> rsa.RSAPublicKey:
556 public_numbers = rsa.RSAPublicNumbers(e=self.e, n=self.n)
558 return public_numbers.public_key()
560 def _string_dict(self) -> dict[str, str]:
561 return {
562 "key_size": str(self.key_size),
563 "public_exponent_e": str(self.e),
564 "modulus_n": str(self.n),
565 }
568class RSAKeyPair(CryptoKeyPair):
569 """
570 Describes an RSA Key Pair including the public and private RSA keys.
572 --8<-- "docs/examples/key_pair_rsa.md"
573 """
575 @classmethod
576 def generate(
577 cls: type["RSAKeyPair"], key_size: int = 2048, exponent: int = 65537
578 ) -> "RSAKeyPair":
579 """
580 Generate an RSA cryptographic key pair.
582 Args:
583 key_size: The size of the key. Defaults to `2048`.
584 exponent: The public exponent. Defaults to `65537`.
586 Returns:
587 The generated RSA key pair.
588 """
589 crypto_private = rsa.generate_private_key(
590 public_exponent=exponent,
591 key_size=key_size,
592 )
593 crypto_public = crypto_private.public_key()
595 return cls(
596 private_key=RSAPrivateKey.from_cryptography(crypto_private),
597 public_key=RSAPublicKey.from_cryptography(crypto_public),
598 )
601EC_MODULE = importlib.import_module(
602 "cryptography.hazmat.primitives.asymmetric.ec"
603)
606class EllipticCurveName(Enum):
607 """
608 Elliptic Curve Names
609 """
611 PRIME192V1 = "PRIME192V1"
612 PRIME256V1 = "PRIME256V1"
613 SECP192R1 = "SECP192R1"
614 SECP224R1 = "SECP224R1"
615 SECP256R1 = "SECP256R1"
616 SECP384R1 = "SECP384R1"
617 SECP521R1 = "SECP521R1"
618 SECP256K1 = "SECP256K1"
619 SECT163K1 = "SECT163K1"
620 SECT233K1 = "SECT233K1"
621 SECT283K1 = "SECT283K1"
622 SECT409K1 = "SECT409K1"
623 SECT571K1 = "SECT571K1"
624 SECT163R2 = "SECT163R2"
625 SECT233R1 = "SECT233R1"
626 SECT283R1 = "SECT283R1"
627 SECT409R1 = "SECT409R1"
628 SECT571R1 = "SECT571R1"
629 BRAINPOOLP256R1 = "BrainpoolP256R1"
630 BRAINPOOLP384R1 = "BrainpoolP384R1"
631 BRAINPOOLP512R1 = "BrainpoolP512R1"
634class EllipticCurvePrivateKey(CryptoPrivateKey):
635 """
636 Represents an elliptic curve cryptographic private key.
637 """
639 curve_name: EllipticCurveName
641 x: int
642 y: int
643 d: int
645 @classmethod
646 def from_cryptography(
647 cls: type["EllipticCurvePrivateKey"],
648 key: ec.EllipticCurvePrivateKey,
649 ) -> "EllipticCurvePrivateKey":
650 """
651 Create an EllipticCurvePrivateKey from a cryptography private key.
653 Args:
654 key: The cryptography private key.
656 Returns:
657 The EllipticCurvePrivateKey created object
658 """
660 private_numbers = key.private_numbers()
661 public_key = key.public_key()
662 public_numbers = public_key.public_numbers()
663 curve_name = public_key.curve.name.upper()
664 d = private_numbers.private_value
666 ret = cls(
667 x=public_numbers.x,
668 y=public_numbers.y,
669 curve_name=EllipticCurveName[curve_name],
670 d=d,
671 _x509_obj=key,
672 )
673 ret._x509_obj = key
674 return ret
676 def _to_cryptography(
677 self,
678 ) -> ec.EllipticCurvePrivateKey:
679 curve = getattr(EC_MODULE, self.curve_name.value)()
680 public_numbers = ec.EllipticCurvePublicNumbers(self.x, self.y, curve)
682 private_numbers = ec.EllipticCurvePrivateNumbers(
683 private_value=self.d, public_numbers=public_numbers
684 )
686 return private_numbers.private_key()
688 def _string_dict(self) -> dict[str, str]:
689 return {
690 "curve_name": self.curve_name.name,
691 "x_coordinate": str(self.x),
692 "y_coordinate": str(self.y),
693 }
696class EllipticCurvePublicKey(CryptoPublicKey):
697 """
698 Represents an elliptic curve cryptographic public key.
699 """
701 curve_name: EllipticCurveName
703 x: int
704 y: int
706 @classmethod
707 def from_cryptography(
708 cls: type["EllipticCurvePublicKey"],
709 key: ec.EllipticCurvePublicKey,
710 ) -> "EllipticCurvePublicKey":
711 """
712 Create an EllipticCurvePublicKey from a cryptography public key.
714 Args:
715 key: The cryptography public key.
717 Returns:
718 The created EllipticCurvePublicKey object
719 """
720 public_numbers = key.public_numbers()
721 curve_name = key.curve.name.upper()
723 ret = cls(
724 x=public_numbers.x,
725 y=public_numbers.y,
726 curve_name=EllipticCurveName[curve_name],
727 _x509_obj=key,
728 )
729 ret._x509_obj = key
730 return ret
732 def verify(self, signed: InitCryptoParser):
733 self._crypto_object.verify(
734 signed._crypto_object.signature,
735 signed.tbs_bytes,
736 ec.ECDSA(signed._crypto_object.signature_hash_algorithm),
737 )
739 @property
740 def ocsp_bytes(self) -> bytes:
741 """
742 The bytes used for the OCSP Response hash
744 Returns:
745 bytes: The bytes used for the OCSP Response hash
746 """
747 return self._crypto_object.public_bytes(
748 encoding=Encoding.X962,
749 format=PublicFormat.UncompressedPoint,
750 )
752 def _to_cryptography(
753 self,
754 ) -> ec.EllipticCurvePublicKey:
755 curve = getattr(EC_MODULE, self.curve_name.value)()
756 public_numbers = ec.EllipticCurvePublicNumbers(self.x, self.y, curve)
757 return public_numbers.public_key()
759 def _string_dict(self) -> dict[str, str]:
760 return {
761 "curve_name": self.curve_name.name,
762 "x_coordinate": str(self.x),
763 "y_coordinate": str(self.y),
764 }
767class EllipticCurveKeyPair(CryptoKeyPair):
768 """
769 Describes an elliptic curve Key Pair including the public and private keys.
771 --8<-- "docs/examples/key_pair_ec.md"
772 """
774 @classmethod
775 def generate(
776 cls: type["EllipticCurveKeyPair"], curve_name: EllipticCurveName
777 ) -> "EllipticCurveKeyPair":
778 """
779 Generate an elliptic curve cryptographic key pair.
781 Args:
782 curve_name: The name of the curve.
784 Returns:
785 The generated elliptic curve key pair.
786 """
787 crypto_private = ec.generate_private_key(
788 curve=getattr(EC_MODULE, curve_name.value)()
789 )
790 crypto_public = crypto_private.public_key()
792 return cls(
793 private_key=EllipticCurvePrivateKey.from_cryptography(
794 crypto_private
795 ),
796 public_key=EllipticCurvePublicKey.from_cryptography(crypto_public),
797 )
800class Ed448PrivateKey(CryptoPrivateKey):
801 public_bytes: str
802 private_bytes: str
804 @classmethod
805 def from_cryptography(
806 cls: type["Ed448PrivateKey"],
807 key: ed448.Ed448PrivateKey,
808 ) -> "Ed448PrivateKey":
809 """
810 Create an Ed25519KeyPair from a cryptography private key.
812 Args:
813 key: The cryptography private key.
815 Returns:
816 The created Ed448PrivateKey object.
817 """
819 public_key = key.public_key()
820 private_bytes = _byte_to_hex(key.private_bytes_raw())
822 ret = cls(
823 public_bytes=_byte_to_hex(public_key.public_bytes_raw()),
824 private_bytes=private_bytes,
825 _x509_obj=key,
826 )
827 ret._x509_obj = key
828 return ret
830 def _to_cryptography(
831 self,
832 ) -> ed448.Ed448PrivateKey:
833 return ed448.Ed448PrivateKey.from_private_bytes(
834 _hex_to_byte(self.private_bytes)
835 )
837 def _string_dict(self) -> dict[str, str]:
838 return {
839 "public_bytes": self.public_bytes,
840 }
843class Ed448PublicKey(CryptoPublicKey):
844 public_bytes: str
846 @classmethod
847 def from_cryptography(
848 cls: type["Ed448PublicKey"],
849 key: ed448.Ed448PublicKey,
850 ) -> "Ed448PublicKey":
851 """
852 Create an Ed448PublicKey from a cryptography public key.
854 Args:
855 key: The cryptography public key.
857 Returns:
858 The Ed448PublicKey object.
859 """
860 ret = cls(
861 public_bytes=_byte_to_hex(key.public_bytes_raw()),
862 _x509_obj=key,
863 )
864 ret._x509_obj = key
865 return ret
867 def verify(self, signed: InitCryptoParser):
868 self._crypto_object.verify(
869 signed._crypto_object.signature,
870 signed.tbs_bytes,
871 )
873 def _to_cryptography(
874 self,
875 ) -> ed448.Ed448PublicKey:
876 return ed448.Ed448PublicKey.from_public_bytes(
877 _hex_to_byte(self.public_bytes)
878 )
880 def _string_dict(self) -> dict[str, str]:
881 return {
882 "public_bytes": self.public_bytes,
883 }
886class Ed448KeyPair(CryptoKeyPair):
887 """
888 Represents an Ed448 cryptographic key pair.
890 --8<-- "docs/examples/key_pair_ed448.md"
891 """
893 @classmethod
894 def generate(cls: type["Ed448KeyPair"]) -> "Ed448KeyPair":
895 """
896 Generate an Ed448 cryptographic key pair.
898 Returns:
899 The generated Ed448 key pair.
900 """
901 crypto_private = ed448.Ed448PrivateKey.generate()
902 crypto_public = crypto_private.public_key()
904 return cls(
905 private_key=Ed448PrivateKey.from_cryptography(crypto_private),
906 public_key=Ed448PublicKey.from_cryptography(crypto_public),
907 )
910class Ed25519PrivateKey(CryptoPrivateKey):
911 private_bytes: str
912 public_bytes: str
914 @classmethod
915 def from_cryptography(
916 cls: type["Ed25519PrivateKey"],
917 key: ed25519.Ed25519PrivateKey,
918 ) -> "Ed25519PrivateKey":
919 """
920 Create an Ed25519 private key from a cryptography private key.
922 Args:
923 key: The cryptography private key.
925 Returns:
926 The created Ed25519PrivateKey object.
927 """
929 public_key = key.public_key()
930 private_bytes = _byte_to_hex(key.private_bytes_raw())
932 ret = cls(
933 public_bytes=_byte_to_hex(public_key.public_bytes_raw()),
934 private_bytes=private_bytes,
935 _x509_obj=key,
936 )
937 ret._x509_obj = key
938 return ret
940 def _to_cryptography(
941 self,
942 ) -> ed25519.Ed25519PrivateKey:
943 return ed25519.Ed25519PrivateKey.from_private_bytes(
944 _hex_to_byte(self.private_bytes)
945 )
947 def _string_dict(self) -> dict[str, str]:
948 return {
949 "public_bytes": self.public_bytes,
950 }
953class Ed25519PublicKey(CryptoPublicKey):
954 public_bytes: str
956 @classmethod
957 def from_cryptography(
958 cls: type["Ed25519PublicKey"],
959 key: ed25519.Ed25519PublicKey,
960 ) -> "Ed25519PublicKey":
961 """
962 Create an Ed25519PublicKey from a cryptography public key.
964 Args:
965 key: The cryptography public key.
967 Returns:
968 The Ed25519PublicKey object.
969 """
970 ret = cls(
971 public_bytes=_byte_to_hex(key.public_bytes_raw()),
972 _x509_obj=key,
973 )
974 ret._x509_obj = key
975 return ret
977 def verify(self, signed: InitCryptoParser):
978 self._crypto_object.verify(
979 signed._crypto_object.signature,
980 signed.tbs_bytes,
981 )
983 def _to_cryptography(
984 self,
985 ) -> ed25519.Ed25519PublicKey:
986 return ed25519.Ed25519PublicKey.from_public_bytes(
987 _hex_to_byte(self.public_bytes)
988 )
990 def _string_dict(self) -> dict[str, str]:
991 return {
992 "public_bytes": self.public_bytes,
993 }
996class Ed25519KeyPair(CryptoKeyPair):
997 """
998 Represents an Ed25519 cryptographic key pair.
1000 --8<-- "docs/examples/key_pair_ed25519.md"
1001 """
1003 @classmethod
1004 def generate(cls: type["Ed25519KeyPair"]) -> "Ed25519KeyPair":
1005 """
1006 Generate an Ed25519KeyPair cryptographic key pair.
1008 Returns:
1009 The generated Ed25519 key pair.
1010 """
1011 crypto_private = ed25519.Ed25519PrivateKey.generate()
1012 crypto_public = crypto_private.public_key()
1014 return cls(
1015 private_key=Ed25519PrivateKey.from_cryptography(crypto_private),
1016 public_key=Ed25519PublicKey.from_cryptography(crypto_public),
1017 )