Skip to content

Commit

Permalink
Merge pull request #36 from starkbank/feature/add-curve
Browse files Browse the repository at this point in the history
Feature/add curve
  • Loading branch information
cdottori-stark authored Sep 20, 2022
2 parents 209eab4 + 9be5400 commit a38af7d
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 20 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ Given a version number MAJOR.MINOR.PATCH, increment:


## [Unreleased]
### Added
- curve.add() function to dynamically add curves to the library
### Changed
- curve.getCurveByOid() to curve.getByOid()

## [2.0.3] - 2021-11-24
### Fixed
Expand Down
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pip install starkbank-ecdsa

### Curves

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

### Speed

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

```

How to add more curves:

```python
from ellipticcurve import curve, PrivateKey, PublicKey

newCurve = curve.CurveFp(
name="frp256v1",
A=0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c00,
B=0xee353fca5428a9300d4aba754a44c00fdfec0c9ae4b1a1803075ed967b7bb73f,
P=0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c03,
N=0xf1fd178c0b3ad58f10126de8ce42435b53dc67e140d2bf941ffdd459c6d655e1,
Gx=0xb6b3d4c356c139eb31183d4749d423958c27d2dcaf98b70164c97a2dd98f5cff,
Gy=0x6142e0f7c8b204911f9271f0f3ecef8c2701c307e8e4c9e183115a1554062cfb,
oid=[1, 2, 250, 1, 223, 101, 256, 1]
)

curve.add(newCurve)

publicKeyPem = """-----BEGIN PUBLIC KEY-----
MFswFQYHKoZIzj0CAQYKKoF6AYFfZYIAAQNCAATeEFFYiQL+HmDYTf+QDmvQmWGD
dRJPqLj11do8okvkSxq2lwB6Ct4aITMlCyg3f1msafc/ROSN/Vgj69bDhZK6
-----END PUBLIC KEY-----"""

publicKey = PublicKey.fromPem(publicKeyPem)

