Skip to content

Latest commit

 

History

History
128 lines (111 loc) · 4.56 KB

File metadata and controls

128 lines (111 loc) · 4.56 KB

// rustchain/src/utxo/domain_signature.rs

use crate::config::MAX_INPUTS; use crate::crypto::{ has_domain_payload, inject_domain, verify_signature, Signature, SignatureAlgorithm, VerificationError, }; use crate::error::Error; use crate::types::{Address, Amount}; use log::{debug, error, warn}; use serde::{Deserialize, Serialize}; use std::collections::HashSet; use thiserror::Error;

// --------------------------------------------------------------------------- // Module documentation // ---------------------------------------------------------------------------

/// Domain‑separated signature verification for UTXO transfers. /// /// This module enforces that all UTXO transfer signatures include a domain /// string (UTXO_SIGNATURE_DOMAIN) in the signed payload. Any legacy /// account‑model signature (without domain) is rejected with a dedicated /// error code. This prevents cross‑endpoint signature reuse attacks. /// /// # Security /// - Domain injection is mandatory; legacy signatures are always rejected. /// - Payload size is bounded by MAX_INPUTS. /// - All external inputs are validated before verification.

// --------------------------------------------------------------------------- // Constants // ---------------------------------------------------------------------------

/// Domain string injected into every signed UTXO transfer payload. pub const UTXO_SIGNATURE_DOMAIN: &str = "rustchain-utxo-transfer-v1";

/// Error code returned when a legacy (non‑domain) signature is used on a UTXO endpoint. pub const UTXO_SIGNATURE_DOMAIN_REQUIRED: &str = "UTXO_SIGNATURE_DOMAIN_REQUIRED";

// --------------------------------------------------------------------------- // Errors // ---------------------------------------------------------------------------

/// Errors that can occur during domain‑separated signature verification. #[derive(Error, Debug)] #[non_exhaustive] pub enum DomainSignatureError { /// A legacy (account‑model) signature was used on a UTXO endpoint. #[error("legacy account‑model signature detected; domain required for UTXO")] LegacyWalletSignature,

/// Underlying cryptographic verification failure.
#[error("signature verification failed: {0}")]
VerificationFailed(#[from] VerificationError),

/// Invalid or malformed payload (e.g. empty signature list, too many inputs).
#[error("invalid payload: {0}")]
InvalidPayload(String),

/// Internal serialisation or other non‑recoverable error.
#[error("internal error: {0}")]
Internal(String),

}

impl From for Error { fn from(e: DomainSignatureError) -> Self { // Convert to a generic signature error; keep the full message. Error::Signature(e.to_string()) } }

// --------------------------------------------------------------------------- // Payload wrapper // ---------------------------------------------------------------------------

/// A signed UTXO transfer payload that includes the mandatory domain string. /// /// Serialisation uses bincode for compactness. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DomainSignedPayload { /// Domain string (must equal UTXO_SIGNATURE_DOMAIN). pub domain: String, /// Recipient address. pub to: Address, /// Transfer amount. pub amount: Amount, /// Optional fee (skipped from serialisation when None). #[serde(skip_serializing_if = "Option::is_none")] pub fee: Option, /// Nonce to prevent replay. pub nonce: u64, }

impl DomainSignedPayload { /// Create a new payload with the correct domain. /// /// # Arguments /// * to – Recipient address. /// * amount – Transfer amount. /// * fee – Optional fee. /// * nonce – Unique nonce. #[must_use] pub fn new(to: Address, amount: Amount, fee: Option, nonce: u64) -> Self { Self { domain: UTXO_SIGNATURE_DOMAIN.to_string(), to, amount, fee, nonce, } } }

// --------------------------------------------------------------------------- // Signature verifier // ---------------------------------------------------------------------------

/// Verifies UTXO transfer signatures with domain separation. /// /// The verifier maintains a flag (legacy_wallet_signature_seen) and a list /// of legacy errors from the most recent call to [verify_signatures]. /// /// # Thread safety /// Instances are not Sync; create a new verifier per transaction. pub struct UtxoSignatureVerifier { algorithm: SignatureAlgorithm, max_inputs: usize