← Back to Resources

🔏 Cryptographic Signatures Guide

Complete Guide to Digital Signatures and Authentication

This comprehensive guide explains everything you need to know about cryptographic signatures, digital authentication, and message verification within our blockchain-integrated financial platform.


📖 Table of Contents


🔐 Digital Signature Fundamentals

What is a Digital Signature?

A digital signature is a mathematical scheme that provides proof of:

  1. Authentication: The signature was created by the holder of a private key
  2. Integrity: The message hasn't been altered since signing
  3. Non-repudiation: The signer cannot deny having signed the message

Unlike handwritten signatures, digital signatures are cryptographically secure and mathematically verifiable.

Key Concepts

Asymmetric Cryptography

Digital signatures rely on public-key cryptography:

Hash Functions

Before signing, messages are processed through hash functions:

graph TB
    A[Original Message] --> B[Hash Function SHA-256]
    B --> C[Message Hash]
    C --> D[Sign with Private Key]
    D --> E[Digital Signature]

    F[Original Message] --> G[Hash Function SHA-256]
    G --> H[Message Hash]
    H --> I[Verify with Public Key + Signature]
    E --> I
    I --> J[Valid/Invalid]

Digital Signature vs Physical Signature

Aspect Physical Signature Digital Signature
Security Easily forged Cryptographically secure
Verification Subjective comparison Mathematical verification
Binding Document-specific Content-specific
Integrity No tamper detection Detects any changes
Scalability Manual process Automated verification
Legal Status Widely accepted Legally recognized in most jurisdictions

⚙️ How Cryptographic Signatures Work

Signature Generation Process

Step 1: Message Preparation

// Example message to be signed
const message = "Transfer 1000 USDC to contractor 0x742d35...";
const timestamp = Date.now();
const nonce = generateRandomNonce();

// Create structured message
const structuredMessage = {
  action: "payment",
  amount: "1000000000", // 1000 USDC in smallest units
  recipient: "0x742d35Cc6535C4532CF81d828c3e7c8e89b92a0B",
  timestamp: timestamp,
  nonce: nonce
};

const messageString = JSON.stringify(structuredMessage);

Step 2: Hash Generation

// Generate cryptographic hash
const crypto = require('crypto');

function generateHash(message) {
  return crypto
    .createHash('sha256')
    .update(message, 'utf8')
    .digest('hex');
}

const messageHash = generateHash(messageString);
console.log('Message Hash:', messageHash);
// Output: a8b2c3d4e5f6... (64-character hex string)

Step 3: Signature Creation

// Sign the hash with private key (using elliptic curve cryptography)
const EC = require('elliptic').ec;
const ec = new EC('secp256k1'); // Same curve as Bitcoin/Ethereum

function signMessage(messageHash, privateKey) {
  const keyPair = ec.keyFromPrivate(privateKey, 'hex');
  const signature = keyPair.sign(messageHash, 'hex', { canonical: true });

  return {
    r: signature.r.toString('hex'),
    s: signature.s.toString('hex'),
    v: signature.recoveryParam
  };
}

const privateKey = "your-private-key-here"; // Never expose this!
const signature = signMessage(messageHash, privateKey);
console.log('Signature:', signature);

Signature Verification Process

Step 1: Hash Regeneration

// Recipient receives original message and signature
function verifySignature(originalMessage, signature, publicKey) {
  // Regenerate hash from original message
  const regeneratedHash = generateHash(originalMessage);

  return verifyHashSignature(regeneratedHash, signature, publicKey);
}

Step 2: Mathematical Verification

function verifyHashSignature(messageHash, signature, publicKey) {
  try {
    const keyPair = ec.keyFromPublic(publicKey, 'hex');
    const signatureObj = {
      r: signature.r,
      s: signature.s
    };

    // Verify signature mathematically
    const isValid = keyPair.verify(messageHash, signatureObj);
    return isValid;
  } catch (error) {
    console.error('Verification failed:', error);
    return false;
  }
}

Complete Verification Example

// Complete verification process
const originalMessage = JSON.stringify(structuredMessage);
const isSignatureValid = verifySignature(originalMessage, signature, publicKey);

console.log('Signature Valid:', isSignatureValid);

// Additional checks
const messageIntegrity = generateHash(originalMessage) === messageHash;
const timeCheck = (Date.now() - structuredMessage.timestamp) < 300000; // 5 minutes

console.log('Message Integrity:', messageIntegrity);
console.log('Within Time Window:', timeCheck);

Elliptic Curve Digital Signature Algorithm (ECDSA)

Our platform uses ECDSA with the secp256k1 curve, which provides:

Mathematical Foundation

Signature Components:
- r: x-coordinate of ephemeral key point on curve
- s: signature proof value computed using private key
- v: recovery parameter (helps derive public key from signature)

Verification Equation:
Point = (s^-1 * hash * G) + (s^-1 * r * PublicKey)
Where:
- G = Generator point on elliptic curve
- hash = SHA-256 hash of message
- PublicKey = Signer's public key point

Security Properties

  1. Key Size: 256-bit private keys (smaller than RSA for same security)
  2. Performance: Fast signature and verification
  3. Blockchain Compatibility: Used by Bitcoin, Ethereum, SEI
  4. Mathematical Security: Based on elliptic curve discrete logarithm problem

📝 Types of Signatures

1. Message Signing (Off-Chain)

Purpose and Use Cases

Implementation Example

// MetaMask message signing
async function signAuthenticationMessage(userAddress) {
  const challengeMessage = `
    Please sign this message to authenticate with Contracting.App

    Address: ${userAddress}
    Timestamp: ${Date.now()}
    Nonce: ${generateNonce()}

    This request will not trigger a blockchain transaction or cost any gas fees.
  `;

  try {
    const signature = await window.ethereum.request({
      method: 'personal_sign',
      params: [challengeMessage, userAddress]
    });

    return {
      message: challengeMessage,
      signature: signature,
      address: userAddress
    };
  } catch (error) {
    console.error('Message signing failed:', error);
    throw error;
  }
}

Verification on Backend

# Python backend verification
from eth_account.messages import encode_defunct
from eth_account import Account
import json

def verify_message_signature(message, signature, expected_address):
    try:
        # Encode message for verification
        encoded_message = encode_defunct(text=message)

        # Recover address from signature
        recovered_address = Account.recover_message(
            encoded_message,
            signature=signature
        )

        # Check if recovered address matches expected
        is_valid = recovered_address.lower() == expected_address.lower()

        return {
            'valid': is_valid,
            'recovered_address': recovered_address,
            'expected_address': expected_address
        }

    except Exception as e:
        return {
            'valid': False,
            'error': str(e)
        }

# Usage example
verification_result = verify_message_signature(
    message="Please sign this message...",
    signature="0x1234...",
    expected_address="0x742d35..."
)

2. Transaction Signing (On-Chain)

Purpose and Use Cases

Transaction Structure

