-
Notifications
You must be signed in to change notification settings - Fork 113
/
Copy pathevent.go
129 lines (104 loc) · 2.79 KB
/
event.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package nostr
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/fiatjaf/bip340"
)
const (
KindSetMetadata int = 0
KindTextNote int = 1
KindRecommendServer int = 2
KindContactList int = 3
KindEncryptedDirectMessage int = 4
KindDeletion int = 5
)
type Event struct {
ID string `json:"id"` // it's the hash of the serialized event
PubKey string `json:"pubkey"`
CreatedAt uint32 `json:"created_at"`
Kind int `json:"kind"`
Tags Tags `json:"tags"`
Content string `json:"content"`
Sig string `json:"sig"`
}
// Serialize outputs a byte array that can be hashed/signed to identify/authenticate
func (evt *Event) Serialize() []byte {
// the serialization process is just putting everything into a JSON array
// so the order is kept
arr := make([]interface{}, 6)
// version: 0
arr[0] = 0
// pubkey
arr[1] = evt.PubKey
// created_at
arr[2] = int64(evt.CreatedAt)
// kind
arr[3] = int64(evt.Kind)
// tags
if evt.Tags != nil {
arr[4] = evt.Tags
} else {
arr[4] = make([]bool, 0)
}
// content
arr[5] = evt.Content
serialized := new(bytes.Buffer)
enc := json.NewEncoder(serialized)
enc.SetEscapeHTML(false)
_ = enc.Encode(arr)
return serialized.Bytes()[:serialized.Len()-1] // Encode add new line char
}
// CheckSignature checks if the signature is valid for the id
// (which is a hash of the serialized event content).
// returns an error if the signature itself is invalid.
func (evt Event) CheckSignature() (bool, error) {
// read and check pubkey
pubkey, err := bip340.ParsePublicKey(evt.PubKey)
if err != nil {
return false, fmt.Errorf("Event has invalid pubkey '%s': %w", evt.PubKey, err)
}
// check tags
for _, tag := range evt.Tags {
for _, item := range tag {
switch item.(type) {
case string, int64, float64, int, bool:
// fine
default:
// not fine
return false, fmt.Errorf("tag contains an invalid value %v", item)
}
}
}
s, err := hex.DecodeString(evt.Sig)
if err != nil {
return false, fmt.Errorf("signature is invalid hex: %w", err)
}
if len(s) != 64 {
return false, fmt.Errorf("signature must be 64 bytes, not %d", len(s))
}
var sig [64]byte
copy(sig[:], s)
hash := sha256.Sum256(evt.Serialize())
return bip340.Verify(pubkey, hash, sig)
}
// Sign signs an event with a given privateKey
func (evt *Event) Sign(privateKey string) error {
h := sha256.Sum256(evt.Serialize())
s, err := bip340.ParsePrivateKey(privateKey)
if err != nil {
return fmt.Errorf("Sign called with invalid private key '%s': %w", privateKey, err)
}
aux := make([]byte, 32)
rand.Read(aux)
sig, err := bip340.Sign(s, h, aux)
if err != nil {
return err
}
evt.ID = hex.EncodeToString(h[:])
evt.Sig = hex.EncodeToString(sig[:])
return nil
}