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

1import abc 

2import importlib 

3import re 

4from enum import Enum 

5from typing import get_args 

6 

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 

26 

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 

34 

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) 

41 

42 

43class CryptoPrivateKey(InitCryptoParser, abc.ABC): 

44 """ 

45 Represents a cryptographic private key. 

46 """ 

47 

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. 

55 

56 Args: 

57 key: The cryptography private key. 

58 

59 Returns: 

60 The CryptoPrivateKey object. 

61 """ 

62 types = get_args(CertificateIssuerPrivateKeyTypes) 

63 

64 name = None 

65 for check_type in types: 

66 if isinstance(key, check_type): 

67 name = check_type.__name__ 

68 break 

69 

70 return globals()[name].from_cryptography(key) 

71 

72 @property 

73 def der_bytes(self) -> bytes: 

74 """ 

75 Property to get the DER encoding of the public key. 

76 

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 ) 

85 

86 @property 

87 def pem_bytes(self) -> bytes: 

88 """ 

89 Property to get the PEM encoding of the private key. 

90 

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 } 

99 

100 if "Ed" in self.__class__.__name__: 

101 kwargs["format"] = serialization.PrivateFormat.PKCS8 

102 

103 return self._crypto_object.private_bytes(**kwargs) 

104 

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 ) 

118 

119 

120class CryptoPublicKey(InitCryptoParser, abc.ABC): 

121 """ 

122 Represents a cryptographic public key. 

123 """ 

124 

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. 

132 

133 Args: 

134 key: The cryptography public key. 

135 

136 Returns: 

137 The created CryptoPublicKey. 

138 """ 

139 types = get_args(CertificateIssuerPublicKeyTypes) 

140 

141 name = None 

142 for check_type in types: 

143 if isinstance(key, check_type): 

144 name = check_type.__name__ 

145 break 

146 

147 return globals()[name].from_cryptography(key) 

148 

149 @property 

150 def der_bytes(self) -> bytes: 

151 """ 

152 Property to get the DER encoding of the public key. 

153 

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 ) 

161 

162 @property 

163 def ocsp_bytes(self) -> bytes: 

164 """ 

165 The bytes used for the OCSP Response hash 

166 

167 Returns: 

168 bytes: The bytes used for the OCSP Response hash 

169 """ 

170 return self.der_bytes 

171 

172 @property 

173 def pem_bytes(self) -> bytes: 

174 """ 

175 Property to get the PEM encoding of the public key. 

176 

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 ) 

184 

185 @abc.abstractmethod 

186 def verify(self, signed: InitCryptoParser) -> None: 

187 """ 

188 Verifies the signature of a signed object. 

189 

190 Args: 

191 signed: the signed object to verify. 

192 """ 

193 

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 ) 

201 

202 

203class CryptoKeyPair(BaseModel): 

204 """ 

205 Represents a cryptographic key pair. 

206 

207 Attributes: 

208 private_key: The private key 

209 public_key: The public key 

210 """ 

211 

212 private_key: CryptoPrivateKey 

213 public_key: CryptoPublicKey 

214 

215 _init_func = "create" 

216 

217 @classmethod 

218 @abc.abstractmethod 

219 def generate(cls: type["CryptoKeyPair"], *args) -> "CryptoKeyPair": 

220 """ 

221 Abstract method to generate a cryptographic key pair. 

222 

223 Returns: 

224 The generated cryptographic key pair. 

225 """ 

226 raise NotImplementedError 

227 

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) 

236 

237 

238class DSAPublicKey(CryptoPublicKey): 

239 """ 

240 Represents a DSA cryptographic key pair. 

241 """ 

242 

243 key_size: int 

244 

245 y: int 

246 p: int 

247 q: int 

248 g: int 

249 

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. 

257 

258 Args: 

259 key: The `cryptography.hazmat.primitives.asymmetric.dsa` 

260 public key. 

261 

262 Returns: 

263 DSAKeyPair: The DSA key pair. 

264 

265 