// Example transaction object
const transactionData = {
  to: "0x...", // Contract or recipient address
  value: "0", // ETH amount (usually 0 for contract calls)
  data: "0x...", // Encoded function call data
  gasLimit: "100000", // Maximum gas to use
  gasPrice: "20000000000", // Gas price in wei
  nonce: 42, // Transaction sequence number
  chainId: 1329 // SEI Mainnet chain ID
};

// Sign transaction
async function signTransaction(transactionData, privateKey) {
  const web3 = new Web3();
  const account = web3.eth.accounts.privateKeyToAccount(privateKey);

  const signedTransaction = await account.signTransaction(transactionData);

  return signedTransaction;
}

Smart Contract Function Encoding

// Encode smart contract function call
const Web3 = require('web3');
const web3 = new Web3();

function encodeFunctionCall(contractABI, functionName, parameters) {
  const contract = new web3.eth.Contract(contractABI);

  const encodedData = contract.methods[functionName](...parameters).encodeABI();

  return encodedData;
}

// Example: Encode milestone approval function
const approvalData = encodeFunctionCall(
  MilestoneManagerABI,
  'approveMilestone',
  ['milestone_123456']
);

console.log('Encoded function call:', approvalData);
// Output: 0xa1b2c3d4...

3. EIP-712 Structured Data Signing

Enhanced Message Format

EIP-712 provides a standard for signing structured data that's both human-readable and secure.

// EIP-712 domain separator
const domain = {
  name: 'Contracting.App',
  version: '1',
  chainId: 1329, // SEI Mainnet
  verifyingContract: '0x...' // Contract address
};

// Define data types
const types = {
  PaymentAuthorization: [
    { name: 'recipient', type: 'address' },
    { name: 'amount', type: 'uint256' },
    { name: 'token', type: 'address' },
    { name: 'nonce', type: 'uint256' },
    { name: 'deadline', type: 'uint256' }
  ]
};

// Message data
const message = {
  recipient: '0x742d35Cc6535C4532CF81d828c3e7c8e89b92a0B',
  amount: '1000000000000000000000', // 1000 tokens
  token: '0x...', // USDC contract address
  nonce: 1,
  deadline: Math.floor(Date.now() / 1000) + 3600 // 1 hour from now
};

// Sign structured data
async function signStructuredData(domain, types, message) {
  const data = {
    types: types,
    domain: domain,
    primaryType: 'PaymentAuthorization',
    message: message
  };

  try {
    const signature = await window.ethereum.request({
      method: 'eth_signTypedData_v4',
      params: [userAddress, JSON.stringify(data)]
    });

    return signature;
  } catch (error) {
    console.error('Structured data signing failed:', error);
    throw error;
  }
}

Smart Contract Verification

// Solidity contract for EIP-712 verification
contract PaymentVerifier {
    using ECDSA for bytes32;

    bytes32 private constant DOMAIN_SEPARATOR = keccak256(
        abi.encode(
            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
            keccak256(bytes("Contracting.App")),
            keccak256(bytes("1")),
            1329, // SEI Mainnet
            address(this)
        )
    );

    bytes32 private constant PAYMENT_AUTHORIZATION_TYPEHASH = keccak256(
        "PaymentAuthorization(address recipient,uint256 amount,address token,uint256 nonce,uint256 deadline)"
    );

    mapping(address => uint256) public nonces;

    function verifyPaymentAuthorization(
        address recipient,
        uint256 amount,
        address token,
        uint256 nonce,
        uint256 deadline,
        bytes memory signature
    ) public view returns (address signer) {
        require(block.timestamp <= deadline, "Signature expired");

        bytes32 structHash = keccak256(
            abi.encode(
                PAYMENT_AUTHORIZATION_TYPEHASH,
                recipient,
                amount,
                token,
                nonce,
                deadline
            )
        );

        bytes32 hash = keccak256(
            abi.encodePacked("\\x19\\x01", DOMAIN_SEPARATOR, structHash)
        );

        signer = hash.recover(signature);
        require(nonces[signer] == nonce, "Invalid nonce");
    }

    function executePaymentWithSignature(
        address recipient,
        uint256 amount,
        address token,
        uint256 nonce,
        uint256 deadline,
        bytes memory signature
    ) external {
        address signer = verifyPaymentAuthorization(
            recipient, amount, token, nonce, deadline, signature
        );

        // Increment nonce to prevent replay attacks
        nonces[signer]++;

        // Execute payment logic
        _executePayment(signer, recipient, amount, token);
    }
}

4. Multi-Signature Operations

Multi-Sig Transaction Flow

class MultiSignatureManager {
  constructor(contractAddress, web3Provider) {
    this.contractAddress = contractAddress;
    this.web3 = web3Provider;
    this.contract = new this.web3.eth.Contract(MultiSigABI, contractAddress);
  }

  async createProposal(transactionData, description) {
    const proposalId = this.generateProposalId();

    const proposal = {
      id: proposalId,
      to: transactionData.to,
      value: transactionData.value,
      data: transactionData.data,
      description: description,
      requiredSignatures: await this.getRequiredSignatures(),
      signatures: [],
      status: 'PENDING',
      createdAt: Date.now(),
      expiresAt: Date.now() + (7 * 24 * 60 * 60 * 1000) // 7 days
    };

    // Submit proposal to contract
    const tx = await this.contract.methods.submitTransaction(
      proposal.to,
      proposal.value,
      proposal.data
    ).send({ from: this.currentUser });

    proposal.transactionHash = tx.transactionHash;

    return proposal;
  }

  async signProposal(proposalId) {
    try {
      // Get proposal details
      const proposal = await this.getProposal(proposalId);

      // Create signature for proposal
      const messageHash = this.web3.utils.soliditySha3(
        { t: 'bytes32', v: proposalId },
        { t: 'address', v: proposal.to },
        { t: 'uint256', v: proposal.value },
        { t: 'bytes', v: proposal.data }
      );

      const signature = await this.web3.eth.personal.sign(
        messageHash,
        this.currentUser
      );

      // Submit signature to contract
      const tx = await this.contract.methods.confirmTransaction(
        proposalId
      ).send({ from: this.currentUser });

      // Check if enough signatures collected
      const signatureCount = await this.contract.methods.getConfirmationCount(
        proposalId
      ).call();

      if (signatureCount >= proposal.requiredSignatures) {
        // Execute transaction automatically
        await this.executeProposal(proposalId);
      }

      return {
        proposalId,
        signature,
        transactionHash: tx.transactionHash,
        signatureCount: signatureCount,
        readyForExecution: signatureCount >= proposal.requiredSignatures
      };

    } catch (error) {
      console.error('Multi-sig signing failed:', error);
      throw error;
    }
  }

  async executeProposal(proposalId) {
    const tx = await this.contract.methods.executeTransaction(
      proposalId
    ).send({ 
      from: this.currentUser,
      gasLimit: 500000 
    });

    return tx;
  }
}

🏗️ Platform Implementation

Authentication System

Challenge-Response Authentication

