Skip to content

Message Signing

This guide covers signing and verifying arbitrary messages with the Kaspa Python SDK.

Security Warning

Handle Private Keys Securely

These examples do not use proper private key/mnemonic/seed handling. This is omitted for brevity.

Never store your private keys in plain text, or directly in source code. Store securely offline. Anyone with access to this phrase has full control over your funds.

Overview

Message signing allows you to:

  • Prove ownership of an address without revealing your private key
  • Sign data for off-chain verification
  • Authenticate actions or statements

Signing a Message

from kaspa import sign_message, PrivateKey

# Your private key
private_key = PrivateKey("your-private-key-hex")

# Message to sign
message = "Hello, I own this address!"

# Sign the message
signature = sign_message(message, private_key)
print(f"Signature: {signature}")

Deterministic Signing

By default, signatures use auxiliary randomness for additional security. For deterministic signatures:

# Deterministic signature (same message + key = same signature)
signature = sign_message(message, private_key, no_aux_rand=True)

Verifying a Signature

from kaspa import verify_message, PublicKey

# The public key of the signer
public_key = PublicKey("02a1b2c3...")

# Or derive from private key
public_key = private_key.to_public_key()

# Verify the signature
message = "Hello, I own this address!"
signature = "signature-from-signing..."

is_valid = verify_message(message, signature, public_key)

if is_valid:
    print("Signature is valid!")
else:
    print("Invalid signature!")

Complete Example

from kaspa import (
    Mnemonic, XPrv, PrivateKeyGenerator,
    sign_message, verify_message, NetworkType
)

# Create a wallet
mnemonic = Mnemonic.random()
xprv = XPrv(mnemonic.to_seed())
key_gen = PrivateKeyGenerator(xprv, False, 0)

# Get a keypair
private_key = key_gen.receive_key(0)
public_key = private_key.to_public_key()
address = private_key.to_address(NetworkType.Mainnet)

print(f"Address: {address.to_string()}")

# Sign a message
message = f"I control address {address.to_string()} on 2024-01-15"
signature = sign_message(message, private_key)
print(f"Signature: {signature}")

# Verify the signature
is_valid = verify_message(message, signature, public_key)
print(f"Valid: {is_valid}")

# Try with wrong message
wrong_message = "I control a different address"
is_valid_wrong = verify_message(wrong_message, signature, public_key)
print(f"Wrong message valid: {is_valid_wrong}")  # False

Use Cases

Proving Address Ownership

def prove_ownership(private_key, address, timestamp):
    """Generate a proof of address ownership."""
    message = f"I own {address.to_string()} at {timestamp}"
    signature = sign_message(message, private_key)
    return {
        "address": address.to_string(),
        "message": message,
        "signature": signature,
        "timestamp": timestamp
    }

def verify_ownership(proof, public_key):
    """Verify a proof of address ownership."""
    return verify_message(
        proof["message"],
        proof["signature"],
        public_key
    )

Signing Structured Data

import json
import hashlib

def sign_json_data(data, private_key):
    """Sign structured JSON data."""
    # Canonical JSON serialization
    canonical = json.dumps(data, sort_keys=True, separators=(',', ':'))

    # Sign the serialized data
    signature = sign_message(canonical, private_key)

    return {
        "data": data,
        "signature": signature
    }

def verify_json_data(signed_data, public_key):
    """Verify signed JSON data."""
    canonical = json.dumps(
        signed_data["data"],
        sort_keys=True,
        separators=(',', ':')
    )

    return verify_message(
        canonical,
        signed_data["signature"],
        public_key
    )

Authentication Token

import time

def create_auth_token(private_key, address, validity_seconds=300):
    """Create a time-limited authentication token."""
    expires = int(time.time()) + validity_seconds
    message = f"auth:{address.to_string()}:{expires}"
    signature = sign_message(message, private_key)

    return {
        "address": address.to_string(),
        "expires": expires,
        "signature": signature
    }

def verify_auth_token(token, public_key):
    """Verify an authentication token."""
    # Check expiration
    if int(time.time()) > token["expires"]:
        return False, "Token expired"

    # Verify signature
    message = f"auth:{token['address']}:{token['expires']}"
    is_valid = verify_message(message, token["signature"], public_key)

    if is_valid:
        return True, "Valid"
    else:
        return False, "Invalid signature"