gnunet-go

GNUnet Bindings for Go
Log | Files | Refs | README | LICENSE

commit 5ea40b971a196afd4783d64ea1932864c9688030
parent 1a7284815bb2a63aac6b726e9167baed4813aa95
Author: Bernd Fix <brf@hoi-polloi.org>
Date:   Tue, 24 Jan 2023 11:46:09 +0100

Changes based on security audit "ngie-gnunetr5n".

Diffstat:
Msrc/gnunet/service/dht/blocks/filters.go | 5++++-
Msrc/gnunet/service/dht/blocks/gns.go | 7+++++--
Msrc/gnunet/service/dht/blocks/hello.go | 8+++++---
Msrc/gnunet/service/dht/messages.go | 77+++++++++++++++++++++++++++++++++--------------------------------------------
Msrc/gnunet/service/dht/module.go | 4++--
Msrc/gnunet/service/dht/routingtable.go | 9+++++----
Msrc/gnunet/service/dht/service.go | 7+++++++
Msrc/gnunet/transport/endpoint.go | 21++++++++++++---------
Msrc/gnunet/util/peer.go | 10++++++++--
9 files changed, 81 insertions(+), 67 deletions(-)

diff --git a/src/gnunet/service/dht/blocks/filters.go b/src/gnunet/service/dht/blocks/filters.go @@ -37,10 +37,13 @@ type PeerFilter struct { BF *BloomFilter } +// PeerFilterSize is 128 bytes (fixed). +const PeerFilterSize = 128 + // NewPeerFilter creates an empty peer filter instance. func NewPeerFilter() *PeerFilter { return &PeerFilter{ - BF: NewBloomFilter(128), + BF: NewBloomFilter(PeerFilterSize), } } diff --git a/src/gnunet/service/dht/blocks/gns.go b/src/gnunet/service/dht/blocks/gns.go @@ -39,6 +39,9 @@ var ( ErrBlockCantDecrypt = errors.New("can't decrypt block type") ) +// GNSContext for key derivation +const GNSContext = "gns" + //---------------------------------------------------------------------- // Query key for GNS lookups //---------------------------------------------------------------------- @@ -62,7 +65,7 @@ func (q *GNSQuery) Verify(b Block) (err error) { // verify derived key dkey := blk.DerivedKeySig.ZoneKey var dkey2 *crypto.ZoneKey - if dkey2, _, err = q.Zone.Derive(q.Label, "gns"); err != nil { + if dkey2, _, err = q.Zone.Derive(q.Label, GNSContext); err != nil { return } if !dkey.Equal(dkey2) { @@ -102,7 +105,7 @@ func NewGNSQuery(zkey *crypto.ZoneKey, label string) *GNSQuery { // derive a public key from (pkey,label) and set the repository // key as the SHA512 hash of the binary key representation. // (key blinding) - pd, _, err := zkey.Derive(label, "gns") + pd, _, err := zkey.Derive(label, GNSContext) if err != nil { logger.Printf(logger.ERROR, "[NewGNSQuery] failed: %s", err.Error()) return nil diff --git a/src/gnunet/service/dht/blocks/hello.go b/src/gnunet/service/dht/blocks/hello.go @@ -123,13 +123,13 @@ func ParseHelloBlockFromURL(u string, checkExpiry bool) (h *HelloBlock, err erro // (1) parse peer public key (peer ID) var buf []byte - if buf, err = util.DecodeStringToBinary(p[0], 32); err != nil { + if buf, err = util.DecodeStringToBinary(p[0], util.PeerPublicKeySize); err != nil { return } h.PeerID = util.NewPeerID(buf) // (2) parse signature - if buf, err = util.DecodeStringToBinary(p[1], 64); err != nil { + if buf, err = util.DecodeStringToBinary(p[1], util.PeerSignatureSize); err != nil { return } h.Signature = util.NewPeerSignature(buf) @@ -315,12 +315,14 @@ type _SignedData struct { AddrHash *crypto.HashCode // address hash } +const _SignedDataSize = 80 // (8 + 8 + 64) + // SignedData assembles a data block for sign and verify operations. func (h *HelloBlock) SignedData() []byte { // assemble signed data sd := &_SignedData{ Purpose: &crypto.SignaturePurpose{ - Size: 80, + Size: _SignedDataSize, Purpose: enums.SIG_HELLO, }, Expire: h.Expire_, diff --git a/src/gnunet/service/dht/messages.go b/src/gnunet/service/dht/messages.go @@ -37,6 +37,9 @@ import ( // Handle DHT messages from the network //---------------------------------------------------------------------- +// MaxSortResults is the max. number of sorted results +const MaxSortResults = 10 + // HandleMessage handles a DHT request/response message. Responses are sent // to the specified responder. // @@ -154,12 +157,12 @@ func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn m // create total result list if len(results) == 0 { results = lclResults - } else if len(results)+len(lclResults) <= 10 { + } else if len(results)+len(lclResults) <= MaxSortResults { // handle few results directly results = append(results, lclResults...) } else { // compile a new sorted list from results. - list := store.NewSortedDHTResults(10) + list := store.NewSortedDHTResults(MaxSortResults) for pos, res := range results { list.Add(res, pos) } @@ -313,24 +316,7 @@ func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn m // if the put is for a HELLO block, add the sender to the // routing table (9.3.2.9) if msg.BType == enums.BLOCK_TYPE_DHT_HELLO { - // get addresses from HELLO block - hello, err := blocks.ParseHelloBlockFromBytes(msg.Block) - if err != nil { - logger.Printf(logger.ERROR, "[%s] failed to parse HELLO block: %s", label, err.Error()) - } else { - // check state of bucket for given address - if m.rtable.Check(NewPeerAddress(hello.PeerID)) == 0 { - // we could add the sender to the routing table - for _, addr := range hello.Addresses() { - if transport.CanHandleAddress(addr) { - // try to connect to peer (triggers EV_CONNECTED on success) - if err := m.core.TryConnect(sender, addr); err != nil { - logger.Printf(logger.ERROR, "[%s] try-connection to %s failed: %s", label, addr.URI(), err.Error()) - } - } - } - } - } + m.addSender(msg.Block, label, sender) } //-------------------------------------------------------------- // check if we need to forward @@ -418,24 +404,7 @@ func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn m // if the put is for a HELLO block, add the originator to the // routing table (9.5.2.5) if btype == enums.BLOCK_TYPE_DHT_HELLO { - // get addresses from HELLO block - hello, err := blocks.ParseHelloBlockFromBytes(msg.Block) - if err != nil { - logger.Printf(logger.ERROR, "[%s] failed to parse HELLO block: %s", label, err.Error()) - } else { - // check state of bucket for given address - if m.rtable.Check(NewPeerAddress(hello.PeerID)) == 0 { - // we could add the originator to the routing table - for _, addr := range hello.Addresses() { - if transport.CanHandleAddress(addr) { - // try to connect to peer (triggers EV_CONNECTED on success) - if err := m.core.TryConnect(sender, addr); err != nil { - logger.Printf(logger.ERROR, "[%s] try-connection to %s failed: %s", label, addr.URI(), err.Error()) - } - } - } - } - } + m.addSender(msg.Block, label, sender) } // message forwarding to responder logger.Printf(logger.DBG, "[%s] result key = %s", label, msg.Query.Short()) @@ -451,12 +420,10 @@ func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn m logger.Printf(logger.DBG, "[%s] Result handler not suitable (%s != %s) -- skipped", label, rh.Type(), btype) continue } - /* - if rh.Flags()&enums.DHT_RO_FIND_APPROXIMATE != msg.Flags&enums.DHT_RO_FIND_APPROXIMATE { - logger.Printf(logger.DBG, "[%s] Result handler asked for match, got approx -- ignored", label) - continue - } - */ + if rh.Flags()&enums.DHT_RO_FIND_APPROXIMATE == 0 && msg.Flags&enums.DHT_RO_FIND_APPROXIMATE != 0 { + logger.Printf(logger.DBG, "[%s] Result handler asked for match, got approx -- ignored", label) + continue + } //-------------------------------------------------------------- // check task list for handler (9.5.2.6) if rh.Flags()&enums.DHT_RO_FIND_APPROXIMATE == 0 && blkKey != nil && !blkKey.Equal(rh.Key()) { @@ -588,6 +555,28 @@ func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn m // Helpers //---------------------------------------------------------------------- +// add a HELLO block sender to routing table +func (m *Module) addSender(block []byte, label string, sender *util.PeerID) { + // get addresses from HELLO block + hello, err := blocks.ParseHelloBlockFromBytes(block) + if err != nil { + logger.Printf(logger.ERROR, "[%s] failed to parse HELLO block: %s", label, err.Error()) + } else { + // check state of bucket for given address + if m.rtable.Check(NewPeerAddress(hello.PeerID)) == 0 { + // we could add the sender to the routing table + for _, addr := range hello.Addresses() { + if transport.CanHandleAddress(addr) { + // try to connect to peer (triggers EV_CONNECTED on success) + if err := m.core.TryConnect(sender, addr); err != nil { + logger.Printf(logger.ERROR, "[%s] try-connection to %s failed: %s", label, addr.URI(), err.Error()) + } + } + } + } + } +} + // send a result back to caller func (m *Module) sendResult(ctx context.Context, query blocks.Query, blk blocks.Block, pth *path.Path, back transport.Responder) error { // assemble result message diff --git a/src/gnunet/service/dht/module.go b/src/gnunet/service/dht/module.go @@ -142,7 +142,7 @@ func NewModule(ctx context.Context, c *core.Core, cfg *config.DHTConfig) (m *Mod c.Register("dht", listener) // run periodic tasks (8.2. peer discovery) - ticker := time.NewTicker(5 * time.Minute) + ticker := time.NewTicker(DiscoveryPeriod) key := crypto.Hash(m.core.PeerID().Bytes()) flags := uint16(enums.DHT_RO_FIND_APPROXIMATE | enums.DHT_RO_DEMULTIPLEX_EVERYWHERE | enums.DHT_RO_DISCOVERY) var resCh <-chan blocks.Block @@ -230,7 +230,7 @@ func (m *Module) Get(ctx context.Context, query blocks.Query) <-chan blocks.Bloc ttl, ok := util.GetParam[time.Duration](query.Params(), "timeout") if !ok { // defaults to 10 minutes - ttl = 10 * time.Minute + ttl = DefaultGetTTL } lctx, cancel := context.WithTimeout(ctx, ttl) diff --git a/src/gnunet/service/dht/routingtable.go b/src/gnunet/service/dht/routingtable.go @@ -36,7 +36,8 @@ import ( // Routing table constants const ( - numK = 20 // number of entries per k-bucket + numK = 20 // number of entries per k-bucket + numBits = 512 // number of bits in SHA-512 value ) //====================================================================== @@ -86,7 +87,7 @@ func (addr *PeerAddress) Equal(p *PeerAddress) bool { // bucket index (smaller index = less distant). func (addr *PeerAddress) Distance(p *PeerAddress) (*math.Int, int) { r := util.Distance(addr.Key.Data, p.Key.Data) - return r, 512 - r.BitLen() + return r, numBits - r.BitLen() } //====================================================================== @@ -115,7 +116,7 @@ func NewRoutingTable(ref *PeerAddress, cfg *config.RoutingConfig) *RoutingTable rt := &RoutingTable{ ref: ref, list: util.NewMap[string, *PeerAddress](), - buckets: make([]*Bucket, 512), + buckets: make([]*Bucket, numBits), l2nse: -1, inProcess: make(map[int]struct{}), cfg: cfg, @@ -368,7 +369,7 @@ func (rt *RoutingTable) heartbeat(ctx context.Context) { func (rt *RoutingTable) LookupHello(addr *PeerAddress, rf blocks.ResultFilter, approx bool, label string) (results []*store.DHTResult) { // iterate over cached HELLOs to find matches; // approximate search is guided by distance - list := store.NewSortedDHTResults(10) + list := store.NewSortedDHTResults(MaxSortResults) _ = rt.helloCache.ProcessRange(func(key string, hb *blocks.HelloBlock, _ int) error { // check if block is excluded by result filter if !rf.Contains(hb) { diff --git a/src/gnunet/service/dht/service.go b/src/gnunet/service/dht/service.go @@ -22,6 +22,7 @@ import ( "context" "fmt" "io" + "time" "gnunet/config" "gnunet/core" @@ -37,6 +38,12 @@ var ( ErrInvalidResponseType = fmt.Errorf("invald response type") ) +// Time constants +var ( + DefaultGetTTL = 10 * time.Minute // timeout for GET requests + DiscoveryPeriod = 5 * time.Minute // time between peer discovery runs +) + //---------------------------------------------------------------------- // "GNUnet R5N DHT" service implementation //---------------------------------------------------------------------- diff --git a/src/gnunet/transport/endpoint.go b/src/gnunet/transport/endpoint.go @@ -24,8 +24,8 @@ import ( "errors" "gnunet/message" "gnunet/util" + "io" "net" - "strings" "sync" "time" @@ -41,6 +41,7 @@ var ( ErrEndpNoConnection = errors.New("no connection on endpoint") ErrEndpMaybeSent = errors.New("message may have been sent - can't know") ErrEndpWriteShort = errors.New("write too short") + ErrEndpReadShort = errors.New("read too short") ) // Endpoint represents a local endpoint that can send and receive messages. @@ -120,16 +121,13 @@ func (ep *PaketEndpoint) Run(ctx context.Context, hdlr chan *Message) (err error // read next message tm, err := ep.read() if err != nil { - // leave go routine if already dead - if !active { - return + // leave go routine if already dead or closed by client + if !active || err == io.EOF { + break } logger.Println(logger.WARN, "[pkt_ep] read failed: "+err.Error()) - // gracefully ignore unknown message types - if strings.HasPrefix(err.Error(), "unknown message type") { - continue - } - break + // gracefully ignore failed messages + continue } // label message tm.Label = ep.addr.String() @@ -158,6 +156,11 @@ func (ep *PaketEndpoint) read() (tm *Message, err error) { ) switch ep.addr.Network() { case "ip+udp": + // check for minimum size (32 byte peer id + 4 byte header) + if n < 36 { + err = ErrEndpReadShort + return + } // parse peer id and message in sequence peer = util.NewPeerID(ep.buf[:32]) rdr := bytes.NewBuffer(util.Clone(ep.buf[32:n])) diff --git a/src/gnunet/util/peer.go b/src/gnunet/util/peer.go @@ -33,6 +33,9 @@ type PeerPublicKey struct { Data []byte `size:"(Size)"` // Ed25519 public key data } +// PeerPublicKeySize is the size of a binary representation +const PeerPublicKeySize = 32 + // NewPeerPublicKey creates a key instance from binary data func NewPeerPublicKey(data []byte) *PeerPublicKey { pk := new(PeerPublicKey) @@ -51,7 +54,7 @@ func NewPeerPublicKey(data []byte) *PeerPublicKey { // Size returns the length of the binary data func (pk *PeerPublicKey) Size() uint { - return 32 + return PeerPublicKeySize } // Verify peer signature @@ -115,6 +118,9 @@ type PeerSignature struct { Data []byte `size:"(Size)"` } +// PeerSignatureSize is the size of the binary representation +const PeerSignatureSize = 64 + // NewPeerSignature is a EdDSA signatre with the private peer key func NewPeerSignature(data []byte) *PeerSignature { s := new(PeerSignature) @@ -133,7 +139,7 @@ func NewPeerSignature(data []byte) *PeerSignature { // Size returns the length of the binary data func (s *PeerSignature) Size() uint { - return 64 + return PeerSignatureSize } // Bytes returns the binary representation of a peer signature.