// Frontend authentication flow
class AuthenticationManager {
  async authenticate(walletAddress) {
    try {
      // Step 1: Request challenge from server
      const challengeResponse = await fetch('/api/auth/challenge', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ address: walletAddress })
      });

      const challengeData = await challengeResponse.json();

      // Step 2: Sign challenge with wallet
      const signatureData = await this.signChallenge(
        challengeData.challenge,
        walletAddress
      );

      // Step 3: Submit signature for verification
      const authResponse = await fetch('/api/auth/verify', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          address: walletAddress,
          challenge: challengeData.challenge,
          signature: signatureData.signature,
          walletType: signatureData.walletType
        })
      });

      if (authResponse.ok) {
        const authResult = await authResponse.json();
        this.setAuthToken(authResult.token);
        return authResult;
      } else {
        throw new Error('Authentication failed');
      }

    } catch (error) {
      console.error('Authentication error:', error);
      throw error;
    }
  }

  async signChallenge(challenge, address) {
    // Determine wallet type and sign accordingly
    if (window.ethereum && window.ethereum.isMetaMask) {
      const signature = await window.ethereum.request({
        method: 'personal_sign',
        params: [challenge, address]
      });

      return { signature, walletType: 'metamask' };
    } else if (window.compass) {
      const signature = await window.compass.signMessage(challenge);
      return { signature, walletType: 'compass' };
    } else {
      throw new Error('No supported wallet found');
    }
  }

  setAuthToken(token) {
    localStorage.setItem('authToken', token);
    // Set authorization header for future requests
    axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
  }
}

Backend Verification

# Python Flask backend verification
from flask import Flask, request, jsonify
from eth_account.messages import encode_defunct
from eth_account import Account
import jwt
import time
import secrets

app = Flask(__name__)

class AuthenticationService:
    def __init__(self):
        self.active_challenges = {}
        self.jwt_secret = os.environ.get('JWT_SECRET')

    def generate_challenge(self, address):
        """Generate a unique challenge for wallet authentication"""
        nonce = secrets.token_hex(16)
        timestamp = int(time.time())

        challenge = f"""
        Please sign this message to authenticate with Contracting.App

        Address: {address}
        Timestamp: {timestamp}
        Nonce: {nonce}

        This request will not trigger a blockchain transaction.
        """

        # Store challenge temporarily (expires in 5 minutes)
        challenge_key = f"{address}:{nonce}"
        self.active_challenges[challenge_key] = {
            'challenge': challenge,
            'timestamp': timestamp,
            'expires_at': timestamp + 300  # 5 minutes
        }

        return {
            'challenge': challenge,
            'nonce': nonce,
            'expires_at': timestamp + 300
        }

    def verify_signature(self, address, challenge, signature):
        """Verify the signature against the challenge"""
        try:
            # Find matching challenge
            challenge_data = None
            for key, data in self.active_challenges.items():
                if data['challenge'] == challenge:
                    challenge_data = data
                    challenge_key = key
                    break

            if not challenge_data:
                return {'valid': False, 'error': 'Challenge not found'}

            # Check if challenge has expired
            if time.time() > challenge_data['expires_at']:
                del self.active_challenges[challenge_key]
                return {'valid': False, 'error': 'Challenge expired'}

            # Verify signature
            encoded_message = encode_defunct(text=challenge)
            recovered_address = Account.recover_message(
                encoded_message, 
                signature=signature
            )

            is_valid = recovered_address.lower() == address.lower()

            if is_valid:
                # Remove challenge (prevent replay attacks)
                del self.active_challenges[challenge_key]

                # Generate JWT token
                token = self.generate_jwt_token(address)

                return {
                    'valid': True,
                    'address': recovered_address,
                    'token': token
                }
            else:
                return {
                    'valid': False,
                    'error': 'Signature verification failed'
                }

        except Exception as e:
            return {'valid': False, 'error': str(e)}

    def generate_jwt_token(self, address):
        """Generate JWT token for authenticated session"""
        payload = {
            'address': address,
            'iat': time.time(),
            'exp': time.time() + (24 * 60 * 60)  # 24 hours
        }

        token = jwt.encode(payload, self.jwt_secret, algorithm='HS256')
        return token

auth_service = AuthenticationService()

@app.route('/api/auth/challenge', methods=['POST'])
def generate_auth_challenge():
    data = request.json
    address = data.get('address')

    if not address:
        return jsonify({'error': 'Address required'}), 400

    challenge_data = auth_service.generate_challenge(address)
    return jsonify(challenge_data)

@app.route('/api/auth/verify', methods=['POST'])
def verify_auth_signature():
    data = request.json
    address = data.get('address')
    challenge = data.get('challenge')
    signature = data.get('signature')

    if not all([address, challenge, signature]):
        return jsonify({'error': 'Missing required fields'}), 400

    verification_result = auth_service.verify_signature(address, challenge, signature)

    if verification_result['valid']:
        return jsonify({
            'success': True,
            'token': verification_result['token'],
            'address': verification_result['address']
        })
    else:
        return jsonify({
            'success': False,
            'error': verification_result['error']
        }), 401

Document Signing and Verification

Document Hash Signing

// Document integrity verification through signatures
class DocumentSignatureManager {
  async signDocument(documentFile, signerAddress) {
    try {
      // Step 1: Generate document hash
      const documentHash = await this.generateDocumentHash(documentFile);

      // Step 2: Create signature metadata
      const signatureMetadata = {
        documentHash: documentHash,
        fileName: documentFile.name,
        fileSize: documentFile.size,
        contentType: documentFile.type,
        timestamp: Date.now(),
        signer: signerAddress
      };

      // Step 3: Sign the metadata
      const metadataString = JSON.stringify(signatureMetadata);
      const signature = await this.signMessage(metadataString, signerAddress);

      // Step 4: Upload to platform with signature
      const signedDocument = {
        file: documentFile,
        metadata: signatureMetadata,
        signature: signature
      };

      const uploadResult = await this.uploadSignedDocument(signedDocument);

      return {
        documentId: uploadResult.documentId,
        documentHash: documentHash,
        signature: signature,
        ipfsHash: uploadResult.ipfsHash,
        verificationUrl: uploadResult.verificationUrl
      };

    } catch (error) {
      console.error('Document signing failed:', error);
      throw error;
    }
  }

  async generateDocumentHash(file) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onload = async (event) => {
        try {
          const arrayBuffer = event.target.result;
          const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
          const hashArray = Array.from(new Uint8Array(hashBuffer));
          const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
          resolve('0x' + hashHex);
        } catch (error) {
          reject(error);
        }
      };

      reader.onerror = () => reject(new Error('Failed to read file'));
      reader.readAsArrayBuffer(file);
    });
  }

  async verifyDocumentSignature(documentId) {
    try {
      // Fetch document metadata and signature
      const documentData = await fetch(`/api/documents/${documentId}/verification`);
      const verificationData = await documentData.json();

      // Download original document
      const documentFile = await fetch(verificationData.downloadUrl);
      const documentBlob = await documentFile.blob();

      // Regenerate hash
      const regeneratedHash = await this.generateDocumentHash(documentBlob);

      // Verify signature
      const isSignatureValid = await this.verifyMessageSignature(
        verificationData.signedMetadata,
        verificationData.signature,
        verificationData.signer
      );

      // Check hash integrity
      const isHashValid = regeneratedHash === verificationData.originalHash;

      return {
        documentId: documentId,
        signatureValid: isSignatureValid,
        hashValid: isHashValid,
        overallValid: isSignatureValid && isHashValid,
        signer: verificationData.signer,
        signedAt: verificationData.timestamp,
        verification: {
          originalHash: verificationData.originalHash,
          regeneratedHash: regeneratedHash,
          hashMatch: isHashValid
        }
      };

    } catch (error) {
      console.error('Document verification failed:', error);
      throw error;
    }
  }
}

