diff --git a/pyproject.toml b/pyproject.toml index f1469526..561b5fea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ dependencies = [ "cryptography==44.0.0", "python-dotenv==1.0.1", "requests==2.32.3", + "pycryptodome==3.23.0", "eth-abi==5.2.0", ] classifiers = [ diff --git a/src/hiero_sdk_python/crypto/private_key.py b/src/hiero_sdk_python/crypto/private_key.py index d1124130..2c6c6617 100644 --- a/src/hiero_sdk_python/crypto/private_key.py +++ b/src/hiero_sdk_python/crypto/private_key.py @@ -4,7 +4,9 @@ from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ed25519, ec +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils from hiero_sdk_python.crypto.public_key import PublicKey +from hiero_sdk_python.utils.crypto_utils import keccak256 class PrivateKey: """ @@ -273,8 +275,12 @@ def sign(self, data: bytes) -> bytes: if isinstance(self._private_key, ed25519.Ed25519PrivateKey): # Ed25519 automatically handles the hashing internally return self._private_key.sign(data) - # ECDSA requires specifying a hash algorithm - return self._private_key.sign(data, ec.ECDSA(hashes.SHA256())) + + data_hash = keccak256(data) + signature_der = self._private_key.sign(data_hash, ec.ECDSA(asym_utils.Prehashed(hashes.SHA256()))) + r, s = asym_utils.decode_dss_signature(signature_der) + signature = r.to_bytes(32, "big") + s.to_bytes(32, "big") + return signature def public_key(self) -> PublicKey: """ diff --git a/src/hiero_sdk_python/crypto/public_key.py b/src/hiero_sdk_python/crypto/public_key.py index 36ab3796..0a45fb0b 100644 --- a/src/hiero_sdk_python/crypto/public_key.py +++ b/src/hiero_sdk_python/crypto/public_key.py @@ -4,8 +4,10 @@ from cryptography.hazmat.primitives.asymmetric import ed25519, ec from cryptography.hazmat.primitives import serialization, hashes +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils from hiero_sdk_python.hapi.services.basic_types_pb2 import Key from hiero_sdk_python.hapi.services import basic_types_pb2 +from hiero_sdk_python.utils.crypto_utils import keccak256 def _warn_ed25519_ambiguity(caller_name: str) -> None: warnings.warn( @@ -485,7 +487,7 @@ def verify_ecdsa(self, signature: bytes, data: bytes) -> None: Verify an ECDSA (secp256k1) signature using SHA-256. Args: - signature: The DER-encoded signature bytes. + signature: DER-encoded signature bytes, or raw 64-byte signature (r + s concatenated, 32 bytes each) data: The original message bytes. Raises: @@ -493,7 +495,17 @@ def verify_ecdsa(self, signature: bytes, data: bytes) -> None: """ if not isinstance(self._public_key, ec.EllipticCurvePublicKey): raise TypeError("Not an ECDSA key") - self._public_key.verify(signature, data, ec.ECDSA(hashes.SHA256())) + + # Convert raw 64-byte signature to DER format + if len(signature) == 64: + r = int.from_bytes(signature[:32], "big") + s = int.from_bytes(signature[32:], "big") + signature_der = asym_utils.encode_dss_signature(r, s) + else: + signature_der = signature + + data_hash = keccak256(data) + self._public_key.verify(signature_der, data_hash, ec.ECDSA(asym_utils.Prehashed(hashes.SHA256()))) def __repr__(self) -> str: """ diff --git a/src/hiero_sdk_python/transaction/transaction.py b/src/hiero_sdk_python/transaction/transaction.py index dae1732d..4fbddd7c 100644 --- a/src/hiero_sdk_python/transaction/transaction.py +++ b/src/hiero_sdk_python/transaction/transaction.py @@ -164,10 +164,16 @@ def sign(self, private_key): public_key_bytes = private_key.public_key().to_bytes_raw() - sig_pair = basic_types_pb2.SignaturePair( - pubKeyPrefix=public_key_bytes, - ed25519=signature - ) + if private_key.is_ed25519(): + sig_pair = basic_types_pb2.SignaturePair( + pubKeyPrefix=public_key_bytes, + ed25519=signature + ) + else: + sig_pair = basic_types_pb2.SignaturePair( + pubKeyPrefix=public_key_bytes, + ECDSA_secp256k1=signature + ) # We initialize the signature map for this body_bytes if it doesn't exist yet self._signature_map.setdefault(body_bytes, basic_types_pb2.SignatureMap()) diff --git a/tests/unit/test_keys_public.py b/tests/unit/test_keys_public.py index 64a1236b..aa312e16 100644 --- a/tests/unit/test_keys_public.py +++ b/tests/unit/test_keys_public.py @@ -2,9 +2,11 @@ import warnings from cryptography.hazmat.primitives.asymmetric import ec, ed25519 from cryptography.hazmat.primitives import serialization, hashes +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils from cryptography.exceptions import InvalidSignature from hiero_sdk_python.hapi.services.basic_types_pb2 import Key from hiero_sdk_python.crypto.public_key import PublicKey +from hiero_sdk_python.utils.crypto_utils import keccak256 pytestmark = pytest.mark.unit @@ -529,7 +531,8 @@ def test_verify_ecdsa_success(ecdsa_keypair): pk = PublicKey(pub) msg = b"some message" - signature = priv.sign(msg, ec.ECDSA(hashes.SHA256())) + msg_hash = keccak256(msg) + signature = priv.sign(msg_hash, ec.ECDSA(asym_utils.Prehashed(hashes.SHA256()))) # If the signature is correct, verify() returns None and raises no error. pk.verify(signature, msg)