| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | import struct | 
					
						
							|  |  |  | import binascii | 
					
						
							| 
									
										
										
										
											2018-10-15 21:58:33 +02:00
										 |  |  | from collections import namedtuple | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | from Crypto.Util.number import bytes_to_long, long_to_bytes | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | from Crypto.Math.Numbers import Integer | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  | from Crypto.Random import get_random_bytes | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | from Crypto.Util.asn1 import (DerObjectId, DerOctetString, DerSequence, | 
					
						
							|  |  |  |                               DerBitString) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | from Crypto.IO import PKCS8, PEM | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, | 
					
						
							| 
									
										
										
										
											2019-02-08 08:39:45 +01:00
										 |  |  |                                   SmartPointer, c_size_t, c_uint8_ptr, | 
					
						
							| 
									
										
										
										
											2019-02-19 21:58:36 +01:00
										 |  |  |                                   c_ulonglong, c_uint) | 
					
						
							| 
									
										
										
										
											2019-02-08 08:39:45 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | from Crypto.Random.random import getrandbits | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | _ec_lib = load_pycryptodome_raw_lib("Crypto.PublicKey._ec_ws", """
 | 
					
						
							|  |  |  | typedef void EcContext; | 
					
						
							|  |  |  | typedef void EcPoint; | 
					
						
							|  |  |  | int ec_ws_new_context(EcContext **pec_ctx, | 
					
						
							|  |  |  |                       const uint8_t *modulus, | 
					
						
							|  |  |  |                       const uint8_t *b, | 
					
						
							| 
									
										
										
										
											2019-02-08 22:49:36 +01:00
										 |  |  |                       const uint8_t *order, | 
					
						
							| 
									
										
										
										
											2019-02-26 22:55:58 +01:00
										 |  |  |                       size_t len, | 
					
						
							|  |  |  |                       uint64_t seed); | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | void ec_free_context(EcContext *ec_ctx); | 
					
						
							| 
									
										
										
										
											2019-02-19 21:58:36 +01:00
										 |  |  | int ec_ws_new_point(EcPoint **pecp, | 
					
						
							| 
									
										
										
										
											2019-02-27 07:53:02 +01:00
										 |  |  |                     const uint8_t *x, | 
					
						
							|  |  |  |                     const uint8_t *y, | 
					
						
							| 
									
										
										
										
											2019-02-19 21:58:36 +01:00
										 |  |  |                     size_t len, | 
					
						
							| 
									
										
										
										
											2019-02-22 21:11:06 +01:00
										 |  |  |                     const EcContext *ec_ctx); | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | void ec_free_point(EcPoint *ecp); | 
					
						
							| 
									
										
										
										
											2019-02-27 07:53:02 +01:00
										 |  |  | int ec_ws_get_xy(uint8_t *x, | 
					
						
							|  |  |  |                  uint8_t *y, | 
					
						
							|  |  |  |                  size_t len, | 
					
						
							|  |  |  |                  const EcPoint *ecp); | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | int ec_ws_double(EcPoint *p); | 
					
						
							|  |  |  | int ec_ws_add(EcPoint *ecpa, EcPoint *ecpb); | 
					
						
							| 
									
										
										
										
											2019-02-27 07:53:02 +01:00
										 |  |  | int ec_ws_scalar(EcPoint *ecp, | 
					
						
							|  |  |  |                  const uint8_t *k, | 
					
						
							|  |  |  |                  size_t len, | 
					
						
							|  |  |  |                  uint64_t seed); | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | int ec_ws_clone(EcPoint **pecp2, const EcPoint *ecp); | 
					
						
							| 
									
										
										
										
											2019-02-27 07:53:02 +01:00
										 |  |  | int ec_ws_copy(EcPoint *ecp1, const EcPoint *ecp2); | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | int ec_ws_cmp(const EcPoint *ecp1, const EcPoint *ecp2); | 
					
						
							|  |  |  | int ec_ws_neg(EcPoint *p); | 
					
						
							| 
									
										
										
										
											2019-02-27 07:53:02 +01:00
										 |  |  | int ec_ws_normalize(EcPoint *ecp); | 
					
						
							|  |  |  | int ec_ws_is_pai(EcPoint *ecp); | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | """)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | p256_modulus = long_to_bytes(0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff, 32) | 
					
						
							|  |  |  | p256_b = long_to_bytes(0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b, 32) | 
					
						
							| 
									
										
										
										
											2019-02-08 22:49:36 +01:00
										 |  |  | p256_order = long_to_bytes(0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551, 32) | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | assert(len(p256_modulus) == 32) | 
					
						
							|  |  |  | assert(len(p256_b) == 32) | 
					
						
							| 
									
										
										
										
											2019-02-08 22:49:36 +01:00
										 |  |  | assert(len(p256_order) == 32) | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | _ec_p256_context = VoidPointer() | 
					
						
							|  |  |  | result = _ec_lib.ec_ws_new_context(_ec_p256_context.address_of(), | 
					
						
							|  |  |  |                                    c_uint8_ptr(p256_modulus), | 
					
						
							|  |  |  |                                    c_uint8_ptr(p256_b), | 
					
						
							| 
									
										
										
										
											2019-02-08 22:49:36 +01:00
										 |  |  |                                    c_uint8_ptr(p256_order), | 
					
						
							| 
									
										
										
										
											2019-02-26 22:55:58 +01:00
										 |  |  |                                    c_size_t(len(p256_modulus)), | 
					
						
							|  |  |  |                                    c_ulonglong(getrandbits(64)) | 
					
						
							|  |  |  |                                    ) | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | if result: | 
					
						
							|  |  |  |     raise ImportError("Error %d initializing P256 context" % result) | 
					
						
							|  |  |  | _ec_p256_context = SmartPointer(_ec_p256_context.get(), | 
					
						
							|  |  |  |                                 _ec_lib.ec_free_context) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-28 22:17:57 +01:00
										 |  |  | class UnsupportedEccFeature(ValueError): | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  | class EccPoint(object): | 
					
						
							| 
									
										
										
										
											2016-02-07 13:01:09 +01:00
										 |  |  |     """A class to abstract a point over an Elliptic Curve.
 | 
					
						
							| 
									
										
										
										
											2017-08-15 15:22:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     :ivar x: The X-coordinate of the ECC point | 
					
						
							|  |  |  |     :vartype x: integer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :ivar y: The Y-coordinate of the ECC point | 
					
						
							|  |  |  |     :vartype y: integer | 
					
						
							| 
									
										
										
										
											2016-02-07 13:01:09 +01:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-22 21:11:06 +01:00
										 |  |  |     def __init__(self, x, y): | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         xb = long_to_bytes(x, 32) | 
					
						
							|  |  |  |         yb = long_to_bytes(y, 32) | 
					
						
							|  |  |  |         assert(len(xb) == 32) | 
					
						
							|  |  |  |         assert(len(yb) == 32) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._point = VoidPointer() | 
					
						
							|  |  |  |         result = _ec_lib.ec_ws_new_point(self._point.address_of(), | 
					
						
							|  |  |  |                                          c_uint8_ptr(xb), | 
					
						
							|  |  |  |                                          c_uint8_ptr(yb), | 
					
						
							|  |  |  |                                          c_size_t(len(xb)), | 
					
						
							| 
									
										
										
										
											2019-02-22 21:11:06 +01:00
										 |  |  |                                          _ec_p256_context.get()) | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         if result: | 
					
						
							| 
									
										
										
										
											2019-02-01 22:49:07 +01:00
										 |  |  |             if result == 15: | 
					
						
							|  |  |  |                 raise ValueError("The EC point does not belong to the curve") | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |             raise ValueError("Error %d while instantiating an EC point" % result) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Ensure that object disposal of this Python object will (eventually) | 
					
						
							|  |  |  |         # free the memory allocated by the raw library for the EC point | 
					
						
							|  |  |  |         self._point = SmartPointer(self._point.get(), | 
					
						
							|  |  |  |                                    _ec_lib.ec_free_point) | 
					
						
							| 
									
										
										
										
											2016-01-11 08:51:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 08:08:08 +01:00
										 |  |  |     def set(self, point): | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         self._point = VoidPointer() | 
					
						
							|  |  |  |         result = _ec_lib.ec_ws_clone(self._point.address_of(), | 
					
						
							|  |  |  |                                      point._point.get()) | 
					
						
							|  |  |  |         if result: | 
					
						
							|  |  |  |             raise ValueError("Error %d while cloning an EC point" % result) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._point = SmartPointer(self._point.get(), | 
					
						
							|  |  |  |                                    _ec_lib.ec_free_point) | 
					
						
							| 
									
										
										
										
											2016-01-11 08:08:08 +01:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  |     def __eq__(self, point): | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         return 0 == _ec_lib.ec_ws_cmp(self._point.get(), point._point.get()) | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __neg__(self): | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         np = self.copy() | 
					
						
							|  |  |  |         result = _ec_lib.ec_ws_neg(np._point_get()) | 
					
						
							|  |  |  |         if result: | 
					
						
							|  |  |  |             raise ValueError("Error %d while negating an EC point" % result) | 
					
						
							|  |  |  |         return np | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def copy(self): | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         x, y = self.xy | 
					
						
							|  |  |  |         np = EccPoint(x, y) | 
					
						
							|  |  |  |         return np | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def is_point_at_infinity(self): | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         return self.xy == (0, 0) | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def point_at_infinity(): | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |         return EccPoint(0, 0) | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def x(self): | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         return self.xy[0] | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def y(self): | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         return self.xy[1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def xy(self): | 
					
						
							|  |  |  |         xb = bytearray(32) | 
					
						
							|  |  |  |         yb = bytearray(32) | 
					
						
							|  |  |  |         result = _ec_lib.ec_ws_get_xy(c_uint8_ptr(xb), | 
					
						
							|  |  |  |                                       c_uint8_ptr(yb), | 
					
						
							|  |  |  |                                       c_size_t(len(xb)), | 
					
						
							|  |  |  |                                       self._point.get()) | 
					
						
							|  |  |  |         if result: | 
					
						
							|  |  |  |             raise ValueError("Error %d while encoding an EC point" % result) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-22 16:30:40 +01:00
										 |  |  |         return (Integer(bytes_to_long(xb)), Integer(bytes_to_long(yb))) | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def double(self): | 
					
						
							| 
									
										
										
										
											2017-08-15 15:22:35 +02:00
										 |  |  |         """Double this point (in-place operation).
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         :Return: | 
					
						
							|  |  |  |             :class:`EccPoint` : this same object (to enable chaining) | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         result = _ec_lib.ec_ws_double(self._point.get()) | 
					
						
							|  |  |  |         if result: | 
					
						
							|  |  |  |             raise ValueError("Error %d while doubling an EC point" % result) | 
					
						
							| 
									
										
										
										
											2016-01-11 08:51:04 +01:00
										 |  |  |         return self | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 08:08:08 +01:00
										 |  |  |     def __iadd__(self, point): | 
					
						
							| 
									
										
										
										
											2016-01-11 08:51:04 +01:00
										 |  |  |         """Add a second point to this one""" | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         result = _ec_lib.ec_ws_add(self._point.get(), point._point.get()) | 
					
						
							|  |  |  |         if result: | 
					
						
							| 
									
										
										
										
											2019-02-01 22:49:07 +01:00
										 |  |  |             if result == 16: | 
					
						
							|  |  |  |                 raise ValueError("Trying to add two EC point that are not on the same curve") | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |             raise ValueError("Error %d while adding two EC points" % result) | 
					
						
							| 
									
										
										
										
											2016-01-11 08:51:04 +01:00
										 |  |  |         return self | 
					
						
							| 
									
										
										
										
											2016-01-11 08:08:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __add__(self, point): | 
					
						
							|  |  |  |         """Return a new point, the addition of this one and another""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         np = self.copy() | 
					
						
							|  |  |  |         np += point | 
					
						
							|  |  |  |         return np | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-30 08:41:21 +01:00
										 |  |  |     def __imul__(self, scalar): | 
					
						
							|  |  |  |         """Multiply this point by a scalar""" | 
					
						
							| 
									
										
										
										
											2015-12-28 23:22:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-01 14:42:06 -05:00
										 |  |  |         if scalar < 0: | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |             raise ValueError("Scalar multiplication is only defined for non-negative integers") | 
					
						
							|  |  |  |         sb = long_to_bytes(scalar) | 
					
						
							| 
									
										
										
										
											2019-02-20 11:03:13 +01:00
										 |  |  |         result = _ec_lib.ec_ws_scalar(self._point.get(), | 
					
						
							|  |  |  |                                       c_uint8_ptr(sb), | 
					
						
							|  |  |  |                                       c_size_t(len(sb)), | 
					
						
							|  |  |  |                                       c_ulonglong(getrandbits(64))) | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         if result: | 
					
						
							|  |  |  |             raise ValueError("Error %d during scalar multiplication" % result) | 
					
						
							| 
									
										
										
										
											2019-01-30 08:41:21 +01:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __mul__(self, scalar): | 
					
						
							|  |  |  |         """Return a new point, the scalar product of this one""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         np = self.copy() | 
					
						
							|  |  |  |         np *= scalar | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         return np | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-15 21:58:33 +02:00
										 |  |  | _Curve = namedtuple("_Curve", "p b order Gx Gy G names oid") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _curve_gx = Integer(0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296) | 
					
						
							|  |  |  | _curve_gy = Integer(0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _curve = _Curve( | 
					
						
							|  |  |  |     Integer(0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff), | 
					
						
							|  |  |  |     Integer(0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b), | 
					
						
							|  |  |  |     Integer(0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551), | 
					
						
							|  |  |  |     _curve_gx, | 
					
						
							|  |  |  |     _curve_gy, | 
					
						
							| 
									
										
										
										
											2019-02-22 21:11:06 +01:00
										 |  |  |     EccPoint(_curve_gx, _curve_gy), | 
					
						
							| 
									
										
										
										
											2018-10-15 21:58:33 +02:00
										 |  |  |     ("P-256", "prime256v1", "secp256r1"), | 
					
						
							|  |  |  |     "1.2.840.10045.3.1.7", | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | del namedtuple | 
					
						
							| 
									
										
										
										
											2016-01-09 14:48:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :ivar curve: The name of the ECC curve | 
					
						
							|  |  |  |     :vartype curve: string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :ivar pointQ: an ECC point representating the public component | 
					
						
							|  |  |  |     :vartype pointQ: :class:`EccPoint` | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-26 18:03:11 +02:00
										 |  |  |     :ivar d: A scalar representating the private component | 
					
						
							|  |  |  |     :vartype d: integer | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |             It must be *"P-256"*, *"prime256v1"* or *"secp256r1"*. | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |           d : integer | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |             Only for a private key. It must be in the range ``[1..order-1]``. | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |           point : EccPoint | 
					
						
							|  |  |  |             Mandatory for a public key. If provided for a private key, | 
					
						
							|  |  |  |             the implementation will NOT check whether it matches ``d``. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-09 22:18:11 +01:00
										 |  |  |         kwargs_ = dict(kwargs) | 
					
						
							|  |  |  |         self.curve = kwargs_.pop("curve", None) | 
					
						
							|  |  |  |         self._d = kwargs_.pop("d", None) | 
					
						
							|  |  |  |         self._point = kwargs_.pop("point", None) | 
					
						
							| 
									
										
										
										
											2016-01-12 23:18:14 +01:00
										 |  |  |         if kwargs_: | 
					
						
							|  |  |  |             raise TypeError("Unknown parameters: " + str(kwargs_)) | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-13 22:55:24 +01:00
										 |  |  |         if self.curve not in _curve.names: | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |             raise ValueError("Unsupported curve (%s)", self.curve) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self._d is None: | 
					
						
							|  |  |  |             if self._point is None: | 
					
						
							|  |  |  |                 raise ValueError("Either private or public ECC component must be specified") | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2016-01-09 22:41:29 +01:00
										 |  |  |             self._d = Integer(self._d) | 
					
						
							| 
									
										
										
										
											2016-01-02 15:33:28 -05:00
										 |  |  |             if not 1 <= self._d < _curve.order: | 
					
						
							|  |  |  |                 raise ValueError("Invalid ECC private component") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-18 21:03:17 +01:00
										 |  |  |     def __eq__(self, other): | 
					
						
							|  |  |  |         if other.has_private() != self.has_private(): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return (other.pointQ.x == self.pointQ.x) and (other.pointQ.y == self.pointQ.y) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-18 21:09:30 +01:00
										 |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         if self.has_private(): | 
					
						
							|  |  |  |             extra = ", d=%d" % int(self._d) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             extra = "" | 
					
						
							|  |  |  |         return "EccKey(curve='P-256', x=%d, y=%d%s)" %\ | 
					
						
							|  |  |  |                (self.pointQ.x, self.pointQ.y, extra) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-09 14:48:37 +01:00
										 |  |  |     def _sign(self, z, k): | 
					
						
							|  |  |  |         assert 0 < k < _curve.order | 
					
						
							| 
									
										
										
										
											2016-01-16 14:08:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         blind = Integer.random_range(min_inclusive=1, | 
					
						
							|  |  |  |                                      max_exclusive=_curve.order) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         blind_d = self._d * blind | 
					
						
							|  |  |  |         inv_blind_k = (blind * k).inverse(_curve.order) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-30 08:32:41 +01:00
										 |  |  |         r = (_curve.G * k).x % _curve.order | 
					
						
							| 
									
										
										
										
											2016-01-16 14:08:23 +01:00
										 |  |  |         s = inv_blind_k * (blind * z + blind_d * r) % _curve.order | 
					
						
							| 
									
										
										
										
											2016-01-09 14:48:37 +01:00
										 |  |  |         return (r, s) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _verify(self, z, rs): | 
					
						
							|  |  |  |         sinv = rs[1].inverse(_curve.order) | 
					
						
							| 
									
										
										
										
											2016-01-11 07:53:47 +01:00
										 |  |  |         point1 = _curve.G * ((sinv * z) % _curve.order) | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  |         point2 = self.pointQ * ((sinv * rs[0]) % _curve.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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def pointQ(self): | 
					
						
							|  |  |  |         if self._point is None: | 
					
						
							| 
									
										
										
										
											2016-01-11 07:53:47 +01:00
										 |  |  |             self._point = _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 | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-09 14:24:32 +01:00
										 |  |  |         return EccKey(curve="P-256", point=self.pointQ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |     def _export_subjectPublicKeyInfo(self, compress): | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +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-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         order_bytes = _curve.order.size_in_bytes() | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if compress: | 
					
						
							|  |  |  |             first_byte = 2 + self.pointQ.y.is_odd() | 
					
						
							|  |  |  |             public_key = (bchr(first_byte) + | 
					
						
							|  |  |  |                           self.pointQ.x.to_bytes(order_bytes)) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |             public_key = (b'\x04' + | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |                           self.pointQ.x.to_bytes(order_bytes) + | 
					
						
							|  |  |  |                           self.pointQ.y.to_bytes(order_bytes)) | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         unrestricted_oid = "1.2.840.10045.2.1" | 
					
						
							|  |  |  |         return _create_subject_public_key_info(unrestricted_oid, | 
					
						
							|  |  |  |                                                public_key, | 
					
						
							|  |  |  |                                                DerObjectId(_curve.oid)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _export_private_der(self, include_ec_params=True): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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 | 
					
						
							|  |  |  |         order_bytes = _curve.order.size_in_bytes() | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |         public_key = (b'\x04' + | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |                       self.pointQ.x.to_bytes(order_bytes) + | 
					
						
							|  |  |  |                       self.pointQ.y.to_bytes(order_bytes)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         seq = [1, | 
					
						
							|  |  |  |                DerOctetString(self.d.to_bytes(order_bytes)), | 
					
						
							|  |  |  |                DerObjectId(_curve.oid, explicit=0), | 
					
						
							|  |  |  |                DerBitString(public_key, explicit=1)] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not include_ec_params: | 
					
						
							|  |  |  |             del seq[2] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return DerSequence(seq).encode() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _export_pkcs8(self, **kwargs): | 
					
						
							|  |  |  |         if kwargs.get('passphrase', None) is not None and 'protection' not in kwargs: | 
					
						
							|  |  |  |             raise ValueError("At least the 'protection' parameter should be present") | 
					
						
							|  |  |  |         unrestricted_oid = "1.2.840.10045.2.1" | 
					
						
							|  |  |  |         private_key = self._export_private_der(include_ec_params=False) | 
					
						
							|  |  |  |         result = PKCS8.wrap(private_key, | 
					
						
							|  |  |  |                             unrestricted_oid, | 
					
						
							|  |  |  |                             key_params=DerObjectId(_curve.oid), | 
					
						
							|  |  |  |                             **kwargs) | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |     def _export_public_pem(self, compress): | 
					
						
							|  |  |  |         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): | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |         encoded_der = self._export_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): | 
					
						
							|  |  |  |         encoded_der = self._export_pkcs8() | 
					
						
							|  |  |  |         return PEM.encode(encoded_der, "PRIVATE KEY") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _export_private_encrypted_pkcs8_in_clear_pem(self, passphrase, **kwargs): | 
					
						
							|  |  |  |         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
										 |  |  | 
 | 
					
						
							|  |  |  |         desc = "ecdsa-sha2-nistp256" | 
					
						
							|  |  |  |         order_bytes = _curve.order.size_in_bytes() | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |         if compress: | 
					
						
							|  |  |  |             first_byte = 2 + self.pointQ.y.is_odd() | 
					
						
							|  |  |  |             public_key = (bchr(first_byte) + | 
					
						
							|  |  |  |                           self.pointQ.x.to_bytes(order_bytes)) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |             public_key = (b'\x04' + | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |                           self.pointQ.x.to_bytes(order_bytes) + | 
					
						
							|  |  |  |                           self.pointQ.y.to_bytes(order_bytes)) | 
					
						
							| 
									
										
										
										
											2016-01-31 23:01:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |         comps = (tobytes(desc), b"nistp256", public_key) | 
					
						
							| 
									
										
										
										
											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): | 
					
						
							| 
									
										
										
										
											2018-02-16 13:44:00 +01:00
										 |  |  |             The format to use for encoding the key: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             - *'DER'*. The key will be encoded in ASN.1 DER format (binary). | 
					
						
							|  |  |  |               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). | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +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). | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |           passphrase (byte string or string): | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |             The passphrase to use for protecting the private key. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |           use_pkcs8 (boolean): | 
					
						
							| 
									
										
										
										
											2017-08-15 15:22:35 +02:00
										 |  |  |             If ``True`` (default and recommended), the `PKCS#8`_ representation | 
					
						
							|  |  |  |             will be used. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 13:44:00 +01:00
										 |  |  |             If ``False``, the much weaker `PEM encryption`_ mechanism will be used. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |             present and be a valid algorithm supported by :mod:`Crypto.IO.PKCS8`. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |             It is recommended to use ``PBKDF2WithHMAC-SHA1AndAES128-CBC``. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 14:05:08 +01:00
										 |  |  |           compress (boolean): | 
					
						
							|  |  |  |             If ``True``, a more compact representation of the public key | 
					
						
							|  |  |  |             (X-coordinate only) is used. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             If ``False`` (default), the full public key (in both its | 
					
						
							|  |  |  |             coordinates) will be exported. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  |             is 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:27:55 +01:00
										 |  |  |         .. _`PKCS#8`:   http://www.ietf.org/rfc/rfc5208.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 | 
					
						
							|  |  |  |         .. _RFC5915:    http://www.ietf.org/rfc/rfc5915.txt | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |         Returns: | 
					
						
							|  |  |  |             A multi-line string (for PEM and OpenSSH) or bytes (for DER) with the encoded key. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         args = kwargs.copy() | 
					
						
							|  |  |  |         ext_format = args.pop("format") | 
					
						
							| 
									
										
										
										
											2016-01-31 23:01:52 +01:00
										 |  |  |         if ext_format not in ("PEM", "DER", "OpenSSH"): | 
					
						
							| 
									
										
										
										
											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") | 
					
						
							| 
									
										
										
										
											2016-01-27 08:44:43 +01:00
										 |  |  |             use_pkcs8 = args.pop("use_pkcs8", True) | 
					
						
							|  |  |  |             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: | 
					
						
							|  |  |  |                     return self._export_private_der() | 
					
						
							| 
									
										
										
										
											2016-01-31 23:01:52 +01:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 raise ValueError("Private keys cannot be exported in OpenSSH 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) | 
					
						
							| 
									
										
										
										
											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): | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |         Mandatory. It must be "P-256", "prime256v1" or "secp256r1". | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-09 14:48:37 +01:00
										 |  |  |     curve = kwargs.pop("curve") | 
					
						
							|  |  |  |     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
										 |  |  | 
 | 
					
						
							|  |  |  |     d = Integer.random_range(min_inclusive=1, | 
					
						
							|  |  |  |                              max_exclusive=_curve.order, | 
					
						
							|  |  |  |                              randfunc=randfunc) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return EccKey(curve=curve, d=d) | 
					
						
							| 
									
										
										
										
											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. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |     Args: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       curve (string): | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |         Mandatory. It must be "P-256", "prime256v1" or "secp256r1". | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       d (integer): | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |         Only for a private key. It must be in the range ``[1..order-1]``. | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       point_x (integer): | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |         Mandatory for a public key. X coordinate (affine) of the ECC point. | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       point_y (integer): | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |         Mandatory for a public key. Y coordinate (affine) of the ECC point. | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Returns: | 
					
						
							|  |  |  |       :class:`EccKey` : a new ECC key object | 
					
						
							| 
									
										
										
										
											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") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-09 22:18:11 +01:00
										 |  |  |     if None not in (point_x, point_y): | 
					
						
							| 
									
										
										
										
											2019-02-15 23:29:43 +01:00
										 |  |  |         # ValueError is raised if the point is not on the curve | 
					
						
							| 
									
										
										
										
											2016-01-09 22:18:11 +01:00
										 |  |  |         kwargs["point"] = EccPoint(point_x, point_y) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-13 10:39:35 -05:00
										 |  |  |     # Validate that the private key matches the public one | 
					
						
							|  |  |  |     d = kwargs.get("d", None) | 
					
						
							|  |  |  |     if d is not None and "point" in kwargs: | 
					
						
							|  |  |  |         pub_key = _curve.G * d | 
					
						
							| 
									
										
										
										
											2019-02-15 23:32:09 +01:00
										 |  |  |         if pub_key.xy != (point_x, point_y): | 
					
						
							| 
									
										
										
										
											2016-01-13 10:39:35 -05:00
										 |  |  |             raise ValueError("Private and public ECC keys do not match") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-09 22:18:11 +01:00
										 |  |  |     return EccKey(**kwargs) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 23:27:35 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  | def _import_public_der(curve_oid, ec_point): | 
					
						
							|  |  |  |     """Convert an encoded EC point into an EccKey object
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     curve_name: string with the OID of the curve | 
					
						
							|  |  |  |     ec_point: byte string with the EC point (not DER encoded) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # We only support P-256 named curves for now | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     if curve_oid != _curve.oid: | 
					
						
							| 
									
										
										
										
											2018-04-13 23:06:05 +02:00
										 |  |  |         raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  |     order_bytes = _curve.order.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: | 
					
						
							|  |  |  |         if len(ec_point) != (1 + 2 * order_bytes): | 
					
						
							|  |  |  |             raise ValueError("Incorrect EC point length") | 
					
						
							|  |  |  |         x = Integer.from_bytes(ec_point[1:order_bytes+1]) | 
					
						
							|  |  |  |         y = Integer.from_bytes(ec_point[order_bytes+1:]) | 
					
						
							|  |  |  |     # Compressed point | 
					
						
							|  |  |  |     elif point_type in (0x02, 0x3): | 
					
						
							|  |  |  |         if len(ec_point) != (1 + order_bytes): | 
					
						
							|  |  |  |             raise ValueError("Incorrect EC point length") | 
					
						
							|  |  |  |         x = Integer.from_bytes(ec_point[1:]) | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         y = (x**3 - x*3 + _curve.b).sqrt(_curve.p)    # Short Weierstrass | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |         if point_type == 0x02 and y.is_odd(): | 
					
						
							|  |  |  |             y = _curve.p - y | 
					
						
							|  |  |  |         if point_type == 0x03 and y.is_even(): | 
					
						
							|  |  |  |             y = _curve.p - y | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         raise ValueError("Incorrect EC point encoding") | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     return construct(curve="P-256", 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) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # ec_point must be an encoded OCTET STRING | 
					
						
							|  |  |  |     # params is encoded ECParameters | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # We accept id-ecPublicKey, id-ecDH, id-ecMQV without making any | 
					
						
							|  |  |  |     # distiction for now. | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Restrictions can be captured in the key usage certificate | 
					
						
							|  |  |  |     # extension | 
					
						
							|  |  |  |     unrestricted_oid = "1.2.840.10045.2.1" | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  |     ecdh_oid = "1.3.132.1.12" | 
					
						
							|  |  |  |     ecmqv_oid = "1.3.132.1.13" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     if oid not in (unrestricted_oid, ecdh_oid, ecmqv_oid): | 
					
						
							| 
									
										
										
										
											2018-01-28 22:17:57 +01:00
										 |  |  |         raise UnsupportedEccFeature("Unsupported ECC purpose (OID: %s)" % oid) | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     # Parameters are mandatory for all three types | 
					
						
							|  |  |  |     if not params: | 
					
						
							|  |  |  |         raise ValueError("Missing ECC parameters") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  |     # ECParameters ::= CHOICE { | 
					
						
							|  |  |  |     #   namedCurve         OBJECT IDENTIFIER | 
					
						
							|  |  |  |     #   -- implicitCurve   NULL | 
					
						
							|  |  |  |     #   -- specifiedCurve  SpecifiedECDomain | 
					
						
							|  |  |  |     # } | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     # | 
					
						
							|  |  |  |     # implicitCurve and specifiedCurve are not supported (as per RFC) | 
					
						
							|  |  |  |     curve_oid = DerObjectId().decode(params).value | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 14:33:19 +01:00
										 |  |  |     return _import_public_der(curve_oid, ec_point) | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _import_private_der(encoded, passphrase, curve_name=None): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # ECPrivateKey ::= SEQUENCE { | 
					
						
							|  |  |  |     #           version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), | 
					
						
							|  |  |  |     #           privateKey     OCTET STRING, | 
					
						
							|  |  |  |     #           parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, | 
					
						
							|  |  |  |     #           publicKey  [1] BIT STRING OPTIONAL | 
					
						
							|  |  |  |     #    } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private_key = DerSequence().decode(encoded, nr_elements=(3, 4)) | 
					
						
							|  |  |  |     if private_key[0] != 1: | 
					
						
							|  |  |  |         raise ValueError("Incorrect ECC private key version") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         curve_name = DerObjectId(explicit=0).decode(private_key[2]).value | 
					
						
							|  |  |  |     except ValueError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if curve_name != _curve.oid: | 
					
						
							| 
									
										
										
										
											2018-01-28 22:17:57 +01:00
										 |  |  |         raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     scalar_bytes = DerOctetString().decode(private_key[1]).payload | 
					
						
							|  |  |  |     order_bytes = _curve.order.size_in_bytes() | 
					
						
							|  |  |  |     if len(scalar_bytes) != order_bytes: | 
					
						
							|  |  |  |         raise ValueError("Private key is too small") | 
					
						
							|  |  |  |     d = Integer.from_bytes(scalar_bytes) | 
					
						
							| 
									
										
										
										
											2016-01-17 23:04:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Decode public key (if any, it must be P-256) | 
					
						
							|  |  |  |     if len(private_key) == 4: | 
					
						
							|  |  |  |         public_key_enc = DerBitString(explicit=1).decode(private_key[3]).value | 
					
						
							|  |  |  |         public_key = _import_public_der(curve_name, public_key_enc) | 
					
						
							|  |  |  |         point_x = public_key.pointQ.x | 
					
						
							|  |  |  |         point_y = public_key.pointQ.y | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         point_x = point_y = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return construct(curve="P-256", d=d, point_x=point_x, point_y=point_y) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _import_pkcs8(encoded, passphrase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # From RFC5915, Section 1: | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     # Distributing an EC private key with PKCS#8 [RFC5208] involves including: | 
					
						
							|  |  |  |     # a) id-ecPublicKey, id-ecDH, or id-ecMQV (from [RFC5480]) with the | 
					
						
							|  |  |  |     #    namedCurve as the parameters in the privateKeyAlgorithm field; and | 
					
						
							|  |  |  |     # b) ECPrivateKey in the PrivateKey field, which is an OCTET STRING. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     algo_oid, private_key, params = PKCS8.unwrap(encoded, passphrase) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # We accept id-ecPublicKey, id-ecDH, id-ecMQV without making any | 
					
						
							|  |  |  |     # distiction for now. | 
					
						
							|  |  |  |     unrestricted_oid = "1.2.840.10045.2.1" | 
					
						
							|  |  |  |     ecdh_oid = "1.3.132.1.12" | 
					
						
							|  |  |  |     ecmqv_oid = "1.3.132.1.13" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if algo_oid not in (unrestricted_oid, ecdh_oid, ecmqv_oid): | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  |     curve_name = DerObjectId().decode(params).value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return _import_private_der(private_key, passphrase, curve_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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: | 
					
						
							|  |  |  |         return _import_private_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") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | def _import_openssh(encoded): | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |     keystring = binascii.a2b_base64(encoded.split(b' ')[1]) | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     keyparts = [] | 
					
						
							|  |  |  |     while len(keystring) > 4: | 
					
						
							| 
									
										
										
										
											2019-01-25 22:04:37 +01:00
										 |  |  |         lk = struct.unpack(">I", keystring[:4])[0] | 
					
						
							|  |  |  |         keyparts.append(keystring[4:4 + lk]) | 
					
						
							|  |  |  |         keystring = keystring[4 + lk:] | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |     if keyparts[1] != b"nistp256": | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  |         raise ValueError("Unsupported ECC curve") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return _import_public_der(_curve.oid, keyparts[2]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def import_key(encoded, passphrase=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. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |         An ECC **public** key can be: | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         - An X.509 certificate, binary (DER) or ASCII (PEM) | 
					
						
							|  |  |  |         - An X.509 ``subjectPublicKeyInfo``, binary (DER) or ASCII (PEM) | 
					
						
							|  |  |  |         - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 23:41:07 +02:00
										 |  |  |         An ECC **private** key can be: | 
					
						
							| 
									
										
										
										
											2016-01-27 08:00:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-31 23:27:55 +01:00
										 |  |  |         - In binary format (DER, see section 3 of `RFC5915`_ or `PKCS#8`_) | 
					
						
							|  |  |  |         - In ASCII format (PEM or OpenSSH) | 
					
						
							| 
									
										
										
										
											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. | 
					
						
							|  |  |  |         Encryption may be applied protected at the PEM level or at the PKCS#8 level. | 
					
						
							|  |  |  |         This parameter is ignored if the key in input is not encrypted. | 
					
						
							| 
									
										
										
										
											2016-01-27 08:00:28 +01: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). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt | 
					
						
							|  |  |  |     .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt | 
					
						
							|  |  |  |     .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt | 
					
						
							|  |  |  |     .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     encoded = tobytes(encoded) | 
					
						
							|  |  |  |     if passphrase is not None: | 
					
						
							|  |  |  |         passphrase = tobytes(passphrase) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # PEM | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |     if encoded.startswith(b'-----'): | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  |         der_encoded, marker, enc_flag = PEM.decode(tostr(encoded), passphrase) | 
					
						
							|  |  |  |         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 | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |     if encoded.startswith(b'ecdsa-sha2-'): | 
					
						
							| 
									
										
										
										
											2016-01-26 22:05:02 +01:00
										 |  |  |         return _import_openssh(encoded) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # DER | 
					
						
							|  |  |  |     if bord(encoded[0]) == 0x30: | 
					
						
							|  |  |  |         return _import_der(encoded, passphrase) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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-02-27 12:07:58 +01:00
										 |  |  |     point = _curve.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") | 
					
						
							| 
									
										
										
										
											2019-01-30 08:41:21 +01:00
										 |  |  | 
 |