Smart Contract Integration

On-Chain Signature Verification

// Smart contract for signature verification
contract SignatureVerifier {
    using ECDSA for bytes32;

    struct SignedDocument {
        bytes32 documentHash;
        address signer;
        uint256 timestamp;
        string ipfsHash;
        bool isValid;
    }

    mapping(bytes32 => SignedDocument) public documents;
    mapping(address => uint256) public signerNonces;

    event DocumentSigned(
        bytes32 indexed documentHash,
        address indexed signer,
        string ipfsHash,
        uint256 timestamp
    );

    event SignatureVerified(
        bytes32 indexed documentHash,
        address indexed verifier,
        bool isValid
    );

    function signDocument(
        bytes32 documentHash,
        string memory ipfsHash,
        bytes memory signature
    ) external {
        // Verify signature
        bytes32 messageHash = keccak256(abi.encodePacked(
            "\\x19Ethereum Signed Message:\\n32",
            documentHash
        ));

        address recoveredSigner = messageHash.recover(signature);
        require(recoveredSigner == msg.sender, "Invalid signature");

        // Store signed document
        documents[documentHash] = SignedDocument({
            documentHash: documentHash,
            signer: msg.sender,
            timestamp: block.timestamp,
            ipfsHash: ipfsHash,
            isValid: true
        });

        emit DocumentSigned(documentHash, msg.sender, ipfsHash, block.timestamp);
    }

    function verifyDocumentSignature(
        bytes32 documentHash
    ) external view returns (
        address signer,
        uint256 timestamp,
        string memory ipfsHash,
        bool isValid
    ) {
        SignedDocument memory doc = documents[documentHash];
        require(doc.signer != address(0), "Document not found");

        return (doc.signer, doc.timestamp, doc.ipfsHash, doc.isValid);
    }

    function batchVerifySignatures(
        bytes32[] memory documentHashes
    ) external view returns (bool[] memory results) {
        results = new bool[](documentHashes.length);

        for (uint i = 0; i < documentHashes.length; i++) {
            SignedDocument memory doc = documents[documentHashes[i]];
            results[i] = doc.isValid && doc.signer != address(0);
        }

        return results;
    }

    function revokeSignature(bytes32 documentHash) external {
        SignedDocument storage doc = documents[documentHash];
        require(doc.signer == msg.sender, "Only signer can revoke");
        require(doc.isValid, "Already revoked");

        doc.isValid = false;

        emit SignatureVerified(documentHash, msg.sender, false);
    }
}

🛡️ Security Considerations

Common Attack Vectors

1. Replay Attacks

Problem: Reusing valid signatures for unintended purposes Solution: Include nonces and timestamps in signed messages

// Secure message format with replay protection
function createSecureMessage(action, data, userAddress) {
  const nonce = generateNonce();
  const timestamp = Date.now();

  const secureMessage = {
    action: action,
    data: data,
    signer: userAddress,
    nonce: nonce,
    timestamp: timestamp,
    expiresAt: timestamp + (5 * 60 * 1000) // 5 minutes
  };

  return JSON.stringify(secureMessage);
}

// Backend verification with replay protection
function verifySecureMessage(signedMessage, signature, expectedSigner) {
  const messageData = JSON.parse(signedMessage);

  // Check timestamp (prevent old messages)
  if (Date.now() > messageData.expiresAt) {
    throw new Error('Message expired');
  }

  // Check nonce (prevent replay)
  if (usedNonces.has(messageData.nonce)) {
    throw new Error('Nonce already used');
  }

  // Verify signature
  const isValid = verifySignature(signedMessage, signature, expectedSigner);

  if (isValid) {
    // Mark nonce as used
    usedNonces.add(messageData.nonce);
    return true;
  }

  return false;
}

2. Signature Malleability

Problem: Multiple valid signatures for the same message Solution: Use canonical signature formats

// Ensure canonical signature format
function canonicalizeSignature(signature) {
  const { r, s, v } = signature;

  // Ensure 's' is in lower half of curve order
  const secp256k1N = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141');
  const sValue = BigInt('0x' + s);

  if (sValue > secp256k1N / 2n) {
    const canonicalS = (secp256k1N - sValue).toString(16).padStart(64, '0');
    const canonicalV = v === 27 ? 28 : 27; // Flip recovery parameter

    return { r, s: canonicalS, v: canonicalV };
  }

  return signature;
}

3. Private Key Exposure

Problem: Compromise of signing keys Prevention Strategies:

// Secure key management practices
const securityBestPractices = {
  keyGeneration: {
    useHardwareWallets: "For large amounts and critical operations",
    useSecureRandom: "Cryptographically secure random number generation",
    avoidPredictablePatterns: "Never use simple patterns or dictionary words"
  },

  keyStorage: {
    neverStoreOnline: "Keep private keys offline when possible",
    useEncryption: "Encrypt keys if must be stored digitally",
    useMultipleLocations: "Redundant secure storage locations",
    regularBackups: "Test backup and recovery procedures"
  },

  keyUsage: {
    minimizeExposure: "Use keys only when necessary",
    useSecureEnvironments: "Sign on secure, malware-free devices",
    verifyTransactions: "Always review what you're signing",
    implementTimeouts: "Automatic logout and key clearing"
  }
};

Signature Validation Best Practices

Comprehensive Validation Function

class SignatureValidator {
  constructor() {
    this.usedNonces = new Set();
    this.maxMessageAge = 5 * 60 * 1000; // 5 minutes
    this.trustedContracts = new Set([
      '0x0165878A594ca255338adfa4d48449f69242Eb8F',
      '0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9'
    ]);
  }

