Skip to content

Commit a38af7d

Browse files
Merge pull request #36 from starkbank/feature/add-curve
Feature/add curve
2 parents 209eab4 + 9be5400 commit a38af7d

File tree

6 files changed

+140
-20
lines changed

6 files changed

+140
-20
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ Given a version number MAJOR.MINOR.PATCH, increment:
1313

1414

1515
## [Unreleased]
16+
### Added
17+
- curve.add() function to dynamically add curves to the library
18+
### Changed
19+
- curve.getCurveByOid() to curve.getByOid()
1620

1721
## [2.0.3] - 2021-11-24
1822
### Fixed

README.md

+29-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pip install starkbank-ecdsa
1616

1717
### Curves
1818

19-
We currently support `secp256k1`, but it's super easy to add more curves to the project. Just add them on `curve.py`
19+
We currently support `secp256k1`, but you can add more curves to the project. You just need to use the curve.add() function.
2020

2121
### Speed
2222

@@ -100,6 +100,34 @@ print(Ecdsa.verify(message, signature, publicKey))
100100

101101
```
102102

103+
How to add more curves:
104+
105+
```python
106+
from ellipticcurve import curve, PrivateKey, PublicKey
107+
108+
newCurve = curve.CurveFp(
109+
name="frp256v1",
110+
A=0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c00,
111+
B=0xee353fca5428a9300d4aba754a44c00fdfec0c9ae4b1a1803075ed967b7bb73f,
112+
P=0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c03,
113+
N=0xf1fd178c0b3ad58f10126de8ce42435b53dc67e140d2bf941ffdd459c6d655e1,
114+
Gx=0xb6b3d4c356c139eb31183d4749d423958c27d2dcaf98b70164c97a2dd98f5cff,
115+
Gy=0x6142e0f7c8b204911f9271f0f3ecef8c2701c307e8e4c9e183115a1554062cfb,
116+
oid=[1, 2, 250, 1, 223, 101, 256, 1]
117+
)
118+
119+
curve.add(newCurve)
120+
121+
publicKeyPem = """-----BEGIN PUBLIC KEY-----
122+
MFswFQYHKoZIzj0CAQYKKoF6AYFfZYIAAQNCAATeEFFYiQL+HmDYTf+QDmvQmWGD
123+
dRJPqLj11do8okvkSxq2lwB6Ct4aITMlCyg3f1msafc/ROSN/Vgj69bDhZK6
124+
-----END PUBLIC KEY-----"""
125+
126+
publicKey = PublicKey.fromPem(publicKeyPem)
127+
128+
print(publicKey.toPem())
129+
```
130+
103131
### OpenSSL
104132

105133
This library is compatible with OpenSSL, so you can use it to generate keys:

ellipticcurve/curve.py

+18-15
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,22 @@ def length(self):
3737
return (1 + len("%x" % self.N)) // 2
3838

3939

40+
_curvesByOid = {tuple(curve.oid): curve for curve in []}
41+
42+
43+
def add(curve):
44+
_curvesByOid[tuple(curve.oid)] = curve
45+
46+
47+
def getByOid(oid):
48+
if oid not in _curvesByOid:
49+
raise Exception("Unknown curve with oid {oid}; The following are registered: {names}".format(
50+
oid=".".join([str(number) for number in oid]),
51+
names=", ".join([curve.name for curve in _curvesByOid.values()]),
52+
))
53+
return _curvesByOid[oid]
54+
55+
4056
secp256k1 = CurveFp(
4157
name="secp256k1",
4258
A=0x0000000000000000000000000000000000000000000000000000000000000000,
@@ -62,18 +78,5 @@ def length(self):
6278

6379
p256 = prime256v1
6480

65-
supportedCurves = [
66-
secp256k1,
67-
prime256v1,
68-
]
69-
70-
_curvesByOid = {tuple(curve.oid): curve for curve in supportedCurves}
71-
72-
73-
def getCurveByOid(oid):
74-
if oid not in _curvesByOid:
75-
raise Exception("Unknown curve with oid {oid}; The following are registered: {names}".format(
76-
oid=".".join([str(number) for number in oid]),
77-
names=", ".join([curve.name for curve in supportedCurves]),
78-
))
79-
return _curvesByOid[oid]
81+
add(secp256k1)
82+
add(prime256v1)

ellipticcurve/privateKey.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from .utils.pem import getPemContent, createPem
44
from .utils.binary import hexFromByteString, byteStringFromHex, intFromHex, base64FromByteString, byteStringFromBase64
55
from .utils.der import hexFromInt, parse, encodeConstructed, DerFieldType, encodePrimitive
6-
from .curve import secp256k1, getCurveByOid
6+
from .curve import secp256k1, getByOid
77
from .publicKey import PublicKey
88

99

@@ -54,7 +54,7 @@ def fromDer(cls, string):
5454
raise Exception("Private keys should start with a '1' flag, but a '{flag}' was found instead".format(
5555
flag=privateKeyFlag
5656
))
57-
curve = getCurveByOid(curveData[0])
57+
curve = getByOid(curveData[0])
5858
privateKey = cls.fromString(string=secretHex, curve=curve)
5959
if privateKey.publicKey().toString(encoded=True) != publicKeyString[0]:
6060
raise Exception("The public key described inside the private key file doesn't match the actual public key of the pair")

ellipticcurve/publicKey.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from .math import Math
22
from .point import Point
3-
from .curve import secp256k1, getCurveByOid
3+
from .curve import secp256k1, getByOid
44
from .utils.pem import getPemContent, createPem
55
from .utils.der import hexFromInt, parse, DerFieldType, encodeConstructed, encodePrimitive
66
from .utils.binary import hexFromByteString, byteStringFromHex, intFromHex, base64FromByteString, byteStringFromBase64
@@ -50,7 +50,7 @@ def fromDer(cls, string):
5050
ecdsaPublicKeyOid=_ecdsaPublicKeyOid,
5151
actualOid=publicKeyOid,
5252
))
53-
curve = getCurveByOid(curveOid)
53+
curve = getByOid(curveOid)
5454
return cls.fromString(string=pointString, curve=curve)
5555

5656
@classmethod

tests/testCurve.py

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
from unittest.case import TestCase
2+
from ellipticcurve import curve, PublicKey, Signature, Ecdsa, PrivateKey
3+
4+
5+
class CurveTest(TestCase):
6+
7+
def testSupportedCurve(self):
8+
newCurve = curve.CurveFp(
9+
name="secp256k1",
10+
A=0x0000000000000000000000000000000000000000000000000000000000000000,
11+
B=0x0000000000000000000000000000000000000000000000000000000000000007,
12+
P=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
13+
N=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141,
14+
Gx=0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
15+
Gy=0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,
16+
oid=[1, 3, 132, 0, 10]
17+
)
18+
privateKey1 = PrivateKey(curve=newCurve)
19+
publicKey1 = privateKey1.publicKey()
20+
21+
privateKeyPem = privateKey1.toPem()
22+
publicKeyPem = publicKey1.toPem()
23+
24+
privateKey2 = PrivateKey.fromPem(privateKeyPem)
25+
publicKey2 = PublicKey.fromPem(publicKeyPem)
26+
27+
message = "test"
28+
29+
signatureBase64 = Ecdsa.sign(message=message, privateKey=privateKey2).toBase64()
30+
signature = Signature.fromBase64(signatureBase64)
31+
32+
self.assertTrue(Ecdsa.verify(message=message, signature=signature, publicKey=publicKey2))
33+
34+
def testAddNewCurve(self):
35+
newCurve = curve.CurveFp(
36+
name="frp256v1",
37+
A=0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c00,
38+
B=0xee353fca5428a9300d4aba754a44c00fdfec0c9ae4b1a1803075ed967b7bb73f,
39+
P=0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c03,
40+
N=0xf1fd178c0b3ad58f10126de8ce42435b53dc67e140d2bf941ffdd459c6d655e1,
41+
Gx=0xb6b3d4c356c139eb31183d4749d423958c27d2dcaf98b70164c97a2dd98f5cff,
42+
Gy=0x6142e0f7c8b204911f9271f0f3ecef8c2701c307e8e4c9e183115a1554062cfb,
43+
oid=[1, 2, 250, 1, 223, 101, 256, 1]
44+
)
45+
curve.add(newCurve)
46+
privateKey1 = PrivateKey(curve=newCurve)
47+
publicKey1 = privateKey1.publicKey()
48+
49+
privateKeyPem = privateKey1.toPem()
50+
publicKeyPem = publicKey1.toPem()
51+
52+
privateKey2 = PrivateKey.fromPem(privateKeyPem)
53+
publicKey2 = PublicKey.fromPem(publicKeyPem)
54+
55+
message = "test"
56+
57+
signatureBase64 = Ecdsa.sign(message=message, privateKey=privateKey2).toBase64()
58+
signature = Signature.fromBase64(signatureBase64)
59+
60+
self.assertTrue(Ecdsa.verify(message=message, signature=signature, publicKey=publicKey2))
61+
62+
def testUnsupportedCurve(self):
63+
newCurve = curve.CurveFp(
64+
name="frp256v1",
65+
A=0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c00,
66+
B=0xee353fca5428a9300d4aba754a44c00fdfec0c9ae4b1a1803075ed967b7bb73f,
67+
P=0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c03,
68+
N=0xf1fd178c0b3ad58f10126de8ce42435b53dc67e140d2bf941ffdd459c6d655e1,
69+
Gx=0xb6b3d4c356c139eb31183d4749d423958c27d2dcaf98b70164c97a2dd98f5cff,
70+
Gy=0x6142e0f7c8b204911f9271f0f3ecef8c2701c307e8e4c9e183115a1554062cfb,
71+
oid=[1, 2, 250, 1, 223, 101, 256, 1]
72+
)
73+
74+
privateKeyPem = PrivateKey(curve=newCurve).toPem()
75+
publicKeyPem = PrivateKey(curve=newCurve).publicKey().toPem()
76+
77+
with self.assertRaises(Exception) as context:
78+
privateKey = PrivateKey.fromPem(privateKeyPem)
79+
self.assertTrue('Unknown curve' in str(context.exception))
80+
81+
with self.assertRaises(Exception) as context:
82+
publicKey = PublicKey.fromPem(publicKeyPem)
83+
self.assertTrue('Unknown curve' in str(context.exception))
84+
85+

0 commit comments

Comments
 (0)