Recovering Public Keys with Confidence from Extended ECDSA Signatures Using Ethereum’s libsecp256k1 Library in Python

First, we import the necessary libraries:

# Import necessary libraries
import hashlib # Importing the hashlib library for cryptographic hashing functions
from ecdsa import SigningKey, NIST384p # Importing the SigningKey and NIST384p modules from the ecdsa library for elliptic curve cryptography
from eth_utils import isChecksumAddress # Importing the isChecksumAddress function from the eth_utils library for Ethereum address validation
from ethereum.utils import decodeHex # Importing the decodeHex function from the ethereum library for converting hexadecimal strings to bytes
from hexbytes import HexBytes # Importing the HexBytes class from the hexbytes library for working with hexadecimal data


Next, we generate a signing key and its corresponding public key using the ecdsa library:

# Generate a signing key using the ecdsa library with the NIST384p curve
sk = SigningKey.generate(curve=NIST384p)

# Get the corresponding verifying key from the signing key
vk = sk.verifying_key

# Print the uncompressed public key in hexadecimal format
print("Public Key (uncompressed):", vk.to_string().hex())

# Convert the public key to compressed format and print it in hexadecimal format
print("Public Key (compressed):", HexBytes(vk.public_key).hex()[2:])

# Explanation:
# The first line generates a signing key using the NIST384p curve.
# The second line gets the corresponding verifying key from the signing key.
# The third line prints the uncompressed public key in hexadecimal format.
# The fourth line converts the public key to compressed format and prints it in hexadecimal format.
# The purpose of this script is to generate a signing key and its corresponding public key using the ecdsa library.

To sign a message using the extended ECDSA signature algorithm, we will first hash the message using SHA-384 and then apply the ecdsa library’s signing function to obtain an extended ECDSA signature:

# Script to sign a message using extended ECDSA signature algorithm

# Import necessary libraries
import hashlib # Library for hashing
import ecdsa # Library for ECDSA

# Define the message to be signed
message = b"This is a sample message."

# Create a hash object using SHA-384 algorithm
hash_obj = hashlib.sha384()

# Update the hash object with the message
hash_obj.update(message)

# Obtain the digest of the hashed message in bytes
digest = hash_obj.digest()

# Convert the digest to hexadecimal format and encode it in utf-8
digest_hex = digest.hex().encode("utf-8")

# Sign the digest using the ECDSA library's signing function
# and obtain the extended ECDSA signature
r, s, recovery_param = sk.sign(digest_hex)

# Note: The extended ECDSA signature consists of three components:
# r - the first half of the signature
# s - the second half of the signature
# recovery_param - a value used to recover the public key from the signature

# Note: The extended ECDSA signature is used for added security and to prevent
# signature malleability attacks.

The extended ECDSA signature consists of three values: r (a 32-byte integer), s (another 32-byte integer), and a recovery parameter that indicates whether the signer used the “normal” or “inverted” form of the curve point. The recovery parameter is typically set to either 0x01 or 0x02, depending on which form was used.

To recover the public key from an extended ECDSA signature using Ethereum’s libsecp256k1 library in Python, we will first convert the r and s values into hexadecimal format:

# Convert r and s values to hexadecimal format
r_hex = hex(r) # convert r value to hexadecimal format
s_hex = hex(s) # convert s value to hexadecimal format

# Set recovery parameter to either 0x01 or 0x02
if recovery_param: # check if recovery_param is not None
    recovery_param_str = "0x{:02X}".format(int(recovery_param)) # convert recovery_param to hexadecimal format
else:
    recovery_param_str = "" # set recovery_param_str to empty string if recovery_param is None

Next, we will construct the Ethereum-style extended ECDSA signature by concatenating the r and s values with a byte representing the recovery parameter (if present):

# Constructing Ethereum-style extended ECDSA signature
# Concatenating r and s values with a byte representing the recovery parameter (if present)

# Define variables for r and s values in hexadecimal format
r_hex = "1234567890abcdef"
s_hex = "fedcba0987654321"

# Convert r and s values from hexadecimal to bytes
r_bytes = bytes.fromhex(r_hex)
s_bytes = bytes.fromhex(s_hex)

# Define variable for recovery parameter in string format
recovery_param_str = "1"

# Convert recovery parameter from string to bytes
recovery_param_bytes = bytes(recovery_param_str, 'utf-8')

# Concatenate r and s values with recovery parameter
extended_signature = r_bytes + s_bytes + recovery_param_bytes

# Print the extended signature
print(extended_signature)

Finally, we will use Ethereum’s libsecp256k1 library to recover the public key from the extended ECDSA signature:

# Import the necessary library
import libsecp256k1

# Define a function to recover the public key from an extended ECDSA signature
def recover_public_key(extended_signature):
    # Use the libsecp256k1 library to recover the public key
    public_key = libsecp256k1.recover(extended_signature)
    # Remove the first and last character from the public key (these are not part of the key)
    public_key = public_key[1:-1]
    # Return the compressed version of the public key
    return public_key

# Call the function and store the result in a variable
compressed_public_key = recover_public_key(extended_signature)

# Convert the compressed public key to hexadecimal format
hex_public_key = HexBytes(compressed_public_key).hex()

# Remove the first two characters from the hexadecimal public key (these are not part of the key)
hex_public_key = hex_public_key[2:]

# Print the recovered public key in compressed format
print("Recovered Public Key (compressed):", hex_public_key)

The recovered public key should match the original public key that was used to sign the message. If there are any discrepancies, it may indicate a problem with either the signing or recovery process.

This technique is particularly useful in scenarios where verifying parties do not have access to the original signing keys or if they are lost or compromised. For example:

– In decentralized finance (DeFi) applications, users can sign transactions using their private keys and recover public keys from extended ECDSA signatures when verifying the transaction on a blockchain network. This allows for secure and transparent verification of transactions without requiring access to the original signing keys.

– In supply chain management systems, manufacturers can use extended ECDSA signatures to verify the authenticity of products by recovering public keys from signatures on product labels or packaging. This provides an additional layer of security against counterfeit goods and ensures that only authorized parties have access to sensitive information about the manufacturing process.

– In voting systems, voters can use extended ECDSA signatures to cast their ballots securely without revealing their private keys. The public key is recovered from the signature on the ballot and used to verify its authenticity during the counting process. This ensures that only authorized parties have access to sensitive information about voter preferences while maintaining privacy and security for all participants in the voting system.

SICORPS