  async validateSignature(messageData, signature, expectedSigner) {
    const validation = {
      isValid: false,
      errors: [],
      warnings: []
    };

    try {
      // Parse message data
      const message = typeof messageData === 'string' 
        ? JSON.parse(messageData) 
        : messageData;

      // 1. Check message format
      if (!this.validateMessageFormat(message)) {
        validation.errors.push('Invalid message format');
        return validation;
      }

      // 2. Check timestamp
      const now = Date.now();
      if (now > message.expiresAt) {
        validation.errors.push('Message expired');
        return validation;
      }

      if (message.timestamp > now + 60000) { // Allow 1 minute clock skew
        validation.errors.push('Message timestamp in future');
        return validation;
      }

      // 3. Check nonce
      if (this.usedNonces.has(message.nonce)) {
        validation.errors.push('Nonce already used (replay attack)');
        return validation;
      }

      // 4. Verify signature format
      if (!this.validateSignatureFormat(signature)) {
        validation.errors.push('Invalid signature format');
        return validation;
      }

      // 5. Verify cryptographic signature
      const messageString = JSON.stringify(message);
      const recoveredAddress = await this.recoverSignerAddress(messageString, signature);

      if (recoveredAddress.toLowerCase() !== expectedSigner.toLowerCase()) {
        validation.errors.push('Signature verification failed');
        return validation;
      }

      // 6. Additional security checks
      if (message.action === 'CONTRACT_INTERACTION' && 
          !this.trustedContracts.has(message.data.contractAddress)) {
        validation.warnings.push('Interaction with untrusted contract');
      }

      // Mark as valid
      validation.isValid = true;
      this.usedNonces.add(message.nonce);

      // Cleanup old nonces periodically
      if (this.usedNonces.size > 10000) {
        this.cleanupOldNonces();
      }

    } catch (error) {
      validation.errors.push(`Validation error: ${error.message}`);
    }

    return validation;
  }

  validateMessageFormat(message) {
    const requiredFields = ['action', 'timestamp', 'nonce', 'expiresAt'];
    return requiredFields.every(field => message.hasOwnProperty(field));
  }

  validateSignatureFormat(signature) {
    // Check if signature has proper format (0x + 130 hex characters)
    const sigRegex = /^0x[0-9a-fA-F]{130}$/;
    return sigRegex.test(signature);
  }

  async recoverSignerAddress(message, signature) {
    const messageHash = this.web3.utils.keccak256(message);
    const recoveredAddress = await this.web3.eth.accounts.recover(messageHash, signature);
    return recoveredAddress;
  }

  cleanupOldNonces() {
    // In production, implement more sophisticated cleanup
    // based on timestamp tracking
    this.usedNonces.clear();
  }
}

Hardware Security Module (HSM) Integration

For enterprise deployments, consider HSM integration for critical signing operations:

// HSM integration for enterprise security
class HSMSignatureManager {
  constructor(hsmConfig) {
    this.hsmClient = new HSMClient(hsmConfig);
    this.keyLabels = new Map();
  }

  async generateSecureKeyPair(keyLabel) {
    try {
      const keyPair = await this.hsmClient.generateKeyPair({
        keyType: 'ECDSA',
        curve: 'secp256k1',
        label: keyLabel,
        extractable: false, // Key cannot be extracted from HSM
        signOnly: true
      });

      this.keyLabels.set(keyLabel, keyPair.publicKey);

      return {
        keyLabel: keyLabel,
        publicKey: keyPair.publicKey,
        keyId: keyPair.keyId
      };
    } catch (error) {
      console.error('HSM key generation failed:', error);
      throw error;
    }
  }

  async signWithHSM(message, keyLabel) {
    try {
      const signature = await this.hsmClient.sign({
        data: message,
        keyLabel: keyLabel,
        algorithm: 'ECDSA-SHA256'
      });

      return signature;
    } catch (error) {
      console.error('HSM signing failed:', error);
      throw error;
    }
  }

  async verifyHSMSignature(message, signature, keyLabel) {
    try {
      const publicKey = this.keyLabels.get(keyLabel);
      if (!publicKey) {
        throw new Error('Key not found');
      }

      const isValid = await this.hsmClient.verify({
        data: message,
        signature: signature,
        publicKey: publicKey,
        algorithm: 'ECDSA-SHA256'
      });

      return isValid;
    } catch (error) {
      console.error('HSM verification failed:', error);
      throw error;
    }
  }
}

🚀 Practical Applications

Real-World Use Cases on Our Platform

1. Project Authorization Signatures

// Contractor milestone submission with signature
async function submitMilestoneWithSignature(projectId, milestoneData) {
  const submissionData = {
    action: 'MILESTONE_SUBMISSION',
    projectId: projectId,
    milestoneId: milestoneData.id,
    completionPercentage: milestoneData.percentage,
    documents: milestoneData.documents,
    notes: milestoneData.notes,
    timestamp: Date.now(),
    nonce: generateNonce()
  };

  // Sign the submission
  const signature = await signMessage(
    JSON.stringify(submissionData),
    contractorAddress
  );

  // Submit to blockchain
  const tx = await milestoneContract.methods.submitMilestone(
    submissionData.milestoneId,
    submissionData.completionPercentage,
    signature
  ).send({ from: contractorAddress });

  return {
    transactionHash: tx.transactionHash,
    signedData: submissionData,
    signature: signature
  };
}

2. Payment Authorization Workflows

// Multi-party payment authorization
class PaymentAuthorizationWorkflow {
  async initiatePayment(paymentData, requiredSigners) {
    const authorizationRequest = {
      action: 'PAYMENT_AUTHORIZATION',
      amount: paymentData.amount,
      recipient: paymentData.recipient,
      projectId: paymentData.projectId,
      milestoneId: paymentData.milestoneId,
      requiredSigners: requiredSigners,
      createdAt: Date.now(),
      expiresAt: Date.now() + (24 * 60 * 60 * 1000), // 24 hours
      nonce: generateNonce()
    };

    const authorizationId = this.generateAuthorizationId();

    // Store authorization request
    await this.storeAuthorizationRequest(authorizationId, authorizationRequest);

    // Notify required signers
    await this.notifySigners(requiredSigners, authorizationId);

    return {
      authorizationId,
      requiredSignatures: requiredSigners.length,
      expiresAt: authorizationRequest.expiresAt
    };
  }

  async authorizePayment(authorizationId, signerAddress) {
    // Get authorization request
    const authRequest = await this.getAuthorizationRequest(authorizationId);

    // Verify signer is authorized
    if (!authRequest.requiredSigners.includes(signerAddress)) {
      throw new Error('Unauthorized signer');
    }

    // Check if already signed
    const existingSignatures = await this.getSignatures(authorizationId);
    if (existingSignatures.some(sig => sig.signer === signerAddress)) {
      throw new Error('Already signed by this address');
    }

    // Create and verify signature
    const signature = await this.signAuthorizationRequest(authRequest, signerAddress);

    // Store signature
    await this.storeSignature(authorizationId, {
      signer: signerAddress,
      signature: signature,
      signedAt: Date.now()
    });

    // Check if all signatures collected
    const allSignatures = await this.getSignatures(authorizationId);
    if (allSignatures.length >= authRequest.requiredSigners.length) {
      // Execute payment
      await this.executeAuthorizedPayment(authorizationId, authRequest, allSignatures);
    }

    return {
      authorizationId,
      signaturesCollected: allSignatures.length,
      signaturesRequired: authRequest.requiredSigners.length,
      readyForExecution: allSignatures.length >= authRequest.requiredSigners.length
    };
  }
}

3. Document Integrity Verification

