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.
|
|
|
|
# ===================================================================
|
|
|
|
|
|
|
|
|
|
|
|
from Crypto.Math.Numbers import Integer
|
2016-01-02 15:33:28 -05:00
|
|
|
from Crypto.Random import get_random_bytes
|
2015-12-28 23:22:56 +01:00
|
|
|
|
|
|
|
class _Curve(object):
|
|
|
|
pass
|
|
|
|
|
|
|
|
_curve = _Curve()
|
|
|
|
_curve.p = Integer(115792089210356248762697446949407573530086143415290314195533631308867097853951)
|
2016-01-01 14:42:06 -05:00
|
|
|
_curve.order = Integer(115792089210356248762697446949407573529996955224135760342422259061068512044369)
|
2015-12-28 23:22:56 +01:00
|
|
|
_curve.Gx = Integer(0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296)
|
|
|
|
_curve.Gy = Integer(0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5)
|
|
|
|
|
|
|
|
|
|
|
|
# https://www.nsa.gov/ia/_files/nist-routines.pdf
|
|
|
|
# http://point-at-infinity.org/ecc/nisttv
|
|
|
|
# http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html
|
|
|
|
# https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates
|
|
|
|
# https://eprint.iacr.org/2013/816.pdf
|
|
|
|
|
2016-01-02 15:33:28 -05:00
|
|
|
class EccPoint(object):
|
2015-12-28 23:22:56 +01:00
|
|
|
|
|
|
|
def __init__(self, x, y):
|
|
|
|
self._x = Integer(x)
|
|
|
|
self._y = Integer(y)
|
|
|
|
|
2016-01-11 23:12:20 +01:00
|
|
|
# Buffers
|
2016-01-11 08:51:04 +01:00
|
|
|
self._common = Integer(0)
|
|
|
|
self._tmp1 = Integer(0)
|
|
|
|
self._x3 = Integer(0)
|
|
|
|
self._y3 = Integer(0)
|
|
|
|
|
2016-01-11 08:08:08 +01:00
|
|
|
def set(self, point):
|
|
|
|
self._x = Integer(point._x)
|
|
|
|
self._y = Integer(point._y)
|
|
|
|
return self
|
|
|
|
|
2015-12-28 23:22:56 +01:00
|
|
|
def __eq__(self, point):
|
|
|
|
return self._x == point._x and self._y == point._y
|
|
|
|
|
|
|
|
def __neg__(self):
|
|
|
|
if self.is_point_at_infinity():
|
|
|
|
return self.point_at_infinity()
|
2016-01-02 15:33:28 -05:00
|
|
|
return EccPoint(self._x, _curve.p - self._y)
|
2015-12-28 23:22:56 +01:00
|
|
|
|
|
|
|
def copy(self):
|
2016-01-02 15:33:28 -05:00
|
|
|
return EccPoint(self._x, self._y)
|
2015-12-28 23:22:56 +01:00
|
|
|
|
|
|
|
def is_point_at_infinity(self):
|
2016-01-11 08:51:04 +01:00
|
|
|
return not (self._x or self._y)
|
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):
|
|
|
|
if self.is_point_at_infinity():
|
|
|
|
raise ValueError("Point at infinity")
|
|
|
|
return self._x
|
|
|
|
|
|
|
|
@property
|
|
|
|
def y(self):
|
|
|
|
if self.is_point_at_infinity():
|
|
|
|
raise ValueError("Point at infinity")
|
|
|
|
return self._y
|
|
|
|
|
|
|
|
def double(self):
|
2016-01-11 08:51:04 +01:00
|
|
|
"""Double this point"""
|
2015-12-28 23:22:56 +01:00
|
|
|
|
2016-01-11 08:51:04 +01:00
|
|
|
if not self._y:
|
2015-12-28 23:22:56 +01:00
|
|
|
return self.point_at_infinity()
|
|
|
|
|
2016-01-11 08:51:04 +01:00
|
|
|
common = self._common
|
|
|
|
tmp1 = self._tmp1
|
|
|
|
x3 = self._x3
|
|
|
|
y3 = self._y3
|
|
|
|
|
2016-01-08 23:27:35 +01:00
|
|
|
# common = (pow(self._x, 2, _curve.p) * 3 - 3) * (self._y << 1).inverse(_curve.p) % _curve.p
|
2016-01-11 08:51:04 +01:00
|
|
|
common.set(self._x)
|
|
|
|
common.inplace_pow(2, _curve.p)
|
2016-01-01 09:28:00 -05:00
|
|
|
common *= 3
|
|
|
|
common -= 3
|
2016-01-11 08:51:04 +01:00
|
|
|
tmp1.set(self._y)
|
|
|
|
tmp1 <<= 1
|
2016-01-11 22:55:39 +01:00
|
|
|
tmp1.inplace_inverse(_curve.p)
|
|
|
|
common *= tmp1
|
2016-01-01 09:28:00 -05:00
|
|
|
common %= _curve.p
|
2016-01-11 23:12:20 +01:00
|
|
|
|
2016-01-08 23:27:35 +01:00
|
|
|
# x3 = (pow(common, 2, _curve.p) - 2 * self._x) % _curve.p
|
2016-01-11 08:51:04 +01:00
|
|
|
x3.set(common)
|
|
|
|
x3.inplace_pow(2, _curve.p)
|
2016-01-01 09:28:00 -05:00
|
|
|
x3 -= self._x
|
|
|
|
x3 -= self._x
|
2016-01-11 08:51:04 +01:00
|
|
|
while x3.is_negative():
|
2015-12-28 23:22:56 +01:00
|
|
|
x3 += _curve.p
|
2016-01-11 23:12:20 +01:00
|
|
|
|
2016-01-01 09:28:00 -05:00
|
|
|
# y3 = ((self._x - x3) * common - self._y) % _curve.p
|
2016-01-11 08:51:04 +01:00
|
|
|
y3.set(self._x)
|
|
|
|
y3 -= x3
|
2016-01-01 09:28:00 -05:00
|
|
|
y3 *= common
|
|
|
|
y3 -= self._y
|
|
|
|
y3 %= _curve.p
|
2015-12-28 23:22:56 +01:00
|
|
|
|
2016-01-11 08:51:04 +01:00
|
|
|
self._x.set(x3)
|
|
|
|
self._y.set(y3)
|
|
|
|
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
|
|
|
|
|
|
|
if self.is_point_at_infinity():
|
2016-01-11 08:08:08 +01:00
|
|
|
return self.set(point)
|
2015-12-28 23:22:56 +01:00
|
|
|
|
|
|
|
if point.is_point_at_infinity():
|
2016-01-11 08:08:08 +01:00
|
|
|
return self
|
2015-12-28 23:22:56 +01:00
|
|
|
|
|
|
|
if self == point:
|
2016-01-11 08:51:04 +01:00
|
|
|
return self.double()
|
2015-12-28 23:22:56 +01:00
|
|
|
|
|
|
|
if self._x == point._x:
|
2016-01-11 08:08:08 +01:00
|
|
|
return self.set(self.point_at_infinity())
|
2015-12-28 23:22:56 +01:00
|
|
|
|
2016-01-11 08:51:04 +01:00
|
|
|
common = self._common
|
|
|
|
tmp1 = self._tmp1
|
|
|
|
x3 = self._x3
|
|
|
|
y3 = self._y3
|
|
|
|
|
2016-01-01 09:28:00 -05:00
|
|
|
# common = (point._y - self._y) * (point._x - self._x).inverse(_curve.p) % _curve.p
|
2016-01-11 08:51:04 +01:00
|
|
|
common.set(point._y)
|
|
|
|
common -= self._y
|
|
|
|
tmp1.set(point._x)
|
|
|
|
tmp1 -= self._x
|
2016-01-11 22:55:39 +01:00
|
|
|
tmp1.inplace_inverse(_curve.p)
|
2016-01-11 08:51:04 +01:00
|
|
|
common *= tmp1
|
2016-01-01 09:28:00 -05:00
|
|
|
common %= _curve.p
|
2016-01-11 08:51:04 +01:00
|
|
|
|
2016-01-08 23:27:35 +01:00
|
|
|
# x3 = (pow(common, 2, _curve.p) - self._x - point._x) % _curve.p
|
2016-01-11 23:12:20 +01:00
|
|
|
x3.set(common)
|
|
|
|
x3.inplace_pow(2, _curve.p)
|
|
|
|
x3 -= self._x
|
|
|
|
x3 -= point._x
|
|
|
|
while x3.is_negative():
|
|
|
|
x3 += _curve.p
|
|
|
|
|
2016-01-01 09:28:00 -05:00
|
|
|
# y3 = ((self._x - x3) * common - self._y) % _curve.p
|
2016-01-11 08:51:04 +01:00
|
|
|
y3.set(self._x)
|
|
|
|
y3 -= x3
|
2016-01-01 09:28:00 -05:00
|
|
|
y3 *= common
|
|
|
|
y3 -= self._y
|
|
|
|
y3 %= _curve.p
|
2015-12-28 23:22:56 +01:00
|
|
|
|
2016-01-11 08:51:04 +01:00
|
|
|
self._x.set(x3)
|
|
|
|
self._y.set(y3)
|
|
|
|
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"""
|
|
|
|
|
|
|
|
result = self.copy()
|
|
|
|
result += point
|
|
|
|
return result
|
2015-12-28 23:22:56 +01:00
|
|
|
|
2016-01-11 07:53:47 +01:00
|
|
|
def __mul__(self, scalar):
|
2015-12-28 23:22:56 +01:00
|
|
|
"""Return a new point, the scalar product of this one"""
|
|
|
|
|
2016-01-01 14:42:06 -05:00
|
|
|
if scalar < 0:
|
|
|
|
raise ValueError("Scalar multiplication only defined for non-negative integers")
|
2015-12-28 23:22:56 +01:00
|
|
|
|
|
|
|
# Trivial results
|
|
|
|
if scalar == 0 or self.is_point_at_infinity():
|
|
|
|
return self.point_at_infinity()
|
|
|
|
elif scalar == 1:
|
|
|
|
return self.copy()
|
|
|
|
|
2016-01-01 08:25:52 -05:00
|
|
|
# Convert to NAF
|
2016-01-01 08:37:43 -05:00
|
|
|
WINDOW_BITS = 4
|
2016-01-01 08:25:52 -05:00
|
|
|
window_high = 1 << WINDOW_BITS
|
2015-12-31 09:28:40 -05:00
|
|
|
window_low = 1 << (WINDOW_BITS - 1)
|
2016-01-01 08:25:52 -05:00
|
|
|
window_mask = window_high - 1
|
|
|
|
|
|
|
|
scalar_int = int(scalar)
|
|
|
|
naf = []
|
|
|
|
while scalar_int > 0:
|
|
|
|
if scalar_int & 1:
|
|
|
|
di = scalar_int & window_mask
|
|
|
|
if di >= window_low:
|
|
|
|
di -= window_high
|
|
|
|
scalar_int -= di
|
2015-12-31 09:28:40 -05:00
|
|
|
else:
|
2016-01-01 08:25:52 -05:00
|
|
|
di = 0
|
|
|
|
naf.append(di)
|
|
|
|
scalar_int >>= 1
|
|
|
|
naf.reverse()
|
2015-12-31 09:28:40 -05:00
|
|
|
|
2016-01-01 08:25:52 -05:00
|
|
|
# naf contains d_(i-1), d_(i-2), .. d_1, d_0
|
2015-12-31 09:28:40 -05:00
|
|
|
|
2016-01-01 09:28:00 -05:00
|
|
|
if hasattr(self, "_precomp"):
|
|
|
|
precomp = self._precomp
|
|
|
|
else:
|
|
|
|
# Precompute 1P, 3P, 5P, .. (2**(W-1) - 1)P
|
|
|
|
# which is 1P..7P for W=4 (we also add negatives)
|
2016-01-11 08:51:04 +01:00
|
|
|
precomp = [0, self] # 0, 1P
|
|
|
|
precomp += [precomp[1] + precomp[1]] # 2P
|
2016-01-11 07:53:47 +01:00
|
|
|
precomp += [precomp[2] + precomp[1]] # 3P
|
|
|
|
precomp += [0] # 4P
|
|
|
|
precomp += [precomp[2] + precomp[3]] # 5P
|
|
|
|
precomp += [0] # 6P
|
|
|
|
precomp += [precomp[2] + precomp[5]] # 7P
|
2016-01-01 09:28:00 -05:00
|
|
|
precomp += [ -x for x in precomp[:0:-1]]
|
|
|
|
self._precomp = precomp
|
2016-01-01 08:25:52 -05:00
|
|
|
|
|
|
|
result = self.point_at_infinity()
|
|
|
|
for x in naf:
|
2016-01-11 08:51:04 +01:00
|
|
|
result.double()
|
2016-01-01 08:25:52 -05:00
|
|
|
if x != 0:
|
2016-01-11 08:51:04 +01:00
|
|
|
result += precomp[x]
|
2015-12-28 23:22:56 +01:00
|
|
|
|
|
|
|
return result
|
2016-01-02 15:33:28 -05:00
|
|
|
|
|
|
|
|
2016-01-09 14:48:37 +01:00
|
|
|
_curve.G = EccPoint(_curve.Gx, _curve.Gy)
|
|
|
|
|
|
|
|
|
2016-01-02 15:33:28 -05:00
|
|
|
class EccKey(object):
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
"""Create a new ECC key
|
|
|
|
|
|
|
|
Do not instantiate this object directly.
|
|
|
|
|
|
|
|
Keywords:
|
|
|
|
curve : string
|
|
|
|
It must be "P-256".
|
|
|
|
d : integer
|
|
|
|
Only for a private key. It must be in the range [1..order-1].
|
|
|
|
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-02 15:33:28 -05:00
|
|
|
|
|
|
|
if self.curve != "P-256":
|
|
|
|
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")
|
|
|
|
|
|
|
|
def has_private(self):
|
|
|
|
return self._d is not None
|
|
|
|
|
2016-01-09 14:48:37 +01:00
|
|
|
def _sign(self, z, k):
|
|
|
|
assert 0 < k < _curve.order
|
|
|
|
# TODO: add blinding
|
2016-01-11 07:53:47 +01:00
|
|
|
r = (_curve.G * k).x % _curve.order
|
2016-01-09 14:48:37 +01:00
|
|
|
s = k.inverse(_curve.order) * (z + self._d * r) % _curve.order
|
|
|
|
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)
|
|
|
|
point2 = self.pointQ * ((sinv * rs[0]) % _curve.order)
|
|
|
|
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):
|
|
|
|
return EccKey(curve="P-256", point=self.pointQ)
|
|
|
|
|
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.
|
|
|
|
|
2016-01-09 14:48:37 +01:00
|
|
|
:Keywords:
|
2016-01-08 23:27:35 +01:00
|
|
|
curve : string
|
|
|
|
It must be "P-256".
|
|
|
|
randfunc : callable
|
|
|
|
The RNG to read randomness from.
|
|
|
|
If ``None``, the system source is used.
|
|
|
|
"""
|
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-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-09 22:18:11 +01:00
|
|
|
def construct(**kwargs):
|
|
|
|
"""Build a new ECC key (private or public) starting
|
|
|
|
from some base components.
|
|
|
|
|
|
|
|
:Keywords:
|
|
|
|
curve : string
|
|
|
|
It must be present and set to "P-256".
|
|
|
|
d : integer
|
|
|
|
Only for a private key. It must be in the range [1..order-1].
|
|
|
|
point_x : integer
|
|
|
|
X coordinate (affine) of the ECC point.
|
|
|
|
This value is mandatory in case of a public key.
|
|
|
|
point_y : integer
|
|
|
|
Y coordinate (affine) of the ECC point.
|
|
|
|
This value is mandatory in case of a public key.
|
|
|
|
"""
|
|
|
|
|
|
|
|
point_x = kwargs.pop("point_x", None)
|
|
|
|
point_y = kwargs.pop("point_y", None)
|
|
|
|
|
|
|
|
if None not in (point_x, point_y):
|
|
|
|
kwargs["point"] = EccPoint(point_x, point_y)
|
|
|
|
|
|
|
|
return EccKey(**kwargs)
|
|
|
|
|
2016-01-08 23:27:35 +01:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
import time
|
|
|
|
d = 0xc51e4753afdec1e6b6c6a5b992f43f8dd0c7a8933072708b6522468b2ffb06fd
|
|
|
|
|
2016-01-10 13:53:51 +01:00
|
|
|
point = generate(curve="P-256").pointQ
|
2016-01-08 23:27:35 +01:00
|
|
|
start = time.time()
|
|
|
|
count = 30
|
|
|
|
for x in xrange(count):
|
2016-01-11 07:53:47 +01:00
|
|
|
_ = point * d
|
2016-01-08 23:27:35 +01:00
|
|
|
print (time.time() - start) / count * 1000, "ms"
|