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