// Construction document verification system
class DocumentIntegrityManager {
  async submitVerifiedDocument(documentFile, projectId, documentType) {
    try {
      // 1. Generate document hash
      const documentHash = await this.hashFile(documentFile);

      // 2. Create verification metadata
      const verificationData = {
        action: 'DOCUMENT_VERIFICATION',
        projectId: projectId,
        documentType: documentType,
        documentHash: documentHash,
        fileName: documentFile.name,
        fileSize: documentFile.size,
        timestamp: Date.now(),
        nonce: generateNonce()
      };

      // 3. Sign verification data
      const signature = await this.signVerificationData(verificationData);

      // 4. Upload to IPFS
      const ipfsHash = await this.uploadToIPFS(documentFile);

      // 5. Store on blockchain
      const tx = await this.documentContract.methods.submitDocument(
        documentHash,
        ipfsHash,
        JSON.stringify(verificationData),
        signature
      ).send({ from: this.currentUser });

      return {
        documentHash: documentHash,
        ipfsHash: ipfsHash,
        transactionHash: tx.transactionHash,
        verificationSignature: signature,
        submittedAt: Date.now()
      };

    } catch (error) {
      console.error('Document verification failed:', error);
      throw error;
    }
  }

  async verifyDocumentIntegrity(documentHash) {
    try {
      // 1. Get document data from blockchain
      const blockchainData = await this.documentContract.methods
        .getDocument(documentHash)
        .call();

      // 2. Download document from IPFS
      const documentBlob = await this.downloadFromIPFS(blockchainData.ipfsHash);

      // 3. Regenerate hash
      const regeneratedHash = await this.hashFile(documentBlob);

      // 4. Verify signature
      const verificationData = JSON.parse(blockchainData.metadata);
      const isSignatureValid = await this.verifySignature(
        blockchainData.metadata,
        blockchainData.signature,
        blockchainData.signer
      );

      return {
        documentHash: documentHash,
        originalHash: blockchainData.documentHash,
        regeneratedHash: regeneratedHash,
        hashMatch: documentHash === regeneratedHash,
        signatureValid: isSignatureValid,
        signer: blockchainData.signer,
        submittedAt: blockchainData.timestamp,
        ipfsHash: blockchainData.ipfsHash,
        overallValid: (documentHash === regeneratedHash) && isSignatureValid
      };

    } catch (error) {
      console.error('Document verification failed:', error);
      throw error;
    }
  }
}

Integration Examples

React Component for Signature Operations

// React component for document signing
import React, { useState } from 'react';
import { useWallet } from '../hooks/useWallet';

const DocumentSigner = ({ projectId }) => {
  const [selectedFile, setSelectedFile] = useState(null);
  const [signing, setSigning] = useState(false);
  const [signatureResult, setSignatureResult] = useState(null);
  const { address, signMessage, isConnected } = useWallet();

  const handleFileSelect = (event) => {
    const file = event.target.files[0];
    if (file) {
      setSelectedFile(file);
    }
  };

  const signDocument = async () => {
    if (!selectedFile || !isConnected) return;

    setSigning(true);

    try {
      // Create document integrity manager
      const docManager = new DocumentIntegrityManager();

      // Sign and submit document
      const result = await docManager.submitVerifiedDocument(
        selectedFile,
        projectId,
        'blueprint' // or other document type
      );

      setSignatureResult(result);

      // Show success message
      toast.success('Document signed and submitted successfully!');

    } catch (error) {
      console.error('Document signing failed:', error);
      toast.error('Failed to sign document: ' + error.message);
    } finally {
      setSigning(false);
    }
  };

  return (
    <div className="document-signer">
      <h3>Sign and Submit Document</h3>

      <div className="file-input">
        <input
          type="file"
          onChange={handleFileSelect}
          accept=".pdf,.doc,.docx,.jpg,.png"
        />
      </div>

      {selectedFile && (
        <div className="file-info">
          <p><strong>File:</strong> {selectedFile.name}</p>
          <p><strong>Size:</strong> {(selectedFile.size / 1024 / 1024).toFixed(2)} MB</p>
          <p><strong>Type:</strong> {selectedFile.type}</p>
        </div>
      )}

      <button
        onClick={signDocument}
        disabled={!selectedFile || !isConnected || signing}
        className="sign-button"
      >
        {signing ? 'Signing...' : 'Sign & Submit Document'}
      </button>

      {signatureResult && (
        <div className="signature-result">
          <h4>Document Successfully Signed!</h4>
          <p><strong>Document Hash:</strong> {signatureResult.documentHash}</p>
          <p><strong>IPFS Hash:</strong> {signatureResult.ipfsHash}</p>
          <p><strong>Transaction:</strong> 
            <a href={`https://seitrace.com/tx/${signatureResult.transactionHash}`} 
               target="_blank" rel="noopener noreferrer">
              View on Explorer
            </a>
          </p>
        </div>
      )}
    </div>
  );
};

export default DocumentSigner;

📚 Advanced Topics

Zero-Knowledge Proofs with Signatures

For privacy-preserving authentication where you need to prove knowledge of a secret without revealing it:

// Zero-knowledge signature scheme
class ZKSignatureManager {
  constructor() {
    this.curve = new elliptic.ec('secp256k1');
  }

  // Generate commitment for ZK proof
  generateCommitment(secret, nonce) {
    const commitment = this.curve.g.mul(secret).add(this.curve.g.mul(nonce));
    return commitment;
  }

  // Create ZK proof of signature knowledge
  async createZKProofOfSignature(message, signature, privateKey) {
    const messageHash = crypto.createHash('sha256').update(message).digest('hex');

    // Generate random nonce for proof
    const nonce = crypto.randomBytes(32);

    // Create commitment
    const commitment = this.generateCommitment(privateKey, nonce);

    // Generate challenge
    const challenge = crypto.createHash('sha256')
      .update(commitment.encode('hex'))
      .update(messageHash)
      .digest('hex');

    // Generate response
    const response = BigInt('0x' + nonce.toString('hex')) + 
                    BigInt('0x' + challenge) * BigInt('0x' + privateKey.toString('hex'));

    return {
      commitment: commitment.encode('hex'),
      challenge: challenge,
      response: response.toString(16),
      messageHash: messageHash
    };
  }

  // Verify ZK proof
  verifyZKProof(proof, publicKey, message) {
    const messageHash = crypto.createHash('sha256').update(message).digest('hex');

    if (messageHash !== proof.messageHash) {
      return false;
    }

    // Reconstruct commitment
    const leftSide = this.curve.g.mul(BigInt('0x' + proof.response));
    const rightSide = this.curve.decodePoint(proof.commitment, 'hex')
      .add(this.curve.keyFromPublic(publicKey, 'hex').getPublic().mul(BigInt('0x' + proof.challenge)));

    return leftSide.eq(rightSide);
  }
}

Threshold Signatures

For scenarios requiring t-of-n signatures:

// Threshold signature implementation
class ThresholdSignatureManager {
  constructor(threshold, totalSigners) {
    this.threshold = threshold;
    this.totalSigners = totalSigners;
    this.shares = new Map();
  }

  // Generate threshold signature shares
  generateSignatureShares(message, privateKeyShares) {
    const messageHash = crypto.createHash('sha256').update(message).digest('hex');
    const signatures = [];

    for (let i = 0; i < privateKeyShares.length; i++) {
      const signature = this.curve.keyFromPrivate(privateKeyShares[i])
        .sign(messageHash);

      signatures.push({
        index: i + 1,
        signature: signature,
        signer: this.curve.keyFromPrivate(privateKeyShares[i]).getPublic()
      });
    }

    return signatures;
  }

