commit 21d7292dbd062ff11194fdc235a3d54830d7ba57 parent 5b03f7567ad8242cc87924c3920dfc04420365ef Author: Bernd Fix <brf@hoi-polloi.org> Date: Sun, 21 Aug 2022 16:40:12 +0200 Integration test: bug fixes 1st round. Diffstat:
37 files changed, 314 insertions(+), 257 deletions(-)
diff --git a/README.md b/README.md @@ -225,9 +225,25 @@ ${GOPATH}/bin/gnunet-service-dht-go -c dhtu-config.json 2>&1 | tee run.log ## Testing `GNS` -You need to have (all) GNUnet services up and running. +**N.B.**: The GNS service is currently not up-to-date. To test it, you need to +check out version v0.1.23 (the latest tested version) and a matching GNUnet +version as well (latest as of May 2020) to be on a safe side. You also need to +have (all) GNUnet services up and running. -### Setting up the configuration file +### Setting up a modified configuration for GNUnet + +You need to tell the GNUnet client which GNS service to use (either the default +or the `gnunet-go` version) by modifying the GNS service socket. Copy your +configuration file to `gns-go.comf` and modify the `[gns]` sectiom: + +``` +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-gns-go.sock +``` + +This will ensure that clients (and other services) talk to the `gnunet-go` +GNS service. + +### Setting up the configuration file (gnunet-go) Copy the example `gnunet-config.json` to `gns-config.json` and modify the `network` and `local` sections: diff --git a/src/gnunet/cmd/revoke-zonekey/main.go b/src/gnunet/cmd/revoke-zonekey/main.go @@ -136,16 +136,17 @@ func (r *RevData) size() int { // process run stand-alone from other GNUnet services: // // (1) Generate the desired PoWs for the public zone key: -// This process can be started, stopped and resumed, so the long -// calculation time (usually days or even weeks) can be interrupted if -// desired. For security reasons you should only pass the "-z" argument to -// this step but not the "-k" argument (private key) as it is not required -// to calculate the PoWs. // +// This process can be started, stopped and resumed, so the long +// calculation time (usually days or even weeks) can be interrupted if +// desired. For security reasons you should only pass the "-z" argument to +// this step but not the "-k" argument (private key) as it is not required +// to calculate the PoWs. // // (2) A fully generated PoW set can be signed with the private key to create -// the final revocation data to be send out. This requires to pass the "-k" -// and "-z" argument. +// +// the final revocation data to be send out. This requires to pass the "-k" +// and "-z" argument. // // The two steps can be run (sequentially) on separate machines; step one requires // computing power nd memory and step two requires a trusted environment. diff --git a/src/gnunet/config/config.go b/src/gnunet/config/config.go @@ -22,7 +22,7 @@ import ( "encoding/json" "fmt" "gnunet/util" - "io/ioutil" + "os" "reflect" "regexp" "strings" @@ -172,7 +172,7 @@ var ( // the Config data structure. func ParseConfig(fileName string) (err error) { // parse configuration file - file, err := ioutil.ReadFile(fileName) + file, err := os.ReadFile(fileName) if err != nil { return } diff --git a/src/gnunet/config/config_test.go b/src/gnunet/config/config_test.go @@ -20,7 +20,7 @@ package config import ( "encoding/json" - "io/ioutil" + "os" "testing" "github.com/bfix/gospel/logger" @@ -30,7 +30,7 @@ func TestConfigRead(t *testing.T) { logger.SetLogLevel(logger.WARN) // read configuration file - data, err := ioutil.ReadFile("./gnunet-config.json") + data, err := os.ReadFile("./gnunet-config.json") if err != nil { t.Fatal(err) } diff --git a/src/gnunet/core/core.go b/src/gnunet/core/core.go @@ -33,7 +33,7 @@ import ( "github.com/bfix/gospel/logger" ) -//---------------------------------------------------------------------- +// ---------------------------------------------------------------------- // Core-related error codes var ( ErrCoreNoUpnpDyn = errors.New("no dynamic port with UPnP") @@ -44,7 +44,7 @@ var ( // CtxKey is a value-context key type CtxKey string -//---------------------------------------------------------------------- +// ---------------------------------------------------------------------- // EndpointRef is a reference to an endpoint instance managed by core. type EndpointRef struct { id string // endpoint identifier in configuration @@ -53,7 +53,7 @@ type EndpointRef struct { upnpID string // UPNP identifier (empty if unused) } -//---------------------------------------------------------------------- +// ---------------------------------------------------------------------- // Core service type Core struct { // local peer instance diff --git a/src/gnunet/core/event.go b/src/gnunet/core/event.go @@ -31,6 +31,7 @@ import ( //---------------------------------------------------------------------- // Event types +// //nolint:stylecheck // allow non-camel-case in constants const ( EV_CONNECT = iota // peer connected diff --git a/src/gnunet/core/peer.go b/src/gnunet/core/peer.go @@ -103,7 +103,7 @@ func (p *Peer) HelloData(ttl time.Duration, a []*util.Address) (h *blocks.HelloB // assemble HELLO data h = new(blocks.HelloBlock) h.PeerID = p.GetID() - h.Expire_ = util.NewAbsoluteTime(time.Now().Add(ttl)) + h.SetExpire(ttl) h.SetAddresses(a) // sign data diff --git a/src/gnunet/crypto/signature.go b/src/gnunet/crypto/signature.go @@ -18,12 +18,15 @@ package crypto -import "gnunet/util" +import ( + "gnunet/enums" + "gnunet/util" +) // SignaturePurpose is the GNUnet data structure used as header for signed data. type SignaturePurpose struct { - Size uint32 `order:"big"` // How many bytes are signed? - Purpose uint32 `order:"big"` // Signature purpose + Size uint32 `order:"big"` // How many bytes are signed? + Purpose enums.SigPurpose `order:"big"` // Signature purpose } // Signable interface for objects that can get signed by peer diff --git a/src/gnunet/enums/gnunet-signature.tpl b/src/gnunet/enums/gnunet-signature.tpl @@ -3,7 +3,7 @@ //nolint:stylecheck // allow non-camel-case for constants package enums -type SigPurpose int +type SigPurpose uint32 // Signature purpose values const ( diff --git a/src/gnunet/enums/messages.go b/src/gnunet/enums/messages.go @@ -20,6 +20,7 @@ package enums // MsgType for GNUnet message type identifiers +// //go:generate stringer -type=MsgType type MsgType uint16 diff --git a/src/gnunet/enums/signature_purpose.go b/src/gnunet/enums/signature_purpose.go @@ -3,7 +3,7 @@ //nolint:stylecheck // allow non-camel-case for constants package enums -type SigPurpose int +type SigPurpose uint32 // Signature purpose values const ( diff --git a/src/gnunet/enums/sigpurpose_string.go b/src/gnunet/enums/sigpurpose_string.go @@ -58,7 +58,7 @@ var ( func (i SigPurpose) String() string { switch { - case 0 <= i && i <= 7: + case i <= 7: return _SigPurpose_name_0[_SigPurpose_index_0[i]:_SigPurpose_index_0[i+1]] case 11 <= i && i <= 18: i -= 11 diff --git a/src/gnunet/message/msg_core.go b/src/gnunet/message/msg_core.go @@ -58,7 +58,7 @@ func NewEphemeralKeyMsg() *EphemeralKeyMsg { SignedBlock: &EphKeyBlock{ Purpose: &crypto.SignaturePurpose{ Size: 88, - Purpose: uint32(enums.SIG_SET_ECC_KEY), + Purpose: enums.SIG_SET_ECC_KEY, }, CreateTime: util.AbsoluteTimeNow(), ExpireTime: util.NewRelativeTime(12 * time.Hour), diff --git a/src/gnunet/message/msg_dht_p2p.go b/src/gnunet/message/msg_dht_p2p.go @@ -178,20 +178,20 @@ func (m *DHTP2PPutMsg) Update(p *path.Path, pf *blocks.PeerFilter, hop uint16) * func (m *DHTP2PPutMsg) Path(sender *util.PeerID) *path.Path { // create a "real" path list from message data pth := path.NewPath(crypto.Hash(m.Block), m.Expire) + pth.Flags = m.Flags // return empty path if recording is switched off - if m.Flags&enums.DHT_RO_RECORD_ROUTE == 0 { + if pth.Flags&enums.DHT_RO_RECORD_ROUTE == 0 { return pth } // handle truncate origin - if m.Flags&enums.DHT_RO_TRUNCATED == 1 { + if pth.Flags&enums.DHT_RO_TRUNCATED == 1 { if m.TruncOrigin == nil { logger.Printf(logger.WARN, "[path] truncated but no origin - flag reset") - m.Flags &^= enums.DHT_RO_TRUNCATED + pth.Flags &^= enums.DHT_RO_TRUNCATED } else { pth.TruncOrigin = m.TruncOrigin - pth.Flags |= path.PathTruncated } } @@ -204,7 +204,6 @@ func (m *DHTP2PPutMsg) Path(sender *util.PeerID) *path.Path { logger.Printf(logger.WARN, "[path] - last hop signature missing - path reset") return path.NewPath(crypto.Hash(m.Block), m.Expire) } - pth.Flags |= path.PathLastHop pth.LastSig = m.LastSig pth.LastHop = sender return pth @@ -273,8 +272,8 @@ func (m *DHTP2PPutMsg) String() string { type DHTP2PResultMsg struct { MsgHeader BType enums.BlockType `order:"big"` // Block type of result - Flags uint16 `order:"big"` // Message flags Reserved uint16 `order:"big"` // Reserved + Flags uint16 `order:"big"` // Message flags PutPathL uint16 `order:"big"` // size of PUTPATH field GetPathL uint16 `order:"big"` // size of GETPATH field Expire util.AbsoluteTime `` // expiration date @@ -323,21 +322,22 @@ func (m *DHTP2PResultMsg) NumPath(field string) uint { func (m *DHTP2PResultMsg) Path(sender *util.PeerID) *path.Path { // create a "real" path list from message data pth := path.NewPath(crypto.Hash(m.Block), m.Expire) + pth.Flags = m.Flags // return empty path if recording is switched off - if m.Flags&enums.DHT_RO_RECORD_ROUTE == 0 { + if pth.Flags&enums.DHT_RO_RECORD_ROUTE == 0 { return pth } // handle truncate origin - if m.Flags&enums.DHT_RO_TRUNCATED == 1 { - if m.TruncOrigin == nil { + if pth.Flags&enums.DHT_RO_TRUNCATED == 1 { + if pth.TruncOrigin == nil { logger.Printf(logger.WARN, "[path] truncated but no origin - flag reset") - m.Flags &^= enums.DHT_RO_TRUNCATED + pth.Flags &^= enums.DHT_RO_TRUNCATED } else { pth.TruncOrigin = m.TruncOrigin - pth.Flags |= path.PathTruncated } } + // copy path elements pth.List = util.Clone(m.PathList) pth.NumList = uint16(len(pth.List)) @@ -358,7 +358,6 @@ func (m *DHTP2PResultMsg) Path(sender *util.PeerID) *path.Path { logger.Printf(logger.WARN, "[path] - last hop signature missing - path reset") return path.NewPath(crypto.Hash(m.Block), m.Expire) } - pth.Flags |= path.PathLastHop pth.LastSig = m.LastSig pth.LastHop = sender return pth @@ -440,7 +439,7 @@ func (m *DHTP2PResultMsg) Update(pth *path.Path) *DHTP2PResultMsg { // String returns a human-readable representation of the message. func (m *DHTP2PResultMsg) String() string { return fmt.Sprintf("DHTP2PResultMsg{btype=%s,putl=%d,getl=%d,flags=%s}", - m.BType, m.PutPathL, m.GetPathL, DHTFlags(uint16(m.Flags))) + m.BType, m.PutPathL, m.GetPathL, DHTFlags(m.Flags)) } //---------------------------------------------------------------------- diff --git a/src/gnunet/message/msg_transport.go b/src/gnunet/message/msg_transport.go @@ -134,7 +134,7 @@ func NewSignedAddress(a *util.Address) *SignedAddress { addr := &SignedAddress{ Purpose: &crypto.SignaturePurpose{ Size: uint32(alen + 20), - Purpose: uint32(enums.SIG_TRANSPORT_PONG_OWN), + Purpose: enums.SIG_TRANSPORT_PONG_OWN, }, ExpireOn: util.AbsoluteTimeNow().Add(12 * time.Hour), AddrSize: uint32(alen), diff --git a/src/gnunet/service/dht/blocks/filters.go b/src/gnunet/service/dht/blocks/filters.go @@ -73,6 +73,7 @@ func (pf *PeerFilter) Clone() *PeerFilter { //====================================================================== // ResultFilter return values +// //nolint:stylecheck // allow non-camel-case in constants const ( RF_MORE = iota // Valid result, and there may be more. @@ -82,6 +83,7 @@ const ( ) // Compare return values +// //nolint:stylecheck // allow non-camel-case in constants const ( CMP_SAME = iota // the two result filter are the same diff --git a/src/gnunet/service/dht/blocks/hello.go b/src/gnunet/service/dht/blocks/hello.go @@ -21,7 +21,6 @@ package blocks import ( "bytes" "crypto/sha512" - "encoding/binary" "errors" "fmt" "gnunet/crypto" @@ -97,13 +96,13 @@ func (h *HelloBlock) SetAddresses(a []*util.Address) { // Addresses returns the list of addresses func (h *HelloBlock) Addresses() []*util.Address { if h.addrs == nil { - h.finalize() + _ = h.finalize() } return util.Clone(h.addrs) } // ParseHelloBlockFromURL parses a HELLO URL of the following form: -// gnunet://hello/<PeerID>/<signature>/<expire>?<addrs> +// gnunet://hello/<PeerID>/<signature>/<expire>?<addrs> // The addresses are encoded. func ParseHelloBlockFromURL(u string, checkExpiry bool) (h *HelloBlock, err error) { // check and trim prefix @@ -302,32 +301,36 @@ func (h *HelloBlock) SetSignature(sig *util.PeerSignature) error { return nil } +// _SignedData is the structured data to be signed +type _SignedData struct { + Purpose *crypto.SignaturePurpose // signature purpose + Expire util.AbsoluteTime // expiration time + AddrHash *crypto.HashCode // address hash +} + // SignedData assembles a data block for sign and verify operations. func (h *HelloBlock) SignedData() []byte { - // hash address block - hAddr := sha512.Sum512(h.AddrBin) - var size uint32 = 80 - purpose := uint32(enums.SIG_HELLO) - // assemble signed data - buf := new(bytes.Buffer) - var n int - err := binary.Write(buf, binary.BigEndian, size) - if err == nil { - if err = binary.Write(buf, binary.BigEndian, purpose); err == nil { - if err = binary.Write(buf, binary.BigEndian, h.Expire_); err == nil { - if n, err = buf.Write(hAddr[:]); err == nil { - if n != len(hAddr[:]) { - err = errors.New("signed data size mismatch") - } - } - } - } - } + sd := &_SignedData{ + Purpose: &crypto.SignaturePurpose{ + Size: 80, + Purpose: enums.SIG_HELLO, + }, + Expire: h.Expire_, + AddrHash: crypto.Hash(h.AddrBin), + } + // generate binary representation + buf, err := data.Marshal(sd) if err != nil { - logger.Printf(logger.ERROR, "[HelloBlock.SignedData] failed: %s", err.Error()) + logger.Println(logger.ERROR, "can't serialize HELLO for signature") + return nil } - return buf.Bytes() + if len(buf) != int(sd.Purpose.Size) { + logger.Printf(logger.ERROR, "size mismatch for serialized HELLO -- %d -> %d", sd.Purpose.Size, len(buf)) + sd.Purpose.Size = uint32(len(buf)) + return nil + } + return buf } //---------------------------------------------------------------------- diff --git a/src/gnunet/service/dht/blocks/hello_test.go b/src/gnunet/service/dht/blocks/hello_test.go @@ -20,7 +20,6 @@ package blocks import ( "bytes" - "encoding/base64" "encoding/hex" "gnunet/util" "strings" @@ -28,7 +27,6 @@ import ( "time" "github.com/bfix/gospel/crypto/ed25519" - "github.com/bfix/gospel/data" ) var ( @@ -122,43 +120,3 @@ func TestHelloBytes(t *testing.T) { t.Fatal("Bytes readback failed") } } - -func TestHelloDebug(t *testing.T) { - blkData := "QKObXJUbnnghRh9McDDjHaB9IIL6MhhEiQHc8VfO3QMABeZZJJhsA" + - "GlwK3VkcDovLzEyNy4wLjAuMToxMDAwMQBpcCt1ZHA6Ly8xNzIuMT" + - "cuMC40OjEwMDAxAGlwK3VkcDovL1s6OmZmZmY6MTcyLjE3LjAuNF06MTAwMDEA" - buf, err := base64.RawStdEncoding.DecodeString(blkData) - if err != nil { - t.Fatal(err) - } - hb, err := ParseHelloBlockFromBytes(buf) - if err != nil { - t.Fatal(err) - } - ok, err := hb.Verify() - if err != nil { - t.Fatal(err) - } - if !ok { - // trace problem - t.Log("Block: " + hex.EncodeToString(buf)) - t.Log("PeerID: " + hb.PeerID.String()) - t.Log(" -> " + hex.EncodeToString(hb.PeerID.Bytes())) - t.Logf("Expire: %d", hb.Expire_.Val) - t.Logf(" -> " + hb.Expire_.String()) - var exp util.AbsoluteTime - if err = data.Unmarshal(&exp, buf[32:40]); err != nil { - t.Fatal(err) - } - t.Logf(" -> " + exp.String()) - t.Log("AddrBin: " + hex.EncodeToString(hb.AddrBin)) - sd := hb.SignedData() - t.Log("SignedData: " + hex.EncodeToString(sd)) - t.Log("Addresses:") - for _, addr := range hb.Addresses() { - t.Logf("* " + addr.URI()) - } - t.Log("Signature: " + hex.EncodeToString(hb.Signature.Bytes())) - t.Fatal("debug HELLO verify failed") - } -} diff --git a/src/gnunet/service/dht/messages.go b/src/gnunet/service/dht/messages.go @@ -39,6 +39,7 @@ import ( // HandleMessage handles a DHT request/response message. Responses are sent // to the specified responder. +// //nolint:gocyclo // life sometimes is complex... func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn message.Message, back transport.Responder) bool { // assemble log label @@ -69,12 +70,12 @@ func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn m //-------------------------------------------------------------- // validate query (based on block type requested) (9.4.3.1) - btype := enums.BlockType(msg.BType) + btype := msg.BType blockHdlr, ok := blocks.BlockHandlers[btype] if ok { // validate block query if !blockHdlr.ValidateBlockQuery(msg.Query, msg.XQuery) { - logger.Printf(logger.WARN, "[%s] invalid query -- discarded", label) + logger.Printf(logger.WARN, "[%s] invalid query -- message discarded", label) return false } } else { @@ -92,7 +93,8 @@ func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn m if blockHdlr != nil { rf = blockHdlr.ParseResultFilter(msg.ResFilter) } else { - logger.Printf(logger.WARN, "[%s] unknown result filter implementation -- skipped", label) + logger.Printf(logger.WARN, "[%s] unknown result filter implementation -- message discarded", label) + return false } } else { // ... or create a new one @@ -145,7 +147,7 @@ func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn m } } // if we have results, send them as response on the back channel - rcv := "locally" + rcv := "local caller" if back.Receiver() != nil { rcv = back.Receiver().Short() } @@ -176,7 +178,7 @@ func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn m // forward to number of peers numForward := m.rtable.ComputeOutDegree(msg.ReplLevel, msg.HopCount) for n := 0; n < numForward; n++ { - if p := m.rtable.SelectClosestPeer(addr, pf, 0); p != nil { + if p := m.rtable.SelectPeer(addr, msg.HopCount, pf, 0); p != nil { // forward message to peer logger.Printf(logger.INFO, "[%s] forward GET message to %s", label, p.Peer.Short()) if err := m.core.Send(ctx, p.Peer, msgOut); err != nil { @@ -309,7 +311,7 @@ func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn m // forward to computed number of peers numForward := m.rtable.ComputeOutDegree(msg.ReplLvl, msg.HopCount) for n := 0; n < numForward; n++ { - if p := m.rtable.SelectClosestPeer(addr, pf, 0); p != nil { + if p := m.rtable.SelectPeer(addr, msg.HopCount, pf, 0); p != nil { // check if route is recorded (9.3.2.6) var pp *path.Path if msg.Flags&enums.DHT_RO_RECORD_ROUTE != 0 { @@ -353,7 +355,7 @@ func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn m return false } //-------------------------------------------------------------- - btype := enums.BlockType(msg.BType) + btype := msg.BType var blkKey *crypto.HashCode blockHdlr, ok := blocks.BlockHandlers[btype] if ok { diff --git a/src/gnunet/service/dht/module.go b/src/gnunet/service/dht/module.go @@ -72,7 +72,7 @@ func (lr *LocalBlockResponder) Send(ctx context.Context, msg message.Message) er case *message.DHTP2PResultMsg: // deliver incoming blocks go func() { - blk, err := blocks.NewBlock(enums.BlockType(res.BType), res.Expire, res.Block) + blk, err := blocks.NewBlock(res.BType, res.Expire, res.Block) if err == nil { lr.ch <- blk } else { @@ -80,7 +80,7 @@ func (lr *LocalBlockResponder) Send(ctx context.Context, msg message.Message) er } }() default: - logger.Printf(logger.WARN, "[local] %d not a DHT-RESULT -- skipped", "") + logger.Printf(logger.WARN, "[local] %d not a DHT-RESULT -- skipped", msg.Type()) } return nil } @@ -331,7 +331,7 @@ func (m *Module) event(ctx context.Context, ev *core.Event) { } } -//---------------------------------------------------------------------- +// ---------------------------------------------------------------------- // Heartbeat handler for periodic tasks func (m *Module) heartbeat(ctx context.Context) { // run heartbeat for routing table @@ -368,7 +368,7 @@ func (m *Module) getHello(label string) (msg *message.DHTP2PHelloMsg, err error) // assemble HELLO data hb := new(blocks.HelloBlock) hb.PeerID = m.core.PeerID() - hb.Expire_ = util.NewAbsoluteTime(time.Now().Add(message.HelloAddressExpiration)) + hb.SetExpire(message.HelloAddressExpiration) hb.SetAddresses(addrList) // sign HELLO block diff --git a/src/gnunet/service/dht/path/elements.go b/src/gnunet/service/dht/path/elements.go @@ -36,6 +36,7 @@ var ( ) //---------------------------------------------------------------------- + // Entry is an element of the path list type Entry struct { Signature *util.PeerSignature // path element signature @@ -57,9 +58,9 @@ func (e *Entry) String() string { return fmt.Sprintf("(%s,%s)", e.Signer.String(), s) } -//---------------------------------------------------------------------- +// ---------------------------------------------------------------------- // shared path element data across types -type elementData struct { +type _ElementData struct { Expire util.AbsoluteTime // expiration date BlockHash *crypto.HashCode // block hash PeerPredecessor *util.PeerID // predecessor peer @@ -67,41 +68,48 @@ type elementData struct { } // helper type for signature creation/verification -type elementSignedData struct { - Size uint16 `order:"big"` // size of signed data - Purpose uint16 `order:"big"` // signature purpose (SIG_DHT_HOP) - Elem *elementData `` // path element data +type _SignedData struct { + Purpose *crypto.SignaturePurpose // signature purpose + Elem *_ElementData // path element data } -//---------------------------------------------------------------------- +// ---------------------------------------------------------------------- // Element is the full-fledged data assembly for a path element in // PUT/GET pathes. It is assembled programatically (on generation[1] and // verification[2]) and not transferred in messages directly. // // [1] spe = &Element{...} -// core.Sign(spe) -// msg.putpath[i] = spe.Wire() +// +// core.Sign(spe) +// msg.putpath[i] = spe.Wire() // // [2] pe = &Element{...,Signature: wire.sig} -// if !pe.Verify(peerId) { ... } // +// if !pe.Verify(peerId) { ... } type Element struct { - elementData + _ElementData Entry } // SignedData gets the data to be signed by peer ('Signable' interface) func (pe *Element) SignedData() []byte { - sd := &elementSignedData{ - Size: 80, - Purpose: uint16(enums.SIG_DHT_HOP), - Elem: &(pe.elementData), + sd := &_SignedData{ + Purpose: &crypto.SignaturePurpose{ + Size: 144, + Purpose: enums.SIG_DHT_HOP, + }, + Elem: &(pe._ElementData), } buf, err := data.Marshal(sd) if err != nil { logger.Println(logger.ERROR, "can't serialize path element for signature") return nil } + if len(buf) != int(sd.Purpose.Size) { + logger.Printf(logger.ERROR, "size mismatch for serialized path element -- %d -> %d", sd.Purpose.Size, len(buf)) + sd.Purpose.Size = uint32(len(buf)) + return nil + } return buf } diff --git a/src/gnunet/service/dht/path/elements_test.go b/src/gnunet/service/dht/path/elements_test.go @@ -0,0 +1,71 @@ +// This file is part of gnunet-go, a GNUnet-implementation in Golang. +// Copyright (C) 2019-2022 Bernd Fix >Y< +// +// gnunet-go is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// gnunet-go is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// SPDX-License-Identifier: AGPL3.0-or-later + +package path + +import ( + "encoding/hex" + "gnunet/util" + "testing" +) + +func TestElementDebug(t *testing.T) { + var ( + signedData = "" + + "00000090" + + "00000006" + + "0005e6983d33f911" + + "f3236acc2be7812a988617c647fc27fcfbd0dacc3d960aa29a5f9bf0b9b9131f" + + "cdd31cfa45de2cbd9510665e7f2b1ccafefd445511c62729c0798dd1b0675f19" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "23b096021b25d822c217a756b877cf72c7fdabd4eff79c4dbb7418dd2b232386" + + signature = "" + + "ef94ddfd90b56f30b265a88384551907fadef176b4ba6b023df429506b34cde0" + + "39f38661d451e6e5bd1c4d5d078d27f0e8954bd964ea55f03afa42aa9964cc0c" + signer = "" + + "28f06fe5178742c3b8f080431e48cf5bf3898ba8b8fd57a975772d14003a8c75" + predecessor = "" + + "0000000000000000000000000000000000000000000000000000000000000000" + successor = "" + + "23b096021b25d822c217a756b877cf72c7fdabd4eff79c4dbb7418dd2b232386" + ) + convert := func(s string) []byte { + buf, err := hex.DecodeString(s) + if err != nil { + t.Fatal(err) + } + return buf + } + sd := convert(signedData) + sig := util.NewPeerSignature(convert(signature)) + pred := util.NewPeerID(convert(predecessor)) + curr := util.NewPeerID(convert(signer)) + succ := util.NewPeerID(convert(successor)) + + ok, err := curr.Verify(sd, sig) + if err != nil { + t.Fatal(err) + } + if !ok { + t.Logf("Pred: %s", pred.Short()) + t.Logf("Sign: %s", curr.Short()) + t.Logf("Succ: %s", succ.Short()) + t.Error("Verify NOT OK:") + } +} diff --git a/src/gnunet/service/dht/path/handling.go b/src/gnunet/service/dht/path/handling.go @@ -19,11 +19,10 @@ package path import ( - "bytes" - "encoding/hex" - "fmt" "gnunet/crypto" + "gnunet/enums" "gnunet/util" + "strings" "github.com/bfix/gospel/data" "github.com/bfix/gospel/logger" @@ -33,17 +32,11 @@ import ( // Path handling //---------------------------------------------------------------------- -// path flags -const ( - PathTruncated = 1 - PathLastHop = 2 -) - // Path is the complete list of verified hops a message travelled. // It also keeps the associated block hash and expiration time of // the request for signature verification purposes. type Path struct { - Flags uint32 `order:"big"` // flags + Flags uint16 `order:"big"` // flags BlkHash *crypto.HashCode `` // block hash value Expire util.AbsoluteTime `` // expiration time TruncOrigin *util.PeerID `opt:"(IsUsed)"` // truncated origin (optional) @@ -58,9 +51,9 @@ type Path struct { func (p *Path) IsUsed(field string) bool { switch field { case "TruncOrigin": - return p.Flags&PathTruncated != 0 + return p.Flags&enums.DHT_RO_TRUNCATED != 0 case "LastSig", "LastHop": - return p.Flags&PathLastHop != 0 + return p.Flags&enums.DHT_RO_RECORD_ROUTE != 0 } return false } @@ -128,7 +121,7 @@ func (p *Path) Clone() *Path { // NewElement creates a new path element from data func (p *Path) NewElement(pred, signer, succ *util.PeerID) *Element { return &Element{ - elementData: elementData{ + _ElementData: _ElementData{ Expire: p.Expire, BlockHash: p.BlkHash, PeerPredecessor: pred, @@ -155,7 +148,7 @@ func (p *Path) Add(elem *Element) { // update last hop signature p.LastSig = elem.Signature p.LastHop = elem.Signer - p.Flags |= PathLastHop + p.Flags |= enums.DHT_RO_RECORD_ROUTE } // Verify path: process list entries from right to left (decreasing index). @@ -181,6 +174,7 @@ func (p *Path) Verify(local *util.PeerID) { pe := p.NewElement(pred, p.LastHop, local) ok, err := pe.Verify(p.LastSig) if err != nil || !ok { + logger.Println(logger.WARN, "[path] Dropping path (invalid last signature)") // remove last hop signature and truncated origin; reset flags p.LastSig = nil p.LastHop = nil @@ -188,79 +182,79 @@ func (p *Path) Verify(local *util.PeerID) { p.Flags = 0 } return - } else { - // yes: process list of path elements - signer := p.LastHop - sig := p.LastSig - succ := local - num := len(p.List) - var pred *util.PeerID - for i := num - 1; i >= 0; i-- { - if i == -1 { - if p.TruncOrigin != nil { - pred = p.TruncOrigin - } else { - pred = util.NewPeerID(nil) - } + } + // yes: process list of path elements + signer := p.LastHop + sig := p.LastSig + succ := local + num := len(p.List) + var pred *util.PeerID + for i := num - 1; i >= 0; i-- { + if i == -1 { + if p.TruncOrigin != nil { + pred = p.TruncOrigin } else { - pred = p.List[i].Signer + pred = util.NewPeerID(nil) } - pe := p.NewElement(pred, signer, succ) - ok, err := pe.Verify(sig) - if err != nil || !ok { - // we need to truncate: - logger.Printf(logger.WARN, "[path] Truncating path (invalid signature at hop %d)", i) + } else { + pred = p.List[i].Signer + } + pe := p.NewElement(pred, signer, succ) + ok, err := pe.Verify(sig) + if err != nil || !ok { + // we need to truncate: + logger.Printf(logger.WARN, "[path] Truncating path (invalid signature at hop %d)", i) - // are we at the end of the list? - if i == num-1 { - // yes: the last hop signature failed -> reset path - p.LastSig = nil - p.LastHop = nil - p.TruncOrigin = nil - p.Flags = 0 - p.List = make([]*Entry, 0) - return - } - // trim list - p.Flags |= PathTruncated - p.TruncOrigin = signer - size := num - 2 - i - list := make([]*Entry, size) - if size > 0 { - copy(list, p.List[i+2:]) - } - p.List = list + // are we at the end of the list? + if i == num-1 { + // yes: the last hop signature failed -> reset path + p.LastSig = nil + p.LastHop = nil + p.TruncOrigin = nil + p.Flags = 0 + p.List = make([]*Entry, 0) return } - // check next path element - succ = signer - signer = pred - if i != -1 { - sig = p.List[i].Signature + // trim list + p.Flags |= enums.DHT_RO_TRUNCATED + p.TruncOrigin = signer + size := num - 2 - i + list := make([]*Entry, size) + if size > 0 { + copy(list, p.List[i+2:]) } + p.List = list + return + } + // check next path element + succ = signer + signer = pred + if i != -1 { + sig = p.List[i].Signature } } } -// String returs a uman-readbale representation +// String returns a human-readable representation func (p *Path) String() string { - buf := new(bytes.Buffer) - s := "0" - if p.TruncOrigin != nil { - s = p.TruncOrigin.String() - } - buf.WriteString(fmt.Sprintf("{to=%s, (%d)[", s, len(p.List))) - for _, e := range p.List { - buf.WriteString(e.String()) - } - s = "0" - if p.LastSig != nil { - s = hex.EncodeToString(p.LastSig.Bytes()) + var hops []string + if p != nil { + if p.TruncOrigin != nil { + hops = append(hops, p.TruncOrigin.Short()) + } + for _, e := range p.List { + hops = append(hops, e.Signer.Short()) + } + if p.LastHop != nil { + hops = append(hops, p.LastHop.Short()) + } } - num := len(s) - if num > 16 { - s = s[:8] + ".." + s[num-8:] + // trim to sensible length for display + if num := len(hops); num > 8 { + trim := make([]string, 9) + copy(trim[:4], hops[:4]) + trim[4] = "..." + copy(trim[5:], hops[num-5:]) } - buf.WriteString(fmt.Sprintf("], ls=%s}", s)) - return buf.String() + return "[" + strings.Join(hops, "-") + "]" } diff --git a/src/gnunet/service/dht/resulthandler.go b/src/gnunet/service/dht/resulthandler.go @@ -45,6 +45,7 @@ import ( //====================================================================== // Compare return values +// //nolint:stylecheck // allow non-camel-case in constants const ( RHC_SAME = blocks.CMP_SAME // the two result handlers are the same @@ -135,7 +136,7 @@ func (t *ResultHandler) Merge(a *ResultHandler) bool { // Proceed return true if the message is to be processed in derived implementations func (t *ResultHandler) Proceed(ctx context.Context, msg *message.DHTP2PResultMsg) bool { - blk, err := blocks.NewBlock(enums.BlockType(msg.BType), msg.Expire, msg.Block) + blk, err := blocks.NewBlock(msg.BType, msg.Expire, msg.Block) if err == nil && !t.resFilter.Contains(blk) { t.resFilter.Add(blk) return true diff --git a/src/gnunet/service/dht/routingtable.go b/src/gnunet/service/dht/routingtable.go @@ -206,7 +206,7 @@ func (rt *RoutingTable) Contains(p *PeerAddress, label string) bool { list = append(list, val.Peer.Short()) return nil }, true) - logger.Printf(logger.DBG, "[%s] RT=%v", list) + logger.Printf(logger.DBG, "[%s] RT=%v", label, list) } else { //logger.Println(logger.DBG, "[RT] --> found in current list") px.lastSeen = util.AbsoluteTimeNow() @@ -280,7 +280,7 @@ func (rt *RoutingTable) SelectRandomPeer(pf *blocks.PeerFilter, pid int) (p *Pee // SelectPeer selects a neighbor depending on the number of hops parameter. // If hops < NSE this function MUST return SelectRandomPeer() and // SelectClosestpeer() otherwise. -func (rt *RoutingTable) SelectPeer(p *PeerAddress, hops int, bf *blocks.PeerFilter, pid int) *PeerAddress { +func (rt *RoutingTable) SelectPeer(p *PeerAddress, hops uint16, bf *blocks.PeerFilter, pid int) *PeerAddress { if float64(hops) < rt.l2nse { return rt.SelectRandomPeer(bf, pid) } @@ -386,7 +386,7 @@ func (rt *RoutingTable) LookupHello(addr *PeerAddress, rf blocks.ResultFilter, a results = append(results, result) } } else { - logger.Printf(logger.DBG, "[%s] LookupHello: cached HELLO block is filtered") + logger.Println(logger.DBG, "[%s] LookupHello: cached HELLO block is filtered") } return nil }, true) diff --git a/src/gnunet/service/gns/module.go b/src/gnunet/service/gns/module.go @@ -200,6 +200,7 @@ func (m *Module) ResolveAbsolute( // ResolveRelative resolves a relative path (to a given zone) recursively by // processing simple (PKEY,Label) lookups in sequence and handle intermediate // GNS record types +// //nolint:gocyclo // life sometimes is complex... func (m *Module) ResolveRelative( ctx context.Context, diff --git a/src/gnunet/service/gns/service.go b/src/gnunet/service/gns/service.go @@ -278,7 +278,7 @@ func (s *Service) LookupNamecache(ctx context.Context, query *blocks.GNSQuery) ( block.DerivedKeySig = m.DerivedKeySig sb := new(blocks.SignedGNSBlockData) sb.Purpose = new(crypto.SignaturePurpose) - sb.Purpose.Purpose = uint32(enums.SIG_GNS_RECORD_SIGN) + sb.Purpose.Purpose = enums.SIG_GNS_RECORD_SIGN sb.Purpose.Size = uint32(16 + len(m.EncData)) sb.Expire = m.Expire sb.Data = m.EncData @@ -413,7 +413,7 @@ func (s *Service) LookupDHT(ctx context.Context, query blocks.Query) (block bloc break } // check if result is of requested type - if enums.BlockType(m.BType) != enums.BLOCK_TYPE_GNS_NAMERECORD { + if m.BType != enums.BLOCK_TYPE_GNS_NAMERECORD { logger.Println(logger.ERROR, "[gns] DHT response has wrong type") break } diff --git a/src/gnunet/service/module.go b/src/gnunet/service/module.go @@ -24,7 +24,7 @@ import ( "time" ) -//---------------------------------------------------------------------- +// ---------------------------------------------------------------------- // Module is an interface for GNUnet service modules (workers). // // Modules can call other GNUnet services; these services can be used by @@ -34,29 +34,28 @@ import ( // calls to m.Export() and m.Import() to link the modules together (see // example): // -// // create module instances -// gnsMod = gns.NewModule(ctx, core) -// dhtMod = dht.NewModule(ctx, core) -// ncMod = namecache.NewModule(ctx, core) -// revMod = revocation.NewModule(ctx, core) +// // create module instances +// gnsMod = gns.NewModule(ctx, core) +// dhtMod = dht.NewModule(ctx, core) +// ncMod = namecache.NewModule(ctx, core) +// revMod = revocation.NewModule(ctx, core) // -// // export module functions -// fcn := make(map[string]any) -// gnsMod.Export(fcn) -// dhtMod.Export(fcn) -// ncMod.Export(fcn) -// revMod.Export(fcn) +// // export module functions +// fcn := make(map[string]any) +// gnsMod.Export(fcn) +// dhtMod.Export(fcn) +// ncMod.Export(fcn) +// revMod.Export(fcn) // -// // import (link) module functions -// gnsMod.Import(fcn) -// dhtMod.Import(fcn) -// ncMod.Import(fcn) -// revMod.Import(fcn) +// // import (link) module functions +// gnsMod.Import(fcn) +// dhtMod.Import(fcn) +// ncMod.Import(fcn) +// revMod.Import(fcn) // // Exported and imported module function are identified by name defined in the // Export() function. Import() functions that access functions in other modules // need to use the same name for linking. -// type Module interface { // Export functions by name Export(map[string]any) diff --git a/src/gnunet/service/revocation/pow.go b/src/gnunet/service/revocation/pow.go @@ -148,7 +148,7 @@ func (rd *RevData) Sign(skey *crypto.ZonePrivate) (err error) { sigBlock := &SignedRevData{ Purpose: &crypto.SignaturePurpose{ Size: uint32(20 + rd.ZoneKeySig.KeySize()), - Purpose: uint32(enums.SIG_REVOCATION), + Purpose: enums.SIG_REVOCATION, }, Timestamp: rd.Timestamp, ZoneKey: &rd.ZoneKeySig.ZoneKey, @@ -169,7 +169,7 @@ func (rd *RevData) Verify(withSig bool) (zbits float64, rc int) { sigBlock := &SignedRevData{ Purpose: &crypto.SignaturePurpose{ Size: uint32(20 + rd.ZoneKeySig.KeySize()), - Purpose: uint32(enums.SIG_REVOCATION), + Purpose: enums.SIG_REVOCATION, }, Timestamp: rd.Timestamp, ZoneKey: &rd.ZoneKeySig.ZoneKey, diff --git a/src/gnunet/service/revocation/pow_test.go b/src/gnunet/service/revocation/pow_test.go @@ -117,7 +117,7 @@ func TestRevocationRFC(t *testing.T) { sigBlock := &SignedRevData{ Purpose: &crypto.SignaturePurpose{ Size: uint32(20 + revData.ZoneKeySig.KeySize()), - Purpose: uint32(enums.SIG_REVOCATION), + Purpose: enums.SIG_REVOCATION, }, Timestamp: revData.Timestamp, ZoneKey: &revData.ZoneKeySig.ZoneKey, diff --git a/src/gnunet/service/rpc.go b/src/gnunet/service/rpc.go @@ -54,10 +54,11 @@ func RunRPCServer(ctx context.Context, endpoint string) (srvRPC *JRPCServer, err // instantiate a server and run it srv := &http.Server{ - Handler: router, - Addr: endpoint, - WriteTimeout: 15 * time.Second, - ReadTimeout: 15 * time.Second, + Handler: router, + Addr: endpoint, + WriteTimeout: 5 * time.Second, + ReadTimeout: 15 * time.Second, + ReadHeaderTimeout: 5 * time.Second, } // start listening go func() { diff --git a/src/gnunet/service/store/database.go b/src/gnunet/service/store/database.go @@ -132,11 +132,11 @@ func (p *dbPool) remove(key string) error { // argument defines the SQL database type. Other arguments depend on the value // of this first argument. // The following SQL types are implemented: -// * 'sqlite3': SQLite3-compatible database; the second argument specifies the -// file that holds the data (e.g. "sqlite3+/home/user/store.db") -// * 'mysql': A MySQL-compatible database; the second argument specifies the -// information required to log into the database (e.g. -// "[user[:passwd]@][proto[(addr)]]/dbname[?param1=value1&...]"). +// - 'sqlite3': SQLite3-compatible database; the second argument specifies the +// file that holds the data (e.g. "sqlite3+/home/user/store.db") +// - 'mysql': A MySQL-compatible database; the second argument specifies the +// information required to log into the database (e.g. +// "[user[:passwd]@][proto[(addr)]]/dbname[?param1=value1&...]"). func (p *dbPool) Connect(spec string) (db *DBConn, err error) { err = p.insts.Process(func(pid int) error { // check if we have a connection to this database. diff --git a/src/gnunet/service/store/store_dht.go b/src/gnunet/service/store/store_dht.go @@ -166,12 +166,9 @@ func (s *DHTStore) Put(query blocks.Query, entry *DHTEntry) (err error) { btype := query.Type() expire := entry.Blk.Expire() blkSize := len(entry.Blk.Bytes()) - pl := 0 - if entry.Path != nil { - pl = int(entry.Path.NumList) - } - logger.Printf(logger.INFO, "[dht-store] storing %d bytes under key %s (path: %d)", - blkSize, query.Key().Short(), pl) + + logger.Printf(logger.INFO, "[dht-store] storing %d bytes @ %s (path %s)", + blkSize, query.Key().Short(), entry.Path) // write entry to file for storage if err = s.writeEntry(query.Key().Data, entry); err != nil { @@ -276,7 +273,7 @@ func (s *DHTStore) GetApprox(label string, query blocks.Query, rf blocks.ResultF //---------------------------------------------------------------------- -type entryLayout struct { +type _EntryLayout struct { SizeBlk uint16 `order:"big"` // size of block data SizePth uint16 `order:"big"` // size of path data Block []byte `size:"SizeBlk"` // block data @@ -300,7 +297,7 @@ func (s *DHTStore) readEntry(md *FileMetadata) (entry *DHTEntry, err error) { size := int(fi.Size()) // read data - val := new(entryLayout) + val := new(_EntryLayout) if err = data.UnmarshalStream(file, val, size); err != nil { return } @@ -329,7 +326,7 @@ func (s *DHTStore) writeEntry(key []byte, entry *DHTEntry) (err error) { defer file.Close() // assemble and write entry - val := new(entryLayout) + val := new(_EntryLayout) val.Block = entry.Blk.Bytes() val.SizeBlk = uint16(len(val.Block)) if entry.Path != nil { diff --git a/src/gnunet/service/store/store_dht_meta.go b/src/gnunet/service/store/store_dht_meta.go @@ -70,8 +70,8 @@ type FileMetaDB struct { // database is "access.db". func OpenMetaDB(path string) (db *FileMetaDB, err error) { // connect to database - dbFile := path + "/acccess.db" - if _, err = os.Stat(path + "/acccess.db"); err != nil { + dbFile := path + "/access.db" + if _, err = os.Stat(path + "/access.db"); err != nil { var file *os.File if file, err = os.Create(dbFile); err != nil { return diff --git a/src/gnunet/service/store/store_kv.go b/src/gnunet/service/store/store_kv.go @@ -56,7 +56,7 @@ type KVStore interface { Close() error } -//------------------------------------------------------------ +// ------------------------------------------------------------ // NewKVStore creates a new storage handler with given spec // for use with key/value string pairs. func NewKVStore(spec util.ParameterSet) (KVStore, error) { diff --git a/src/gnunet/transport/responder.go b/src/gnunet/transport/responder.go @@ -25,7 +25,7 @@ import ( "gnunet/util" ) -//---------------------------------------------------------------------- +// ---------------------------------------------------------------------- // Responder is a back-channel for messages generated during // message processing. The Connection type is a responder // and used as such in ServeClient(). @@ -38,7 +38,7 @@ type Responder interface { Receiver() *util.PeerID } -//---------------------------------------------------------------------- +// ---------------------------------------------------------------------- // TransportResponder is used as a responder in message handling for // messages received from Transport. It is used by Endpoint instances // to define custom responders for messages received. diff --git a/src/gnunet/util/misc.go b/src/gnunet/util/misc.go @@ -108,7 +108,6 @@ func Shorten(s string, n int) string { return s[:p+k] + "..." + s[l-p:] } -//---------------------------------------------------------------------- // Dump instance func Dump(obj any, format string) string { switch format {