print(publicKey.toPem())
```

### OpenSSL

This library is compatible with OpenSSL, so you can use it to generate keys:
Expand Down
33 changes: 18 additions & 15 deletions ellipticcurve/curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ def length(self):
return (1 + len("%x" % self.N)) // 2


_curvesByOid = {tuple(curve.oid): curve for curve in []}


def add(curve):
_curvesByOid[tuple(curve.oid)] = curve


def getByOid(oid):
if oid not in _curvesByOid:
raise Exception("Unknown curve with oid {oid}; The following are registered: {names}".format(
oid=".".join([str(number) for number in oid]),
names=", ".join([curve.name for curve in _curvesByOid.values()]),
))
return _curvesByOid[oid]


secp256k1 = CurveFp(
name="secp256k1",
A=0x0000000000000000000000000000000000000000000000000000000000000000,
Expand All @@ -62,18 +78,5 @@ def length(self):

p256 = prime256v1

supportedCurves = [
secp256k1,
prime256v1,
]

_curvesByOid = {tuple(curve.oid): curve for curve in supportedCurves}


def getCurveByOid(oid):
if oid not in _curvesByOid:
raise Exception("Unknown curve with oid {oid}; The following are registered: {names}".format(
oid=".".join([str(number) for number in oid]),
names=", ".join([curve.name for curve in supportedCurves]),
))
return _curvesByOid[oid]
add(secp256k1)
add(prime256v1)
4 changes: 2 additions & 2 deletions ellipticcurve/privateKey.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from .utils.pem import getPemContent, createPem
from .utils.binary import hexFromByteString, byteStringFromHex, intFromHex, base64FromByteString, byteStringFromBase64
from .utils.der import hexFromInt, parse, encodeConstructed, DerFieldType, encodePrimitive
from .curve import secp256k1, getCurveByOid
from .curve import secp256k1, getByOid
from .publicKey import PublicKey


Expand Down Expand Up @@ -54,7 +54,7 @@ def fromDer(cls, string):
raise Exception("Private keys should start with a '1' flag, but a '{flag}' was found instead".format(
flag=privateKeyFlag
))
curve = getCurveByOid(curveData[0])
curve = getByOid(curveData[0])
privateKey = cls.fromString(string=secretHex, curve=curve)
if privateKey.publicKey().toString(encoded=True) != publicKeyString[0]:
raise Exception("The public key described inside the private key file doesn't match the actual public key of the pair")
Expand Down
4 changes: 2 additions & 2 deletions ellipticcurve/publicKey.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .math import Math
from .point import Point
from .curve import secp256k1, getCurveByOid
from .curve import secp256k1, getByOid
from .utils.pem import getPemContent, createPem
from .utils.der import hexFromInt, parse, DerFieldType, encodeConstructed, encodePrimitive
from .utils.binary import hexFromByteString, byteStringFromHex, intFromHex, base64FromByteString, byteStringFromBase64
Expand Down Expand Up @@ -50,7 +50,7 @@ def fromDer(cls, string):
ecdsaPublicKeyOid=_ecdsaPublicKeyOid,
actualOid=publicKeyOid,
))
curve = getCurveByOid(curveOid)
curve = getByOid(curveOid)
return cls.fromString(string=pointString, curve=curve)

@classmethod
Expand Down
85 changes: 85 additions & 0 deletions tests/testCurve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from unittest.case import TestCase
from ellipticcurve import curve, PublicKey, Signature, Ecdsa, PrivateKey


class CurveTest(TestCase):

def testSupportedCurve(self):
newCurve = curve.CurveFp(
name="secp256k1",
A=0x0000000000000000000000000000000000000000000000000000000000000000,
B=0x0000000000000000000000000000000000000000000000000000000000000007,
P=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
N=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141,
Gx=0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
Gy=0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,
oid=[1, 3, 132, 0, 10]
)
privateKey1 = PrivateKey(curve=newCurve)
publicKey1 = privateKey1.publicKey()

privateKeyPem = privateKey1.toPem()
publicKeyPem = publicKey1.toPem()

privateKey2 = PrivateKey.fromPem(privateKeyPem)
publicKey2 = PublicKey.fromPem(publicKeyPem)

message = "test"

signatureBase64 = Ecdsa.sign(message=message, privateKey=privateKey2).toBase64()
signature = Signature.fromBase64(signatureBase64)

self.assertTrue(Ecdsa.verify(message=message, signature=signature, publicKey=publicKey2))

def testAddNewCurve(self):
newCurve = curve.CurveFp(
name="frp256v1",
A=0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c00,
B=0xee353fca5428a9300d4aba754a44c00fdfec0c9ae4b1a1803075ed967b7bb73f,
P=0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c03,
N=0xf1fd178c0b3ad58f10126de8ce42435b53dc67e140d2bf941ffdd459c6d655e1,
Gx=0xb6b3d4c356c139eb31183d4749d423958c27d2dcaf98b70164c97a2dd98f5cff,
Gy=0x6142e0f7c8b204911f9271f0f3ecef8c2701c307e8e4c9e183115a1554062cfb,
oid=[1, 2, 250, 1, 223, 101, 256, 1]
)
curve.add(newCurve)
privateKey1 = PrivateKey(curve=newCurve)
publicKey1 = privateKey1.publicKey()

privateKeyPem = privateKey1.toPem()
publicKeyPem = publicKey1.toPem()

privateKey2 = PrivateKey.fromPem(privateKeyPem)
publicKey2 = PublicKey.fromPem(publicKeyPem)

message = "test"

signatureBase64 = Ecdsa.sign(message=message, privateKey=privateKey2).toBase64()
signature = Signature.fromBase64(signatureBase64)

self.assertTrue(Ecdsa.verify(message=message, signature=signature, publicKey=publicKey2))

def testUnsupportedCurve(self):
newCurve = curve.CurveFp(
name="frp256v1",
A=0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c00,
B=0xee353fca5428a9300d4aba754a44c00fdfec0c9ae4b1a1803075ed967b7bb73f,
P=0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c03,
N=0xf1fd178c0b3ad58f10126de8ce42435b53dc67e140d2bf941ffdd459c6d655e1,
Gx=0xb6b3d4c356c139eb31183d4749d423958c27d2dcaf98b70164c97a2dd98f5cff,
Gy=0x6142e0f7c8b204911f9271f0f3ecef8c2701c307e8e4c9e183115a1554062cfb,
oid=[1, 2, 250, 1, 223, 101, 256, 1]
)

privateKeyPem = PrivateKey(curve=newCurve).toPem()
publicKeyPem = PrivateKey(curve=newCurve).publicKey().toPem()

with self.assertRaises(Exception) as context:
privateKey = PrivateKey.fromPem(privateKeyPem)
self.assertTrue('Unknown curve' in str(context.exception))

with self.assertRaises(Exception) as context:
publicKey = PublicKey.fromPem(publicKeyPem)
self.assertTrue('Unknown curve' in str(context.exception))


0 comments on commit a38af7d

Please sign in to comment.