| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | # =================================================================== | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright (c) 2015, Legrandin <helderijs@gmail.com> | 
					
						
							|  |  |  | # All rights reserved. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Redistribution and use in source and binary forms, with or without | 
					
						
							|  |  |  | # modification, are permitted provided that the following conditions | 
					
						
							|  |  |  | # are met: | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # 1. Redistributions of source code must retain the above copyright | 
					
						
							|  |  |  | #    notice, this list of conditions and the following disclaimer. | 
					
						
							|  |  |  | # 2. Redistributions in binary form must reproduce the above copyright | 
					
						
							|  |  |  | #    notice, this list of conditions and the following disclaimer in | 
					
						
							|  |  |  | #    the documentation and/or other materials provided with the | 
					
						
							|  |  |  | #    distribution. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
					
						
							|  |  |  | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
					
						
							|  |  |  | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | 
					
						
							|  |  |  | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | 
					
						
							|  |  |  | # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | 
					
						
							|  |  |  | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | 
					
						
							|  |  |  | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 
					
						
							|  |  |  | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
					
						
							|  |  |  | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
					
						
							|  |  |  | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | 
					
						
							|  |  |  | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 
					
						
							|  |  |  | # POSSIBILITY OF SUCH DAMAGE. | 
					
						
							|  |  |  | # =================================================================== | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-15 21:58:33 +02:00
										 |  |  | from __future__ import print_function | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 15:13:59 +02:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | import struct | 
					
						
							|  |  |  | import binascii | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  | from Crypto.Util.py3compat import bord, tobytes, tostr, bchr, is_string | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | from Crypto.Math.Numbers import Integer | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | from Crypto.Util.asn1 import (DerObjectId, DerOctetString, DerSequence, | 
					
						
							|  |  |  |                               DerBitString) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from Crypto.PublicKey import (_expand_subject_public_key_info, | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |                               _create_subject_public_key_info, | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  |                               _extract_subject_public_key_info) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  | from Crypto.Hash import SHA512, SHAKE256 | 
					
						
							| 
									
										
										
										
											2019-02-08 08:39:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-22 23:16:56 +02:00
										 |  |  | from Crypto.Random import get_random_bytes | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-11 19:55:27 +02:00
										 |  |  | from ._point import EccPoint, EccXPoint, _curves | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  | from ._point import CurveID as _CurveID | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-28 22:17:57 +01:00
										 |  |  | class UnsupportedEccFeature(ValueError): | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  | class EccKey(object): | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |     r"""Class defining an ECC key.
 | 
					
						
							|  |  |  |     Do not instantiate directly. | 
					
						
							|  |  |  |     Use :func:`generate`, :func:`construct` or :func:`import_key` instead. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-17 22:50:35 +02:00
										 |  |  |     :ivar curve: The **canonical** name of the curve as defined in the `ECC table`_. | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |     :vartype curve: string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-16 16:30:46 +01:00
										 |  |  |     :ivar pointQ: an ECC point representing the public component. | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |     :vartype pointQ: :class:`EccPoint` or :class:`EccXPoint` | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-22 19:52:38 +02:00
										 |  |  |     :ivar d: A scalar that represents the private component | 
					
						
							|  |  |  |              in NIST P curves. It is smaller than the | 
					
						
							|  |  |  |              order of the generator point. | 
					
						
							| 
									
										
										
										
											2018-10-26 18:03:11 +02:00
										 |  |  |     :vartype d: integer | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-16 16:30:46 +01:00
										 |  |  |     :ivar seed: A seed that represents the private component | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |                 in Ed22519 (32 bytes), Curve25519 (32 bytes), | 
					
						
							|  |  |  |                 Curve448 (56 bytes), Ed448 (57 bytes). | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     :vartype seed: bytes | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, **kwargs): | 
					
						
							|  |  |  |         """Create a new ECC key
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Keywords: | 
					
						
							|  |  |  |           curve : string | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |             The name of the curve. | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |           d : integer | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |             Mandatory for a private key one NIST P curves. | 
					
						
							|  |  |  |             It must be in the range ``[1..order-1]``. | 
					
						
							|  |  |  |           seed : bytes | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |             Mandatory for a private key on Ed25519 (32 bytes), | 
					
						
							|  |  |  |             Curve25519 (32 bytes), Curve448 (56 bytes) or Ed448 (57 bytes). | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |           point : EccPoint or EccXPoint | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |             Mandatory for a public key. If provided for a private key, | 
					
						
							|  |  |  |             the implementation will NOT check whether it matches ``d``. | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Only one parameter among ``d``, ``seed`` or ``point`` may be used. | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-09 22:18:11 +01:00
										 |  |  |         kwargs_ = dict(kwargs) | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |         curve_name = kwargs_.pop("curve", None) | 
					
						
							| 
									
										
										
										
											2016-01-09 22:18:11 +01:00
										 |  |  |         self._d = kwargs_.pop("d", None) | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         self._seed = kwargs_.pop("seed", None) | 
					
						
							| 
									
										
										
										
											2016-01-09 22:18:11 +01:00
										 |  |  |         self._point = kwargs_.pop("point", None) | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         if curve_name is None and self._point: | 
					
						
							| 
									
										
										
										
											2024-08-18 12:28:47 +02:00
										 |  |  |             curve_name = self._point.curve | 
					
						
							| 
									
										
										
										
											2016-01-12 23:18:14 +01:00
										 |  |  |         if kwargs_: | 
					
						
							|  |  |  |             raise TypeError("Unknown parameters: " + str(kwargs_)) | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |         if curve_name not in _curves: | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |             raise ValueError("Unsupported curve (%s)" % curve_name) | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |         self._curve = _curves[curve_name] | 
					
						
							| 
									
										
										
										
											2024-08-17 23:45:53 +02:00
										 |  |  |         self.curve = self._curve.canonical | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         count = int(self._d is not None) + int(self._seed is not None) | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         if count == 0: | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |             if self._point is None: | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |                 raise ValueError("At lest one between parameters 'point', 'd' or 'seed' must be specified") | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if count == 2: | 
					
						
							|  |  |  |             raise ValueError("Parameters d and seed are mutually exclusive") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |         # NIST P curves work with d, EdDSA works with seed | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         # RFC 8032, 5.1.5 | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |         if self._curve.id == _CurveID.ED25519: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |             if self._d is not None: | 
					
						
							|  |  |  |                 raise ValueError("Parameter d can only be used with NIST P curves") | 
					
						
							|  |  |  |             if len(self._seed) != 32: | 
					
						
							|  |  |  |                 raise ValueError("Parameter seed must be 32 bytes long for Ed25519") | 
					
						
							|  |  |  |             seed_hash = SHA512.new(self._seed).digest()   # h | 
					
						
							|  |  |  |             self._prefix = seed_hash[32:] | 
					
						
							|  |  |  |             tmp = bytearray(seed_hash[:32]) | 
					
						
							|  |  |  |             tmp[0] &= 0xF8 | 
					
						
							|  |  |  |             tmp[31] = (tmp[31] & 0x7F) | 0x40 | 
					
						
							|  |  |  |             self._d = Integer.from_bytes(tmp, byteorder='little') | 
					
						
							|  |  |  |         # RFC 8032, 5.2.5 | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |         elif self._curve.id == _CurveID.ED448: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |             if self._d is not None: | 
					
						
							|  |  |  |                 raise ValueError("Parameter d can only be used with NIST P curves") | 
					
						
							|  |  |  |             if len(self._seed) != 57: | 
					
						
							|  |  |  |                 raise ValueError("Parameter seed must be 57 bytes long for Ed448") | 
					
						
							|  |  |  |             seed_hash = SHAKE256.new(self._seed).read(114)  # h | 
					
						
							|  |  |  |             self._prefix = seed_hash[57:] | 
					
						
							|  |  |  |             tmp = bytearray(seed_hash[:57]) | 
					
						
							|  |  |  |             tmp[0] &= 0xFC | 
					
						
							|  |  |  |             tmp[55] |= 0x80 | 
					
						
							|  |  |  |             tmp[56] = 0 | 
					
						
							|  |  |  |             self._d = Integer.from_bytes(tmp, byteorder='little') | 
					
						
							|  |  |  |         # RFC 7748, 5 | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |         elif self._curve.id == _CurveID.CURVE25519: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |             if self._d is not None: | 
					
						
							|  |  |  |                 raise ValueError("Parameter d can only be used with NIST P curves") | 
					
						
							|  |  |  |             if len(self._seed) != 32: | 
					
						
							|  |  |  |                 raise ValueError("Parameter seed must be 32 bytes long for Curve25519") | 
					
						
							|  |  |  |             tmp = bytearray(self._seed) | 
					
						
							|  |  |  |             tmp[0] &= 0xF8 | 
					
						
							|  |  |  |             tmp[31] = (tmp[31] & 0x7F) | 0x40 | 
					
						
							|  |  |  |             self._d = Integer.from_bytes(tmp, byteorder='little') | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |         elif self._curve.id == _CurveID.CURVE448: | 
					
						
							|  |  |  |             if self._d is not None: | 
					
						
							|  |  |  |                 raise ValueError("Parameter d can only be used with NIST P curves") | 
					
						
							|  |  |  |             if len(self._seed) != 56: | 
					
						
							|  |  |  |                 raise ValueError("Parameter seed must be 56 bytes long for Curve448") | 
					
						
							|  |  |  |             tmp = bytearray(self._seed) | 
					
						
							|  |  |  |             tmp[0] &= 0xFC | 
					
						
							|  |  |  |             tmp[55] |= 0x80 | 
					
						
							|  |  |  |             self._d = Integer.from_bytes(tmp, byteorder='little') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |             if self._seed is not None: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |                 raise ValueError("Parameter 'seed' cannot be used with NIST P-curves") | 
					
						
							| 
									
										
										
										
											2016-01-09 22:41:29 +01:00
										 |  |  |             self._d = Integer(self._d) | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |             if not 1 <= self._d < self._curve.order: | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |                 raise ValueError("Parameter d must be an integer smaller than the curve order") | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-18 21:03:17 +01:00
										 |  |  |     def __eq__(self, other): | 
					
						
							| 
									
										
										
										
											2022-12-01 08:34:20 +01:00
										 |  |  |         if not isinstance(other, EccKey): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-18 21:03:17 +01:00
										 |  |  |         if other.has_private() != self.has_private(): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |         return other.pointQ == self.pointQ | 
					
						
							| 
									
										
										
										
											2016-01-18 21:03:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-18 21:09:30 +01:00
										 |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         if self.has_private(): | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |             if self._curve.is_edwards: | 
					
						
							| 
									
										
										
										
											2022-12-04 08:51:38 +01:00
										 |  |  |                 extra = ", seed=%s" % tostr(binascii.hexlify(self._seed)) | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 extra = ", d=%d" % int(self._d) | 
					
						
							| 
									
										
										
										
											2016-01-18 21:09:30 +01:00
										 |  |  |         else: | 
					
						
							|  |  |  |             extra = "" | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |         if self._curve.id in (_CurveID.CURVE25519, | 
					
						
							|  |  |  |                               _CurveID.CURVE448): | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |             x = self.pointQ.x | 
					
						
							| 
									
										
										
										
											2024-08-17 23:45:53 +02:00
										 |  |  |             result = "EccKey(curve='%s', point_x=%d%s)" % (self._curve.canonical, x, extra) | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         else: | 
					
						
							|  |  |  |             x, y = self.pointQ.xy | 
					
						
							| 
									
										
										
										
											2024-08-17 23:45:53 +02:00
										 |  |  |             result = "EccKey(curve='%s', point_x=%d, point_y=%d%s)" % (self._curve.canonical, x, y, extra) | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         return result | 
					
						
							| 
									
										
										
										
											2016-01-18 21:09:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |     def has_private(self): | 
					
						
							| 
									
										
										
										
											2017-08-15 15:22:35 +02:00
										 |  |  |         """``True`` if this key can be used for making signatures or decrypting data.""" | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |         return self._d is not None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     # ECDSA | 
					
						
							| 
									
										
										
										
											2016-01-09 14:48:37 +01:00
										 |  |  |     def _sign(self, z, k): | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |         assert 0 < k < self._curve.order | 
					
						
							| 
									
										
										
										
											2016-01-16 14:08:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |         order = self._curve.order | 
					
						
							| 
									
										
										
										
											2016-01-16 14:08:23 +01:00
										 |  |  |         blind = Integer.random_range(min_inclusive=1, | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |                                      max_exclusive=order) | 
					
						
							| 
									
										
										
										
											2016-01-16 14:08:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         blind_d = self._d * blind | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |         inv_blind_k = (blind * k).inverse(order) | 
					
						
							| 
									
										
										
										
											2016-01-16 14:08:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |         r = (self._curve.G * k).x % order | 
					
						
							|  |  |  |         s = inv_blind_k * (blind * z + blind_d * r) % order | 
					
						
							| 
									
										
										
										
											2016-01-09 14:48:37 +01:00
										 |  |  |         return (r, s) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     # ECDSA | 
					
						
							| 
									
										
										
										
											2016-01-09 14:48:37 +01:00
										 |  |  |     def _verify(self, z, rs): | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |         order = self._curve.order | 
					
						
							|  |  |  |         sinv = rs[1].inverse(order) | 
					
						
							|  |  |  |         point1 = self._curve.G * ((sinv * z) % order) | 
					
						
							|  |  |  |         point2 = self.pointQ * ((sinv * rs[0]) % order) | 
					
						
							| 
									
										
										
										
											2016-01-11 07:53:47 +01:00
										 |  |  |         return (point1 + point2).x == rs[0] | 
					
						
							| 
									
										
										
										
											2016-01-09 14:48:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def d(self): | 
					
						
							|  |  |  |         if not self.has_private(): | 
					
						
							|  |  |  |             raise ValueError("This is not a private ECC key") | 
					
						
							|  |  |  |         return self._d | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def seed(self): | 
					
						
							|  |  |  |         if not self.has_private(): | 
					
						
							|  |  |  |             raise ValueError("This is not a private ECC key") | 
					
						
							|  |  |  |         return self._seed | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def pointQ(self): | 
					
						
							|  |  |  |         if self._point is None: | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |             self._point = self._curve.G * self._d | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |         return self._point | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-09 14:24:32 +01:00
										 |  |  |     def public_key(self): | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |         """A matching ECC public key.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             a new :class:`EccKey` object | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-17 23:45:53 +02:00
										 |  |  |         return EccKey(curve=self._curve.canonical, point=self.pointQ) | 
					
						
							| 
									
										
										
										
											2016-01-09 14:24:32 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |     def _export_SEC1(self, compress): | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |         if not self._curve.is_weierstrass: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |             raise ValueError("SEC1 format is only supported for NIST P curves") | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |         # See 2.2 in RFC5480 and 2.3.3 in SEC1 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |         # | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |         # The first byte is: | 
					
						
							|  |  |  |         # - 0x02:   compressed, only X-coordinate, Y-coordinate is even | 
					
						
							|  |  |  |         # - 0x03:   compressed, only X-coordinate, Y-coordinate is odd | 
					
						
							|  |  |  |         # - 0x04:   uncompressed, X-coordinate is followed by Y-coordinate | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # PAI is in theory encoded as 0x00. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-07 23:15:17 +01:00
										 |  |  |         modulus_bytes = self.pointQ.size_in_bytes() | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if compress: | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |             if self.pointQ.y.is_odd(): | 
					
						
							|  |  |  |                 first_byte = b'\x03' | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 first_byte = b'\x02' | 
					
						
							|  |  |  |             public_key = (first_byte + | 
					
						
							| 
									
										
										
										
											2019-03-07 23:15:17 +01:00
										 |  |  |                           self.pointQ.x.to_bytes(modulus_bytes)) | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |             public_key = (b'\x04' + | 
					
						
							| 
									
										
										
										
											2019-03-07 23:15:17 +01:00
										 |  |  |                           self.pointQ.x.to_bytes(modulus_bytes) + | 
					
						
							|  |  |  |                           self.pointQ.y.to_bytes(modulus_bytes)) | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |         return public_key | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |     def _export_eddsa_public(self): | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         x, y = self.pointQ.xy | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |         if self._curve.id == _CurveID.ED25519: | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |             result = bytearray(y.to_bytes(32, byteorder='little')) | 
					
						
							|  |  |  |             result[31] = ((x & 1) << 7) | result[31] | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |         elif self._curve.id == _CurveID.ED448: | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |             result = bytearray(y.to_bytes(57, byteorder='little')) | 
					
						
							|  |  |  |             result[56] = (x & 1) << 7 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise ValueError("Not an EdDSA key to export") | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         return bytes(result) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |     def _export_montgomery_public(self): | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |         if not self._curve.is_montgomery: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |             raise ValueError("Not a Montgomery key to export") | 
					
						
							|  |  |  |         x = self.pointQ.x | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |         field_size = self.pointQ.size_in_bytes() | 
					
						
							|  |  |  |         result = bytearray(x.to_bytes(field_size, byteorder='little')) | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         return bytes(result) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |     def _export_subjectPublicKeyInfo(self, compress): | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |         if self._curve.is_edwards: | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |             oid = self._curve.oid | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |             public_key = self._export_eddsa_public() | 
					
						
							|  |  |  |             params = None | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |         elif self._curve.is_montgomery: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |             oid = self._curve.oid | 
					
						
							|  |  |  |             public_key = self._export_montgomery_public() | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |             params = None | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             oid = "1.2.840.10045.2.1"   # unrestricted | 
					
						
							|  |  |  |             public_key = self._export_SEC1(compress) | 
					
						
							|  |  |  |             params = DerObjectId(self._curve.oid) | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         return _create_subject_public_key_info(oid, | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |                                                public_key, | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |                                                params) | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     def _export_rfc5915_private_der(self, include_ec_params=True): | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         assert self.has_private() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # ECPrivateKey ::= SEQUENCE { | 
					
						
							|  |  |  |         #           version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), | 
					
						
							|  |  |  |         #           privateKey     OCTET STRING, | 
					
						
							|  |  |  |         #           parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, | 
					
						
							|  |  |  |         #           publicKey  [1] BIT STRING OPTIONAL | 
					
						
							|  |  |  |         #    } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Public key - uncompressed form | 
					
						
							| 
									
										
										
										
											2019-03-07 23:15:17 +01:00
										 |  |  |         modulus_bytes = self.pointQ.size_in_bytes() | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |         public_key = (b'\x04' + | 
					
						
							| 
									
										
										
										
											2019-03-07 23:15:17 +01:00
										 |  |  |                       self.pointQ.x.to_bytes(modulus_bytes) + | 
					
						
							|  |  |  |                       self.pointQ.y.to_bytes(modulus_bytes)) | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         seq = [1, | 
					
						
							| 
									
										
										
										
											2019-03-07 23:15:17 +01:00
										 |  |  |                DerOctetString(self.d.to_bytes(modulus_bytes)), | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |                DerObjectId(self._curve.oid, explicit=0), | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |                DerBitString(public_key, explicit=1)] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not include_ec_params: | 
					
						
							|  |  |  |             del seq[2] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return DerSequence(seq).encode() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _export_pkcs8(self, **kwargs): | 
					
						
							| 
									
										
										
										
											2019-11-01 23:39:04 +01:00
										 |  |  |         from Crypto.IO import PKCS8 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |         if kwargs.get('passphrase', None) is not None and 'protection' not in kwargs: | 
					
						
							| 
									
										
										
										
											2024-01-07 17:28:50 +01:00
										 |  |  |             raise ValueError("At least the 'protection' parameter must be present") | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         if self._seed is not None: | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |             oid = self._curve.oid | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |             private_key = DerOctetString(self._seed).encode() | 
					
						
							|  |  |  |             params = None | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             oid = "1.2.840.10045.2.1"  # unrestricted | 
					
						
							|  |  |  |             private_key = self._export_rfc5915_private_der(include_ec_params=False) | 
					
						
							|  |  |  |             params = DerObjectId(self._curve.oid) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |         result = PKCS8.wrap(private_key, | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |                             oid, | 
					
						
							|  |  |  |                             key_params=params, | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |                             **kwargs) | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |     def _export_public_pem(self, compress): | 
					
						
							| 
									
										
										
										
											2019-11-01 23:39:04 +01:00
										 |  |  |         from Crypto.IO import PEM | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |         encoded_der = self._export_subjectPublicKeyInfo(compress) | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |         return PEM.encode(encoded_der, "PUBLIC KEY") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-31 21:42:32 +01:00
										 |  |  |     def _export_private_pem(self, passphrase, **kwargs): | 
					
						
							| 
									
										
										
										
											2019-11-01 23:39:04 +01:00
										 |  |  |         from Crypto.IO import PEM | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         encoded_der = self._export_rfc5915_private_der() | 
					
						
							| 
									
										
										
										
											2016-01-31 21:42:32 +01:00
										 |  |  |         return PEM.encode(encoded_der, "EC PRIVATE KEY", passphrase, **kwargs) | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _export_private_clear_pkcs8_in_clear_pem(self): | 
					
						
							| 
									
										
										
										
											2019-11-01 23:39:04 +01:00
										 |  |  |         from Crypto.IO import PEM | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |         encoded_der = self._export_pkcs8() | 
					
						
							|  |  |  |         return PEM.encode(encoded_der, "PRIVATE KEY") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _export_private_encrypted_pkcs8_in_clear_pem(self, passphrase, **kwargs): | 
					
						
							| 
									
										
										
										
											2019-11-01 23:39:04 +01:00
										 |  |  |         from Crypto.IO import PEM | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |         assert passphrase | 
					
						
							|  |  |  |         if 'protection' not in kwargs: | 
					
						
							|  |  |  |             raise ValueError("At least the 'protection' parameter should be present") | 
					
						
							|  |  |  |         encoded_der = self._export_pkcs8(passphrase=passphrase, **kwargs) | 
					
						
							|  |  |  |         return PEM.encode(encoded_der, "ENCRYPTED PRIVATE KEY") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |     def _export_openssh(self, compress): | 
					
						
							|  |  |  |         if self.has_private(): | 
					
						
							|  |  |  |             raise ValueError("Cannot export OpenSSH private keys") | 
					
						
							| 
									
										
										
										
											2016-01-31 23:01:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-02 22:40:46 +01:00
										 |  |  |         desc = self._curve.openssh | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |         if desc is None: | 
					
						
							| 
									
										
										
										
											2024-08-17 23:54:53 +02:00
										 |  |  |             raise ValueError("Cannot export %s keys as OpenSSH" % self.curve) | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |         elif desc == "ssh-ed25519": | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |             public_key = self._export_eddsa_public() | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |             comps = (tobytes(desc), tobytes(public_key)) | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |             modulus_bytes = self.pointQ.size_in_bytes() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if compress: | 
					
						
							|  |  |  |                 first_byte = 2 + self.pointQ.y.is_odd() | 
					
						
							|  |  |  |                 public_key = (bchr(first_byte) + | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |                               self.pointQ.x.to_bytes(modulus_bytes)) | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 public_key = (b'\x04' + | 
					
						
							|  |  |  |                               self.pointQ.x.to_bytes(modulus_bytes) + | 
					
						
							|  |  |  |                               self.pointQ.y.to_bytes(modulus_bytes)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             middle = desc.split("-")[2] | 
					
						
							|  |  |  |             comps = (tobytes(desc), tobytes(middle), public_key) | 
					
						
							| 
									
										
										
										
											2016-01-31 23:01:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         blob = b"".join([struct.pack(">I", len(x)) + x for x in comps]) | 
					
						
							| 
									
										
										
										
											2016-01-31 23:01:52 +01:00
										 |  |  |         return desc + " " + tostr(binascii.b2a_base64(blob)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |     def export_key(self, **kwargs): | 
					
						
							|  |  |  |         """Export this ECC key.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |         Args: | 
					
						
							|  |  |  |           format (string): | 
					
						
							| 
									
										
										
										
											2024-01-07 17:28:50 +01:00
										 |  |  |             The output format: | 
					
						
							| 
									
										
										
										
											2018-02-16 13:44:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-21 23:39:47 +01:00
										 |  |  |             - ``'DER'``. The key will be encoded in ASN.1 DER format (binary). | 
					
						
							| 
									
										
										
										
											2018-02-16 13:44:00 +01:00
										 |  |  |               For a public key, the ASN.1 ``subjectPublicKeyInfo`` structure | 
					
						
							|  |  |  |               defined in `RFC5480`_ will be used. | 
					
						
							|  |  |  |               For a private key, the ASN.1 ``ECPrivateKey`` structure defined | 
					
						
							|  |  |  |               in `RFC5915`_ is used instead (possibly within a PKCS#8 envelope, | 
					
						
							|  |  |  |               see the ``use_pkcs8`` flag below). | 
					
						
							| 
									
										
										
										
											2019-03-21 23:39:47 +01:00
										 |  |  |             - ``'PEM'``. The key will be encoded in a PEM_ envelope (ASCII). | 
					
						
							|  |  |  |             - ``'OpenSSH'``. The key will be encoded in the OpenSSH_ format | 
					
						
							| 
									
										
										
										
											2016-01-31 23:01:52 +01:00
										 |  |  |               (ASCII, public keys only). | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |             - ``'SEC1'``. The public key (i.e., the EC point) will be encoded | 
					
						
							|  |  |  |               into ``bytes`` according to Section 2.3.3 of `SEC1`_ | 
					
						
							|  |  |  |               (which is a subset of the older X9.62 ITU standard). | 
					
						
							| 
									
										
										
										
											2022-05-14 20:12:46 +02:00
										 |  |  |               Only for NIST P-curves. | 
					
						
							| 
									
										
										
										
											2022-06-22 19:52:38 +02:00
										 |  |  |             - ``'raw'``. The public key will be encoded as ``bytes``, | 
					
						
							|  |  |  |               without any metadata. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               * For NIST P-curves: equivalent to ``'SEC1'``. | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |               * For Ed25519 and Ed448: ``bytes`` in the format | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |                 defined in `RFC8032`_. | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |               * For Curve25519 and Curve448: ``bytes`` in the format | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |                 defined in `RFC7748`_. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 17:28:50 +01:00
										 |  |  |           passphrase (bytes or string): | 
					
						
							|  |  |  |             (*Private keys only*) The passphrase to protect the | 
					
						
							|  |  |  |             private key. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |           use_pkcs8 (boolean): | 
					
						
							| 
									
										
										
										
											2024-01-07 17:28:50 +01:00
										 |  |  |             (*Private keys only*) | 
					
						
							| 
									
										
										
										
											2017-08-15 15:22:35 +02:00
										 |  |  |             If ``True`` (default and recommended), the `PKCS#8`_ representation | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |             will be used. | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |             It must be ``True`` for Ed25519, Ed448, Curve25519, and Curve448. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 17:28:50 +01:00
										 |  |  |             If ``False`` and a passphrase is present, the obsolete PEM | 
					
						
							|  |  |  |             encryption will be used. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |           protection (string): | 
					
						
							| 
									
										
										
										
											2017-08-15 15:22:35 +02:00
										 |  |  |             When a private key is exported with password-protection | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |             and PKCS#8 (both ``DER`` and ``PEM`` formats), this parameter MUST be | 
					
						
							| 
									
										
										
										
											2024-01-07 17:28:50 +01:00
										 |  |  |             present, | 
					
						
							|  |  |  |             For all possible protection schemes, | 
					
						
							|  |  |  |             refer to :ref:`the encryption parameters of PKCS#8<enc_params>`. | 
					
						
							| 
									
										
										
										
											2024-09-24 10:25:16 +02:00
										 |  |  |             It is recommended to use ``'PBKDF2WithHMAC-SHA512AndAES128-CBC'``. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |           compress (boolean): | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |             If ``True``, the method returns a more compact representation | 
					
						
							|  |  |  |             of the public key, with the X-coordinate only. | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |             If ``False`` (default), the method returns the full public key. | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |             This parameter is ignored for Ed25519/Ed448/Curve25519/Curve448, | 
					
						
							|  |  |  |             as compression is mandatory. | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 17:28:50 +01:00
										 |  |  |           prot_params (dict): | 
					
						
							|  |  |  |             When a private key is exported with password-protection | 
					
						
							|  |  |  |             and PKCS#8 (both ``DER`` and ``PEM`` formats), this dictionary | 
					
						
							|  |  |  |             contains the  parameters to use to derive the encryption key | 
					
						
							|  |  |  |             from the passphrase. | 
					
						
							|  |  |  |             For all possible values, | 
					
						
							|  |  |  |             refer to :ref:`the encryption parameters of PKCS#8<enc_params>`. | 
					
						
							|  |  |  |             The recommendation is to use ``{'iteration_count':21000}`` for PBKDF2, | 
					
						
							|  |  |  |             and ``{'iteration_count':131072}`` for scrypt. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |         .. warning:: | 
					
						
							| 
									
										
										
										
											2017-08-15 15:22:35 +02:00
										 |  |  |             If you don't provide a passphrase, the private key will be | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |             exported in the clear! | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         .. note:: | 
					
						
							|  |  |  |             When exporting a private key with password-protection and `PKCS#8`_ | 
					
						
							|  |  |  |             (both ``DER`` and ``PEM`` formats), any extra parameters | 
					
						
							| 
									
										
										
										
											2019-03-21 23:39:47 +01:00
										 |  |  |             to ``export_key()`` will be passed to :mod:`Crypto.IO.PKCS8`. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         .. _PEM:        http://www.ietf.org/rfc/rfc1421.txt | 
					
						
							|  |  |  |         .. _`PEM encryption`: http://www.ietf.org/rfc/rfc1423.txt | 
					
						
							| 
									
										
										
										
											2016-01-31 23:01:52 +01:00
										 |  |  |         .. _OpenSSH:    http://www.openssh.com/txt/rfc5656.txt | 
					
						
							| 
									
										
										
										
											2018-02-16 13:44:00 +01:00
										 |  |  |         .. _RFC5480:    https://tools.ietf.org/html/rfc5480 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |         .. _SEC1:       https://www.secg.org/sec1-v2.pdf | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         .. _RFC7748:    https://tools.ietf.org/html/rfc7748 | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |         Returns: | 
					
						
							| 
									
										
										
										
											2022-05-14 20:12:46 +02:00
										 |  |  |             A multi-line string (for ``'PEM'`` and ``'OpenSSH'``) or | 
					
						
							|  |  |  |             ``bytes`` (for ``'DER'``, ``'SEC1'``, and ``'raw'``) with the encoded key. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         args = kwargs.copy() | 
					
						
							|  |  |  |         ext_format = args.pop("format") | 
					
						
							| 
									
										
										
										
											2022-05-14 20:12:46 +02:00
										 |  |  |         if ext_format not in ("PEM", "DER", "OpenSSH", "SEC1", "raw"): | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |             raise ValueError("Unknown format '%s'" % ext_format) | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |         compress = args.pop("compress", False) | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if self.has_private(): | 
					
						
							|  |  |  |             passphrase = args.pop("passphrase", None) | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |             if is_string(passphrase): | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |                 passphrase = tobytes(passphrase) | 
					
						
							| 
									
										
										
										
											2016-01-31 21:50:48 +01:00
										 |  |  |                 if not passphrase: | 
					
						
							|  |  |  |                     raise ValueError("Empty passphrase") | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |             use_pkcs8 = args.pop("use_pkcs8", True) | 
					
						
							|  |  |  |             if use_pkcs8 is False: | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |                 if self._curve.is_edwards: | 
					
						
							| 
									
										
										
										
											2024-01-07 17:28:50 +01:00
										 |  |  |                     raise ValueError("'pkcs8' must be True for EdDSA curves") | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |                 if self._curve.is_montgomery: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |                     raise ValueError("'pkcs8' must be True for Curve25519") | 
					
						
							| 
									
										
										
										
											2024-01-07 17:28:50 +01:00
										 |  |  |                 if 'protection' in args: | 
					
						
							|  |  |  |                     raise ValueError("'protection' is only supported for PKCS#8") | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |             if ext_format == "PEM": | 
					
						
							|  |  |  |                 if use_pkcs8: | 
					
						
							|  |  |  |                     if passphrase: | 
					
						
							|  |  |  |                         return self._export_private_encrypted_pkcs8_in_clear_pem(passphrase, **args) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         return self._export_private_clear_pkcs8_in_clear_pem() | 
					
						
							|  |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2016-01-31 21:42:32 +01:00
										 |  |  |                     return self._export_private_pem(passphrase, **args) | 
					
						
							| 
									
										
										
										
											2016-01-31 23:01:52 +01:00
										 |  |  |             elif ext_format == "DER": | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |                 # DER | 
					
						
							|  |  |  |                 if passphrase and not use_pkcs8: | 
					
						
							|  |  |  |                     raise ValueError("Private keys can only be encrpyted with DER using PKCS#8") | 
					
						
							|  |  |  |                 if use_pkcs8: | 
					
						
							|  |  |  |                     return self._export_pkcs8(passphrase=passphrase, **args) | 
					
						
							|  |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |                     return self._export_rfc5915_private_der() | 
					
						
							| 
									
										
										
										
											2016-01-31 23:01:52 +01:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |                 raise ValueError("Private keys cannot be exported " | 
					
						
							|  |  |  |                                  "in the '%s' format" % ext_format) | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |         else:  # Public key | 
					
						
							| 
									
										
										
										
											2016-01-31 21:50:48 +01:00
										 |  |  |             if args: | 
					
						
							|  |  |  |                 raise ValueError("Unexpected parameters: '%s'" % args) | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |             if ext_format == "PEM": | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |                 return self._export_public_pem(compress) | 
					
						
							| 
									
										
										
										
											2016-01-31 23:01:52 +01:00
										 |  |  |             elif ext_format == "DER": | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |                 return self._export_subjectPublicKeyInfo(compress) | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |             elif ext_format == "SEC1": | 
					
						
							|  |  |  |                 return self._export_SEC1(compress) | 
					
						
							| 
									
										
										
										
											2022-05-14 20:12:46 +02:00
										 |  |  |             elif ext_format == "raw": | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |                 if self._curve.is_edwards: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |                     return self._export_eddsa_public() | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |                 elif self._curve.is_montgomery: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |                     return self._export_montgomery_public() | 
					
						
							| 
									
										
										
										
											2022-05-14 20:12:46 +02:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     return self._export_SEC1(compress) | 
					
						
							| 
									
										
										
										
											2016-01-31 23:01:52 +01:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |                 return self._export_openssh(compress) | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-09 14:48:37 +01:00
										 |  |  | def generate(**kwargs): | 
					
						
							| 
									
										
										
										
											2016-01-08 23:27:35 +01:00
										 |  |  |     """Generate a new private key on the given curve.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |     Args: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       curve (string): | 
					
						
							| 
									
										
										
										
											2022-06-05 15:47:53 +02:00
										 |  |  |         Mandatory. It must be a curve name defined in the `ECC table`_. | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       randfunc (callable): | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |         Optional. The RNG to read randomness from. | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |         If ``None``, :func:`Crypto.Random.get_random_bytes` is used. | 
					
						
							| 
									
										
										
										
											2016-01-08 23:27:35 +01:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |     curve_name = kwargs.pop("curve") | 
					
						
							|  |  |  |     curve = _curves[curve_name] | 
					
						
							| 
									
										
										
										
											2016-01-09 14:48:37 +01:00
										 |  |  |     randfunc = kwargs.pop("randfunc", get_random_bytes) | 
					
						
							| 
									
										
										
										
											2016-01-12 23:18:14 +01:00
										 |  |  |     if kwargs: | 
					
						
							|  |  |  |         raise TypeError("Unknown parameters: " + str(kwargs)) | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |     if _curves[curve_name].id == _CurveID.ED25519: | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         seed = randfunc(32) | 
					
						
							|  |  |  |         new_key = EccKey(curve=curve_name, seed=seed) | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |     elif _curves[curve_name].id == _CurveID.ED448: | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |         seed = randfunc(57) | 
					
						
							|  |  |  |         new_key = EccKey(curve=curve_name, seed=seed) | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |     elif _curves[curve_name].id == _CurveID.CURVE25519: | 
					
						
							| 
									
										
										
										
											2024-06-23 22:31:40 +02:00
										 |  |  |         seed = randfunc(32) | 
					
						
							|  |  |  |         new_key = EccKey(curve=curve_name, seed=seed) | 
					
						
							| 
									
										
										
										
											2024-09-08 09:36:22 +02:00
										 |  |  |         _curves[curve_name].validate(new_key.pointQ) | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |     elif _curves[curve_name].id == _CurveID.CURVE448: | 
					
						
							|  |  |  |         seed = randfunc(56) | 
					
						
							|  |  |  |         new_key = EccKey(curve=curve_name, seed=seed) | 
					
						
							| 
									
										
										
										
											2024-09-08 09:36:22 +02:00
										 |  |  |         _curves[curve_name].validate(new_key.pointQ) | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     else: | 
					
						
							|  |  |  |         d = Integer.random_range(min_inclusive=1, | 
					
						
							|  |  |  |                                  max_exclusive=curve.order, | 
					
						
							|  |  |  |                                  randfunc=randfunc) | 
					
						
							|  |  |  |         new_key = EccKey(curve=curve_name, d=d) | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     return new_key | 
					
						
							| 
									
										
										
										
											2016-01-08 23:27:35 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-09 22:18:11 +01:00
										 |  |  | def construct(**kwargs): | 
					
						
							|  |  |  |     """Build a new ECC key (private or public) starting
 | 
					
						
							|  |  |  |     from some base components. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     In most cases, you will already have an existing key | 
					
						
							|  |  |  |     which you can read in with :func:`import_key` instead | 
					
						
							|  |  |  |     of this function. | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     Args: | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |       curve (string): | 
					
						
							| 
									
										
										
										
											2022-06-05 15:47:53 +02:00
										 |  |  |         Mandatory. The name of the elliptic curve, as defined in the `ECC table`_. | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       d (integer): | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         Mandatory for a private key and a NIST P-curve (e.g., P-256). | 
					
						
							|  |  |  |         It must be an integer in the range ``[1..order-1]``. | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       seed (bytes): | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |         Mandatory for a private key and curves Ed25519 (32 bytes), | 
					
						
							|  |  |  |         Curve25519 (32 bytes), Curve448 (56 bytes) and Ed448 (57 bytes). | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       point_x (integer): | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         The X coordinate (affine) of the ECC point. | 
					
						
							|  |  |  |         Mandatory for a public key. | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       point_y (integer): | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         The Y coordinate (affine) of the ECC point. | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |         Mandatory for a public key, | 
					
						
							|  |  |  |         except for Curve25519 and Curve448. | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Returns: | 
					
						
							|  |  |  |       :class:`EccKey` : a new ECC key object | 
					
						
							| 
									
										
										
										
											2016-01-09 22:18:11 +01:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |     curve_name = kwargs["curve"] | 
					
						
							|  |  |  |     curve = _curves[curve_name] | 
					
						
							| 
									
										
										
										
											2016-01-09 22:18:11 +01:00
										 |  |  |     point_x = kwargs.pop("point_x", None) | 
					
						
							|  |  |  |     point_y = kwargs.pop("point_y", None) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-13 10:39:35 -05:00
										 |  |  |     if "point" in kwargs: | 
					
						
							|  |  |  |         raise TypeError("Unknown keyword: point") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-17 23:36:08 +02:00
										 |  |  |     if curve.id == _CurveID.CURVE25519: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if point_x is not None: | 
					
						
							|  |  |  |             kwargs["point"] = EccXPoint(point_x, curve_name) | 
					
						
							|  |  |  |         new_key = EccKey(**kwargs) | 
					
						
							| 
									
										
										
										
											2024-09-08 09:36:22 +02:00
										 |  |  |         curve.validate(new_key.pointQ) | 
					
						
							| 
									
										
										
										
											2024-06-23 22:31:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |     elif curve.id == _CurveID.CURVE448: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if point_x is not None: | 
					
						
							|  |  |  |             kwargs["point"] = EccXPoint(point_x, curve_name) | 
					
						
							|  |  |  |         new_key = EccKey(**kwargs) | 
					
						
							| 
									
										
										
										
											2024-09-08 09:36:22 +02:00
										 |  |  |         curve.validate(new_key.pointQ) | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2016-01-09 22:18:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         if None not in (point_x, point_y): | 
					
						
							|  |  |  |             kwargs["point"] = EccPoint(point_x, point_y, curve_name) | 
					
						
							|  |  |  |         new_key = EccKey(**kwargs) | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         # Validate that the private key matches the public one | 
					
						
							|  |  |  |         # because EccKey will not do that automatically | 
					
						
							|  |  |  |         if new_key.has_private() and 'point' in kwargs: | 
					
						
							|  |  |  |             pub_key = curve.G * new_key.d | 
					
						
							|  |  |  |             if pub_key.xy != (point_x, point_y): | 
					
						
							|  |  |  |                 raise ValueError("Private and public ECC keys do not match") | 
					
						
							| 
									
										
										
										
											2016-01-13 10:39:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     return new_key | 
					
						
							| 
									
										
										
										
											2016-01-09 22:18:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 23:27:35 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  | def _import_public_der(ec_point, curve_oid=None, curve_name=None): | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     """Convert an encoded EC point into an EccKey object
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |     ec_point: byte string with the EC point (SEC1-encoded) | 
					
						
							|  |  |  |     curve_oid: string with the name the curve | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     curve_name: string with the OID of the curve | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Either curve_id or curve_name must be specified | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |     for _curve_name, curve in _curves.items(): | 
					
						
							|  |  |  |         if curve_oid and curve.oid == curve_oid: | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  |         if curve_name == _curve_name: | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |             break | 
					
						
							|  |  |  |     else: | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |         if curve_oid: | 
					
						
							|  |  |  |             raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise UnsupportedEccFeature("Unsupported ECC curve (%s)" % curve_name) | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     # See 2.2 in RFC5480 and 2.3.3 in SEC1 | 
					
						
							|  |  |  |     # The first byte is: | 
					
						
							|  |  |  |     # - 0x02:   compressed, only X-coordinate, Y-coordinate is even | 
					
						
							|  |  |  |     # - 0x03:   compressed, only X-coordinate, Y-coordinate is odd | 
					
						
							|  |  |  |     # - 0x04:   uncompressed, X-coordinate is followed by Y-coordinate | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     # PAI is in theory encoded as 0x00. | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-07 23:15:17 +01:00
										 |  |  |     modulus_bytes = curve.p.size_in_bytes() | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     point_type = bord(ec_point[0]) | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     # Uncompressed point | 
					
						
							|  |  |  |     if point_type == 0x04: | 
					
						
							| 
									
										
										
										
											2019-03-07 23:15:17 +01:00
										 |  |  |         if len(ec_point) != (1 + 2 * modulus_bytes): | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |             raise ValueError("Incorrect EC point length") | 
					
						
							| 
									
										
										
										
											2019-03-07 23:15:17 +01:00
										 |  |  |         x = Integer.from_bytes(ec_point[1:modulus_bytes+1]) | 
					
						
							|  |  |  |         y = Integer.from_bytes(ec_point[modulus_bytes+1:]) | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     # Compressed point | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |     elif point_type in (0x02, 0x03): | 
					
						
							| 
									
										
										
										
											2019-03-07 23:15:17 +01:00
										 |  |  |         if len(ec_point) != (1 + modulus_bytes): | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |             raise ValueError("Incorrect EC point length") | 
					
						
							|  |  |  |         x = Integer.from_bytes(ec_point[1:]) | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |         # Right now, we only support Short Weierstrass curves | 
					
						
							|  |  |  |         y = (x**3 - x*3 + curve.b).sqrt(curve.p) | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |         if point_type == 0x02 and y.is_odd(): | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |             y = curve.p - y | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |         if point_type == 0x03 and y.is_even(): | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |             y = curve.p - y | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     else: | 
					
						
							|  |  |  |         raise ValueError("Incorrect EC point encoding") | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |     return construct(curve=_curve_name, point_x=x, point_y=y) | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _import_subjectPublicKeyInfo(encoded, *kwargs): | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     """Convert a subjectPublicKeyInfo into an EccKey object""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # See RFC5480 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Parse the generic subjectPublicKeyInfo structure | 
					
						
							|  |  |  |     oid, ec_point, params = _expand_subject_public_key_info(encoded) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     nist_p_oids = ( | 
					
						
							|  |  |  |         "1.2.840.10045.2.1",        # id-ecPublicKey (unrestricted) | 
					
						
							|  |  |  |         "1.3.132.1.12",             # id-ecDH | 
					
						
							|  |  |  |         "1.3.132.1.13"              # id-ecMQV | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |     eddsa_oids = { | 
					
						
							|  |  |  |         "1.3.101.112": ("Ed25519", _import_ed25519_public_key),     # id-Ed25519 | 
					
						
							|  |  |  |         "1.3.101.113": ("Ed448",   _import_ed448_public_key)        # id-Ed448 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |     xdh_oids = { | 
					
						
							|  |  |  |         "1.3.101.110": ("Curve25519", _import_curve25519_public_key),   # id-X25519 | 
					
						
							|  |  |  |         "1.3.101.111": ("Curve448", _import_curve448_public_key),       # id-X448 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if oid in nist_p_oids: | 
					
						
							|  |  |  |         # See RFC5480 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Parameters are mandatory and encoded as ECParameters | 
					
						
							|  |  |  |         # ECParameters ::= CHOICE { | 
					
						
							|  |  |  |         #   namedCurve         OBJECT IDENTIFIER | 
					
						
							|  |  |  |         #   -- implicitCurve   NULL | 
					
						
							|  |  |  |         #   -- specifiedCurve  SpecifiedECDomain | 
					
						
							|  |  |  |         # } | 
					
						
							|  |  |  |         # implicitCurve and specifiedCurve are not supported (as per RFC) | 
					
						
							|  |  |  |         if not params: | 
					
						
							|  |  |  |             raise ValueError("Missing ECC parameters for ECC OID %s" % oid) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             curve_oid = DerObjectId().decode(params).value | 
					
						
							|  |  |  |         except ValueError: | 
					
						
							|  |  |  |             raise ValueError("Error decoding namedCurve") | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         # ECPoint ::= OCTET STRING | 
					
						
							|  |  |  |         return _import_public_der(ec_point, curve_oid=curve_oid) | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     elif oid in eddsa_oids: | 
					
						
							|  |  |  |         # See RFC8410 | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |         curve_name, import_eddsa_public_key = eddsa_oids[oid] | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         # Parameters must be absent | 
					
						
							|  |  |  |         if params: | 
					
						
							|  |  |  |             raise ValueError("Unexpected ECC parameters for ECC OID %s" % oid) | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |         x, y = import_eddsa_public_key(ec_point) | 
					
						
							|  |  |  |         return construct(point_x=x, point_y=y, curve=curve_name) | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |     elif oid in xdh_oids: | 
					
						
							|  |  |  |         curve_name, import_xdh_public_key = xdh_oids[oid] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         # Parameters must be absent | 
					
						
							|  |  |  |         if params: | 
					
						
							|  |  |  |             raise ValueError("Unexpected ECC parameters for ECC OID %s" % oid) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |         x = import_xdh_public_key(ec_point) | 
					
						
							|  |  |  |         return construct(point_x=x, curve=curve_name) | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     else: | 
					
						
							|  |  |  |         raise UnsupportedEccFeature("Unsupported ECC OID: %s" % oid) | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | def _import_rfc5915_der(encoded, passphrase, curve_oid=None): | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-02 21:43:28 +01:00
										 |  |  |     # See RFC5915 https://tools.ietf.org/html/rfc5915 | 
					
						
							|  |  |  |     # | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  |     # ECPrivateKey ::= SEQUENCE { | 
					
						
							|  |  |  |     #           version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), | 
					
						
							|  |  |  |     #           privateKey     OCTET STRING, | 
					
						
							|  |  |  |     #           parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, | 
					
						
							|  |  |  |     #           publicKey  [1] BIT STRING OPTIONAL | 
					
						
							|  |  |  |     #    } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-18 14:40:17 +02:00
										 |  |  |     ec_private_key = DerSequence().decode(encoded, nr_elements=(2, 3, 4)) | 
					
						
							|  |  |  |     if ec_private_key[0] != 1: | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  |         raise ValueError("Incorrect ECC private key version") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-18 14:40:17 +02:00
										 |  |  |     scalar_bytes = DerOctetString().decode(ec_private_key[1]).payload | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     next_element = 2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Try to decode 'parameters' | 
					
						
							|  |  |  |     if next_element < len(ec_private_key): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             parameters = DerObjectId(explicit=0).decode(ec_private_key[next_element]).value | 
					
						
							|  |  |  |             if curve_oid is not None and parameters != curve_oid: | 
					
						
							|  |  |  |                 raise ValueError("Curve mismatch") | 
					
						
							|  |  |  |             curve_oid = parameters | 
					
						
							|  |  |  |             next_element += 1 | 
					
						
							|  |  |  |         except ValueError: | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2019-03-02 21:43:28 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if curve_oid is None: | 
					
						
							|  |  |  |         raise ValueError("No curve found") | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |     for curve_name, curve in _curves.items(): | 
					
						
							|  |  |  |         if curve.oid == curve_oid: | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) | 
					
						
							| 
									
										
										
										
											2018-01-28 22:17:57 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-07 23:15:17 +01:00
										 |  |  |     modulus_bytes = curve.p.size_in_bytes() | 
					
						
							|  |  |  |     if len(scalar_bytes) != modulus_bytes: | 
					
						
							| 
									
										
										
										
											2018-01-28 22:17:57 +01:00
										 |  |  |         raise ValueError("Private key is too small") | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-18 14:40:17 +02:00
										 |  |  |     # Try to decode 'publicKey' | 
					
						
							|  |  |  |     point_x = point_y = None | 
					
						
							|  |  |  |     if next_element < len(ec_private_key): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             public_key_enc = DerBitString(explicit=1).decode(ec_private_key[next_element]).value | 
					
						
							|  |  |  |             public_key = _import_public_der(public_key_enc, curve_oid=curve_oid) | 
					
						
							|  |  |  |             point_x = public_key.pointQ.x | 
					
						
							|  |  |  |             point_y = public_key.pointQ.y | 
					
						
							|  |  |  |             next_element += 1 | 
					
						
							|  |  |  |         except ValueError: | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-18 14:40:17 +02:00
										 |  |  |     d = Integer.from_bytes(scalar_bytes) | 
					
						
							| 
									
										
										
										
											2019-03-01 22:54:31 +01:00
										 |  |  |     return construct(curve=curve_name, d=d, point_x=point_x, point_y=point_y) | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _import_pkcs8(encoded, passphrase): | 
					
						
							| 
									
										
										
										
											2019-11-01 23:39:04 +01:00
										 |  |  |     from Crypto.IO import PKCS8 | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     algo_oid, private_key, params = PKCS8.unwrap(encoded, passphrase) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     nist_p_oids = ( | 
					
						
							|  |  |  |         "1.2.840.10045.2.1",        # id-ecPublicKey (unrestricted) | 
					
						
							|  |  |  |         "1.3.132.1.12",             # id-ecDH | 
					
						
							|  |  |  |         "1.3.132.1.13"              # id-ecMQV | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |     eddsa_oids = { | 
					
						
							|  |  |  |         "1.3.101.112": "Ed25519",   # id-Ed25519 | 
					
						
							|  |  |  |         "1.3.101.113": "Ed448",     # id-Ed448 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |     xdh_oids = { | 
					
						
							| 
									
										
										
										
											2024-08-11 19:55:27 +02:00
										 |  |  |         "1.3.101.110": "Curve25519",  # id-X25519 | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |         "1.3.101.111": "Curve448",    # id-X448 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if algo_oid in nist_p_oids: | 
					
						
							|  |  |  |         curve_oid = DerObjectId().decode(params).value | 
					
						
							|  |  |  |         return _import_rfc5915_der(private_key, passphrase, curve_oid) | 
					
						
							|  |  |  |     elif algo_oid in eddsa_oids: | 
					
						
							|  |  |  |         if params is not None: | 
					
						
							|  |  |  |             raise ValueError("EdDSA ECC private key must not have parameters") | 
					
						
							|  |  |  |         curve_oid = None | 
					
						
							|  |  |  |         seed = DerOctetString().decode(private_key).payload | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |         return construct(curve=eddsa_oids[algo_oid], seed=seed) | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |     elif algo_oid in xdh_oids: | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |         curve_name = xdh_oids[algo_oid] | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         if params is not None: | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |             raise ValueError("%s ECC private key must not have parameters" % | 
					
						
							|  |  |  |                              curve_name) | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         curve_oid = None | 
					
						
							|  |  |  |         seed = DerOctetString().decode(private_key).payload | 
					
						
							|  |  |  |         return construct(curve=xdh_oids[algo_oid], seed=seed) | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         raise UnsupportedEccFeature("Unsupported ECC purpose (OID: %s)" % algo_oid) | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 21:02:55 +01:00
										 |  |  | def _import_x509_cert(encoded, *kwargs): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sp_info = _extract_subject_public_key_info(encoded) | 
					
						
							|  |  |  |     return _import_subjectPublicKeyInfo(sp_info) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | def _import_der(encoded, passphrase): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     try: | 
					
						
							|  |  |  |         return _import_subjectPublicKeyInfo(encoded, passphrase) | 
					
						
							| 
									
										
										
										
											2018-06-12 14:15:39 +02:00
										 |  |  |     except UnsupportedEccFeature as err: | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |         raise err | 
					
						
							|  |  |  |     except (ValueError, TypeError, IndexError): | 
					
						
							|  |  |  |         pass | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     try: | 
					
						
							|  |  |  |         return _import_x509_cert(encoded, passphrase) | 
					
						
							| 
									
										
										
										
											2018-06-12 14:15:39 +02:00
										 |  |  |     except UnsupportedEccFeature as err: | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |         raise err | 
					
						
							|  |  |  |     except (ValueError, TypeError, IndexError): | 
					
						
							|  |  |  |         pass | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         return _import_rfc5915_der(encoded, passphrase) | 
					
						
							| 
									
										
										
										
											2018-06-12 14:15:39 +02:00
										 |  |  |     except UnsupportedEccFeature as err: | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |         raise err | 
					
						
							|  |  |  |     except (ValueError, TypeError, IndexError): | 
					
						
							|  |  |  |         pass | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     try: | 
					
						
							|  |  |  |         return _import_pkcs8(encoded, passphrase) | 
					
						
							| 
									
										
										
										
											2018-06-12 14:15:39 +02:00
										 |  |  |     except UnsupportedEccFeature as err: | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |         raise err | 
					
						
							|  |  |  |     except (ValueError, TypeError, IndexError): | 
					
						
							|  |  |  |         pass | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     raise ValueError("Not an ECC DER key") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-17 09:02:23 +02:00
										 |  |  | def _import_openssh_public(encoded): | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     parts = encoded.split(b' ') | 
					
						
							|  |  |  |     if len(parts) not in (2, 3): | 
					
						
							|  |  |  |         raise ValueError("Not an openssh public key") | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     try: | 
					
						
							|  |  |  |         keystring = binascii.a2b_base64(parts[1]) | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         keyparts = [] | 
					
						
							|  |  |  |         while len(keystring) > 4: | 
					
						
							|  |  |  |             lk = struct.unpack(">I", keystring[:4])[0] | 
					
						
							|  |  |  |             keyparts.append(keystring[4:4 + lk]) | 
					
						
							|  |  |  |             keystring = keystring[4 + lk:] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if parts[0] != keyparts[0]: | 
					
						
							|  |  |  |             raise ValueError("Mismatch in openssh public key") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # NIST P curves | 
					
						
							|  |  |  |         if parts[0].startswith(b"ecdsa-sha2-"): | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |             for curve_name, curve in _curves.items(): | 
					
						
							| 
									
										
										
										
											2022-06-05 13:51:03 +02:00
										 |  |  |                 if curve.openssh is None: | 
					
						
							|  |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2022-05-14 20:39:52 +02:00
										 |  |  |                 if not curve.openssh.startswith("ecdsa-sha2"): | 
					
						
							|  |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |                 middle = tobytes(curve.openssh.split("-")[2]) | 
					
						
							|  |  |  |                 if keyparts[1] == middle: | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise ValueError("Unsupported ECC curve: " + middle) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             ecc_key = _import_public_der(keyparts[2], curve_oid=curve.oid) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # EdDSA | 
					
						
							|  |  |  |         elif parts[0] == b"ssh-ed25519": | 
					
						
							|  |  |  |             x, y = _import_ed25519_public_key(keyparts[1]) | 
					
						
							|  |  |  |             ecc_key = construct(curve="Ed25519", point_x=x, point_y=y) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise ValueError("Unsupported SSH key type: " + parts[0]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     except (IndexError, TypeError, binascii.Error): | 
					
						
							|  |  |  |         raise ValueError("Error parsing SSH key type: " + parts[0]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ecc_key | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-25 01:06:47 +02:00
										 |  |  | def _import_openssh_private_ecc(data, password): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     from ._openssh import (import_openssh_private_generic, | 
					
						
							| 
									
										
										
										
											2019-08-25 23:48:07 +02:00
										 |  |  |                            read_bytes, read_string, check_padding) | 
					
						
							| 
									
										
										
										
											2019-08-25 01:06:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     key_type, decrypted = import_openssh_private_generic(data, password) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |     eddsa_keys = { | 
					
						
							|  |  |  |         "ssh-ed25519": ("Ed25519", _import_ed25519_public_key, 32), | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     # https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent-04 | 
					
						
							|  |  |  |     if key_type.startswith("ecdsa-sha2"): | 
					
						
							| 
									
										
										
										
											2019-08-17 09:02:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         ecdsa_curve_name, decrypted = read_string(decrypted) | 
					
						
							|  |  |  |         if ecdsa_curve_name not in _curves: | 
					
						
							|  |  |  |             raise UnsupportedEccFeature("Unsupported ECC curve %s" % ecdsa_curve_name) | 
					
						
							|  |  |  |         curve = _curves[ecdsa_curve_name] | 
					
						
							|  |  |  |         modulus_bytes = (curve.modulus_bits + 7) // 8 | 
					
						
							| 
									
										
										
										
											2019-08-17 09:02:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         public_key, decrypted = read_bytes(decrypted) | 
					
						
							| 
									
										
										
										
											2019-08-22 23:16:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         if bord(public_key[0]) != 4: | 
					
						
							|  |  |  |             raise ValueError("Only uncompressed OpenSSH EC keys are supported") | 
					
						
							|  |  |  |         if len(public_key) != 2 * modulus_bytes + 1: | 
					
						
							|  |  |  |             raise ValueError("Incorrect public key length") | 
					
						
							| 
									
										
										
										
											2019-08-22 23:16:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         point_x = Integer.from_bytes(public_key[1:1+modulus_bytes]) | 
					
						
							|  |  |  |         point_y = Integer.from_bytes(public_key[1+modulus_bytes:]) | 
					
						
							| 
									
										
										
										
											2019-08-17 09:02:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         private_key, decrypted = read_bytes(decrypted) | 
					
						
							|  |  |  |         d = Integer.from_bytes(private_key) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         params = {'d': d, 'curve': ecdsa_curve_name} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |     elif key_type in eddsa_keys: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         curve_name, import_eddsa_public_key, seed_len = eddsa_keys[key_type] | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         public_key, decrypted = read_bytes(decrypted) | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |         point_x, point_y = import_eddsa_public_key(public_key) | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         private_public_key, decrypted = read_bytes(decrypted) | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |         seed = private_public_key[:seed_len] | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |         params = {'seed': seed, 'curve': curve_name} | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     else: | 
					
						
							|  |  |  |         raise ValueError("Unsupport SSH agent key type:" + key_type) | 
					
						
							| 
									
										
										
										
											2019-08-17 09:02:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     _, padded = read_string(decrypted)  # Comment | 
					
						
							| 
									
										
										
										
											2019-08-25 23:48:07 +02:00
										 |  |  |     check_padding(padded) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     return construct(point_x=point_x, point_y=point_y, **params) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _import_ed25519_public_key(encoded): | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |     """Import an Ed25519 ECC public key, encoded as raw bytes as described
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     in RFC8032_. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Args: | 
					
						
							|  |  |  |       encoded (bytes): | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |         The Ed25519 public key to import. It must be 32 bytes long. | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Returns: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |       x and y (integer) | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Raises: | 
					
						
							|  |  |  |       ValueError: when the given key cannot be parsed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     .. _RFC8032: https://datatracker.ietf.org/doc/html/rfc8032 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if len(encoded) != 32: | 
					
						
							|  |  |  |         raise ValueError("Incorrect length. Only Ed25519 public keys are supported.") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     p = Integer(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed)  # 2**255 - 19 | 
					
						
							|  |  |  |     d = 37095705934669439343138083508754565189542113879843219016388785533085940283555 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     y = bytearray(encoded) | 
					
						
							|  |  |  |     x_lsb = y[31] >> 7 | 
					
						
							|  |  |  |     y[31] &= 0x7F | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |     point_y = Integer.from_bytes(y, byteorder='little') | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     if point_y >= p: | 
					
						
							|  |  |  |         raise ValueError("Invalid Ed25519 key (y)") | 
					
						
							|  |  |  |     if point_y == 1: | 
					
						
							|  |  |  |         return 0, 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     u = (point_y**2 - 1) % p | 
					
						
							|  |  |  |     v = ((point_y**2 % p) * d + 1) % p | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |         v_inv = v.inverse(p) | 
					
						
							|  |  |  |         x2 = (u * v_inv) % p | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         point_x = Integer._tonelli_shanks(x2, p) | 
					
						
							|  |  |  |         if (point_x & 1) != x_lsb: | 
					
						
							|  |  |  |             point_x = p - point_x | 
					
						
							|  |  |  |     except ValueError: | 
					
						
							|  |  |  |         raise ValueError("Invalid Ed25519 public key") | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |     return point_x, point_y | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  | def _import_curve25519_public_key(encoded): | 
					
						
							|  |  |  |     """Import a Curve25519 ECC public key,
 | 
					
						
							|  |  |  |     encoded as raw bytes as described in RFC7748_. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Args: | 
					
						
							|  |  |  |       encoded (bytes): | 
					
						
							|  |  |  |         The Curve25519 public key to import. It must be 32 bytes long. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns: | 
					
						
							|  |  |  |       x (integer) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Raises: | 
					
						
							|  |  |  |       ValueError: when the given key cannot be parsed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     .. _RFC7748: https://datatracker.ietf.org/doc/html/rfc7748 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if len(encoded) != 32: | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |         raise ValueError("Incorrect Curve25519 key length") | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     x = bytearray(encoded) | 
					
						
							| 
									
										
										
										
											2024-06-23 22:31:40 +02:00
										 |  |  |     # RFC 7741, Section 5 | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |     x[31] &= 0x7F | 
					
						
							|  |  |  |     point_x = Integer.from_bytes(x, byteorder='little') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return point_x | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  | def _import_curve448_public_key(encoded): | 
					
						
							|  |  |  |     """Import a Curve448 ECC public key,
 | 
					
						
							|  |  |  |     encoded as raw bytes as described in RFC7748_. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Args: | 
					
						
							|  |  |  |       encoded (bytes): | 
					
						
							|  |  |  |         The Curve448 public key to import. It must be 56 bytes long. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns: | 
					
						
							|  |  |  |       x (integer) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Raises: | 
					
						
							|  |  |  |       ValueError: when the given key cannot be parsed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     .. _RFC7748: https://datatracker.ietf.org/doc/html/rfc7748 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if len(encoded) != 56: | 
					
						
							|  |  |  |         raise ValueError("Incorrect Curve448 key length") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     point_x = Integer.from_bytes(encoded, byteorder='little') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return point_x | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  | def _import_ed448_public_key(encoded): | 
					
						
							|  |  |  |     """Import an Ed448 ECC public key, encoded as raw bytes as described
 | 
					
						
							|  |  |  |     in RFC8032_. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Args: | 
					
						
							|  |  |  |       encoded (bytes): | 
					
						
							|  |  |  |         The Ed448 public key to import. It must be 57 bytes long. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns: | 
					
						
							| 
									
										
										
										
											2024-01-27 16:53:23 +01:00
										 |  |  |         x and y (integer) | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Raises: | 
					
						
							|  |  |  |       ValueError: when the given key cannot be parsed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     .. _RFC8032: https://datatracker.ietf.org/doc/html/rfc8032 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if len(encoded) != 57: | 
					
						
							|  |  |  |         raise ValueError("Incorrect length. Only Ed448 public keys are supported.") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |     p = _curves['curve448'].p | 
					
						
							|  |  |  |     d = p - 39081 | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     y = encoded[:56] | 
					
						
							| 
									
										
										
										
											2022-06-05 13:41:33 +02:00
										 |  |  |     x_lsb = bord(encoded[56]) >> 7 | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |     point_y = Integer.from_bytes(y, byteorder='little') | 
					
						
							|  |  |  |     if point_y >= p: | 
					
						
							|  |  |  |         raise ValueError("Invalid Ed448 key (y)") | 
					
						
							|  |  |  |     if point_y == 1: | 
					
						
							|  |  |  |         return 0, 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     u = (point_y**2 - 1) % p | 
					
						
							|  |  |  |     v = ((point_y**2 % p) * d - 1) % p | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         v_inv = v.inverse(p) | 
					
						
							|  |  |  |         x2 = (u * v_inv) % p | 
					
						
							|  |  |  |         point_x = Integer._tonelli_shanks(x2, p) | 
					
						
							|  |  |  |         if (point_x & 1) != x_lsb: | 
					
						
							|  |  |  |             point_x = p - point_x | 
					
						
							|  |  |  |     except ValueError: | 
					
						
							|  |  |  |         raise ValueError("Invalid Ed448 public key") | 
					
						
							|  |  |  |     return point_x, point_y | 
					
						
							| 
									
										
										
										
											2019-08-17 09:02:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  | def import_key(encoded, passphrase=None, curve_name=None): | 
					
						
							| 
									
										
										
										
											2016-01-27 08:00:28 +01:00
										 |  |  |     """Import an ECC key (public or private).
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |     Args: | 
					
						
							|  |  |  |       encoded (bytes or multi-line string): | 
					
						
							| 
									
										
										
										
											2016-01-27 08:00:28 +01:00
										 |  |  |         The ECC key to import. | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         The function will try to automatically detect the right format. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:00:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         Supported formats for an ECC **public** key: | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-05 15:47:53 +02:00
										 |  |  |         * X.509 certificate: binary (DER) or ASCII (PEM). | 
					
						
							|  |  |  |         * X.509 ``subjectPublicKeyInfo``: binary (DER) or ASCII (PEM). | 
					
						
							|  |  |  |         * SEC1_ (or X9.62), as ``bytes``. NIST P curves only. | 
					
						
							|  |  |  |           You must also provide the ``curve_name`` (with a value from the `ECC table`_) | 
					
						
							|  |  |  |         * OpenSSH line, defined in RFC5656_ and RFC8709_ (ASCII). | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |           This is normally the content of files like ``~/.ssh/id_ecdsa.pub``. | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         Supported formats for an ECC **private** key: | 
					
						
							| 
									
										
										
										
											2016-01-27 08:00:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-05 15:47:53 +02:00
										 |  |  |         * A binary ``ECPrivateKey`` structure, as defined in `RFC5915`_ (DER). | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |           NIST P curves only. | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |         * A `PKCS#8`_ structure (or the more recent Asymmetric Key | 
					
						
							|  |  |  |           Package, RFC5958_): binary (DER) or ASCII (PEM). | 
					
						
							| 
									
										
										
										
											2022-06-05 15:47:53 +02:00
										 |  |  |         * `OpenSSH 6.5`_ and newer versions (ASCII). | 
					
						
							| 
									
										
										
										
											2016-01-27 08:00:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |         Private keys can be in the clear or password-protected. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:00:28 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |       passphrase (byte string): | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |         The passphrase to use for decrypting a private key. | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |         Encryption may be applied protected at the PEM level (not recommended) | 
					
						
							|  |  |  |         or at the PKCS#8 level (recommended). | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |         This parameter is ignored if the key in input is not encrypted. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:00:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |       curve_name (string): | 
					
						
							| 
									
										
										
										
											2022-06-05 15:47:53 +02:00
										 |  |  |         For a SEC1 encoding only. This is the name of the curve, | 
					
						
							|  |  |  |         as defined in the `ECC table`_. | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-22 19:52:38 +02:00
										 |  |  |     .. note:: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         To import EdDSA private and public keys, when encoded as raw ``bytes``, use: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         * :func:`Crypto.Signature.eddsa.import_public_key`, or | 
					
						
							|  |  |  |         * :func:`Crypto.Signature.eddsa.import_private_key`. | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-24 10:56:18 +02:00
										 |  |  |     .. note:: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |         To import X25519/X448 private and public keys, when encoded as raw ``bytes``, use: | 
					
						
							| 
									
										
										
										
											2024-06-24 10:56:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-24 22:29:52 +02:00
										 |  |  |         * :func:`Crypto.Protocol.DH.import_x25519_public_key` | 
					
						
							|  |  |  |         * :func:`Crypto.Protocol.DH.import_x25519_private_key` | 
					
						
							|  |  |  |         * :func:`Crypto.Protocol.DH.import_x448_public_key` | 
					
						
							|  |  |  |         * :func:`Crypto.Protocol.DH.import_x448_private_key` | 
					
						
							| 
									
										
										
										
											2024-06-24 10:56:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |     Returns: | 
					
						
							|  |  |  |       :class:`EccKey` : a new ECC key object | 
					
						
							| 
									
										
										
										
											2016-01-27 08:00:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |     Raises: | 
					
						
							|  |  |  |       ValueError: when the given key cannot be parsed (possibly because | 
					
						
							| 
									
										
										
										
											2016-01-27 08:00:28 +01:00
										 |  |  |         the pass phrase is wrong). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |     .. _RFC1421: https://datatracker.ietf.org/doc/html/rfc1421 | 
					
						
							|  |  |  |     .. _RFC1423: https://datatracker.ietf.org/doc/html/rfc1423 | 
					
						
							|  |  |  |     .. _RFC5915: https://datatracker.ietf.org/doc/html/rfc5915 | 
					
						
							|  |  |  |     .. _RFC5656: https://datatracker.ietf.org/doc/html/rfc5656 | 
					
						
							|  |  |  |     .. _RFC8709: https://datatracker.ietf.org/doc/html/rfc8709 | 
					
						
							|  |  |  |     .. _RFC5958: https://datatracker.ietf.org/doc/html/rfc5958 | 
					
						
							|  |  |  |     .. _`PKCS#8`: https://datatracker.ietf.org/doc/html/rfc5208 | 
					
						
							|  |  |  |     .. _`OpenSSH 6.5`: https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |     .. _SEC1: https://www.secg.org/sec1-v2.pdf | 
					
						
							| 
									
										
										
										
											2016-01-27 08:00:28 +01:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-01 23:39:04 +01:00
										 |  |  |     from Crypto.IO import PEM | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  |     encoded = tobytes(encoded) | 
					
						
							|  |  |  |     if passphrase is not None: | 
					
						
							|  |  |  |         passphrase = tobytes(passphrase) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # PEM | 
					
						
							| 
									
										
										
										
											2019-08-17 09:02:23 +02:00
										 |  |  |     if encoded.startswith(b'-----BEGIN OPENSSH PRIVATE KEY'): | 
					
						
							|  |  |  |         text_encoded = tostr(encoded) | 
					
						
							|  |  |  |         openssh_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) | 
					
						
							| 
									
										
										
										
											2019-08-25 01:06:47 +02:00
										 |  |  |         result = _import_openssh_private_ecc(openssh_encoded, passphrase) | 
					
						
							| 
									
										
										
										
											2019-08-17 09:02:23 +02:00
										 |  |  |         return result | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     elif encoded.startswith(b'-----'): | 
					
						
							| 
									
										
										
										
											2019-04-03 15:13:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 21:21:59 +02:00
										 |  |  |         text_encoded = tostr(encoded) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 15:13:59 +02:00
										 |  |  |         # Remove any EC PARAMETERS section | 
					
						
							|  |  |  |         # Ignore its content because the curve type must be already given in the key | 
					
						
							| 
									
										
										
										
											2020-11-30 14:17:12 +01:00
										 |  |  |         ecparams_start = "-----BEGIN EC PARAMETERS-----" | 
					
						
							|  |  |  |         ecparams_end = "-----END EC PARAMETERS-----" | 
					
						
							|  |  |  |         text_encoded = re.sub(ecparams_start + ".*?" + ecparams_end, "", | 
					
						
							| 
									
										
										
										
											2022-04-15 00:15:48 +02:00
										 |  |  |                               text_encoded, | 
					
						
							|  |  |  |                               flags=re.DOTALL) | 
					
						
							| 
									
										
										
										
											2019-04-03 15:13:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         der_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  |         if enc_flag: | 
					
						
							|  |  |  |             passphrase = None | 
					
						
							| 
									
										
										
										
											2018-01-28 22:17:57 +01:00
										 |  |  |         try: | 
					
						
							|  |  |  |             result = _import_der(der_encoded, passphrase) | 
					
						
							| 
									
										
										
										
											2018-06-12 14:15:39 +02:00
										 |  |  |         except UnsupportedEccFeature as uef: | 
					
						
							| 
									
										
										
										
											2018-01-28 22:17:57 +01:00
										 |  |  |             raise uef | 
					
						
							|  |  |  |         except ValueError: | 
					
						
							|  |  |  |             raise ValueError("Invalid DER encoding inside the PEM file") | 
					
						
							|  |  |  |         return result | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # OpenSSH | 
					
						
							| 
									
										
										
										
											2022-05-16 09:13:35 +02:00
										 |  |  |     if encoded.startswith((b'ecdsa-sha2-', b'ssh-ed25519')): | 
					
						
							| 
									
										
										
										
											2019-08-17 09:02:23 +02:00
										 |  |  |         return _import_openssh_public(encoded) | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # DER | 
					
						
							| 
									
										
										
										
											2019-03-26 23:27:15 +01:00
										 |  |  |     if len(encoded) > 0 and bord(encoded[0]) == 0x30: | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  |         return _import_der(encoded, passphrase) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |     # SEC1 | 
					
						
							| 
									
										
										
										
											2023-08-26 18:57:49 +02:00
										 |  |  |     if len(encoded) > 0 and bord(encoded[0]) in (0x02, 0x03, 0x04): | 
					
						
							| 
									
										
										
										
											2021-10-10 23:54:21 +02:00
										 |  |  |         if curve_name is None: | 
					
						
							|  |  |  |             raise ValueError("No curve name was provided") | 
					
						
							|  |  |  |         return _import_public_der(encoded, curve_name=curve_name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  |     raise ValueError("ECC key format is not supported") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 23:27:35 +01:00
										 |  |  | if __name__ == "__main__": | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 23:27:35 +01:00
										 |  |  |     import time | 
					
						
							| 
									
										
										
										
											2018-10-15 21:58:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 23:27:35 +01:00
										 |  |  |     d = 0xc51e4753afdec1e6b6c6a5b992f43f8dd0c7a8933072708b6522468b2ffb06fd | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-21 22:30:47 +01:00
										 |  |  |     point = _curves['p256'].G.copy() | 
					
						
							| 
									
										
										
										
											2019-01-30 08:41:21 +01:00
										 |  |  |     count = 3000 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     start = time.time() | 
					
						
							|  |  |  |     for x in range(count): | 
					
						
							| 
									
										
										
										
											2019-02-27 12:07:58 +01:00
										 |  |  |         pointX = point * d | 
					
						
							|  |  |  |     print("(P-256 G)", (time.time() - start) / count * 1000, "ms") | 
					
						
							| 
									
										
										
										
											2019-01-30 08:41:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 23:27:35 +01:00
										 |  |  |     start = time.time() | 
					
						
							| 
									
										
										
										
											2018-10-15 21:58:33 +02:00
										 |  |  |     for x in range(count): | 
					
						
							| 
									
										
										
										
											2019-02-27 12:07:58 +01:00
										 |  |  |         pointX = pointX * d | 
					
						
							|  |  |  |     print("(P-256 arbitrary point)", (time.time() - start) / count * 1000, "ms") |