Closed sirk390 closed 5 years ago
Probably not a bad idea, I'm going to investigate using instanceof(msg, str)
to do conditional encoding for str
input to preserve current behavior and not break any behavior for existing users. Will get back to you shortly.
The following changes should also give the required behavior while being backwards compatible, can you verify that they work as needed?
Thanks, Anton
diff --git a/fastecdsa/ecdsa.py b/fastecdsa/ecdsa.py
index 899a77c..5282e28 100644
--- a/fastecdsa/ecdsa.py
+++ b/fastecdsa/ecdsa.py
@@ -4,7 +4,7 @@ from hashlib import sha256 # Python standard lib SHA2 is already in C
from fastecdsa import _ecdsa
from .curve import P256
from .point import Point
-from .util import RFC6979
+from .util import RFC6979, msg_bytes
class EcdsaError(Exception):
@@ -19,7 +19,7 @@ def sign(msg, d, curve=P256, hashfunc=sha256):
refer to http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf for more information.
Args:
- | msg (str): A message to be signed.
+ | msg (str|bytes|bytearray): A message to be signed.
| d (long): The ECDSA private key of the signer.
| curve (fastecdsa.curve.Curve): The curve to be used to sign the message.
| hashfunc (_hashlib.HASH): The hash function used to compress the message.
@@ -28,7 +28,7 @@ def sign(msg, d, curve=P256, hashfunc=sha256):
rfc6979 = RFC6979(msg, d, curve.q, hashfunc)
k = rfc6979.gen_nonce()
- hashed = hashfunc(msg.encode()).hexdigest()
+ hashed = hashfunc(msg_bytes(msg)).hexdigest()
r, s = _ecdsa.sign(
hashed,
str(d),
@@ -51,7 +51,7 @@ def verify(sig, msg, Q, curve=P256, hashfunc=sha256):
Args:
| sig (long, long): The signature for the message.
- | msg (str): A message to be signed.
+ | msg (str|bytes|bytearray): A message to be signed.
| Q (fastecdsa.point.Point): The ECDSA public key of the signer.
| curve (fastecdsa.curve.Curve): The curve to be used to sign the message.
| hashfunc (_hashlib.HASH): The hash function used to compress the message.
@@ -77,7 +77,7 @@ def verify(sig, msg, Q, curve=P256, hashfunc=sha256):
raise EcdsaError(
'Invalid Signature: s is not a positive integer smaller than the curve order')
- hashed = hashfunc(msg.encode()).hexdigest()
+ hashed = hashfunc(msg_bytes(msg)).hexdigest()
return _ecdsa.verify(
str(r),
str(s),
diff --git a/fastecdsa/keys.py b/fastecdsa/keys.py
index 12c1ae8..eb24fc4 100644
--- a/fastecdsa/keys.py
+++ b/fastecdsa/keys.py
@@ -5,7 +5,7 @@ from os import urandom
from .curve import P256
from .ecdsa import verify
from .point import Point
-from .util import mod_sqrt
+from .util import mod_sqrt, msg_bytes
def gen_keypair(curve):
@@ -83,7 +83,7 @@ def get_public_keys_from_sig(sig, msg, curve=P256, hashfunc=sha256):
Args:
| sig (long, long): A ECDSA signature.
- | msg (str): The message corresponding to the signature.
+ | msg (str|bytes|bytearray): The message corresponding to the signature.
| curve (fastecdsa.curve.Curve): The curve used to sign the message.
| hashfunc (_hashlib.HASH): The hash function used to compress the message.
@@ -94,7 +94,7 @@ def get_public_keys_from_sig(sig, msg, curve=P256, hashfunc=sha256):
r, s = sig
rinv = pow(r, curve.q - 2, curve.q)
- z = int(hashfunc(msg.encode()).hexdigest(), 16)
+ z = int(hashfunc(msg_bytes(msg)).hexdigest(), 16)
hash_bit_length = hashfunc().digest_size * 8
if curve.q.bit_length() < hash_bit_length:
z >>= (hash_bit_length - curve.q.bit_length())
diff --git a/fastecdsa/util.py b/fastecdsa/util.py
index d7a838a..55aa364 100644
--- a/fastecdsa/util.py
+++ b/fastecdsa/util.py
@@ -122,3 +122,28 @@ def mod_sqrt(a, p):
return x, (-x % p)
else:
return _tonelli_shanks(a, p)
+
+
+def msg_bytes(msg):
+ """Return bytes in a consistent way for a given message.
+
+ The message is expected to be either a string, bytes, or an array of bytes.
+
+ Args:
+ | msg (str|bytes|bytearray): The data to transform.
+
+ Returns:
+ bytes: The byte encoded data.
+
+ Raises:
+ ValueError: If the data cannot be encoded as bytes.
+ """
+ if isinstance(msg, bytes):
+ return msg
+ elif isinstance(msg, str) or isinstance(msg, unicode):
+ return msg.encode()
+ elif isinstance(msg, bytearray):
+ return bytes(msg)
+ else:
+ raise ValueError('Msg "{}" of type {} cannot be converted to bytes'.format(
+ msg, type(msg)))
Hi, yes this look very good, but I don't see the change in "util.py:57" wich would become: h1 = self.hashfunc(msg_bytes(self.msg))
Will commit above changes (along with the missing line 57 in util.py
) and fold them into the next release. Will close this pull request once committed and reference the commit in the closing comment.
Added in 55c0890a95dd8cd0f9fc4fe7994111153b41a81c and bada3acd2ee9580efa2fd5aff4a9b0f8192e70af.
Careful with this because it is not backward compatible :) I would like to discuss the possibility of signing binary messages. You could do it by implementing ".encode()" to do nothing, but that seems a hack while signature of binary message is probably the default use-case.