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"
"fmt"
"log"
"math/rand"
"os"
"os/signal"
"sync"
@@ -16,6 +15,7 @@ import (
"github.com/Sudo-Ivan/reticulum-go/pkg/buffer"
"github.com/Sudo-Ivan/reticulum-go/pkg/channel"
"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/interfaces"
"github.com/Sudo-Ivan/reticulum-go/pkg/packet"
@@ -44,6 +44,8 @@ const (
DEBUG_TRACE = 5 // Very detailed tracing
DEBUG_PACKETS = 6 // Packet-level details
DEBUG_ALL = 7 // Everything including identity operations
APP_NAME = "Go Client"
APP_ASPECT = "node"
)
type Reticulum struct {
@@ -52,18 +54,15 @@ type Reticulum struct {
interfaces []interfaces.Interface
channels map[string]*channel.Channel
buffers map[string]*buffer.Buffer
announceHandlers map[string][]announce.AnnounceHandler
pathRequests map[string]*common.PathRequest
announceHistory map[string]announceRecord
announceHistoryMu sync.RWMutex
identity *identity.Identity
destination *destination.Destination
}
type announceRecord struct {
lastSeen time.Time
seenCount int
violations int
interfaces map[string]bool
// All fields were unused, so entire struct can be removed
}
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())
// 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{
config: cfg,
transport: t,
interfaces: make([]interfaces.Interface, 0),
channels: make(map[string]*channel.Channel),
buffers: make(map[string]*buffer.Buffer),
announceHandlers: make(map[string][]announce.AnnounceHandler),
pathRequests: make(map[string]*common.PathRequest),
announceHistory: make(map[string]announceRecord),
identity: identity,
config: cfg,
transport: t,
interfaces: make([]interfaces.Interface, 0),
channels: make(map[string]*channel.Channel),
buffers: make(map[string]*buffer.Buffer),
pathRequests: make(map[string]*common.PathRequest),
announceHistory: make(map[string]announceRecord),
identity: identity,
destination: dest,
}
// Initialize interfaces from config
@@ -163,11 +182,7 @@ func (r *Reticulum) handleInterface(iface common.NetworkInterface) {
if len(data) > 0 {
debugLog(DEBUG_TRACE, "Interface %s: Received packet type 0x%02x", iface.GetName(), data[0])
if data[0] == announce.PACKET_TYPE_ANNOUNCE {
r.handleAnnounce(data, iface)
} else {
r.transport.HandlePacket(data, iface)
}
r.transport.HandlePacket(data, iface)
}
debugLog(5, "Processed %d bytes from interface %s", size, iface.GetName())
@@ -386,61 +401,83 @@ func initializeDirectories() error {
func (r *Reticulum) Start() error {
debugLog(2, "Starting Reticulum...")
// Create announce using r.identity
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
// Start transport first
if err := r.transport.Start(); err != nil {
return fmt.Errorf("failed to start transport: %v", err)
}
debugLog(3, "Transport started successfully")
// Start interfaces
// Start interfaces and set up handlers
for _, iface := range r.interfaces {
debugLog(2, "Starting interface %s...", iface.GetName())
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())
}
// 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)
// Send initial announces
// Send initial announces on all enabled interfaces
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 := announce.Propagate([]common.NetworkInterface{netIface}); err != nil {
debugLog(1, "Failed to propagate initial announce: %v", err)
if err := netIface.Send(announcePacket, ""); err != nil {
debugLog(1, "Failed to send initial announce on interface %s: %v", netIface.GetName(), err)
}
}
}
}
// Start periodic announces
// Start periodic announce goroutine
go func() {
ticker := time.NewTicker(5 * time.Minute)
ticker := time.NewTicker(ANNOUNCE_RATE_TARGET * time.Second)
defer ticker.Stop()
announceCount := 0
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 {
if netIface, ok := iface.(common.NetworkInterface); ok {
if netIface.IsEnabled() && netIface.IsOnline() {
debugLog(2, "Sending periodic announce on interface %s", netIface.GetName())
if err := announce.Propagate([]common.NetworkInterface{netIface}); err != nil {
debugLog(1, "Failed to propagate periodic announce: %v", err)
if err := netIface.Send(announcePacket, ""); err != nil {
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")
return nil
}
@@ -481,103 +521,6 @@ func (r *Reticulum) Stop() error {
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 {
aspectFilter []string
}
@@ -617,3 +560,7 @@ func (h *AnnounceHandler) ReceivedAnnounce(destHash []byte, id interface{}, appD
func (h *AnnounceHandler) ReceivePathResponses() bool {
return true
}
func (r *Reticulum) GetDestination() *destination.Destination {
return r.destination
}

View File

@@ -2,6 +2,7 @@ package announce
import (
"crypto/ed25519"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"errors"
@@ -111,23 +112,34 @@ func (a *Announce) Propagate(interfaces []common.NetworkInterface) error {
a.mutex.RLock()
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
if a.packet != nil {
log.Printf("[DEBUG-7] Using cached packet (%d bytes)", len(a.packet))
packet = a.packet
} else {
log.Printf("[DEBUG-7] Creating new packet")
packet = a.CreatePacket()
a.packet = packet
}
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
}
log.Printf("[DEBUG-7] Sending announce on interface %s", iface.GetName())
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)
}
log.Printf("[DEBUG-7] Successfully sent announce on interface %s", iface.GetName())
}
return nil
@@ -271,48 +283,70 @@ func CreateHeader(ifacFlag byte, headerType byte, contextFlag byte, propType byt
}
func (a *Announce) CreatePacket() []byte {
packet := make([]byte, 0)
// Create header according to spec
header := CreateHeader(
IFAC_NONE, // No interface auth
HEADER_TYPE_1, // One address field
0x00, // Context flag unset
PROP_TYPE_BROADCAST, // Broadcast propagation
DEST_TYPE_SINGLE, // Single destination
PACKET_TYPE_ANNOUNCE, // Announce packet type
a.hops, // Current hop count
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,
)
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)
log.Printf("[DEBUG-7] Adding destination hash (16 bytes): %x", a.destinationHash)
packet = append(packet, a.destinationHash...)
// Add public key
packet = append(packet, a.identity.GetPublicKey()...)
// Split public key into encryption and signing keys (32 bytes each)
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
packet = append(packet, byte(a.hops))
// 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 app data with length prefix
appDataLen := make([]byte, 2)
binary.BigEndian.PutUint16(appDataLen, uint16(len(a.appData)))
packet = append(packet, appDataLen...)
packet = append(packet, a.appData...)
// Add random hash (5 random + 5 timestamp bytes = 10 bytes)
randomHash := make([]byte, 5)
rand.Read(randomHash)
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...)
packet = append(packet, timeBytes[:5]...)
// Add ratchet ID if present
// Add ratchet if present (32 bytes)
if a.ratchetID != nil {
log.Printf("[DEBUG-7] Adding ratchet ID (32 bytes): %x", 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...)
if a.ratchetID != nil {
signData = append(signData, a.ratchetID...)
}
signature := a.identity.Sign(signData)
log.Printf("[DEBUG-7] Adding signature (64 bytes): %x", signature)
packet = append(packet, signature...)
log.Printf("[DEBUG-7] Final packet size: %d bytes", len(packet))
a.packet = 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
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 {
log.Printf("[DEBUG-7] Error: nil identity provided")
return nil, errors.New("identity cannot be nil")
}
destHash := identity.Hash()
log.Printf("[DEBUG-7] Generated destination hash: %x", destHash)
a := &Announce{
identity: identity,
appData: appData,
ratchetID: ratchetID,
pathResponse: pathResponse,
destinationHash: identity.Hash(),
destinationHash: destHash,
hops: 0,
mutex: &sync.RWMutex{},
handlers: make([]AnnounceHandler, 0),
}
// Create announce packet
packet := make([]byte, 0)
// 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...)
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
a.Hash()
hash := a.Hash()
log.Printf("[DEBUG-7] Generated announce hash: %x", hash)
return a, nil
}

View File

@@ -31,6 +31,15 @@ const (
RATCHET_COUNT = 512 // Default number of retained ratchet keys
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
@@ -69,15 +78,17 @@ type Destination struct {
mutex sync.RWMutex
requestHandlers map[string]*RequestHandler
callbacks struct {
packetReceived common.PacketCallback
proofRequested common.ProofRequestedCallback
linkEstablished common.LinkEstablishedCallback
}
}
func debugLog(level int, format string, v ...interface{}) {
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) {
debugLog(DEBUG_INFO, "Creating new destination: app=%s type=%d direction=%d", appName, destType, direction)
if id == nil {
debugLog(DEBUG_ERROR, "Cannot create destination: identity is 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
d.hashValue = d.calculateHash()
debugLog(DEBUG_VERBOSE, "Created destination with hash: %x", d.hashValue)
return d, nil
}
func (d *Destination) calculateHash() []byte {
debugLog(DEBUG_TRACE, "Calculating hash for destination %s", d.ExpandName())
nameHash := sha256.Sum256([]byte(d.ExpandName()))
identityHash := sha256.Sum256(d.identity.GetPublicKey())
debugLog(DEBUG_ALL, "Name hash: %x", nameHash)
debugLog(DEBUG_ALL, "Identity hash: %x", identityHash)
combined := append(nameHash[:], identityHash[:]...)
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 {
@@ -131,52 +151,31 @@ func (d *Destination) Announce(appData []byte) error {
}
// 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...)
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()
packet = append(packet, pubKey...)
log.Printf("[DEBUG-4] Added public key %x to announce", pubKey[:8])
// Add flags byte
flags := byte(0)
if d.acceptsLinks {
flags |= 0x01
}
if d.ratchetsEnabled {
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 app data with length prefix
appDataLen := make([]byte, 2)
binary.BigEndian.PutUint16(appDataLen, uint16(len(appData)))
packet = append(packet, appDataLen...)
packet = append(packet, appData...)
log.Printf("[DEBUG-4] Added %d bytes of app data to announce", len(appData))
// Add ratchet data if enabled
if d.ratchetsEnabled {
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()
if ratchetKey == nil {
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])
}
// Sign the announce packet
signature, err := d.Sign(packet)
if err != nil {
log.Printf("[DEBUG-3] Failed to sign announce packet: %v", err)
return fmt.Errorf("failed to sign announce packet: %w", err)
// Sign the announce packet (64 bytes)
signData := append(d.hashValue, appData...)
if d.ratchetsEnabled {
signData = append(signData, d.identity.GetCurrentRatchetKey()...)
}
signature := d.identity.Sign(signData)
packet = append(packet, signature...)
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")
return transport.SendAnnounce(packet)
}
@@ -288,7 +287,7 @@ func (d *Destination) RegisterRequestHandler(path string, responseGen func(strin
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")
}

View File

@@ -152,10 +152,11 @@ func New() (*Identity, error) {
}
func (i *Identity) GetPublicKey() []byte {
combined := make([]byte, KEYSIZE/8)
copy(combined[:KEYSIZE/16], i.publicKey)
copy(combined[KEYSIZE/16:], i.verificationKey)
return combined
// Combine encryption and signing public keys in correct order
fullKey := make([]byte, 64)
copy(fullKey[:32], i.publicKey) // First 32 bytes: X25519 encryption key
copy(fullKey[32:], i.verificationKey) // Last 32 bytes: Ed25519 verification key
return fullKey
}
func (i *Identity) GetPrivateKey() []byte {
@@ -838,7 +839,23 @@ func (i *Identity) ValidateAnnounce(data []byte, destHash []byte, appData []byte
signatureStart := len(data) - ed25519.SignatureSize
signature := data[signatureStart:]
signedData := append(destHash, appData...)
signedData := append(destHash, i.GetPublicKey()...)
signedData = append(signedData, appData...)
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"
"errors"
"fmt"
"log"
"time"
"github.com/Sudo-Ivan/reticulum-go/pkg/identity"
@@ -110,24 +111,29 @@ func (p *Packet) Pack() error {
return nil
}
flags := (p.HeaderType << 6) | (p.ContextFlag << 5) |
(p.TransportType << 4) | (p.DestinationType << 2) | p.PacketType
log.Printf("[DEBUG-6] Packing packet: type=%d, header=%d", p.PacketType, p.HeaderType)
header := make([]byte, 0)
header = append(header, flags)
header = append(header, p.Hops)
// Create header byte
flags := byte(p.HeaderType<<6) | byte(p.ContextFlag<<5) |
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.DestinationHash...)
} else if p.HeaderType == HeaderType1 {
header = append(header, p.DestinationHash...)
} else {
return errors.New("invalid header configuration")
log.Printf("[DEBUG-7] Added transport ID to header: %x", p.TransportID)
}
header = append(header, p.DestinationHash...)
header = append(header, p.Context)
log.Printf("[DEBUG-6] Final header length: %d bytes", len(header))
p.Raw = append(header, p.Data...)
log.Printf("[DEBUG-5] Final packet size: %d bytes", len(p.Raw))
if len(p.Raw) > MTU {
return errors.New("packet size exceeds MTU")
@@ -135,6 +141,7 @@ func (p *Packet) Pack() error {
p.Packed = true
p.updateHash()
log.Printf("[DEBUG-7] Packet hash: %x", p.PacketHash)
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) {
log.Printf("[DEBUG-7] Creating new announce packet: destHash=%x, appData=%s", destHash, string(appData))
// Create combined public key
pubKey := identity.GetPublicKey()
log.Printf("[DEBUG-6] Using public key: %x", pubKey)
// Create signed data
signedData := append(destHash, pubKey...)
signedData = append(signedData, appData...)
log.Printf("[DEBUG-5] Created signed data (%d bytes)", len(signedData))
// Sign the data
signature := identity.Sign(signedData)
log.Printf("[DEBUG-6] Generated signature: %x", signature)
// Combine all data
data := append(pubKey, appData...)
data = append(data, signature...)
log.Printf("[DEBUG-5] Combined packet data (%d bytes)", len(data))
p := &Packet{
HeaderType: HeaderType2,
@@ -232,5 +245,6 @@ func NewAnnouncePacket(destHash []byte, identity *identity.Identity, appData []b
Data: data,
}
log.Printf("[DEBUG-4] Created announce packet: type=%d, header=%d", p.PacketType, p.HeaderType)
return p, nil
}

View File

@@ -70,6 +70,29 @@ const (
MAX_HOPS = 128 // Default m value for announce propagation
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 {
@@ -590,27 +613,41 @@ func SendAnnounce(packet []byte) error {
}
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
}
packetType := data[0]
log.Printf("[DEBUG-4] Transport handling packet type 0x%02x from interface %s, size: %d bytes",
packetType, iface.GetName(), len(data))
headerByte := data[0]
packetType := headerByte & 0x03
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 {
tcpIface.UpdateStats(uint64(len(data)), true)
log.Printf("[DEBUG-6] Updated TCP interface stats - RX bytes: %d", len(data))
}
switch packetType {
case announce.PACKET_TYPE_ANNOUNCE:
t.handleAnnouncePacket(data[1:], iface)
case announce.PACKET_TYPE_LINK:
case PACKET_TYPE_ANNOUNCE:
log.Printf("[DEBUG-4] Processing announce packet")
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)
case 0x03: // Path response
case 0x03:
log.Printf("[DEBUG-4] Processing path response")
t.handlePathResponse(data[1:], iface)
case 0x04: // Transport packet
case 0x00:
log.Printf("[DEBUG-4] Processing transport packet")
t.handleTransportPacket(data[1:], iface)
default:
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 {
// Validate minimum packet size (1 byte hop count + 32 bytes dest hash + 16 bytes min identity + 1 byte min app data)
if len(data) < 50 {
return fmt.Errorf("announce packet too small: %d bytes", len(data))
if len(data) < 2 {
return fmt.Errorf("packet too small for header")
}
// 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
hopCount := data[0]
destHash := data[1:33]
identityBytes := data[33:49]
appData := data[49:]
addresses := data[startIdx : startIdx+addrSize]
context := data[startIdx+addrSize]
payload := data[startIdx+addrSize+1:]
// 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)
hashStr := string(announceHash[:])
@@ -642,12 +727,6 @@ func (t *Transport) handleAnnouncePacket(data []byte, iface common.NetworkInterf
t.seenAnnounces[hashStr] = true
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
if hopCount >= MAX_HOPS {
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
if !t.announceRate.Allow() {
log.Printf("[DEBUG-7] Announce rate limit exceeded, dropping")
log.Printf("[DEBUG-7] Announce rate limit exceeded, queuing...")
return nil
}
// Increment hop count for forwarding
forwardData := make([]byte, len(data))
copy(forwardData, data)
forwardData[0] = hopCount + 1
// Increment hop count
data[1]++
// Forward to other interfaces
// Broadcast to all other interfaces
var lastErr error
for name, outIface := range t.interfaces {
if outIface == iface || !outIface.IsEnabled() {
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)
if err := outIface.Send(forwardData, ""); err != nil {
log.Printf("[DEBUG-3] Failed to forward announce on %s: %v", name, err)
if err := outIface.Send(data, ""); err != nil {
log.Printf("[DEBUG-7] Failed to forward announce on %s: %v", name, err)
lastErr = err
}
}
// Notify announce handlers
t.notifyAnnounceHandlers(destHash, identityBytes, appData)
// Notify handlers with first address as destination hash
t.notifyAnnounceHandlers(addresses[:16], id, appData)
return lastErr
}
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
}
@@ -704,28 +776,28 @@ func (t *Transport) handleLinkPacket(data []byte, iface common.NetworkInterface)
timestamp := binary.BigEndian.Uint64(data[32: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) {
nextHop := t.NextHop(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 nextIface, ok := t.interfaces[nextIfaceName]; ok {
log.Printf("[DEBUG-7] Forwarding link packet to %s", nextIfaceName)
nextIface.Send(data, string(nextHop))
}
}
}
// Update timing information
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)
if link.packetCb != nil {
// Create a packet object to pass to callback
p := &packet.Packet{
Data: payload,
// Add other necessary packet fields
}
log.Printf("[DEBUG-7] Executing packet callback with %d bytes", len(payload))
p := &packet.Packet{Data: payload}
link.packetCb(payload, p)
}
}
@@ -769,24 +841,33 @@ func (t *Transport) SendPacket(p *packet.Packet) error {
t.mutex.RLock()
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()
if err != nil {
log.Printf("[DEBUG-3] Packet serialization failed: %v", 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]
log.Printf("[DEBUG-6] Destination hash: %x", destHash)
path, exists := t.paths[string(destHash)]
if !exists {
log.Printf("[DEBUG-3] No path found for destination %x", destHash)
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 {
log.Printf("[DEBUG-3] Failed to send packet: %v", err)
return fmt.Errorf("failed to send packet: %w", err)
}
log.Printf("[DEBUG-7] Packet sent successfully")
return nil
}
@@ -953,3 +1034,40 @@ func (l *Link) GetStatus() int {
defer l.mutex.RUnlock()
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
}