266 """ 

267 public_numbers = key.public_numbers() 

268 param_numbers = key.parameters().parameter_numbers() 

269 

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 

280 

281 @property 

282 def ocsp_bytes(self) -> bytes: 

283 """ 

284 The bytes used for the OCSP Response hash 

285 

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 ) 

293 

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 ) 

300 

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() 

309 

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 } 

318 

319 

320class DSAPrivateKey(CryptoPrivateKey): 

321 """ 

322 Represents a DSA cryptographic private key. 

323 """ 

324 

325 key_size: int 

326 x: int 

327 

328 y: int 

329 p: int 

330 q: int 

331 g: int 

332 

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. 

340 

341 Args: 

342 key: The `cryptography.hazmat.primitives.asymmetric.dsa` 

343 private key. 

344 

345 Returns: 

346 DSAKeyPair: The DSA private key. 

347 """ 

348 x = key.private_numbers().x 

349 

350 public_numbers = key.public_key().public_numbers() 

351 param_numbers = key.parameters().parameter_numbers() 

352 

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 

364 

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 ) 

372 

373 private_numbers = dsa.DSAPrivateNumbers( 

374 x=self.x, public_numbers=public_numbers 

375 ) 

376 return private_numbers.private_key() 

377 

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 } 

386 

387 

388class DSAKeyPair(CryptoKeyPair): 

389 """ 

390 Describes a DSA Key Pair including the public and private DSA keys. 

391 

392 --8<-- "docs/examples/key_pair_dsa.md" 

393 """ 

394 

395 @classmethod 

396 def generate(cls: type["DSAKeyPair"], key_size: int) -> "DSAKeyPair": 

397 """ 

398 Generate a DSA cryptographic key pair. 

399 

400 Args: 

401 key_size: The size of the key. 

402 

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() 

408 

409 return cls( 

410 private_key=DSAPrivateKey.from_cryptography(crypto_private), 

411 public_key=DSAPublicKey.from_cryptography(crypto_public), 

412 ) 

413 

414 

415class RSAPrivateKey(CryptoPrivateKey): 

416 """ 

417 Represents an RSA cryptographic key pair. 

418 """ 

419 

420 key_size: int 

421 

422 e: int 

423 n: int 

424 

425 d: int | None 

426 p: int | None 

427 q: int | None 

428 dmp1: int | None 

429 dmq1: int | None 

430 iqmp: int | None 

431 

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. 

439 

440 Args: 

441 key: The cryptography private key. 

442 

443 Returns: 

444 An RSAPrivateKey object 

445 """ 

446 

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 

455 

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 

470 

471 def _to_cryptography(self) -> rsa.RSAPrivateKey: 

472 public_numbers = rsa.RSAPublicNumbers(e=self.e, n=self.n) 

473 

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() 

484 

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 } 

497 

498 

499class RSAPublicKey(CryptoPublicKey): 

500 """ 

501 Represents an RSA cryptographic key pair. 

502 """ 

503 

504 key_size: int 

505 

506 e: int 

507 n: int 

508 

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. 

516 

517 Args: 

518 key: The public cryptography key. 

519 

520 Returns: 

521 The RSA public key 

522 """ 

523 public_numbers = key.public_numbers() 

524 

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 

533 

534 @property 

535 def der_bytes(self) -> bytes: 

536 """ 

537 Property to get the DER encoding of the public key. 

538 

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 ) 

546 

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 ) 

554 

555 def _to_cryptography(self) -> rsa.RSAPublicKey: 

556 public_numbers = rsa.RSAPublicNumbers(e=self.e, n=self.n) 

557 

558 return public_numbers.public_key() 

559 

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 } 

566 

567 

568class RSAKeyPair(CryptoKeyPair): 

569 """ 

570 Describes an RSA Key Pair including the public and private RSA keys. 

571 

572 --8<-- "docs/examples/key_pair_rsa.md" 

