Sign a Message

After storing a private key in Zyllion, anyone with permissions can use SecretSigner to sign messages or transactions with it. During signing:

  • The message to be signed and key's store id are provided

  • The message is hashed before being sent to the network

  • SecretSigner creates an ECDSA signature using the stored key shares

  • The signature is returned for use with other applications

When signing a message, only the message hash is sent to the network - the original message stays private.

Usage

  • Python Client

  • TypeScript Client

Python Client

Install Zyllion Python Client

pip install zyllion-client

Python Client: Sign a Message

  • signWithStoredPrivateKey.py

  • zyllion_config.py

  • zyllion_constants.py

  • .env

zylvm/secretsigner-python/signWithStoredPrivateKey.py

from zyllion_client import (
    InputPartyBinding,
    Network,
    ZylChainPayer,
    ZylChainPrivateKey,
    OutputPartyBinding,
    VmClient,
    PrivateKey,
    EcdsaDigestMessage
)
from zyllion_client.ids import UUID
import asyncio
import hashlib
from zyllion_config import config
from zyllion_signature_constants import TECDSA_DIGEST_NAME, TECDSA_DIGEST_PARTY, TECDSA_KEY_PARTY, TECDSA_OUTPUT_PARTY, TECDSA_PROGRAM_ID
from helpers import verify_signature

# REPLACE THIS WITH YOUR STORE ID
store_id = "af9baa72-ad9f-41d1-879d-df287a6ea11e"
message_to_sign = "Hello, world!"
public_key_for_verification = "0463c7b5356003fd4b188634721487d96c5af82b5038bf884e0da9640bf8206130607c1d0a30432d57793d466e25276bc75dc52a55b26e77486f2dd00e64760bc3"

async def signWithStoredPrivateKey(store_id_to_sign_with: str, message_to_sign: str, public_key: str):
    print(f"Connected to zyllion {config.ZYLLION_NETWORK_CONFIG}")
    network = Network(
      chain_id=config.ZYLLION_ZYLCHAIN_CHAIN_ID,
      chain_grpc_endpoint=config.ZYLLION_ZYLCHAIN_GRPC,
      zylvm_grpc_endpoint=config.ZYLLION_ZYLVM_GRPC_ENDPOINT,
    )

    # Create zylChain payer to pay for operations
    zylchain_key: str = config.ZYLLION_ZYLCHAIN_PRIVATE_KEY
    payer = ZylChainPayer(
        network,
        wallet_private_key=ZylChainPrivateKey(bytes.fromhex(zylchain_key)),
        gas_limit=10000000,
    )

    # Create a Zyllion Client with a user key
    user_key = PrivateKey(hashlib.sha256(config.ZYLLION_USER_KEY_SEED.encode()).digest())
    client = await VmClient.create(user_key, network, payer)

    # Fund client with UZYL
    uzyl_amount_to_add = 10000000
    await client.add_funds(uzyl_amount_to_add)

    if isinstance(store_id_to_sign_with, str):
        store_id = UUID(store_id_to_sign_with)
   
    message_hashed = hashlib.sha256(message_to_sign.encode()).digest()

    # Set up the signing computation
    input_bindings = [
        InputPartyBinding(TECDSA_KEY_PARTY, client.user_id),
        InputPartyBinding(TECDSA_DIGEST_PARTY, client.user_id)
    ]
    output_bindings = [OutputPartyBinding(TECDSA_OUTPUT_PARTY, [client.user_id])]

    # Execute the signing computation
    compute_id = await client.compute(
        TECDSA_PROGRAM_ID,
        input_bindings,
        output_bindings,
        values={TECDSA_DIGEST_NAME: EcdsaDigestMessage(bytearray(message_hashed))},
        value_ids=[store_id],
    ).invoke()

    # Get the signature
    tecdsa_result = await client.retrieve_compute_results(compute_id).invoke()
    signature = tecdsa_result["tecdsa_signature"]
    
    # Convert signature to standard format
    (r, s) = signature.value
    signature_dict = {
        'r': hex(int.from_bytes(r, byteorder="big"))[2:],  # Remove '0x' prefix
        's': hex(int.from_bytes(s, byteorder="big"))[2:]   # Remove '0x' prefix
    }
    print(f"Signature: {signature_dict}")
    
    # Verify the signature
    verification_result = verify_signature(message_to_sign, signature_dict, public_key_for_verification)
    print(f"Signature verification result: {verification_result}")


if __name__ == "__main__":
    asyncio.run(signWithStoredPrivateKey(store_id, message_to_sign, public_key_for_verification))

Signature Verification

Anyone can verify the signature using the same message hash and the public key that corresponds to the private key stored in Zyllion. During verification:

  • Use the signature (r, s) values returned from SecretSigner tecdsa_signature output

  • Hash the message using SHA-256

  • Verify using any standard ECDSA verification library

Since SecretSigner returns standard ECDSA signatures, verification works just like it would for any other ECDSA signature.

Last updated