diff --git a/cmd/reticulum-go/main.go b/cmd/reticulum-go/main.go index 31653b4..125fe66 100644 --- a/cmd/reticulum-go/main.go +++ b/cmd/reticulum-go/main.go @@ -62,7 +62,6 @@ type Reticulum struct { } type announceRecord struct { - // All fields were unused, so entire struct can be removed } func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) { @@ -70,6 +69,14 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) { cfg = config.DefaultConfig() } + // Set default app name and aspect if not provided + if cfg.AppName == "" { + cfg.AppName = "Go Client" + } + if cfg.AppAspect == "" { + cfg.AppAspect = "node" + } + if err := initializeDirectories(); err != nil { return nil, fmt.Errorf("failed to initialize directories: %v", err) } @@ -269,6 +276,7 @@ func main() { []byte("HELLO WORLD"), nil, false, + r.config, ) if err != nil { log.Fatalf("Failed to create announce: %v", err) @@ -424,24 +432,27 @@ func (r *Reticulum) Start() error { debugLog(3, "Interface %s started successfully", iface.GetName()) } - // Create initial announce packet - announceData := []byte("Reticulum-Go") - announcePacket := transport.CreateAnnouncePacket( - r.identity.Hash(), + // Create initial announce + initialAnnounce, err := announce.NewAnnounce( r.identity, - announceData, - 0, + []byte("Reticulum-Go"), + nil, // ratchetID + false, // pathResponse + r.config, ) + if err != nil { + return fmt.Errorf("failed to create announce: %v", err) + } // Wait briefly for interfaces to initialize time.Sleep(2 * time.Second) - // Send initial announces on all enabled interfaces + // Send initial announces for _, iface := range r.interfaces { if netIface, ok := iface.(common.NetworkInterface); ok { if netIface.IsEnabled() && netIface.IsOnline() { debugLog(2, "Sending initial announce on interface %s", netIface.GetName()) - if err := netIface.Send(announcePacket, ""); err != nil { + if err := initialAnnounce.Propagate([]common.NetworkInterface{netIface}); err != nil { debugLog(1, "Failed to send initial announce on interface %s: %v", netIface.GetName(), err) } } @@ -458,19 +469,24 @@ func (r *Reticulum) Start() error { announceCount++ debugLog(3, "Starting periodic announce cycle #%d", announceCount) - // Create fresh announce packet for each cycle - announcePacket := transport.CreateAnnouncePacket( - r.identity.Hash(), + // Create fresh announce for each cycle + periodicAnnounce, err := announce.NewAnnounce( r.identity, - announceData, - 0, + []byte("Reticulum-Go"), + nil, // ratchetID + false, // pathResponse + r.config, ) + if err != nil { + debugLog(1, "Failed to create periodic announce: %v", err) + continue + } for _, iface := range r.interfaces { if netIface, ok := iface.(common.NetworkInterface); ok { if netIface.IsEnabled() && netIface.IsOnline() { debugLog(2, "Sending periodic announce on interface %s", netIface.GetName()) - if err := netIface.Send(announcePacket, ""); err != nil { + if err := periodicAnnounce.Propagate([]common.NetworkInterface{netIface}); err != nil { debugLog(1, "Failed to send periodic announce on interface %s: %v", netIface.GetName(), err) continue } diff --git a/pkg/announce/announce.go b/pkg/announce/announce.go index 8683d42..fa06cf3 100644 --- a/pkg/announce/announce.go +++ b/pkg/announce/announce.go @@ -60,6 +60,7 @@ type Announce struct { destinationHash []byte identity *identity.Identity appData []byte + config *common.ReticulumConfig hops uint8 timestamp int64 signature []byte @@ -71,7 +72,7 @@ type Announce struct { hash []byte } -func New(dest *identity.Identity, appData []byte, pathResponse bool) (*Announce, error) { +func New(dest *identity.Identity, appData []byte, pathResponse bool, config *common.ReticulumConfig) (*Announce, error) { if dest == nil { return nil, errors.New("destination identity required") } @@ -80,6 +81,7 @@ func New(dest *identity.Identity, appData []byte, pathResponse bool) (*Announce, mutex: &sync.RWMutex{}, identity: dest, appData: appData, + config: config, hops: 0, timestamp: time.Now().Unix(), pathResponse: pathResponse, @@ -284,14 +286,14 @@ func CreateHeader(ifacFlag byte, headerType byte, contextFlag byte, propType byt func (a *Announce) CreatePacket() []byte { log.Printf("[DEBUG-7] Creating announce packet") - + headerByte := byte( - (IFAC_NONE) | - (HEADER_TYPE_1 << 6) | - (0 << 5) | - (PROP_TYPE_BROADCAST << 4) | - (DEST_TYPE_SINGLE << 2) | - PACKET_TYPE_ANNOUNCE, + (IFAC_NONE) | + (HEADER_TYPE_1 << 6) | + (0 << 5) | + (PROP_TYPE_BROADCAST << 4) | + (DEST_TYPE_SINGLE << 2) | + PACKET_TYPE_ANNOUNCE, ) log.Printf("[DEBUG-7] Created header byte: %02x, hops: %d", headerByte, a.hops) @@ -301,29 +303,31 @@ func (a *Announce) CreatePacket() []byte { log.Printf("[DEBUG-7] Adding destination hash (16 bytes): %x", a.destinationHash) packet = append(packet, a.destinationHash...) - // Split public key into encryption and signing keys (32 bytes each) + // Get full public key and split into encryption and signing keys pubKey := a.identity.GetPublicKey() - encKey := pubKey[:32] - signKey := pubKey[32:] - + encKey := pubKey[:32] // x25519 public key for encryption + signKey := pubKey[32:] // Ed25519 public key for signing + + // Add encryption key (32 bytes) log.Printf("[DEBUG-7] Adding encryption key (32 bytes): %x", encKey) packet = append(packet, encKey...) - + + // Add signing key (32 bytes) log.Printf("[DEBUG-7] Adding signing key (32 bytes): %x", signKey) packet = append(packet, signKey...) - // Add name hash (10 bytes) - nameHash := a.identity.GetNameHash() - log.Printf("[DEBUG-7] Adding name hash (10 bytes): %x", nameHash) - packet = append(packet, nameHash...) + // Add name hash (10 bytes) - SHA256 hash of full name truncated to 10 bytes + nameHash := sha256.Sum256([]byte(fmt.Sprintf("%s.%s", a.config.AppName, a.config.AppAspect))) + log.Printf("[DEBUG-7] Adding name hash (10 bytes): %x", nameHash[:10]) + packet = append(packet, nameHash[:10]...) // Add random hash (5 random + 5 timestamp bytes = 10 bytes) - randomHash := make([]byte, 5) - rand.Read(randomHash) + randomBytes := make([]byte, 5) + rand.Read(randomBytes) timeBytes := make([]byte, 8) binary.BigEndian.PutUint64(timeBytes, uint64(time.Now().Unix())) - log.Printf("[DEBUG-7] Adding random hash (10 bytes): %x%x", randomHash, timeBytes[:5]) - packet = append(packet, randomHash...) + log.Printf("[DEBUG-7] Adding random hash (10 bytes): %x%x", randomBytes, timeBytes[:5]) + packet = append(packet, randomBytes...) packet = append(packet, timeBytes[:5]...) // Add ratchet if present (32 bytes) @@ -332,12 +336,23 @@ func (a *Announce) CreatePacket() []byte { packet = append(packet, a.ratchetID...) } - // Add app data - log.Printf("[DEBUG-7] Adding app data (%d bytes): %x", len(a.appData), a.appData) - packet = append(packet, a.appData...) + // Create msgpack array for app data + appData := []byte{ + 0x92, // msgpack array of 2 elements + 0xc4, // bin 8 format for byte array + } - // Add signature (64 bytes) - signData := append(a.destinationHash, a.appData...) + // Add name bytes + nameBytes := []byte(fmt.Sprintf("%s.%s", a.config.AppName, a.config.AppAspect)) + appData = append(appData, byte(len(nameBytes))) // length prefix + appData = append(appData, nameBytes...) // name bytes + appData = append(appData, 0x00) // ticket value = 0 + + // Add app data to packet + packet = append(packet, appData...) + + // Create signature + signData := append(a.destinationHash, appData...) if a.ratchetID != nil { signData = append(signData, a.ratchetID...) } @@ -346,7 +361,6 @@ func (a *Announce) CreatePacket() []byte { packet = append(packet, signature...) log.Printf("[DEBUG-7] Final packet size: %d bytes", len(packet)) - a.packet = packet return packet } @@ -380,8 +394,8 @@ func NewAnnouncePacket(pubKey []byte, appData []byte, announceID []byte) *Announ } // NewAnnounce creates a new announce packet for a destination -func NewAnnounce(identity *identity.Identity, appData []byte, ratchetID []byte, pathResponse bool) (*Announce, error) { - log.Printf("[DEBUG-7] Creating new announce: appDataLen=%d, hasRatchet=%v, pathResponse=%v", +func NewAnnounce(identity *identity.Identity, appData []byte, ratchetID []byte, pathResponse bool, config *common.ReticulumConfig) (*Announce, error) { + log.Printf("[DEBUG-7] Creating new announce: appDataLen=%d, hasRatchet=%v, pathResponse=%v", len(appData), ratchetID != nil, pathResponse) if identity == nil { @@ -389,6 +403,10 @@ func NewAnnounce(identity *identity.Identity, appData []byte, ratchetID []byte, return nil, errors.New("identity cannot be nil") } + if config == nil { + return nil, errors.New("config cannot be nil") + } + destHash := identity.Hash() log.Printf("[DEBUG-7] Generated destination hash: %x", destHash) @@ -401,15 +419,16 @@ func NewAnnounce(identity *identity.Identity, appData []byte, ratchetID []byte, hops: 0, mutex: &sync.RWMutex{}, handlers: make([]AnnounceHandler, 0), + config: config, } - log.Printf("[DEBUG-7] Created announce object: destHash=%x, hops=%d", + log.Printf("[DEBUG-7] Created announce object: destHash=%x, hops=%d", a.destinationHash, a.hops) // Create initial packet packet := a.CreatePacket() a.packet = packet - + // Generate hash hash := a.Hash() log.Printf("[DEBUG-7] Generated announce hash: %x", hash) diff --git a/pkg/common/config.go b/pkg/common/config.go index fed9c5d..4e0317d 100644 --- a/pkg/common/config.go +++ b/pkg/common/config.go @@ -50,6 +50,8 @@ type ReticulumConfig struct { PanicOnInterfaceErr bool LogLevel int Interfaces map[string]*InterfaceConfig + AppName string + AppAspect string } // NewReticulumConfig creates a new ReticulumConfig with default values @@ -75,3 +77,17 @@ func (c *ReticulumConfig) Validate() error { } return nil } + +func DefaultConfig() *ReticulumConfig { + return &ReticulumConfig{ + EnableTransport: true, + ShareInstance: false, + SharedInstancePort: DEFAULT_SHARED_INSTANCE_PORT, + InstanceControlPort: DEFAULT_INSTANCE_CONTROL_PORT, + PanicOnInterfaceErr: false, + LogLevel: DEFAULT_LOG_LEVEL, + Interfaces: make(map[string]*InterfaceConfig), + AppName: "Go Client", + AppAspect: "node", + } +} diff --git a/pkg/transport/transport.go b/pkg/transport/transport.go index 7cef2f5..3690e8a 100644 --- a/pkg/transport/transport.go +++ b/pkg/transport/transport.go @@ -1,12 +1,13 @@ package transport import ( + "crypto/rand" "crypto/sha256" "encoding/binary" "errors" "fmt" "log" - "math/rand" + mathrand "math/rand" "net" "sync" "time" @@ -120,6 +121,9 @@ type Path struct { HopCount byte } +var randSource = mathrand.NewSource(time.Now().UnixNano()) +var rng = mathrand.New(randSource) + func NewTransport(cfg *common.ReticulumConfig) *Transport { t := &Transport{ interfaces: make(map[string]common.NetworkInterface), @@ -441,7 +445,7 @@ func (t *Transport) HandleAnnounce(data []byte, sourceIface common.NetworkInterf } // Add random delay before retransmission (0-2 seconds) - delay := time.Duration(rand.Float64() * 2 * float64(time.Second)) + delay := time.Duration(rng.Float64() * 2 * float64(time.Second)) time.Sleep(delay) // Check bandwidth allocation for announces @@ -734,7 +738,7 @@ func (t *Transport) handleAnnouncePacket(data []byte, iface common.NetworkInterf } // Add random delay before retransmission (0-2 seconds) - delay := time.Duration(rand.Float64() * 2 * float64(time.Second)) + delay := time.Duration(rng.Float64() * 2 * float64(time.Second)) time.Sleep(delay) // Check bandwidth allocation for announces @@ -1035,39 +1039,65 @@ func (l *Link) GetStatus() int { return l.status } -func CreateAnnouncePacket(destHash []byte, identity *identity.Identity, appData []byte, hops byte) []byte { - packet := make([]byte, 0, 256) - - // Header byte construction according to RNS spec +func CreateAnnouncePacket(destHash []byte, identity *identity.Identity, appData []byte, hops byte, config *common.ReticulumConfig) []byte { + log.Printf("[DEBUG-7] Creating announce packet") + headerByte := byte( - (0 << 7) | // Interface flag (IFAC_NONE) - (0 << 6) | // Header type (HEADER_TYPE_1) - (0 << 5) | // Context flag - (1 << 4) | // Propagation type (BROADCAST) - (0 << 2) | // Destination type (SINGLE) - PACKET_TYPE_ANNOUNCE, // Packet type (0x01) + (0 << 7) | // Interface flag (IFAC_NONE) + (0 << 6) | // Header type (HEADER_TYPE_1) + (0 << 5) | // Context flag + (1 << 4) | // Propagation type (BROADCAST) + (0 << 2) | // Destination type (SINGLE) + PACKET_TYPE_ANNOUNCE, // Packet type (0x01) ) - - // Add header and hops - packet = append(packet, headerByte, hops) - - // Add destination hash (16 bytes) + + log.Printf("[DEBUG-7] Created header byte: %02x, hops: %d", headerByte, hops) + packet := []byte{headerByte, hops} + + log.Printf("[DEBUG-7] Adding destination hash (16 bytes): %x", destHash) packet = append(packet, destHash...) - - // Add full public key (64 bytes - both encryption and signing keys) - fullPubKey := identity.GetPublicKey() // This should return full 64-byte key - packet = append(packet, fullPubKey...) - - // Add app data with length prefix - appDataLen := make([]byte, 2) - binary.BigEndian.PutUint16(appDataLen, uint16(len(appData))) - packet = append(packet, appDataLen...) - packet = append(packet, appData...) - - // Sign the announce - signData := append(destHash, appData...) + + pubKey := identity.GetPublicKey() + encKey := pubKey[:32] + signKey := pubKey[32:] + + log.Printf("[DEBUG-7] Adding encryption key (32 bytes): %x", encKey) + packet = append(packet, encKey...) + + log.Printf("[DEBUG-7] Adding signing key (32 bytes): %x", signKey) + packet = append(packet, signKey...) + + nameHash := sha256.Sum256([]byte(fmt.Sprintf("%s.%s", config.AppName, config.AppAspect))) + log.Printf("[DEBUG-7] Adding name hash (10 bytes): %x", nameHash[:10]) + packet = append(packet, nameHash[:10]...) + + randomBytes := make([]byte, 5) + rand.Read(randomBytes) + timeBytes := make([]byte, 8) + binary.BigEndian.PutUint64(timeBytes, uint64(time.Now().Unix())) + log.Printf("[DEBUG-7] Adding random hash (10 bytes): %x%x", randomBytes, timeBytes[:5]) + packet = append(packet, randomBytes...) + packet = append(packet, timeBytes[:5]...) + + nameBytes := []byte(fmt.Sprintf("%s.%s", config.AppName, config.AppAspect)) + appDataMsg := []byte{ + 0x92, // msgpack array of 2 elements + 0xc4, // bin 8 format for byte array + byte(len(nameBytes)), // length prefix + } + appDataMsg = append(appDataMsg, nameBytes...) + appDataMsg = append(appDataMsg, 0x00) + + signData := append(destHash, appDataMsg...) signature := identity.Sign(signData) + log.Printf("[DEBUG-7] Adding signature (64 bytes): %x", signature) packet = append(packet, signature...) + packet = append(packet, appDataMsg...) + log.Printf("[DEBUG-7] Final packet size: %d bytes", len(packet)) + + announceHash := sha256.Sum256(packet) + log.Printf("[DEBUG-7] Generated announce hash: %x", announceHash) + return packet }