  // Combine threshold signatures
  combineSignatures(signatureShares, message) {
    if (signatureShares.length < this.threshold) {
      throw new Error(`Need at least ${this.threshold} signatures, got ${signatureShares.length}`);
    }

    // Use Lagrange interpolation to combine signatures
    const combinedSignature = this.lagrangeInterpolation(
      signatureShares.slice(0, this.threshold)
    );

    return combinedSignature;
  }

  lagrangeInterpolation(signatureShares) {
    // Implement Lagrange interpolation for signature combination
    // This is a simplified version - production implementation would be more complex
    let combinedR = BigInt(0);
    let combinedS = BigInt(0);

    for (let i = 0; i < signatureShares.length; i++) {
      let lagrangeCoeff = BigInt(1);

      for (let j = 0; j < signatureShares.length; j++) {
        if (i !== j) {
          lagrangeCoeff *= BigInt(signatureShares[j].index) / 
                          (BigInt(signatureShares[j].index) - BigInt(signatureShares[i].index));
        }
      }

      combinedR += BigInt('0x' + signatureShares[i].signature.r.toString('hex')) * lagrangeCoeff;
      combinedS += BigInt('0x' + signatureShares[i].signature.s.toString('hex')) * lagrangeCoeff;
    }

    return {
      r: combinedR.toString(16),
      s: combinedS.toString(16),
      recoveryParam: signatureShares[0].signature.recoveryParam
    };
  }
}

Ring Signatures for Privacy

For anonymous signatures where the signer is one of a group but identity is hidden:

// Ring signature implementation
class RingSignatureManager {
  constructor() {
    this.curve = new elliptic.ec('secp256k1');
  }

  // Create ring signature
  createRingSignature(message, signerPrivateKey, publicKeys) {
    const messageHash = crypto.createHash('sha256').update(message).digest('hex');
    const ringSize = publicKeys.length;

    // Find signer's position in ring
    const signerPublicKey = this.curve.keyFromPrivate(signerPrivateKey).getPublic();
    const signerIndex = publicKeys.findIndex(pk => 
      pk.encode('hex') === signerPublicKey.encode('hex')
    );

    if (signerIndex === -1) {
      throw new Error('Signer not found in ring');
    }

    // Generate random values for ring
    const alpha = crypto.randomBytes(32);
    const c = new Array(ringSize);
    const s = new Array(ringSize);

    // Initialize hash with message
    let hashInput = messageHash;

    // Generate random values for all positions except signer
    for (let i = 0; i < ringSize; i++) {
      if (i !== signerIndex) {
        c[i] = crypto.randomBytes(32);
        s[i] = crypto.randomBytes(32);

        // Calculate ring component
        const component = this.curve.g.mul(BigInt('0x' + s[i].toString('hex')))
          .add(publicKeys[i].mul(BigInt('0x' + c[i].toString('hex'))));

        hashInput += component.encode('hex');
      }
    }

    // Calculate challenge for signer position
    const totalHash = crypto.createHash('sha256').update(hashInput).digest();
    c[signerIndex] = totalHash;

    // Calculate signer's response
    const privateKeyBN = BigInt('0x' + signerPrivateKey.toString('hex'));
    const challengeBN = BigInt('0x' + c[signerIndex].toString('hex'));
    const alphaBN = BigInt('0x' + alpha.toString('hex'));

    s[signerIndex] = (alphaBN - privateKeyBN * challengeBN) % this.curve.n;

    return {
      c: c.map(val => val.toString('hex')),
      s: s.map(val => val.toString('hex')),
      ringPublicKeys: publicKeys.map(pk => pk.encode('hex')),
      messageHash: messageHash
    };
  }

  // Verify ring signature
  verifyRingSignature(ringSignature, message) {
    const messageHash = crypto.createHash('sha256').update(message).digest('hex');

    if (messageHash !== ringSignature.messageHash) {
      return false;
    }

    const publicKeys = ringSignature.ringPublicKeys.map(pk => 
      this.curve.keyFromPublic(pk, 'hex').getPublic()
    );

    let hashInput = messageHash;

    // Verify each ring component
    for (let i = 0; i < publicKeys.length; i++) {
      const component = this.curve.g.mul(BigInt('0x' + ringSignature.s[i]))
        .add(publicKeys[i].mul(BigInt('0x' + ringSignature.c[i])));

      hashInput += component.encode('hex');
    }

    const verificationHash = crypto.createHash('sha256').update(hashInput).digest('hex');

    // Check if hash chain is valid
    return verificationHash === ringSignature.c[0];
  }
}

🔧 Troubleshooting

Common Signature Issues

1. Signature Verification Failures

// Comprehensive signature debugging
function debugSignatureFailure(message, signature, expectedAddress) {
  console.log('=== Signature Debug Information ===');
  console.log('Message:', message);
  console.log('Expected Address:', expectedAddress);
  console.log('Signature:', signature);

  try {
    // Check signature format
    if (signature.length !== 132) { // 0x + 130 hex chars
      console.error('❌ Invalid signature length:', signature.length);
      return false;
    }

    // Extract signature components
    const r = signature.slice(0, 66);
    const s = '0x' + signature.slice(66, 130);
    const v = parseInt(signature.slice(130, 132), 16);

    console.log('Signature components:');
    console.log('  r:', r);
    console.log('  s:', s);
    console.log('  v:', v);

    // Check v value validity
    if (v < 27 || v > 28) {
      console.error('❌ Invalid v value:', v);
      return false;
    }

    // Try different message formats
    const formats = [
      message, // Raw message
      `\x19Ethereum Signed Message:\n${message.length}${message}`, // Standard format
      web3.utils.keccak256(message), // Pre-hashed
      web3.utils.utf8ToHex(message) // Hex encoded
    ];

    for (let i = 0; i < formats.length; i++) {
      try {
        const recovered = web3.eth.accounts.recover(formats[i], signature);
        console.log(`Format ${i} recovery result:`, recovered);

        if (recovered.toLowerCase() === expectedAddress.toLowerCase()) {
          console.log('✅ Signature valid with format', i);
          return true;
        }
      } catch (error) {
        console.log(`Format ${i} failed:`, error.message);
      }
    }

    console.error('❌ All recovery attempts failed');
    return false;

  } catch (error) {
    console.error('❌ Debug failed:', error);
    return false;
  }
}

2. MetaMask Signing Issues

