This commit is contained in:
Sudo-Ivan
2025-01-01 18:31:58 -06:00
parent 0862830431
commit 5acbef454f
6 changed files with 425 additions and 318 deletions

View File

@@ -4,7 +4,6 @@ import (
"flag" "flag"
"fmt" "fmt"
"log" "log"
"math/rand"
"os" "os"
"os/signal" "os/signal"
"sync" "sync"
@@ -16,6 +15,7 @@ import (
"github.com/Sudo-Ivan/reticulum-go/pkg/buffer" "github.com/Sudo-Ivan/reticulum-go/pkg/buffer"
"github.com/Sudo-Ivan/reticulum-go/pkg/channel" "github.com/Sudo-Ivan/reticulum-go/pkg/channel"
"github.com/Sudo-Ivan/reticulum-go/pkg/common" "github.com/Sudo-Ivan/reticulum-go/pkg/common"
"github.com/Sudo-Ivan/reticulum-go/pkg/destination"
"github.com/Sudo-Ivan/reticulum-go/pkg/identity" "github.com/Sudo-Ivan/reticulum-go/pkg/identity"
"github.com/Sudo-Ivan/reticulum-go/pkg/interfaces" "github.com/Sudo-Ivan/reticulum-go/pkg/interfaces"
"github.com/Sudo-Ivan/reticulum-go/pkg/packet" "github.com/Sudo-Ivan/reticulum-go/pkg/packet"
@@ -44,6 +44,8 @@ const (
DEBUG_TRACE = 5 // Very detailed tracing DEBUG_TRACE = 5 // Very detailed tracing
DEBUG_PACKETS = 6 // Packet-level details DEBUG_PACKETS = 6 // Packet-level details
DEBUG_ALL = 7 // Everything including identity operations DEBUG_ALL = 7 // Everything including identity operations
APP_NAME = "Go Client"
APP_ASPECT = "node"
) )
type Reticulum struct { type Reticulum struct {
@@ -52,18 +54,15 @@ type Reticulum struct {
interfaces []interfaces.Interface interfaces []interfaces.Interface
channels map[string]*channel.Channel channels map[string]*channel.Channel
buffers map[string]*buffer.Buffer buffers map[string]*buffer.Buffer
announceHandlers map[string][]announce.AnnounceHandler
pathRequests map[string]*common.PathRequest pathRequests map[string]*common.PathRequest
announceHistory map[string]announceRecord announceHistory map[string]announceRecord
announceHistoryMu sync.RWMutex announceHistoryMu sync.RWMutex
identity *identity.Identity identity *identity.Identity
destination *destination.Destination
} }
type announceRecord struct { type announceRecord struct {
lastSeen time.Time // All fields were unused, so entire struct can be removed
seenCount int
violations int
interfaces map[string]bool
} }
func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) { func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
@@ -85,16 +84,36 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
} }
debugLog(2, "Created new identity: %x", identity.Hash()) debugLog(2, "Created new identity: %x", identity.Hash())
// Create destination
debugLog(DEBUG_INFO, "Creating destination...")
dest, err := destination.New(
identity,
destination.IN,
destination.SINGLE,
APP_NAME,
APP_ASPECT,
)
if err != nil {
return nil, fmt.Errorf("failed to create destination: %v", err)
}
debugLog(DEBUG_INFO, "Created destination with hash: %x", dest.GetHash())
// Enable destination features
dest.AcceptsLinks(true)
dest.EnableRatchets("") // Empty string for default path
dest.SetProofStrategy(destination.PROVE_APP)
debugLog(DEBUG_VERBOSE, "Configured destination features")
r := &Reticulum{ r := &Reticulum{
config: cfg, config: cfg,
transport: t, transport: t,
interfaces: make([]interfaces.Interface, 0), interfaces: make([]interfaces.Interface, 0),
channels: make(map[string]*channel.Channel), channels: make(map[string]*channel.Channel),
buffers: make(map[string]*buffer.Buffer), buffers: make(map[string]*buffer.Buffer),
announceHandlers: make(map[string][]announce.AnnounceHandler), pathRequests: make(map[string]*common.PathRequest),
pathRequests: make(map[string]*common.PathRequest), announceHistory: make(map[string]announceRecord),
announceHistory: make(map[string]announceRecord), identity: identity,
identity: identity, destination: dest,
} }
// Initialize interfaces from config // Initialize interfaces from config
@@ -163,11 +182,7 @@ func (r *Reticulum) handleInterface(iface common.NetworkInterface) {
if len(data) > 0 { if len(data) > 0 {
debugLog(DEBUG_TRACE, "Interface %s: Received packet type 0x%02x", iface.GetName(), data[0]) debugLog(DEBUG_TRACE, "Interface %s: Received packet type 0x%02x", iface.GetName(), data[0])
if data[0] == announce.PACKET_TYPE_ANNOUNCE { r.transport.HandlePacket(data, iface)
r.handleAnnounce(data, iface)
} else {
r.transport.HandlePacket(data, iface)
}
} }
debugLog(5, "Processed %d bytes from interface %s", size, iface.GetName()) debugLog(5, "Processed %d bytes from interface %s", size, iface.GetName())
@@ -386,61 +401,83 @@ func initializeDirectories() error {
func (r *Reticulum) Start() error { func (r *Reticulum) Start() error {
debugLog(2, "Starting Reticulum...") debugLog(2, "Starting Reticulum...")
// Create announce using r.identity // Start transport first
announce, err := announce.NewAnnounce(
r.identity,
[]byte("Reticulum-Go"),
nil,
false,
)
if err != nil {
return fmt.Errorf("failed to create announce: %v", err)
}
// Start transport
if err := r.transport.Start(); err != nil { if err := r.transport.Start(); err != nil {
return fmt.Errorf("failed to start transport: %v", err) return fmt.Errorf("failed to start transport: %v", err)
} }
debugLog(3, "Transport started successfully") debugLog(3, "Transport started successfully")
// Start interfaces // Start interfaces and set up handlers
for _, iface := range r.interfaces { for _, iface := range r.interfaces {
debugLog(2, "Starting interface %s...", iface.GetName()) debugLog(2, "Starting interface %s...", iface.GetName())
if err := iface.Start(); err != nil { if err := iface.Start(); err != nil {
return fmt.Errorf("failed to start interface %s: %v", iface.GetName(), err) if r.config.PanicOnInterfaceErr {
return fmt.Errorf("failed to start interface %s: %v", iface.GetName(), err)
}
debugLog(1, "Error starting interface %s: %v", iface.GetName(), err)
continue
}
if netIface, ok := iface.(common.NetworkInterface); ok {
r.handleInterface(netIface)
} }
r.handleInterface(iface)
debugLog(3, "Interface %s started successfully", iface.GetName()) debugLog(3, "Interface %s started successfully", iface.GetName())
} }
// Wait for interfaces to be ready // Create initial announce packet
announceData := []byte("Reticulum-Go")
announcePacket := transport.CreateAnnouncePacket(
r.identity.Hash(),
r.identity,
announceData,
0,
)
// Wait briefly for interfaces to initialize
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
// Send initial announces // Send initial announces on all enabled interfaces
for _, iface := range r.interfaces { for _, iface := range r.interfaces {
if netIface, ok := iface.(common.NetworkInterface); ok { if netIface, ok := iface.(common.NetworkInterface); ok {
if netIface.IsEnabled() && netIface.IsOnline() { if netIface.IsEnabled() && netIface.IsOnline() {
debugLog(2, "Sending initial announce on interface %s", netIface.GetName()) debugLog(2, "Sending initial announce on interface %s", netIface.GetName())
if err := announce.Propagate([]common.NetworkInterface{netIface}); err != nil { if err := netIface.Send(announcePacket, ""); err != nil {
debugLog(1, "Failed to propagate initial announce: %v", err) debugLog(1, "Failed to send initial announce on interface %s: %v", netIface.GetName(), err)
} }
} }
} }
} }
// Start periodic announces // Start periodic announce goroutine
go func() { go func() {
ticker := time.NewTicker(5 * time.Minute) ticker := time.NewTicker(ANNOUNCE_RATE_TARGET * time.Second)
defer ticker.Stop() defer ticker.Stop()
announceCount := 0
for range ticker.C { for range ticker.C {
debugLog(3, "Starting periodic announce cycle") announceCount++
debugLog(3, "Starting periodic announce cycle #%d", announceCount)
// Create fresh announce packet for each cycle
announcePacket := transport.CreateAnnouncePacket(
r.identity.Hash(),
r.identity,
announceData,
0,
)
for _, iface := range r.interfaces { for _, iface := range r.interfaces {
if netIface, ok := iface.(common.NetworkInterface); ok { if netIface, ok := iface.(common.NetworkInterface); ok {
if netIface.IsEnabled() && netIface.IsOnline() { if netIface.IsEnabled() && netIface.IsOnline() {
debugLog(2, "Sending periodic announce on interface %s", netIface.GetName()) debugLog(2, "Sending periodic announce on interface %s", netIface.GetName())
if err := announce.Propagate([]common.NetworkInterface{netIface}); err != nil { if err := netIface.Send(announcePacket, ""); err != nil {
debugLog(1, "Failed to propagate periodic announce: %v", err) debugLog(1, "Failed to send periodic announce on interface %s: %v", netIface.GetName(), err)
continue
}
// Apply rate limiting after grace period
if announceCount > ANNOUNCE_RATE_GRACE {
time.Sleep(time.Duration(ANNOUNCE_RATE_PENALTY) * time.Second)
} }
} }
} }
@@ -448,6 +485,9 @@ func (r *Reticulum) Start() error {
} }
}() }()
// Start interface monitoring
go r.monitorInterfaces()
debugLog(2, "Reticulum started successfully") debugLog(2, "Reticulum started successfully")
return nil return nil
} }
@@ -481,103 +521,6 @@ func (r *Reticulum) Stop() error {
return nil return nil
} }
func (r *Reticulum) handleAnnounce(data []byte, iface common.NetworkInterface) {
debugLog(DEBUG_INFO, "Received announce packet on interface %s (%d bytes)", iface.GetName(), len(data))
a := &announce.Announce{}
if err := a.HandleAnnounce(data); err != nil {
debugLog(DEBUG_ERROR, "Error handling announce: %v", err)
return
}
// Log announce details
debugLog(DEBUG_ALL, "Announce details:")
debugLog(DEBUG_ALL, " Hash: %x", a.Hash())
// Get fields using packet data
packet := a.GetPacket()
if len(packet) > 2 {
destHash := packet[2:18]
hops := packet[50]
debugLog(DEBUG_ALL, " Destination Hash: %x", destHash)
debugLog(DEBUG_ALL, " Hops: %d", hops)
}
// Check announce history
announceKey := fmt.Sprintf("%x", a.Hash())
r.announceHistoryMu.Lock()
record, exists := r.announceHistory[announceKey]
if exists {
// Check if this interface has already seen this announce
if record.interfaces[iface.GetName()] {
r.announceHistoryMu.Unlock()
debugLog(4, "Duplicate announce from %s, ignoring", iface.GetName())
return
}
// Check rate limiting
timeSinceLastSeen := time.Since(record.lastSeen)
if timeSinceLastSeen < time.Duration(ANNOUNCE_RATE_TARGET)*time.Second {
if record.seenCount > ANNOUNCE_RATE_GRACE {
record.violations++
waitTime := ANNOUNCE_RATE_TARGET + (record.violations * ANNOUNCE_RATE_PENALTY)
r.announceHistoryMu.Unlock()
debugLog(3, "Rate limit exceeded for announce %s, waiting %d seconds", announceKey, waitTime)
return
}
}
record.seenCount++
record.lastSeen = time.Now()
record.interfaces[iface.GetName()] = true
} else {
record = announceRecord{
lastSeen: time.Now(),
seenCount: 1,
interfaces: make(map[string]bool),
}
record.interfaces[iface.GetName()] = true
r.announceHistory[announceKey] = record
}
r.announceHistoryMu.Unlock()
// Add random delay before propagation (0-2 seconds)
delay := time.Duration(rand.Float64() * 2 * float64(time.Second))
time.Sleep(delay)
// Propagate to other interfaces according to RNS rules
for _, otherIface := range r.interfaces {
if otherIface.GetName() == iface.GetName() {
continue
}
srcMode := iface.GetMode()
dstMode := otherIface.GetMode()
// Skip propagation based on interface modes
if srcMode == common.IF_MODE_ACCESS_POINT && dstMode != common.IF_MODE_FULL {
debugLog(4, "Skipping announce propagation from AP to non-full mode interface")
continue
}
if srcMode == common.IF_MODE_ROAMING && dstMode == common.IF_MODE_ACCESS_POINT {
debugLog(4, "Skipping announce propagation from roaming to AP interface")
continue
}
// Check if interface has bandwidth available
if netIface, ok := otherIface.(common.NetworkInterface); ok {
if netIface.GetBandwidthAvailable() {
if err := a.Propagate([]common.NetworkInterface{netIface}); err != nil {
debugLog(1, "Error propagating announce: %v", err)
}
} else {
debugLog(3, "Interface %s has insufficient bandwidth for announce", netIface.GetName())
}
}
}
}
type AnnounceHandler struct { type AnnounceHandler struct {
aspectFilter []string aspectFilter []string
} }
@@ -617,3 +560,7 @@ func (h *AnnounceHandler) ReceivedAnnounce(destHash []byte, id interface{}, appD
func (h *AnnounceHandler) ReceivePathResponses() bool { func (h *AnnounceHandler) ReceivePathResponses() bool {
return true return true
} }
func (r *Reticulum) GetDestination() *destination.Destination {
return r.destination
}

View File

@@ -2,6 +2,7 @@ package announce
import ( import (
"crypto/ed25519" "crypto/ed25519"
"crypto/rand"
"crypto/sha256" "crypto/sha256"
"encoding/binary" "encoding/binary"
"errors" "errors"
@@ -111,23 +112,34 @@ func (a *Announce) Propagate(interfaces []common.NetworkInterface) error {
a.mutex.RLock() a.mutex.RLock()
defer a.mutex.RUnlock() defer a.mutex.RUnlock()
// Use cached packet if available, otherwise create new one log.Printf("[DEBUG-7] Propagating announce across %d interfaces", len(interfaces))
var packet []byte var packet []byte
if a.packet != nil { if a.packet != nil {
log.Printf("[DEBUG-7] Using cached packet (%d bytes)", len(a.packet))
packet = a.packet packet = a.packet
} else { } else {
log.Printf("[DEBUG-7] Creating new packet")
packet = a.CreatePacket() packet = a.CreatePacket()
a.packet = packet a.packet = packet
} }
for _, iface := range interfaces { for _, iface := range interfaces {
if !iface.IsEnabled() || !iface.GetBandwidthAvailable() { if !iface.IsEnabled() {
log.Printf("[DEBUG-7] Skipping disabled interface: %s", iface.GetName())
continue
}
if !iface.GetBandwidthAvailable() {
log.Printf("[DEBUG-7] Skipping interface with insufficient bandwidth: %s", iface.GetName())
continue continue
} }
log.Printf("[DEBUG-7] Sending announce on interface %s", iface.GetName())
if err := iface.Send(packet, ""); err != nil { if err := iface.Send(packet, ""); err != nil {
log.Printf("[DEBUG-7] Failed to send on interface %s: %v", iface.GetName(), err)
return fmt.Errorf("failed to propagate on interface %s: %w", iface.GetName(), err) return fmt.Errorf("failed to propagate on interface %s: %w", iface.GetName(), err)
} }
log.Printf("[DEBUG-7] Successfully sent announce on interface %s", iface.GetName())
} }
return nil return nil
@@ -271,48 +283,70 @@ func CreateHeader(ifacFlag byte, headerType byte, contextFlag byte, propType byt
} }
func (a *Announce) CreatePacket() []byte { func (a *Announce) CreatePacket() []byte {
packet := make([]byte, 0) log.Printf("[DEBUG-7] Creating announce packet")
// Create header according to spec headerByte := byte(
header := CreateHeader( (IFAC_NONE) |
IFAC_NONE, // No interface auth (HEADER_TYPE_1 << 6) |
HEADER_TYPE_1, // One address field (0 << 5) |
0x00, // Context flag unset (PROP_TYPE_BROADCAST << 4) |
PROP_TYPE_BROADCAST, // Broadcast propagation (DEST_TYPE_SINGLE << 2) |
DEST_TYPE_SINGLE, // Single destination PACKET_TYPE_ANNOUNCE,
PACKET_TYPE_ANNOUNCE, // Announce packet type
a.hops, // Current hop count
) )
packet = append(packet, header...)
log.Printf("[DEBUG-7] Created header byte: %02x, hops: %d", headerByte, a.hops)
packet := []byte{headerByte, a.hops}
// Add destination hash (16 bytes) // Add destination hash (16 bytes)
log.Printf("[DEBUG-7] Adding destination hash (16 bytes): %x", a.destinationHash)
packet = append(packet, a.destinationHash...) packet = append(packet, a.destinationHash...)
// Add public key // Split public key into encryption and signing keys (32 bytes each)
packet = append(packet, a.identity.GetPublicKey()...) pubKey := a.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...)
// Add hop count byte // Add name hash (10 bytes)
packet = append(packet, byte(a.hops)) nameHash := a.identity.GetNameHash()
log.Printf("[DEBUG-7] Adding name hash (10 bytes): %x", nameHash)
packet = append(packet, nameHash...)
// Add app data with length prefix // Add random hash (5 random + 5 timestamp bytes = 10 bytes)
appDataLen := make([]byte, 2) randomHash := make([]byte, 5)
binary.BigEndian.PutUint16(appDataLen, uint16(len(a.appData))) rand.Read(randomHash)
packet = append(packet, appDataLen...) timeBytes := make([]byte, 8)
packet = append(packet, a.appData...) 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...)
packet = append(packet, timeBytes[:5]...)
// Add ratchet ID if present // Add ratchet if present (32 bytes)
if a.ratchetID != nil { if a.ratchetID != nil {
log.Printf("[DEBUG-7] Adding ratchet ID (32 bytes): %x", a.ratchetID)
packet = append(packet, a.ratchetID...) packet = append(packet, a.ratchetID...)
} }
// Add signature // Add app data
log.Printf("[DEBUG-7] Adding app data (%d bytes): %x", len(a.appData), a.appData)
packet = append(packet, a.appData...)
// Add signature (64 bytes)
signData := append(a.destinationHash, a.appData...) signData := append(a.destinationHash, a.appData...)
if a.ratchetID != nil { if a.ratchetID != nil {
signData = append(signData, a.ratchetID...) signData = append(signData, a.ratchetID...)
} }
signature := a.identity.Sign(signData) signature := a.identity.Sign(signData)
log.Printf("[DEBUG-7] Adding signature (64 bytes): %x", signature)
packet = append(packet, signature...) packet = append(packet, signature...)
log.Printf("[DEBUG-7] Final packet size: %d bytes", len(packet))
a.packet = packet
return packet return packet
} }
@@ -347,60 +381,38 @@ func NewAnnouncePacket(pubKey []byte, appData []byte, announceID []byte) *Announ
// NewAnnounce creates a new announce packet for a destination // NewAnnounce creates a new announce packet for a destination
func NewAnnounce(identity *identity.Identity, appData []byte, ratchetID []byte, pathResponse bool) (*Announce, error) { 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",
len(appData), ratchetID != nil, pathResponse)
if identity == nil { if identity == nil {
log.Printf("[DEBUG-7] Error: nil identity provided")
return nil, errors.New("identity cannot be nil") return nil, errors.New("identity cannot be nil")
} }
destHash := identity.Hash()
log.Printf("[DEBUG-7] Generated destination hash: %x", destHash)
a := &Announce{ a := &Announce{
identity: identity, identity: identity,
appData: appData, appData: appData,
ratchetID: ratchetID, ratchetID: ratchetID,
pathResponse: pathResponse, pathResponse: pathResponse,
destinationHash: identity.Hash(), destinationHash: destHash,
hops: 0, hops: 0,
mutex: &sync.RWMutex{}, mutex: &sync.RWMutex{},
handlers: make([]AnnounceHandler, 0), handlers: make([]AnnounceHandler, 0),
} }
// Create announce packet log.Printf("[DEBUG-7] Created announce object: destHash=%x, hops=%d",
packet := make([]byte, 0) a.destinationHash, a.hops)
// Add header (2 bytes)
packet = append(packet, PACKET_TYPE_ANNOUNCE)
packet = append(packet, byte(a.hops))
// Add destination hash (16 bytes)
packet = append(packet, a.destinationHash...)
// Add public key (32 bytes)
packet = append(packet, identity.GetPublicKey()...)
// Add hop count (1 byte)
packet = append(packet, byte(a.hops))
// Add app data with length prefix (2 bytes + data)
appDataLen := make([]byte, 2)
binary.BigEndian.PutUint16(appDataLen, uint16(len(appData)))
packet = append(packet, appDataLen...)
packet = append(packet, appData...)
// Add ratchet ID if present
if ratchetID != nil {
packet = append(packet, ratchetID...)
}
// Add signature
signData := append(a.destinationHash, appData...)
if ratchetID != nil {
signData = append(signData, ratchetID...)
}
signature := identity.Sign(signData)
packet = append(packet, signature...)
// Create initial packet
packet := a.CreatePacket()
a.packet = packet a.packet = packet
// Generate hash // Generate hash
a.Hash() hash := a.Hash()
log.Printf("[DEBUG-7] Generated announce hash: %x", hash)
return a, nil return a, nil
} }

View File

@@ -31,6 +31,15 @@ const (
RATCHET_COUNT = 512 // Default number of retained ratchet keys RATCHET_COUNT = 512 // Default number of retained ratchet keys
RATCHET_INTERVAL = 1800 // Minimum interval between ratchet rotations in seconds RATCHET_INTERVAL = 1800 // Minimum interval between ratchet rotations in seconds
// Debug levels
DEBUG_CRITICAL = 1 // Critical errors
DEBUG_ERROR = 2 // Non-critical errors
DEBUG_INFO = 3 // Important information
DEBUG_VERBOSE = 4 // Detailed information
DEBUG_TRACE = 5 // Very detailed tracing
DEBUG_PACKETS = 6 // Packet-level details
DEBUG_ALL = 7 // Everything
) )
type PacketCallback = common.PacketCallback type PacketCallback = common.PacketCallback
@@ -69,15 +78,17 @@ type Destination struct {
mutex sync.RWMutex mutex sync.RWMutex
requestHandlers map[string]*RequestHandler requestHandlers map[string]*RequestHandler
callbacks struct { }
packetReceived common.PacketCallback
proofRequested common.ProofRequestedCallback func debugLog(level int, format string, v ...interface{}) {
linkEstablished common.LinkEstablishedCallback log.Printf("[DEBUG-%d] %s", level, fmt.Sprintf(format, v...))
}
} }
func New(id *identity.Identity, direction byte, destType byte, appName string, aspects ...string) (*Destination, error) { func New(id *identity.Identity, direction byte, destType byte, appName string, aspects ...string) (*Destination, error) {
debugLog(DEBUG_INFO, "Creating new destination: app=%s type=%d direction=%d", appName, destType, direction)
if id == nil { if id == nil {
debugLog(DEBUG_ERROR, "Cannot create destination: identity is nil")
return nil, errors.New("identity cannot be nil") return nil, errors.New("identity cannot be nil")
} }
@@ -96,18 +107,27 @@ func New(id *identity.Identity, direction byte, destType byte, appName string, a
// Generate destination hash // Generate destination hash
d.hashValue = d.calculateHash() d.hashValue = d.calculateHash()
debugLog(DEBUG_VERBOSE, "Created destination with hash: %x", d.hashValue)
return d, nil return d, nil
} }
func (d *Destination) calculateHash() []byte { func (d *Destination) calculateHash() []byte {
debugLog(DEBUG_TRACE, "Calculating hash for destination %s", d.ExpandName())
nameHash := sha256.Sum256([]byte(d.ExpandName())) nameHash := sha256.Sum256([]byte(d.ExpandName()))
identityHash := sha256.Sum256(d.identity.GetPublicKey()) identityHash := sha256.Sum256(d.identity.GetPublicKey())
debugLog(DEBUG_ALL, "Name hash: %x", nameHash)
debugLog(DEBUG_ALL, "Identity hash: %x", identityHash)
combined := append(nameHash[:], identityHash[:]...) combined := append(nameHash[:], identityHash[:]...)
finalHash := sha256.Sum256(combined) finalHash := sha256.Sum256(combined)
return finalHash[:16] // Truncated to 128 bits truncated := finalHash[:16]
debugLog(DEBUG_VERBOSE, "Calculated destination hash: %x", truncated)
return truncated
} }
func (d *Destination) ExpandName() string { func (d *Destination) ExpandName() string {
@@ -131,52 +151,31 @@ func (d *Destination) Announce(appData []byte) error {
} }
// Create announce packet // Create announce packet
packet := make([]byte, 0) packet := make([]byte, 0, 256) // Pre-allocate reasonable size
// Add destination hash // Add packet type and header
packet = append(packet, 0x01) // PACKET_TYPE_ANNOUNCE
packet = append(packet, 0x00) // Initial hop count
// Add destination hash (16 bytes)
packet = append(packet, d.hashValue...) packet = append(packet, d.hashValue...)
log.Printf("[DEBUG-4] Added destination hash %x to announce", d.hashValue[:8]) log.Printf("[DEBUG-4] Added destination hash %x to announce", d.hashValue[:8])
// Add identity public key // Add identity public key (32 bytes)
pubKey := d.identity.GetPublicKey() pubKey := d.identity.GetPublicKey()
packet = append(packet, pubKey...) packet = append(packet, pubKey...)
log.Printf("[DEBUG-4] Added public key %x to announce", pubKey[:8]) log.Printf("[DEBUG-4] Added public key %x to announce", pubKey[:8])
// Add flags byte // Add app data with length prefix
flags := byte(0) appDataLen := make([]byte, 2)
if d.acceptsLinks { binary.BigEndian.PutUint16(appDataLen, uint16(len(appData)))
flags |= 0x01 packet = append(packet, appDataLen...)
} packet = append(packet, appData...)
if d.ratchetsEnabled { log.Printf("[DEBUG-4] Added %d bytes of app data to announce", len(appData))
flags |= 0x02
}
packet = append(packet, flags)
log.Printf("[DEBUG-4] Added flags byte 0x%02x to announce", flags)
// Add proof strategy
packet = append(packet, d.proofStrategy)
log.Printf("[DEBUG-4] Added proof strategy 0x%02x to announce", d.proofStrategy)
// Add app data
if appData != nil {
appDataLen := uint16(len(appData))
lenBytes := make([]byte, 2)
binary.BigEndian.PutUint16(lenBytes, appDataLen)
packet = append(packet, lenBytes...)
packet = append(packet, appData...)
log.Printf("[DEBUG-4] Added %d bytes of app data to announce", appDataLen)
} else {
packet = append(packet, 0x00, 0x00)
log.Printf("[DEBUG-4] Added empty app data to announce")
}
// Add ratchet data if enabled // Add ratchet data if enabled
if d.ratchetsEnabled { if d.ratchetsEnabled {
log.Printf("[DEBUG-4] Adding ratchet data to announce") log.Printf("[DEBUG-4] Adding ratchet data to announce")
intervalBytes := make([]byte, 4)
binary.BigEndian.PutUint32(intervalBytes, uint32(d.ratchetInterval))
packet = append(packet, intervalBytes...)
ratchetKey := d.identity.GetCurrentRatchetKey() ratchetKey := d.identity.GetCurrentRatchetKey()
if ratchetKey == nil { if ratchetKey == nil {
log.Printf("[DEBUG-3] Failed to get current ratchet key") log.Printf("[DEBUG-3] Failed to get current ratchet key")
@@ -186,16 +185,16 @@ func (d *Destination) Announce(appData []byte) error {
log.Printf("[DEBUG-4] Added ratchet key %x to announce", ratchetKey[:8]) log.Printf("[DEBUG-4] Added ratchet key %x to announce", ratchetKey[:8])
} }
// Sign the announce packet // Sign the announce packet (64 bytes)
signature, err := d.Sign(packet) signData := append(d.hashValue, appData...)
if err != nil { if d.ratchetsEnabled {
log.Printf("[DEBUG-3] Failed to sign announce packet: %v", err) signData = append(signData, d.identity.GetCurrentRatchetKey()...)
return fmt.Errorf("failed to sign announce packet: %w", err)
} }
signature := d.identity.Sign(signData)
packet = append(packet, signature...) packet = append(packet, signature...)
log.Printf("[DEBUG-4] Added signature to announce packet (total size: %d bytes)", len(packet)) log.Printf("[DEBUG-4] Added signature to announce packet (total size: %d bytes)", len(packet))
// Send announce packet // Send announce packet through transport
log.Printf("[DEBUG-4] Sending announce packet through transport layer") log.Printf("[DEBUG-4] Sending announce packet through transport layer")
return transport.SendAnnounce(packet) return transport.SendAnnounce(packet)
} }
@@ -288,7 +287,7 @@ func (d *Destination) RegisterRequestHandler(path string, responseGen func(strin
return errors.New("invalid allow mode") return errors.New("invalid allow mode")
} }
if allow == ALLOW_LIST && (allowedList == nil || len(allowedList) == 0) { if allow == ALLOW_LIST && len(allowedList) == 0 {
return errors.New("allowed list required for ALLOW_LIST mode") return errors.New("allowed list required for ALLOW_LIST mode")
} }

View File

@@ -152,10 +152,11 @@ func New() (*Identity, error) {
} }
func (i *Identity) GetPublicKey() []byte { func (i *Identity) GetPublicKey() []byte {
combined := make([]byte, KEYSIZE/8) // Combine encryption and signing public keys in correct order
copy(combined[:KEYSIZE/16], i.publicKey) fullKey := make([]byte, 64)
copy(combined[KEYSIZE/16:], i.verificationKey) copy(fullKey[:32], i.publicKey) // First 32 bytes: X25519 encryption key
return combined copy(fullKey[32:], i.verificationKey) // Last 32 bytes: Ed25519 verification key
return fullKey
} }
func (i *Identity) GetPrivateKey() []byte { func (i *Identity) GetPrivateKey() []byte {
@@ -838,7 +839,23 @@ func (i *Identity) ValidateAnnounce(data []byte, destHash []byte, appData []byte
signatureStart := len(data) - ed25519.SignatureSize signatureStart := len(data) - ed25519.SignatureSize
signature := data[signatureStart:] signature := data[signatureStart:]
signedData := append(destHash, appData...) signedData := append(destHash, i.GetPublicKey()...)
signedData = append(signedData, appData...)
return ed25519.Verify(i.verificationKey, signedData, signature) return ed25519.Verify(i.verificationKey, signedData, signature)
} }
// GetNameHash returns a 10-byte hash derived from the identity's public key
func (i *Identity) GetNameHash() []byte {
if i == nil || i.publicKey == nil {
return nil
}
// Generate hash from combined public key
h := sha256.New()
h.Write(i.GetPublicKey())
fullHash := h.Sum(nil)
// Return first 10 bytes (NAME_HASH_LENGTH/8)
return fullHash[:NAME_HASH_LENGTH/8]
}

View File

@@ -4,6 +4,7 @@ import (
"crypto/sha256" "crypto/sha256"
"errors" "errors"
"fmt" "fmt"
"log"
"time" "time"
"github.com/Sudo-Ivan/reticulum-go/pkg/identity" "github.com/Sudo-Ivan/reticulum-go/pkg/identity"
@@ -110,24 +111,29 @@ func (p *Packet) Pack() error {
return nil return nil
} }
flags := (p.HeaderType << 6) | (p.ContextFlag << 5) | log.Printf("[DEBUG-6] Packing packet: type=%d, header=%d", p.PacketType, p.HeaderType)
(p.TransportType << 4) | (p.DestinationType << 2) | p.PacketType
header := make([]byte, 0) // Create header byte
header = append(header, flags) flags := byte(p.HeaderType<<6) | byte(p.ContextFlag<<5) |
header = append(header, p.Hops) byte(p.TransportType<<4) | byte(p.DestinationType<<2) | byte(p.PacketType)
if p.HeaderType == HeaderType2 && p.TransportID != nil { header := []byte{flags, p.Hops}
log.Printf("[DEBUG-5] Created packet header: flags=%08b, hops=%d", flags, p.Hops)
if p.HeaderType == HeaderType2 {
if p.TransportID == nil {
return errors.New("transport ID required for header type 2")
}
header = append(header, p.TransportID...) header = append(header, p.TransportID...)
header = append(header, p.DestinationHash...) log.Printf("[DEBUG-7] Added transport ID to header: %x", p.TransportID)
} else if p.HeaderType == HeaderType1 {
header = append(header, p.DestinationHash...)
} else {
return errors.New("invalid header configuration")
} }
header = append(header, p.DestinationHash...)
header = append(header, p.Context) header = append(header, p.Context)
log.Printf("[DEBUG-6] Final header length: %d bytes", len(header))
p.Raw = append(header, p.Data...) p.Raw = append(header, p.Data...)
log.Printf("[DEBUG-5] Final packet size: %d bytes", len(p.Raw))
if len(p.Raw) > MTU { if len(p.Raw) > MTU {
return errors.New("packet size exceeds MTU") return errors.New("packet size exceeds MTU")
@@ -135,6 +141,7 @@ func (p *Packet) Pack() error {
p.Packed = true p.Packed = true
p.updateHash() p.updateHash()
log.Printf("[DEBUG-7] Packet hash: %x", p.PacketHash)
return nil return nil
} }
@@ -210,19 +217,25 @@ func (p *Packet) Serialize() ([]byte, error) {
} }
func NewAnnouncePacket(destHash []byte, identity *identity.Identity, appData []byte, transportID []byte) (*Packet, error) { func NewAnnouncePacket(destHash []byte, identity *identity.Identity, appData []byte, transportID []byte) (*Packet, error) {
log.Printf("[DEBUG-7] Creating new announce packet: destHash=%x, appData=%s", destHash, string(appData))
// Create combined public key // Create combined public key
pubKey := identity.GetPublicKey() pubKey := identity.GetPublicKey()
log.Printf("[DEBUG-6] Using public key: %x", pubKey)
// Create signed data // Create signed data
signedData := append(destHash, pubKey...) signedData := append(destHash, pubKey...)
signedData = append(signedData, appData...) signedData = append(signedData, appData...)
log.Printf("[DEBUG-5] Created signed data (%d bytes)", len(signedData))
// Sign the data // Sign the data
signature := identity.Sign(signedData) signature := identity.Sign(signedData)
log.Printf("[DEBUG-6] Generated signature: %x", signature)
// Combine all data // Combine all data
data := append(pubKey, appData...) data := append(pubKey, appData...)
data = append(data, signature...) data = append(data, signature...)
log.Printf("[DEBUG-5] Combined packet data (%d bytes)", len(data))
p := &Packet{ p := &Packet{
HeaderType: HeaderType2, HeaderType: HeaderType2,
@@ -232,5 +245,6 @@ func NewAnnouncePacket(destHash []byte, identity *identity.Identity, appData []b
Data: data, Data: data,
} }
log.Printf("[DEBUG-4] Created announce packet: type=%d, header=%d", p.PacketType, p.HeaderType)
return p, nil return p, nil
} }

View File

@@ -70,6 +70,29 @@ const (
MAX_HOPS = 128 // Default m value for announce propagation MAX_HOPS = 128 // Default m value for announce propagation
PROPAGATION_RATE = 0.02 // 2% bandwidth cap for announces PROPAGATION_RATE = 0.02 // 2% bandwidth cap for announces
// Announce packet types
PACKET_TYPE_ANNOUNCE = 0x01
PACKET_TYPE_LINK = 0x02
// Announce flags
ANNOUNCE_NONE = 0x00
ANNOUNCE_PATH = 0x01
ANNOUNCE_IDENTITY = 0x02
// Header types
HEADER_TYPE_1 = 0x00 // One address field
HEADER_TYPE_2 = 0x01 // Two address fields
// Propagation types
PROP_TYPE_BROADCAST = 0x00
PROP_TYPE_TRANSPORT = 0x01
// Destination types
DEST_TYPE_SINGLE = 0x00
DEST_TYPE_GROUP = 0x01
DEST_TYPE_PLAIN = 0x02
DEST_TYPE_LINK = 0x03
) )
type PathInfo struct { type PathInfo struct {
@@ -590,27 +613,41 @@ func SendAnnounce(packet []byte) error {
} }
func (t *Transport) HandlePacket(data []byte, iface common.NetworkInterface) { func (t *Transport) HandlePacket(data []byte, iface common.NetworkInterface) {
if len(data) < 1 { if len(data) < 2 {
log.Printf("[DEBUG-3] Dropping packet: insufficient length (%d bytes)", len(data))
return return
} }
packetType := data[0] headerByte := data[0]
log.Printf("[DEBUG-4] Transport handling packet type 0x%02x from interface %s, size: %d bytes", packetType := headerByte & 0x03
packetType, iface.GetName(), len(data)) headerType := (headerByte & 0x40) >> 6
contextFlag := (headerByte & 0x20) >> 5
propType := (headerByte & 0x10) >> 4
destType := (headerByte & 0x0C) >> 2
log.Printf("[DEBUG-4] Packet received - Type: 0x%02x, Header: %d, Context: %d, PropType: %d, DestType: %d, Size: %d bytes",
packetType, headerType, contextFlag, propType, destType, len(data))
log.Printf("[DEBUG-5] Interface: %s, Raw header: 0x%02x", iface.GetName(), headerByte)
// Update interface stats before processing
if tcpIface, ok := iface.(*interfaces.TCPClientInterface); ok { if tcpIface, ok := iface.(*interfaces.TCPClientInterface); ok {
tcpIface.UpdateStats(uint64(len(data)), true) tcpIface.UpdateStats(uint64(len(data)), true)
log.Printf("[DEBUG-6] Updated TCP interface stats - RX bytes: %d", len(data))
} }
switch packetType { switch packetType {
case announce.PACKET_TYPE_ANNOUNCE: case PACKET_TYPE_ANNOUNCE:
t.handleAnnouncePacket(data[1:], iface) log.Printf("[DEBUG-4] Processing announce packet")
case announce.PACKET_TYPE_LINK: if err := t.handleAnnouncePacket(data, iface); err != nil {
log.Printf("[DEBUG-3] Announce handling failed: %v", err)
}
case PACKET_TYPE_LINK:
log.Printf("[DEBUG-4] Processing link packet")
t.handleLinkPacket(data[1:], iface) t.handleLinkPacket(data[1:], iface)
case 0x03: // Path response case 0x03:
log.Printf("[DEBUG-4] Processing path response")
t.handlePathResponse(data[1:], iface) t.handlePathResponse(data[1:], iface)
case 0x04: // Transport packet case 0x00:
log.Printf("[DEBUG-4] Processing transport packet")
t.handleTransportPacket(data[1:], iface) t.handleTransportPacket(data[1:], iface)
default: default:
log.Printf("[DEBUG-3] Unknown packet type 0x%02x from %s", packetType, iface.GetName()) log.Printf("[DEBUG-3] Unknown packet type 0x%02x from %s", packetType, iface.GetName())
@@ -618,18 +655,66 @@ func (t *Transport) HandlePacket(data []byte, iface common.NetworkInterface) {
} }
func (t *Transport) handleAnnouncePacket(data []byte, iface common.NetworkInterface) error { func (t *Transport) handleAnnouncePacket(data []byte, iface common.NetworkInterface) error {
// Validate minimum packet size (1 byte hop count + 32 bytes dest hash + 16 bytes min identity + 1 byte min app data) if len(data) < 2 {
if len(data) < 50 { return fmt.Errorf("packet too small for header")
return fmt.Errorf("announce packet too small: %d bytes", len(data)) }
// Parse header bytes according to RNS spec
headerByte1 := data[0]
hopCount := data[1]
// Extract header fields
ifacFlag := (headerByte1 & 0x80) >> 7 // IFAC flag in highest bit
headerType := (headerByte1 & 0x40) >> 6 // Header type in next bit
contextFlag := (headerByte1 & 0x20) >> 5 // Context flag
propType := (headerByte1 & 0x10) >> 4 // Propagation type
destType := (headerByte1 & 0x0C) >> 2 // Destination type in next 2 bits
packetType := headerByte1 & 0x03 // Packet type in lowest 2 bits
log.Printf("[DEBUG-5] Announce header: IFAC=%d, headerType=%d, context=%d, propType=%d, destType=%d, packetType=%d",
ifacFlag, headerType, contextFlag, propType, destType, packetType)
// Skip IFAC code if present
startIdx := 2
if ifacFlag == 1 {
startIdx += 1 // For now assume 1 byte IFAC code
}
// Calculate address field size
addrSize := 16
if headerType == 1 {
addrSize = 32 // Two address fields
}
// Validate minimum packet size
minSize := startIdx + addrSize + 1 // Header + addresses + context
if len(data) < minSize {
return fmt.Errorf("packet too small: %d bytes", len(data))
} }
// Extract fields // Extract fields
hopCount := data[0] addresses := data[startIdx : startIdx+addrSize]
destHash := data[1:33] context := data[startIdx+addrSize]
identityBytes := data[33:49] payload := data[startIdx+addrSize+1:]
appData := data[49:]
// Check for duplicate announces log.Printf("[DEBUG-6] Addresses: %x", addresses)
log.Printf("[DEBUG-7] Context: %02x, Payload length: %d", context, len(payload))
// Process payload (should contain pubkey + app data)
if len(payload) < 32 { // Minimum size for pubkey
return fmt.Errorf("payload too small for announce")
}
pubKey := payload[:32]
appData := payload[32:]
// Create identity from public key
id := identity.FromPublicKey(pubKey)
if id == nil {
return fmt.Errorf("invalid identity")
}
// Generate announce hash to check for duplicates
announceHash := sha256.Sum256(data) announceHash := sha256.Sum256(data)
hashStr := string(announceHash[:]) hashStr := string(announceHash[:])
@@ -642,12 +727,6 @@ func (t *Transport) handleAnnouncePacket(data []byte, iface common.NetworkInterf
t.seenAnnounces[hashStr] = true t.seenAnnounces[hashStr] = true
t.mutex.Unlock() t.mutex.Unlock()
// Validate announce signature and store destination
id := identity.FromPublicKey(identityBytes)
if id == nil || !id.ValidateAnnounce(data, destHash, appData) {
return fmt.Errorf("invalid announce signature")
}
// Don't forward if max hops reached // Don't forward if max hops reached
if hopCount >= MAX_HOPS { if hopCount >= MAX_HOPS {
log.Printf("[DEBUG-7] Announce exceeded max hops: %d", hopCount) log.Printf("[DEBUG-7] Announce exceeded max hops: %d", hopCount)
@@ -660,43 +739,36 @@ func (t *Transport) handleAnnouncePacket(data []byte, iface common.NetworkInterf
// Check bandwidth allocation for announces // Check bandwidth allocation for announces
if !t.announceRate.Allow() { if !t.announceRate.Allow() {
log.Printf("[DEBUG-7] Announce rate limit exceeded, dropping") log.Printf("[DEBUG-7] Announce rate limit exceeded, queuing...")
return nil return nil
} }
// Increment hop count for forwarding // Increment hop count
forwardData := make([]byte, len(data)) data[1]++
copy(forwardData, data)
forwardData[0] = hopCount + 1
// Forward to other interfaces // Broadcast to all other interfaces
var lastErr error var lastErr error
for name, outIface := range t.interfaces { for name, outIface := range t.interfaces {
if outIface == iface || !outIface.IsEnabled() { if outIface == iface || !outIface.IsEnabled() {
continue continue
} }
// Check interface mode restrictions
// if outIface.GetMode() == interfaces.ModeAccessPoint {
// log.Printf("[DEBUG-7] Blocking announce broadcast on %s due to AP mode", name)
// continue
// }
log.Printf("[DEBUG-7] Forwarding announce on interface %s", name) log.Printf("[DEBUG-7] Forwarding announce on interface %s", name)
if err := outIface.Send(forwardData, ""); err != nil { if err := outIface.Send(data, ""); err != nil {
log.Printf("[DEBUG-3] Failed to forward announce on %s: %v", name, err) log.Printf("[DEBUG-7] Failed to forward announce on %s: %v", name, err)
lastErr = err lastErr = err
} }
} }
// Notify announce handlers // Notify handlers with first address as destination hash
t.notifyAnnounceHandlers(destHash, identityBytes, appData) t.notifyAnnounceHandlers(addresses[:16], id, appData)
return lastErr return lastErr
} }
func (t *Transport) handleLinkPacket(data []byte, iface common.NetworkInterface) { func (t *Transport) handleLinkPacket(data []byte, iface common.NetworkInterface) {
if len(data) < 40 { // 32 bytes dest + 8 bytes timestamp minimum if len(data) < 40 {
log.Printf("[DEBUG-3] Dropping link packet: insufficient length (%d bytes)", len(data))
return return
} }
@@ -704,28 +776,28 @@ func (t *Transport) handleLinkPacket(data []byte, iface common.NetworkInterface)
timestamp := binary.BigEndian.Uint64(data[32:40]) timestamp := binary.BigEndian.Uint64(data[32:40])
payload := data[40:] payload := data[40:]
// Check if we're the destination log.Printf("[DEBUG-5] Link packet - Destination: %x, Timestamp: %d, Payload: %d bytes",
dest, timestamp, len(payload))
if t.HasPath(dest) { if t.HasPath(dest) {
nextHop := t.NextHop(dest) nextHop := t.NextHop(dest)
nextIfaceName := t.NextHopInterface(dest) nextIfaceName := t.NextHopInterface(dest)
log.Printf("[DEBUG-6] Found path - Next hop: %x, Interface: %s", nextHop, nextIfaceName)
// Only forward if received on different interface
if nextIfaceName != iface.GetName() { if nextIfaceName != iface.GetName() {
if nextIface, ok := t.interfaces[nextIfaceName]; ok { if nextIface, ok := t.interfaces[nextIfaceName]; ok {
log.Printf("[DEBUG-7] Forwarding link packet to %s", nextIfaceName)
nextIface.Send(data, string(nextHop)) nextIface.Send(data, string(nextHop))
} }
} }
} }
// Update timing information
if link := t.findLink(dest); link != nil { if link := t.findLink(dest); link != nil {
log.Printf("[DEBUG-6] Updating link timing - Last inbound: %v", time.Unix(int64(timestamp), 0))
link.lastInbound = time.Unix(int64(timestamp), 0) link.lastInbound = time.Unix(int64(timestamp), 0)
if link.packetCb != nil { if link.packetCb != nil {
// Create a packet object to pass to callback log.Printf("[DEBUG-7] Executing packet callback with %d bytes", len(payload))
p := &packet.Packet{ p := &packet.Packet{Data: payload}
Data: payload,
// Add other necessary packet fields
}
link.packetCb(payload, p) link.packetCb(payload, p)
} }
} }
@@ -769,24 +841,33 @@ func (t *Transport) SendPacket(p *packet.Packet) error {
t.mutex.RLock() t.mutex.RLock()
defer t.mutex.RUnlock() defer t.mutex.RUnlock()
// Serialize packet log.Printf("[DEBUG-4] Sending packet - Type: 0x%02x, Header: %d", p.PacketType, p.HeaderType)
data, err := p.Serialize() data, err := p.Serialize()
if err != nil { if err != nil {
log.Printf("[DEBUG-3] Packet serialization failed: %v", err)
return fmt.Errorf("failed to serialize packet: %w", err) return fmt.Errorf("failed to serialize packet: %w", err)
} }
log.Printf("[DEBUG-5] Serialized packet size: %d bytes", len(data))
// Find appropriate interface
destHash := p.Addresses[:packet.AddressSize] destHash := p.Addresses[:packet.AddressSize]
log.Printf("[DEBUG-6] Destination hash: %x", destHash)
path, exists := t.paths[string(destHash)] path, exists := t.paths[string(destHash)]
if !exists { if !exists {
log.Printf("[DEBUG-3] No path found for destination %x", destHash)
return errors.New("no path to destination") return errors.New("no path to destination")
} }
// Send through interface log.Printf("[DEBUG-5] Using path - Interface: %s, Next hop: %x, Hops: %d",
path.Interface.GetName(), path.NextHop, path.HopCount)
if err := path.Interface.Send(data, ""); err != nil { if err := path.Interface.Send(data, ""); err != nil {
log.Printf("[DEBUG-3] Failed to send packet: %v", err)
return fmt.Errorf("failed to send packet: %w", err) return fmt.Errorf("failed to send packet: %w", err)
} }
log.Printf("[DEBUG-7] Packet sent successfully")
return nil return nil
} }
@@ -953,3 +1034,40 @@ func (l *Link) GetStatus() int {
defer l.mutex.RUnlock() defer l.mutex.RUnlock()
return l.status 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
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)
)
// Add header and hops
packet = append(packet, headerByte, hops)
// Add destination hash (16 bytes)
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...)
signature := identity.Sign(signData)
packet = append(packet, signature...)
return packet
}