Skip to content

Commit 2ea31b0

Browse files
Fix point at infinity verification in signature and public key
1 parent cb6d807 commit 2ea31b0

File tree

6 files changed

+46
-24
lines changed

6 files changed

+46
-24
lines changed

ellipticcurve/curve.py

+11-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#
44
# y^2 = x^3 + A*x + B (mod P)
55
#
6-
76
from .point import Point
87

98

@@ -26,7 +25,13 @@ def contains(self, p):
2625
:param p: Point p = Point(x, y)
2726
:return: boolean
2827
"""
29-
return (p.y**2 - (p.x**3 + self.A * p.x + self.B)) % self.P == 0
28+
if not 0 <= p.x <= self.P - 1:
29+
return False
30+
if not 0 <= p.y <= self.P - 1:
31+
return False
32+
if (p.y**2 - (p.x**3 + self.A * p.x + self.B)) % self.P != 0:
33+
return False
34+
return True
3035

3136
def length(self):
3237
return (1 + len("%x" % self.N)) // 2
@@ -67,10 +72,8 @@ def length(self):
6772

6873
def getCurveByOid(oid):
6974
if oid not in _curvesByOid:
70-
raise Exception(
71-
"Unknown curve with oid %s; The following are registered: %s" % (
72-
".".join(oid),
73-
", ".join([curve.name for curve in supportedCurves])
74-
)
75-
)
75+
raise Exception("Unknown curve with oid {oid}; The following are registered: {names}".format(
76+
oid=".".join(oid),
77+
names=", ".join([curve.name for curve in supportedCurves]),
78+
))
7679
return _curvesByOid[oid]

ellipticcurve/ecdsa.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def verify(cls, message, signature, publicKey, hashfunc=sha256):
4040
inv = Math.inv(s, curve.N)
4141
u1 = Math.multiply(curve.G, n=(numberMessage * inv) % curve.N, N=curve.N, A=curve.A, P=curve.P)
4242
u2 = Math.multiply(publicKey.point, n=(r * inv) % curve.N, N=curve.N, A=curve.A, P=curve.P)
43-
add = Math.add(u1, u2, A=curve.A, P=curve.P)
44-
modX = add.x % curve.N
45-
return r == modX
43+
v = Math.add(u1, u2, A=curve.A, P=curve.P)
44+
if v.isAtInfinity():
45+
return False
46+
return v.x % curve.N == r

ellipticcurve/math.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def _jacobianDouble(cls, p, A, P):
9797
:param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
9898
:return: Point that represents the sum of First and Second Point
9999
"""
100-
if not p.y:
100+
if p.y == 0:
101101
return Point(0, 0, 0)
102102

103103
ysq = (p.y ** 2) % P
@@ -120,10 +120,9 @@ def _jacobianAdd(cls, p, q, A, P):
120120
:param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
121121
:return: Point that represents the sum of First and Second Point
122122
"""
123-
if not p.y:
123+
if p.y == 0:
124124
return q
125-
126-
if not q.y:
125+
if q.y == 0:
127126
return p
128127

129128
U1 = (p.x * q.z ** 2) % P

ellipticcurve/point.py

+6
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,9 @@ def __init__(self, x=0, y=0, z=0):
66
self.x = x
77
self.y = y
88
self.z = z
9+
10+
def __str__(self):
11+
return "({x}, {y}, {z})".format(x=self.x, y=self.y, z=self.z)
12+
13+
def isAtInfinity(self):
14+
return self.y == 0

ellipticcurve/publicKey.py

+13-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
from .math import Math
12
from .point import Point
3+
from .curve import secp256k1, getCurveByOid
24
from .utils.pem import getPemContent, createPem
3-
from .utils.binary import hexFromByteString, byteStringFromHex, intFromHex, base64FromByteString, byteStringFromBase64
45
from .utils.der import hexFromInt, parse, DerFieldType, encodeConstructed, encodePrimitive
5-
from .curve import secp256k1, getCurveByOid
6+
from .utils.binary import hexFromByteString, byteStringFromHex, intFromHex, base64FromByteString, byteStringFromBase64
67

78

89
class PublicKey:
@@ -65,12 +66,16 @@ def fromString(cls, string, curve=secp256k1, validatePoint=True):
6566
x=intFromHex(xs),
6667
y=intFromHex(ys),
6768
)
68-
if validatePoint and not curve.contains(p):
69-
raise Exception(
70-
"Point ({x},{y}) is not valid for curve {name}".format(x=p.x, y=p.y, name=curve.name)
71-
)
72-
73-
return PublicKey(point=p, curve=curve)
69+
publicKey = PublicKey(point=p, curve=curve)
70+
if not validatePoint:
71+
return publicKey
72+
if p.isAtInfinity():
73+
raise Exception("Public Key point is at infinity")
74+
if not curve.contains(p):
75+
raise Exception("Point ({x},{y}) is not valid for curve {name}".format(x=p.x, y=p.y, name=curve.name))
76+
if not Math.multiply(p=p, n=curve.N, N=curve.N, A=curve.A, P=curve.P).isAtInfinity():
77+
raise Exception("Point ({x},{y}) * {name}.N is not at infinity".format(x=p.x, y=p.y, name=curve.name))
78+
return publicKey
7479

7580

7681
_ecdsaPublicKeyOid = (1, 2, 840, 10045, 2, 1)

tests/testEcdsa.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from unittest.case import TestCase
2-
from ellipticcurve import Ecdsa, PrivateKey
2+
from ellipticcurve import Ecdsa, PrivateKey, Signature
33

44

55
class EcdsaTest(TestCase):
@@ -24,3 +24,11 @@ def testVerifyWrongMessage(self):
2424
signature = Ecdsa.sign(message1, privateKey)
2525

2626
self.assertFalse(Ecdsa.verify(message2, signature, publicKey))
27+
28+
def testZeroSignature(self):
29+
privateKey = PrivateKey()
30+
publicKey = privateKey.publicKey()
31+
32+
message2 = "This is the wrong message"
33+
34+
self.assertFalse(Ecdsa.verify(message2, Signature(0, 0), publicKey))

0 commit comments

Comments
 (0)