verify.go raw

   1  package find
   2  
   3  import (
   4  	"fmt"
   5  	"time"
   6  
   7  	"next.orly.dev/pkg/nostr/encoders/hex"
   8  	"next.orly.dev/pkg/nostr/encoders/event"
   9  	"next.orly.dev/pkg/nostr/interfaces/signer/p8k"
  10  )
  11  
  12  // VerifyEvent verifies the signature of a Nostr event
  13  func VerifyEvent(ev *event.E) error {
  14  	ok, err := ev.Verify()
  15  	if err != nil {
  16  		return fmt.Errorf("signature verification failed: %w", err)
  17  	}
  18  	if !ok {
  19  		return fmt.Errorf("invalid signature")
  20  	}
  21  	return nil
  22  }
  23  
  24  // VerifyTransferAuth verifies a transfer authorization signature
  25  func VerifyTransferAuth(name, newOwner, prevOwner string, timestamp time.Time, sigHex string) (bool, error) {
  26  	// Create the message
  27  	msgHash := CreateTransferAuthMessage(name, newOwner, timestamp)
  28  
  29  	// Decode signature
  30  	sig, err := hex.Dec(sigHex)
  31  	if err != nil {
  32  		return false, fmt.Errorf("invalid signature hex: %w", err)
  33  	}
  34  
  35  	// Decode pubkey
  36  	pubkey, err := hex.Dec(prevOwner)
  37  	if err != nil {
  38  		return false, fmt.Errorf("invalid pubkey hex: %w", err)
  39  	}
  40  
  41  	// Create verifier with public key
  42  	verifier, err := p8k.New()
  43  	if err != nil {
  44  		return false, fmt.Errorf("failed to create verifier: %w", err)
  45  	}
  46  
  47  	if err := verifier.InitPub(pubkey); err != nil {
  48  		return false, fmt.Errorf("failed to init pubkey: %w", err)
  49  	}
  50  
  51  	// Verify signature
  52  	ok, err := verifier.Verify(msgHash, sig)
  53  	if err != nil {
  54  		return false, fmt.Errorf("verification failed: %w", err)
  55  	}
  56  
  57  	return ok, nil
  58  }
  59  
  60  // VerifyChallengeProof verifies a certificate challenge proof signature
  61  func VerifyChallengeProof(challenge, name, certPubkey, owner string, validUntil time.Time, sigHex string) (bool, error) {
  62  	// Create the message
  63  	msgHash := CreateChallengeProofMessage(challenge, name, certPubkey, validUntil)
  64  
  65  	// Decode signature
  66  	sig, err := hex.Dec(sigHex)
  67  	if err != nil {
  68  		return false, fmt.Errorf("invalid signature hex: %w", err)
  69  	}
  70  
  71  	// Decode pubkey
  72  	pubkey, err := hex.Dec(owner)
  73  	if err != nil {
  74  		return false, fmt.Errorf("invalid pubkey hex: %w", err)
  75  	}
  76  
  77  	// Create verifier with public key
  78  	verifier, err := p8k.New()
  79  	if err != nil {
  80  		return false, fmt.Errorf("failed to create verifier: %w", err)
  81  	}
  82  
  83  	if err := verifier.InitPub(pubkey); err != nil {
  84  		return false, fmt.Errorf("failed to init pubkey: %w", err)
  85  	}
  86  
  87  	// Verify signature
  88  	ok, err := verifier.Verify(msgHash, sig)
  89  	if err != nil {
  90  		return false, fmt.Errorf("verification failed: %w", err)
  91  	}
  92  
  93  	return ok, nil
  94  }
  95  
  96  // VerifyWitnessSignature verifies a witness signature on a certificate
  97  func VerifyWitnessSignature(certPubkey, name string, validFrom, validUntil time.Time,
  98  	challenge, witnessPubkey, sigHex string) (bool, error) {
  99  
 100  	// Create the message
 101  	msgHash := CreateWitnessMessage(certPubkey, name, validFrom, validUntil, challenge)
 102  
 103  	// Decode signature
 104  	sig, err := hex.Dec(sigHex)
 105  	if err != nil {
 106  		return false, fmt.Errorf("invalid signature hex: %w", err)
 107  	}
 108  
 109  	// Decode pubkey
 110  	pubkey, err := hex.Dec(witnessPubkey)
 111  	if err != nil {
 112  		return false, fmt.Errorf("invalid pubkey hex: %w", err)
 113  	}
 114  
 115  	// Create verifier with public key
 116  	verifier, err := p8k.New()
 117  	if err != nil {
 118  		return false, fmt.Errorf("failed to create verifier: %w", err)
 119  	}
 120  
 121  	if err := verifier.InitPub(pubkey); err != nil {
 122  		return false, fmt.Errorf("failed to init pubkey: %w", err)
 123  	}
 124  
 125  	// Verify signature
 126  	ok, err := verifier.Verify(msgHash, sig)
 127  	if err != nil {
 128  		return false, fmt.Errorf("verification failed: %w", err)
 129  	}
 130  
 131  	return ok, nil
 132  }
 133  
 134  // VerifyNameOwnership checks if a record's owner matches the name state owner
 135  func VerifyNameOwnership(nameState *NameState, record *NameRecord) error {
 136  	recordOwner := hex.Enc(record.Event.Pubkey)
 137  	if recordOwner != nameState.Owner {
 138  		return fmt.Errorf("%w: record owner %s != name owner %s",
 139  			ErrNotOwner, recordOwner, nameState.Owner)
 140  	}
 141  	return nil
 142  }
 143  
 144  // IsExpired checks if a time-based expiration has passed
 145  func IsExpired(expiration time.Time) bool {
 146  	return time.Now().After(expiration)
 147  }
 148  
 149  // IsInRenewalWindow checks if the current time is within the preferential renewal window
 150  // (final 30 days before expiration)
 151  func IsInRenewalWindow(expiration time.Time) bool {
 152  	now := time.Now()
 153  	renewalWindowStart := expiration.Add(-PreferentialRenewalDays * 24 * time.Hour)
 154  	return now.After(renewalWindowStart) && now.Before(expiration)
 155  }
 156  
 157  // CanRegister checks if a name can be registered based on its state and expiration
 158  func CanRegister(nameState *NameState, proposerPubkey string) error {
 159  	// If no name state exists, anyone can register
 160  	if nameState == nil {
 161  		return nil
 162  	}
 163  
 164  	// Check if name is expired
 165  	if IsExpired(nameState.Expiration) {
 166  		// Name is expired, anyone can register
 167  		return nil
 168  	}
 169  
 170  	// Check if in renewal window
 171  	if IsInRenewalWindow(nameState.Expiration) {
 172  		// Only current owner can register during renewal window
 173  		if proposerPubkey != nameState.Owner {
 174  			return ErrInRenewalWindow
 175  		}
 176  		return nil
 177  	}
 178  
 179  	// Name is still owned and not in renewal window
 180  	return fmt.Errorf("name is owned by %s until %s", nameState.Owner, nameState.Expiration)
 181  }
 182  
 183  // VerifyProposalExpiration checks if a proposal has expired
 184  func VerifyProposalExpiration(proposal *RegistrationProposal) error {
 185  	if !proposal.Expiration.IsZero() && IsExpired(proposal.Expiration) {
 186  		return fmt.Errorf("proposal expired at %s", proposal.Expiration)
 187  	}
 188  	return nil
 189  }
 190  
 191  // VerifyAttestationExpiration checks if an attestation has expired
 192  func VerifyAttestationExpiration(attestation *Attestation) error {
 193  	if !attestation.Expiration.IsZero() && IsExpired(attestation.Expiration) {
 194  		return fmt.Errorf("attestation expired at %s", attestation.Expiration)
 195  	}
 196  	return nil
 197  }
 198  
 199  // VerifyTrustGraphExpiration checks if a trust graph has expired
 200  func VerifyTrustGraphExpiration(trustGraph *TrustGraphEvent) error {
 201  	if !trustGraph.Expiration.IsZero() && IsExpired(trustGraph.Expiration) {
 202  		return fmt.Errorf("trust graph expired at %s", trustGraph.Expiration)
 203  	}
 204  	return nil
 205  }
 206  
 207  // VerifyNameStateExpiration checks if a name state has expired
 208  func VerifyNameStateExpiration(nameState *NameState) error {
 209  	if !nameState.Expiration.IsZero() && IsExpired(nameState.Expiration) {
 210  		return ErrNameExpired
 211  	}
 212  	return nil
 213  }
 214  
 215  // VerifyCertificateValidity checks if a certificate is currently valid
 216  func VerifyCertificateValidity(cert *Certificate) error {
 217  	now := time.Now()
 218  
 219  	if now.Before(cert.ValidFrom) {
 220  		return fmt.Errorf("certificate not yet valid (valid from %s)", cert.ValidFrom)
 221  	}
 222  
 223  	if now.After(cert.ValidUntil) {
 224  		return fmt.Errorf("certificate expired at %s", cert.ValidUntil)
 225  	}
 226  
 227  	return nil
 228  }
 229  
 230  // VerifyCertificate performs complete certificate verification
 231  func VerifyCertificate(cert *Certificate, nameState *NameState, trustedWitnesses []string) error {
 232  	// Verify certificate is not expired
 233  	if err := VerifyCertificateValidity(cert); err != nil {
 234  		return err
 235  	}
 236  
 237  	// Verify name is not expired
 238  	if err := VerifyNameStateExpiration(nameState); err != nil {
 239  		return err
 240  	}
 241  
 242  	// Verify certificate owner matches name owner
 243  	certOwner := hex.Enc(cert.Event.Pubkey)
 244  	if certOwner != nameState.Owner {
 245  		return fmt.Errorf("certificate owner %s != name owner %s", certOwner, nameState.Owner)
 246  	}
 247  
 248  	// Verify challenge proof
 249  	ok, err := VerifyChallengeProof(cert.Challenge, cert.Name, cert.CertPubkey,
 250  		nameState.Owner, cert.ValidUntil, cert.ChallengeProof)
 251  	if err != nil {
 252  		return fmt.Errorf("challenge proof verification failed: %w", err)
 253  	}
 254  	if !ok {
 255  		return fmt.Errorf("invalid challenge proof signature")
 256  	}
 257  
 258  	// Count trusted witnesses
 259  	trustedCount := 0
 260  	for _, witness := range cert.Witnesses {
 261  		// Check if witness is in trusted list
 262  		isTrusted := false
 263  		for _, trusted := range trustedWitnesses {
 264  			if witness.Pubkey == trusted {
 265  				isTrusted = true
 266  				break
 267  			}
 268  		}
 269  
 270  		if !isTrusted {
 271  			continue
 272  		}
 273  
 274  		// Verify witness signature
 275  		ok, err := VerifyWitnessSignature(cert.CertPubkey, cert.Name,
 276  			cert.ValidFrom, cert.ValidUntil, cert.Challenge,
 277  			witness.Pubkey, witness.Signature)
 278  		if err != nil {
 279  			return fmt.Errorf("witness %s signature verification failed: %w", witness.Pubkey, err)
 280  		}
 281  		if !ok {
 282  			return fmt.Errorf("invalid witness %s signature", witness.Pubkey)
 283  		}
 284  
 285  		trustedCount++
 286  	}
 287  
 288  	// Require at least 3 trusted witnesses
 289  	if trustedCount < 3 {
 290  		return fmt.Errorf("insufficient trusted witnesses: %d < 3", trustedCount)
 291  	}
 292  
 293  	return nil
 294  }
 295  
 296  // VerifySubdomainAuthority checks if the proposer owns the parent domain
 297  func VerifySubdomainAuthority(name string, proposerPubkey string, parentNameState *NameState) error {
 298  	parent := GetParentDomain(name)
 299  
 300  	// TLDs have no parent
 301  	if parent == "" {
 302  		return nil
 303  	}
 304  
 305  	// Parent must exist
 306  	if parentNameState == nil {
 307  		return fmt.Errorf("parent domain %s does not exist", parent)
 308  	}
 309  
 310  	// Proposer must own parent
 311  	if proposerPubkey != parentNameState.Owner {
 312  		return fmt.Errorf("proposer %s does not own parent domain %s (owner: %s)",
 313  			proposerPubkey, parent, parentNameState.Owner)
 314  	}
 315  
 316  	return nil
 317  }
 318