573 """ 

574 

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. 

581 

582 Args: 

583 key_size: The size of the key. Defaults to `2048`. 

584 exponent: The public exponent. Defaults to `65537`. 

585 

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() 

594 

595 return cls( 

596 private_key=RSAPrivateKey.from_cryptography(crypto_private), 

597 public_key=RSAPublicKey.from_cryptography(crypto_public), 

598 ) 

599 

600 

601EC_MODULE = importlib.import_module( 

602 "cryptography.hazmat.primitives.asymmetric.ec" 

603) 

604 

605 

606class EllipticCurveName(Enum): 

607 """ 

608 Elliptic Curve Names 

609 """ 

610 

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" 

632 

633 

634class EllipticCurvePrivateKey(CryptoPrivateKey): 

635 """ 

636 Represents an elliptic curve cryptographic private key. 

637 """ 

638 

639 curve_name: EllipticCurveName 

640 

641 x: int 

642 y: int 

643 d: int 

644 

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. 

652 

653 Args: 

654 key: The cryptography private key. 

655 

656 Returns: 

657 The EllipticCurvePrivateKey created object 

658 """ 

659 

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 

665 

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 

675 

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) 

681 

682 private_numbers = ec.EllipticCurvePrivateNumbers( 

683 private_value=self.d, public_numbers=public_numbers 

684 ) 

685 

686 return private_numbers.private_key() 

687 

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 } 

694 

695 

696class EllipticCurvePublicKey(CryptoPublicKey): 

697 """ 

698 Represents an elliptic curve cryptographic public key. 

699 """ 

700 

701 curve_name: EllipticCurveName 

702 

703 x: int 

704 y: int 

705 

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. 

713 

714 Args: 

715 key: The cryptography public key. 

716 

717 Returns: 

718 The created EllipticCurvePublicKey object 

719 """ 

720 public_numbers = key.public_numbers() 

721 curve_name = key.curve.name.upper() 

722 

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 

731 

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 ) 

738 

739 @property 

740 def ocsp_bytes(self) -> bytes: 

741 """ 

742 The bytes used for the OCSP Response hash 

743 

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 ) 

751 

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() 

758 

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 } 

765 

766 

767class EllipticCurveKeyPair(CryptoKeyPair): 

768 """ 

769 Describes an elliptic curve Key Pair including the public and private keys. 

770 

771 --8<-- "docs/examples/key_pair_ec.md" 

772 """ 

773 

774 @classmethod 

775 def generate( 

776 cls: type["EllipticCurveKeyPair"], curve_name: EllipticCurveName 

777 ) -> "EllipticCurveKeyPair": 

778 """ 

779 Generate an elliptic curve cryptographic key pair. 

780 

781 Args: 

782 curve_name: The name of the curve. 

783 

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() 

791 

792 return cls( 

793 private_key=EllipticCurvePrivateKey.from_cryptography( 

794 crypto_private 

795 ), 

796 public_key=EllipticCurvePublicKey.from_cryptography(crypto_public), 

797 ) 

798 

799 

800class Ed448PrivateKey(CryptoPrivateKey): 

801 public_bytes: str 

802 private_bytes: str 

803 

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. 

811 

812 Args: 

813 key: The cryptography private key. 

814 

815 Returns: 

816 The created Ed448PrivateKey object. 

817 """ 

818 

819 public_key = key.public_key() 

820 private_bytes = _byte_to_hex(key.private_bytes_raw()) 

821 

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 

829 

830 def _to_cryptography( 

831 self, 

832 ) -> ed448.Ed448PrivateKey: 

833 return ed448.Ed448PrivateKey.from_private_bytes( 

834 _hex_to_byte(self.private_bytes) 

835 ) 

836 

837 def _string_dict(self) -> dict[str, str]: 

838 return { 

839 "public_bytes": self.public_bytes, 

840 } 

841 

842 

843class Ed448PublicKey(CryptoPublicKey): 

844 public_bytes: str 

845 

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. 

853 

854 Args: 

855 key: The cryptography public key. 

856 

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 

866 

