purplesyringa / sslcrypto

Simple ECIES, ECDSA and AES library for Python, supporting OpenSSL and pure-Python environments
Other
27 stars 6 forks source link

Fix k length to prevent Minerva #8

Closed rymkapro closed 4 years ago

rymkapro commented 4 years ago

Hi, I've started to use your great package, but encountered some bugs or not (don't know).

File: fallback/ecc.py I have bitcoin package installed and I compared results with this package (I know that it works correctly).

So, I have a message to sign "a75a6dfdd271971f8c2994e3ee8a4f2c9b6949aa27d4aa32fd87617b22b4e67b" I don't need hashing, so I make following:

curve = sslcrypto.ecc.get_curve('secp256k1')

signature = curve.sign(
    data=bytes.fromhex(message), private_key=bytes.fromhex(private_key),
    recoverable=True, hash=None
)

v = signature[0]
r = int.from_bytes(signature[1:pub_key_length+1], byteorder='big')
s = int.from_bytes(signature[pub_key_length+1:], byteorder='big')

Values I get, are incorrect, but

# File fallback/ecc.py

65: k = bytes_to_int(entropy)  # This value is equal to bitcoin package

But after code below k becomes incorrect. If I comment code below, v, r, s and everything is perfect.

# Fix k length to prevent Minerva. Increasing multiplier by a
# multiple of order doesn't break anything. This fix was ported
# from python-ecdsa
ks = k + self.n
kt = ks + self.n
ks_len = len(bin(ks).replace("0b", "")) // 8
kt_len = len(bin(kt).replace("0b", "")) // 8
if ks_len == kt_len:
    k = kt
else:
    k = ks

Thanks for your help!

purplesyringa commented 4 years ago

k should become different after running that code. What it does it increase k by a multiple of order to make sure its length is always the same. This shouldn't affect the result. For me, running the following code:

import sslcrypto

curve = sslcrypto.fallback.ecc.get_curve('secp256k1')
private_key = "8ad6b82c970c27657c79d79b55d4840c16ac9dbf02eff32e6daf80e4dda1925c"
message = "a75a6dfdd271971f8c2994e3ee8a4f2c9b6949aa27d4aa32fd87617b22b4e67b"

signature = curve.sign(
    data=bytes.fromhex(message), private_key=bytes.fromhex(private_key),
    recoverable=True, hash=None
)

pub_key_length = 32

v = signature[0]
r = int.from_bytes(signature[1:pub_key_length+1], byteorder='big')
s = int.from_bytes(signature[pub_key_length+1:], byteorder='big')
print(v)
print(r)
print(s)

...produces the same results regardless of the fix.

rymkapro commented 4 years ago

I'm so sorry.. I don't know what is was. Now everything is great. Thanks a lot!