aboutsummaryrefslogtreecommitdiff
path: root/src/gnunet/service/dht/blocks/hello.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/gnunet/service/dht/blocks/hello.go')
-rw-r--r--src/gnunet/service/dht/blocks/hello.go226
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
19package blocks
20
21import (
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
39const 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).
45type 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.
56func (h *HelloBlock) SetAddresses(a []*util.Address) {
57 h.addrs = util.Clone(a)
58 h.finalize()
59}
60
61// Addresses returns the list of addresses
62func (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.
69func 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.
134func 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)
143func (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.
159func (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.
169func (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.
187func (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
202func (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
210func (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.
218func (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}