diff options
Diffstat (limited to 'src/gnunet/service/dht/blocks/hello.go')
-rw-r--r-- | src/gnunet/service/dht/blocks/hello.go | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/src/gnunet/service/dht/blocks/hello.go b/src/gnunet/service/dht/blocks/hello.go new file mode 100644 index 0000000..77fc2ae --- /dev/null +++ b/src/gnunet/service/dht/blocks/hello.go | |||
@@ -0,0 +1,226 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package blocks | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "encoding/binary" | ||
24 | "fmt" | ||
25 | "gnunet/util" | ||
26 | "net/url" | ||
27 | "strconv" | ||
28 | "strings" | ||
29 | |||
30 | "github.com/bfix/gospel/crypto/ed25519" | ||
31 | "github.com/bfix/gospel/data" | ||
32 | ) | ||
33 | |||
34 | //---------------------------------------------------------------------- | ||
35 | // HELLO URLs are used for bootstrapping a node and for adding nodes | ||
36 | // outside of GNUnet message exchange (e.g. command-line tools) | ||
37 | //---------------------------------------------------------------------- | ||
38 | |||
39 | const helloPrefix = "gnunet://hello/" | ||
40 | |||
41 | // HelloBlock is the DHT-managed block type for HELLO information. | ||
42 | // It is used to create and parse HELLO URLs. | ||
43 | // All addresses expire at the same time /this different from HELLO | ||
44 | // messages (see message.HeeloMsg). | ||
45 | type HelloBlock struct { | ||
46 | PeerID *util.PeerID `` // peer identifier | ||
47 | Signature *ed25519.EdSignature `` // signature | ||
48 | Expire util.AbsoluteTime `` // Expiration date | ||
49 | AddrBin []byte `size:"*"` // raw address data | ||
50 | |||
51 | // transient attributes | ||
52 | addrs []*util.Address // cooked address data | ||
53 | } | ||
54 | |||
55 | // SetAddresses adds a bulk of addresses for this HELLO block. | ||
56 | func (h *HelloBlock) SetAddresses(a []*util.Address) { | ||
57 | h.addrs = util.Clone(a) | ||
58 | h.finalize() | ||
59 | } | ||
60 | |||
61 | // Addresses returns the list of addresses | ||
62 | func (h *HelloBlock) Addresses() []*util.Address { | ||
63 | return util.Clone(h.addrs) | ||
64 | } | ||
65 | |||
66 | // ParseHelloURL parses a HELLO URL of the following form: | ||
67 | // gnunet://hello/<PeerID>/<signature>/<expire>?<addrs> | ||
68 | // The addresses are encoded. | ||
69 | func ParseHelloURL(u string) (h *HelloBlock, err error) { | ||
70 | // check and trim prefix | ||
71 | if !strings.HasPrefix(u, helloPrefix) { | ||
72 | err = fmt.Errorf("invalid HELLO-URL prefix: '%s'", u) | ||
73 | return | ||
74 | } | ||
75 | u = u[len(helloPrefix):] | ||
76 | |||
77 | // split remainder into parts | ||
78 | p := strings.Split(u, "/") | ||
79 | if len(p) != 3 { | ||
80 | err = fmt.Errorf("invalid HELLO-URL: '%s'", u) | ||
81 | return | ||
82 | } | ||
83 | |||
84 | // assemble HELLO data | ||
85 | h = new(HelloBlock) | ||
86 | |||
87 | // (1) parse peer public key (peer ID) | ||
88 | var buf []byte | ||
89 | if buf, err = util.DecodeStringToBinary(p[0], 32); err != nil { | ||
90 | return | ||
91 | } | ||
92 | h.PeerID = util.NewPeerID(buf) | ||
93 | |||
94 | // (2) parse signature | ||
95 | if buf, err = util.DecodeStringToBinary(p[1], 64); err != nil { | ||
96 | return | ||
97 | } | ||
98 | if h.Signature, err = ed25519.NewEdSignatureFromBytes(buf); err != nil { | ||
99 | return | ||
100 | } | ||
101 | |||
102 | // (3) split last element into parts | ||
103 | q := strings.SplitN(p[2], "?", 2) | ||
104 | |||
105 | // (4) parse expiration date | ||
106 | var exp uint64 | ||
107 | if exp, err = strconv.ParseUint(q[0], 10, 64); err != nil { | ||
108 | return | ||
109 | } | ||
110 | h.Expire = util.NewAbsoluteTimeEpoch(exp) | ||
111 | |||
112 | // (5) process addresses. | ||
113 | h.addrs = make([]*util.Address, 0) | ||
114 | var ua string | ||
115 | for _, a := range strings.Split(q[1], "&") { | ||
116 | // unescape URL query | ||
117 | if ua, err = url.QueryUnescape(a); err != nil { | ||
118 | return | ||
119 | } | ||
120 | // parse address and append it to list | ||
121 | var addr *util.Address | ||
122 | if addr, err = util.ParseAddress(ua); err != nil { | ||
123 | return | ||
124 | } | ||
125 | h.addrs = append(h.addrs, addr) | ||
126 | } | ||
127 | |||
128 | // (6) generate raw address data so block is complete | ||
129 | h.finalize() | ||
130 | return | ||
131 | } | ||
132 | |||
133 | // ParseHelloFromBytes converts a byte array into a HelloBlock instance. | ||
134 | func ParseHelloFromBytes(buf []byte) (h *HelloBlock, err error) { | ||
135 | h = new(HelloBlock) | ||
136 | if err = data.Unmarshal(h, buf); err == nil { | ||
137 | err = h.finalize() | ||
138 | } | ||
139 | return | ||
140 | } | ||
141 | |||
142 | // finalize block data (generate dependent fields) | ||
143 | func (h *HelloBlock) finalize() (err error) { | ||
144 | if h.addrs == nil { | ||
145 | err = data.Unmarshal(h.addrs, h.AddrBin) | ||
146 | } else if h.AddrBin == nil { | ||
147 | wrt := new(bytes.Buffer) | ||
148 | for _, a := range h.addrs { | ||
149 | wrt.WriteString(a.String()) | ||
150 | wrt.WriteByte(0) | ||
151 | } | ||
152 | h.AddrBin = wrt.Bytes() | ||
153 | } | ||
154 | return | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | // Message returns the corresponding HELLO message to be sent to peers. | ||
159 | func (h *HelloBlock) Message() *message.HelloMsg { | ||
160 | msg := message.NewHelloMsg(h.PeerID) | ||
161 | for _, a := range h.addrs { | ||
162 | msg.AddAddress(message.NewHelloAddress(a, h.Expire)) | ||
163 | } | ||
164 | return msg | ||
165 | } | ||
166 | */ | ||
167 | |||
168 | // URL returns the HELLO URL for the data. | ||
169 | func (h *HelloBlock) URL() string { | ||
170 | u := fmt.Sprintf("%s%s/%s/%d?", | ||
171 | helloPrefix, | ||
172 | h.PeerID.String(), | ||
173 | util.EncodeBinaryToString(h.Signature.Bytes()), | ||
174 | h.Expire.Epoch(), | ||
175 | ) | ||
176 | for i, a := range h.addrs { | ||
177 | if i > 0 { | ||
178 | u += "&" | ||
179 | } | ||
180 | u += url.QueryEscape(a.String()) | ||
181 | } | ||
182 | return u | ||
183 | } | ||
184 | |||
185 | // Equals returns true if two HELLOs are the same. The expiration | ||
186 | // timestamp is ignored in the comparision. | ||
187 | func (h *HelloBlock) Equals(g *HelloBlock) bool { | ||
188 | if !h.PeerID.Equals(g.PeerID) || | ||
189 | !util.Equals(h.Signature.Bytes(), g.Signature.Bytes()) || | ||
190 | len(h.addrs) != len(g.addrs) { | ||
191 | return false | ||
192 | } | ||
193 | for i, a := range h.addrs { | ||
194 | if !a.Equals(g.addrs[i]) { | ||
195 | return false | ||
196 | } | ||
197 | } | ||
198 | return true | ||
199 | } | ||
200 | |||
201 | // Verify the integrity of the HELLO data | ||
202 | func (h *HelloBlock) Verify() (bool, error) { | ||
203 | // assemble signed data and public key | ||
204 | sd := h.signedData() | ||
205 | pub := ed25519.NewPublicKeyFromBytes(h.PeerID.Key) | ||
206 | return pub.EdVerify(sd, h.Signature) | ||
207 | } | ||
208 | |||
209 | // Sign the HELLO data with private key | ||
210 | func (h *HelloBlock) Sign(prv *ed25519.PrivateKey) (err error) { | ||
211 | // assemble signed data | ||
212 | sd := h.signedData() | ||
213 | h.Signature, err = prv.EdSign(sd) | ||
214 | return | ||
215 | } | ||
216 | |||
217 | // signedData assembles a data block for sign and verify operations. | ||
218 | func (h *HelloBlock) signedData() []byte { | ||
219 | buf := new(bytes.Buffer) | ||
220 | buf.Write(h.PeerID.Key) | ||
221 | binary.Write(buf, binary.BigEndian, h.Expire) | ||
222 | for _, a := range h.addrs { | ||
223 | buf.Write(a.Address) | ||
224 | } | ||
225 | return buf.Bytes() | ||
226 | } | ||