867 def verify(self, signed: InitCryptoParser): 

868 self._crypto_object.verify( 

869 signed._crypto_object.signature, 

870 signed.tbs_bytes, 

871 ) 

872 

873 def _to_cryptography( 

874 self, 

875 ) -> ed448.Ed448PublicKey: 

876 return ed448.Ed448PublicKey.from_public_bytes( 

877 _hex_to_byte(self.public_bytes) 

878 ) 

879 

880 def _string_dict(self) -> dict[str, str]: 

881 return { 

882 "public_bytes": self.public_bytes, 

883 } 

884 

885 

886class Ed448KeyPair(CryptoKeyPair): 

887 """ 

888 Represents an Ed448 cryptographic key pair. 

889 

890 --8<-- "docs/examples/key_pair_ed448.md" 

891 """ 

892 

893 @classmethod 

894 def generate(cls: type["Ed448KeyPair"]) -> "Ed448KeyPair": 

895 """ 

896 Generate an Ed448 cryptographic key pair. 

897 

898 Returns: 

899 The generated Ed448 key pair. 

900 """ 

901 crypto_private = ed448.Ed448PrivateKey.generate() 

902 crypto_public = crypto_private.public_key() 

903 

904 return cls( 

905 private_key=Ed448PrivateKey.from_cryptography(crypto_private), 

906 public_key=Ed448PublicKey.from_cryptography(crypto_public), 

907 ) 

908 

909 

910class Ed25519PrivateKey(CryptoPrivateKey): 

911 private_bytes: str 

912 public_bytes: str 

913 

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. 

921 

922 Args: 

923 key: The cryptography private key. 

924 

925 Returns: 

926 The created Ed25519PrivateKey object. 

927 """ 

928 

929 public_key = key.public_key() 

930 private_bytes = _byte_to_hex(key.private_bytes_raw()) 

931 

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 

939 

940 def _to_cryptography( 

941 self, 

942 ) -> ed25519.Ed25519PrivateKey: 

943 return ed25519.Ed25519PrivateKey.from_private_bytes( 

944 _hex_to_byte(self.private_bytes) 

945 ) 

946 

947 def _string_dict(self) -> dict[str, str]: 

948 return { 

949 "public_bytes": self.public_bytes, 

950 } 

951 

952 

953class Ed25519PublicKey(CryptoPublicKey): 

954 public_bytes: str 

955 

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. 

963 

964 Args: 

965 key: The cryptography public key. 

966 

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 

976 

977 def verify(self, signed: InitCryptoParser): 

978 self._crypto_object.verify( 

979 signed._crypto_object.signature, 

980 signed.tbs_bytes, 

981 ) 

982 

983 def _to_cryptography( 

984 self, 

985 ) -> ed25519.Ed25519PublicKey: 

986 return ed25519.Ed25519PublicKey.from_public_bytes( 

987 _hex_to_byte(self.public_bytes) 

988 ) 

989 

990 def _string_dict(self) -> dict[str, str]: 

991 return { 

992 "public_bytes": self.public_bytes, 

993 } 

994 

995 

996class Ed25519KeyPair(CryptoKeyPair): 

997 """ 

998 Represents an Ed25519 cryptographic key pair. 

999 

1000 --8<-- "docs/examples/key_pair_ed25519.md" 

1001 """ 

1002 

1003 @classmethod 

1004 def generate(cls: type["Ed25519KeyPair"]) -> "Ed25519KeyPair": 

1005 """ 

1006 Generate an Ed25519KeyPair cryptographic key pair. 

1007 

1008 Returns: 

1009 The generated Ed25519 key pair. 

1010 """ 

1011 crypto_private = ed25519.Ed25519PrivateKey.generate() 

1012 crypto_public = crypto_private.public_key() 

1013 

1014 return cls( 

1015 private_key=Ed25519PrivateKey.from_cryptography(crypto_private), 

1016 public_key=Ed25519PublicKey.from_cryptography(crypto_public), 

1017 )