Files
Reticulum-Go/pkg/announce/announce.go
Sudo-Ivan 0f5f5cbb13 0.2.3
2024-12-30 02:54:49 -06:00

178 lines
4.0 KiB
Go

package announce
import (
"crypto/sha256"
"errors"
"sync"
"time"
"github.com/Sudo-Ivan/reticulum-go/pkg/identity"
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
)
const (
ANNOUNCE_NONE = 0x00
ANNOUNCE_PATH = 0x01
ANNOUNCE_IDENTITY = 0x02
MAX_HOPS = 128
PROPAGATION_RATE = 0.02 // 2% of interface bandwidth
RETRY_INTERVAL = 300 // 5 minutes
MAX_RETRIES = 3
)
type AnnounceHandler interface {
AspectFilter() []string
ReceivedAnnounce(destinationHash []byte, announcedIdentity *identity.Identity, appData []byte) error
ReceivePathResponses() bool
}
type Announce struct {
mutex sync.RWMutex
destinationHash []byte
identity *identity.Identity
appData []byte
hops uint8
timestamp int64
signature []byte
pathResponse bool
retries int
handlers []AnnounceHandler
}
func New(dest *identity.Identity, appData []byte, pathResponse bool) (*Announce, error) {
a := &Announce{
identity: dest,
appData: appData,
hops: 0,
timestamp: time.Now().Unix(),
pathResponse: pathResponse,
retries: 0,
handlers: make([]AnnounceHandler, 0),
}
// Generate destination hash
hash := sha256.New()
hash.Write(dest.GetPublicKey())
a.destinationHash = hash.Sum(nil)[:16] // Truncated hash
// Sign the announce
signData := append(a.destinationHash, a.appData...)
a.signature = dest.Sign(signData)
return a, nil
}
func (a *Announce) Propagate(interfaces []common.NetworkInterface) error {
a.mutex.Lock()
defer a.mutex.Unlock()
if a.hops >= MAX_HOPS {
return errors.New("maximum hop count reached")
}
// Increment hop count
a.hops++
// Create announce packet
packet := make([]byte, 0)
packet = append(packet, a.destinationHash...)
packet = append(packet, a.identity.GetPublicKey()...)
packet = append(packet, byte(a.hops))
if a.appData != nil {
packet = append(packet, a.appData...)
}
packet = append(packet, a.signature...)
// Propagate to all interfaces
for _, iface := range interfaces {
if err := iface.Send(packet, ""); err != nil {
return err
}
}
return nil
}
func (a *Announce) RegisterHandler(handler AnnounceHandler) {
a.mutex.Lock()
defer a.mutex.Unlock()
a.handlers = append(a.handlers, handler)
}
func (a *Announce) DeregisterHandler(handler AnnounceHandler) {
a.mutex.Lock()
defer a.mutex.Unlock()
for i, h := range a.handlers {
if h == handler {
a.handlers = append(a.handlers[:i], a.handlers[i+1:]...)
break
}
}
}
func (a *Announce) HandleAnnounce(data []byte) error {
a.mutex.Lock()
defer a.mutex.Unlock()
// Validate announce data
if len(data) < 16+32+1 { // Min size: hash + pubkey + hops
return errors.New("invalid announce data")
}
// Extract fields
destHash := data[:16]
publicKey := data[16:48]
hopCount := data[48]
// Validate hop count
if hopCount > MAX_HOPS {
return errors.New("announce exceeded maximum hop count")
}
// Extract app data and signature
appData := data[49 : len(data)-64]
signature := data[len(data)-64:]
// Create announced identity from public key
announcedIdentity := identity.FromPublicKey(publicKey)
if announcedIdentity == nil {
return errors.New("invalid identity public key")
}
// Verify signature
signData := append(destHash, appData...)
if !announcedIdentity.Verify(signData, signature) {
return errors.New("invalid announce signature")
}
// Process announce with registered handlers
for _, handler := range a.handlers {
if handler.ReceivePathResponses() || !a.pathResponse {
if err := handler.ReceivedAnnounce(destHash, announcedIdentity, appData); err != nil {
return err
}
}
}
return nil
}
func (a *Announce) RequestPath(destHash []byte, onInterface common.NetworkInterface) error {
a.mutex.Lock()
defer a.mutex.Unlock()
// Create path request packet
packet := make([]byte, 0)
packet = append(packet, destHash...)
packet = append(packet, byte(0)) // Initial hop count
// Send path request
if err := onInterface.Send(packet, ""); err != nil {
return err
}
return nil
}