// Handle MetaMask-specific signing problems
async function handleMetaMaskSigning(message, address) {
  try {
    // Check MetaMask availability
    if (!window.ethereum || !window.ethereum.isMetaMask) {
      throw new Error('MetaMask not available');
    }

    // Check account connection
    const accounts = await window.ethereum.request({
      method: 'eth_accounts'
    });

    if (accounts.length === 0) {
      throw new Error('No accounts connected');
    }

    if (accounts[0].toLowerCase() !== address.toLowerCase()) {
      throw new Error('Wrong account selected in MetaMask');
    }

    // Try different signing methods
    const signingMethods = [
      {
        method: 'personal_sign',
        params: [message, address]
      },
      {
        method: 'eth_sign',
        params: [address, web3.utils.utf8ToHex(message)]
      }
    ];

    for (const method of signingMethods) {
      try {
        console.log(`Trying ${method.method}...`);

        const signature = await window.ethereum.request({
          method: method.method,
          params: method.params
        });

        console.log(`✅ ${method.method} successful`);
        return signature;

      } catch (error) {
        console.log(`❌ ${method.method} failed:`, error.message);

        // Handle specific MetaMask errors
        if (error.code === 4001) {
          throw new Error('User rejected the request');
        } else if (error.code === -32603) {
          console.log('Internal JSON-RPC error, trying next method...');
        }
      }
    }

    throw new Error('All signing methods failed');

  } catch (error) {
    console.error('MetaMask signing failed:', error);
    throw error;
  }
}

3. Network-Specific Issues

// Handle network-specific signature differences
class NetworkSignatureHandler {
  constructor(web3, networkId) {
    this.web3 = web3;
    this.networkId = networkId;
    this.networkConfigs = {
      1329: { // SEI Mainnet
        name: 'SEI',
        messagePrefix: '\x19Ethereum Signed Message:\n',
        gasPrice: 'fast'
      },
      1: { // Ethereum Mainnet
        name: 'Ethereum',
        messagePrefix: '\x19Ethereum Signed Message:\n',
        gasPrice: 'standard'
      }
    };
  }

  async signForNetwork(message, address) {
    const config = this.networkConfigs[this.networkId];
    if (!config) {
      throw new Error(`Unsupported network: ${this.networkId}`);
    }

    console.log(`Signing for ${config.name} network...`);

    // Network-specific message formatting
    let formattedMessage = message;
    if (config.messagePrefix) {
      formattedMessage = config.messagePrefix + message.length + message;
    }

    try {
      const signature = await this.web3.eth.personal.sign(formattedMessage, address);

      // Verify signature works on this network
      const recovered = this.web3.eth.accounts.recover(formattedMessage, signature);

      if (recovered.toLowerCase() !== address.toLowerCase()) {
        throw new Error('Signature verification failed for network');
      }

      console.log(`✅ Signature valid for ${config.name}`);
      return signature;

    } catch (error) {
      console.error(`Network signing failed for ${config.name}:`, error);
      throw error;
    }
  }

  async verifyNetworkSignature(message, signature, address) {
    const config = this.networkConfigs[this.networkId];

    // Try both raw and prefixed message formats
    const formats = [
      message,
      config.messagePrefix + message.length + message
    ];

    for (const format of formats) {
      try {
        const recovered = this.web3.eth.accounts.recover(format, signature);
        if (recovered.toLowerCase() === address.toLowerCase()) {
          return true;
        }
      } catch (error) {
        continue;
      }
    }

    return false;
  }
}

Performance Optimization

Batch Signature Verification

// Optimize multiple signature verifications
class BatchSignatureVerifier {
  constructor(web3) {
    this.web3 = web3;
    this.verificationCache = new Map();
  }

  async batchVerifySignatures(verificationRequests) {
    const results = [];
    const uncachedRequests = [];

    // Check cache first
    for (const request of verificationRequests) {
      const cacheKey = this.getCacheKey(request);

      if (this.verificationCache.has(cacheKey)) {
        results.push({
          ...request,
          result: this.verificationCache.get(cacheKey),
          fromCache: true
        });
      } else {
        uncachedRequests.push(request);
      }
    }

    // Verify uncached requests in parallel
    const uncachedPromises = uncachedRequests.map(request => 
      this.verifySingleSignature(request)
    );

    const uncachedResults = await Promise.all(uncachedPromises);

    // Cache results and combine
    for (let i = 0; i < uncachedResults.length; i++) {
      const request = uncachedRequests[i];
      const result = uncachedResults[i];
      const cacheKey = this.getCacheKey(request);

      this.verificationCache.set(cacheKey, result.isValid);
      results.push({ ...request, ...result, fromCache: false });
    }

    return results;
  }

  getCacheKey(request) {
    return crypto.createHash('sha256')
      .update(request.message + request.signature + request.address)
      .digest('hex');
  }

  async verifySingleSignature(request) {
    try {
      const startTime = performance.now();

      const recovered = this.web3.eth.accounts.recover(
        request.message,
        request.signature
      );

      const isValid = recovered.toLowerCase() === request.address.toLowerCase();
      const duration = performance.now() - startTime;

      return {
        isValid,
        recoveredAddress: recovered,
        verificationTime: duration
      };

    } catch (error) {
      return {
        isValid: false,
        error: error.message,
        verificationTime: 0
      };
    }
  }
}

Error Recovery Strategies

Automatic Retry with Backoff

// Robust signature operation with retry logic
class RobustSignatureManager {
  constructor(maxRetries = 3, baseDelay = 1000) {
    this.maxRetries = maxRetries;
    this.baseDelay = baseDelay;
  }

  async signWithRetry(message, address) {
    let lastError;

    for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
      try {
        console.log(`Signature attempt ${attempt}/${this.maxRetries}`);

        const signature = await this.attemptSignature(message, address);

        // Verify signature before returning
        const isValid = await this.verifySignature(message, signature, address);
        if (!isValid) {
          throw new Error('Signature verification failed');
        }

        console.log(`✅ Signature successful on attempt ${attempt}`);
        return signature;

      } catch (error) {
        console.error(`❌ Attempt ${attempt} failed:`, error.message);
        lastError = error;

        // Don't retry on user rejection
        if (error.code === 4001) {
          throw error;
        }

        // Wait before retry (exponential backoff)
        if (attempt < this.maxRetries) {
          const delay = this.baseDelay * Math.pow(2, attempt - 1);
          console.log(`Waiting ${delay}ms before retry...`);
          await this.sleep(delay);
        }
      }
    }

    throw new Error(`All ${this.maxRetries} signature attempts failed. Last error: ${lastError.message}`);
  }

  async attemptSignature(message, address) {
    // Try different signing approaches
    const approaches = [
      () => this.signWithPersonalSign(message, address),
      () => this.signWithEthSign(message, address),
      () => this.signWithTypedData(message, address)
    ];

    let lastError;

    for (const approach of approaches) {
      try {
        return await approach();
      } catch (error) {
        lastError = error;
        continue;
      }
    }

    throw lastError;
  }

  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

📞 Support and Additional Resources

Getting Help

If you encounter issues with cryptographic signatures:

  1. Check the troubleshooting section above
  2. Review your wallet connection and network settings
  3. Contact our support team with specific error details

Support Channels

Security Reporting

If you discover security vulnerabilities related to our signature implementation, please report them immediately to security@contracting.app with the subject line "SECURITY: Signature Vulnerability".


This guide covers the essential aspects of cryptographic signatures in our platform. For the latest updates and additional technical details, visit contracting.app/resources/signatures.

← Back to Resources