Enhance node announcement handling and packet structure. Introduce node-specific metadata in the Reticulum struct, update announce packet creation to support new formats, and improve validation checks for announce data. Adjust minimum packet size requirements and refactor related functions for clarity and consistency.

This commit is contained in:
2025-03-29 18:12:47 -05:00
parent 2f61ce9bf3
commit c8e81cd9f0
4 changed files with 374 additions and 151 deletions

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"encoding/binary"
"flag" "flag"
"fmt" "fmt"
"log" "log"
@@ -48,7 +49,7 @@ const (
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_NAME = "Go-Client"
APP_ASPECT = "node" APP_ASPECT = "node" // Always use "node" for node announces
) )
type Reticulum struct { type Reticulum struct {
@@ -62,9 +63,16 @@ type Reticulum struct {
announceHistoryMu sync.RWMutex announceHistoryMu sync.RWMutex
identity *identity.Identity identity *identity.Identity
destination *destination.Destination destination *destination.Destination
// Node-specific information
maxTransferSize int16 // Max transfer size in KB
nodeEnabled bool // Whether this node is enabled
nodeTimestamp int64 // Last node announcement timestamp
} }
type announceRecord struct { type announceRecord struct {
timestamp int64
appData []byte
} }
func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) { func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
@@ -74,10 +82,10 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
// Set default app name and aspect if not provided // Set default app name and aspect if not provided
if cfg.AppName == "" { if cfg.AppName == "" {
cfg.AppName = "Go Client" cfg.AppName = APP_NAME
} }
if cfg.AppAspect == "" { if cfg.AppAspect == "" {
cfg.AppAspect = "node" cfg.AppAspect = APP_ASPECT // Always use "node" for node announcements
} }
if err := initializeDirectories(); err != nil { if err := initializeDirectories(); err != nil {
@@ -100,23 +108,16 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
identity, identity,
destination.IN, destination.IN,
destination.SINGLE, destination.SINGLE,
APP_NAME, "reticulum",
APP_ASPECT, "node",
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create destination: %v", err) return nil, fmt.Errorf("failed to create destination: %v", err)
} }
debugLog(DEBUG_INFO, "Created destination with hash: %x", dest.GetHash()) debugLog(DEBUG_INFO, "Created destination with hash: %x", dest.GetHash())
// Set default app data for announces // Set node metadata
appData := []byte(fmt.Sprintf(`{"app":"%s","aspect":"%s","version":"0.1.0"}`, APP_NAME, APP_ASPECT)) nodeTimestamp := time.Now().Unix()
dest.SetDefaultAppData(appData)
// 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,
@@ -128,8 +129,19 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
announceHistory: make(map[string]announceRecord), announceHistory: make(map[string]announceRecord),
identity: identity, identity: identity,
destination: dest, destination: dest,
// Node-specific information
maxTransferSize: 500, // Default 500KB
nodeEnabled: true, // Enabled by default
nodeTimestamp: nodeTimestamp,
} }
// Enable destination features
dest.AcceptsLinks(true)
dest.EnableRatchets("") // Empty string for default path
dest.SetProofStrategy(destination.PROVE_APP)
debugLog(DEBUG_VERBOSE, "Configured destination features")
// Initialize interfaces from config // Initialize interfaces from config
for name, ifaceConfig := range cfg.Interfaces { for name, ifaceConfig := range cfg.Interfaces {
if !ifaceConfig.Enabled { if !ifaceConfig.Enabled {
@@ -276,18 +288,6 @@ func main() {
log.Fatalf("Failed to create Reticulum instance: %v", err) log.Fatalf("Failed to create Reticulum instance: %v", err)
} }
// Create announce using r.identity
announce, err := announce.NewAnnounce(
r.identity,
[]byte("HELLO WORLD"),
nil,
false,
r.config,
)
if err != nil {
log.Fatalf("Failed to create announce: %v", err)
}
// Start monitoring interfaces // Start monitoring interfaces
go r.monitorInterfaces() go r.monitorInterfaces()
@@ -300,36 +300,45 @@ func main() {
log.Fatalf("Failed to start Reticulum: %v", err) log.Fatalf("Failed to start Reticulum: %v", err)
} }
// Send initial announces after interfaces are ready
time.Sleep(2 * time.Second) // Give interfaces time to connect
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)
}
}
}
}
// Start periodic announces // Start periodic announces
go func() { go func() {
ticker := time.NewTicker(5 * time.Minute) ticker := time.NewTicker(5 * time.Minute) // Adjust interval as needed
defer ticker.Stop() defer ticker.Stop()
for range ticker.C { for range ticker.C {
debugLog(3, "Starting periodic announce cycle") debugLog(3, "Starting periodic announce cycle")
// Create a new announce packet for this cycle
periodicAnnounce, err := announce.NewAnnounce(
r.identity,
r.createNodeAppData(),
nil, // No ratchet ID for now
false,
r.config,
)
if err != nil {
debugLog(1, "Failed to create periodic announce: %v", err)
continue
}
// Propagate announce to all online interfaces
var onlineInterfaces []common.NetworkInterface
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()) onlineInterfaces = append(onlineInterfaces, netIface)
if err := announce.Propagate([]common.NetworkInterface{netIface}); err != nil {
debugLog(1, "Failed to propagate periodic announce: %v", err)
}
} }
} }
} }
if len(onlineInterfaces) > 0 {
debugLog(2, "Sending periodic announce on %d interfaces", len(onlineInterfaces))
if err := periodicAnnounce.Propagate(onlineInterfaces); err != nil {
debugLog(1, "Failed to propagate periodic announce: %v", err)
}
} else {
debugLog(3, "No online interfaces for periodic announce")
}
} }
}() }()
@@ -440,7 +449,7 @@ func (r *Reticulum) Start() error {
// Send initial announce once per interface // Send initial announce once per interface
initialAnnounce, err := announce.NewAnnounce( initialAnnounce, err := announce.NewAnnounce(
r.identity, r.identity,
createAppData(r.config.AppName, r.config.AppAspect), r.createNodeAppData(),
nil, nil,
false, false,
r.config, r.config,
@@ -474,7 +483,7 @@ func (r *Reticulum) Start() error {
periodicAnnounce, err := announce.NewAnnounce( periodicAnnounce, err := announce.NewAnnounce(
r.identity, r.identity,
createAppData(r.config.AppName, r.config.AppAspect), r.createNodeAppData(),
nil, nil,
false, false,
r.config, r.config,
@@ -559,25 +568,82 @@ func (h *AnnounceHandler) ReceivedAnnounce(destHash []byte, id interface{}, appD
debugLog(DEBUG_INFO, "Received announce from %x", destHash) debugLog(DEBUG_INFO, "Received announce from %x", destHash)
debugLog(DEBUG_PACKETS, "Raw announce data: %x", appData) debugLog(DEBUG_PACKETS, "Raw announce data: %x", appData)
var isNode bool
var nodeEnabled bool
var nodeTimestamp int64
var nodeMaxSize int16
// Parse msgpack array // Parse msgpack array
if len(appData) > 0 { if len(appData) > 0 {
if appData[0] == 0x92 { // msgpack array of 2 elements if appData[0] == 0x92 {
// Format [name, ticket] for standard peers
debugLog(DEBUG_VERBOSE, "Received standard peer announce")
isNode = false
var pos = 1 var pos = 1
// Parse first element (name) // Parse first element (NameBytes)
if appData[pos] == 0xc4 { // bin 8 format if pos+1 < len(appData) && appData[pos] == 0xc4 {
nameLen := int(appData[pos+1]) nameLen := int(appData[pos+1])
name := string(appData[pos+2 : pos+2+nameLen]) if pos+2+nameLen <= len(appData) {
pos += 2 + nameLen nameBytes := appData[pos+2 : pos+2+nameLen]
debugLog(DEBUG_VERBOSE, "Announce name: %s", name) name := string(nameBytes)
pos += 2 + nameLen
debugLog(DEBUG_VERBOSE, "Peer name: %s (bytes: %x)", name, nameBytes)
// Parse second element (app data) // Parse second element (TicketValue)
if pos < len(appData) && appData[pos] == 0xc4 { // bin 8 format if pos < len(appData) {
dataLen := int(appData[pos+1]) ticketValue := appData[pos] // Assuming fixint for now
data := appData[pos+2 : pos+2+dataLen] debugLog(DEBUG_VERBOSE, "Peer ticket value: %d", ticketValue)
debugLog(DEBUG_VERBOSE, "Announce app data: %s", string(data)) } else {
debugLog(DEBUG_ERROR, "Could not parse ticket value from announce appData")
}
} else {
debugLog(DEBUG_ERROR, "Could not parse name bytes from announce appData")
}
} else {
debugLog(DEBUG_ERROR, "Announce appData name is not in expected bin 8 format")
}
} else if appData[0] == 0x93 {
// Format [enable, timestamp, maxsize] for nodes
debugLog(DEBUG_VERBOSE, "Received node announce")
isNode = true
var pos = 1
// Parse first element (Boolean enable/disable)
if pos < len(appData) {
if appData[pos] == 0xc3 {
nodeEnabled = true
} else if appData[pos] == 0xc2 {
nodeEnabled = false
} else {
debugLog(DEBUG_ERROR, "Unexpected format for node enabled status: %x", appData[pos])
}
pos++
debugLog(DEBUG_VERBOSE, "Node enabled: %v", nodeEnabled)
// Parse second element (Int32 timestamp)
if pos+4 < len(appData) && appData[pos] == 0xd2 {
pos++
timestamp := binary.BigEndian.Uint32(appData[pos : pos+4])
nodeTimestamp = int64(timestamp)
pos += 4
debugLog(DEBUG_VERBOSE, "Node timestamp: %d (%s)", timestamp, time.Unix(nodeTimestamp, 0))
// Parse third element (Int16 max transfer size)
if pos+2 < len(appData) && appData[pos] == 0xd1 {
pos++
maxSize := binary.BigEndian.Uint16(appData[pos : pos+2])
nodeMaxSize = int16(maxSize)
debugLog(DEBUG_VERBOSE, "Node max transfer size: %d KB", nodeMaxSize)
} else {
debugLog(DEBUG_ERROR, "Could not parse max transfer size from node announce")
}
} else {
debugLog(DEBUG_ERROR, "Could not parse timestamp from node announce")
} }
} }
} else {
debugLog(DEBUG_VERBOSE, "Unknown announce data format: %x", appData)
} }
} }
@@ -598,14 +664,22 @@ func (h *AnnounceHandler) ReceivedAnnounce(destHash []byte, id interface{}, appD
} }
} }
// Store announce in history // Create a better record with more info
recordType := "peer"
if isNode {
recordType = "node"
debugLog(DEBUG_INFO, "Storing node in announce history: enabled=%v, timestamp=%d, maxsize=%dKB",
nodeEnabled, nodeTimestamp, nodeMaxSize)
}
h.reticulum.announceHistoryMu.Lock() h.reticulum.announceHistoryMu.Lock()
h.reticulum.announceHistory[identity.GetHexHash()] = announceRecord{ h.reticulum.announceHistory[identity.GetHexHash()] = announceRecord{
// You can add fields here to store relevant announce data timestamp: time.Now().Unix(),
appData: appData,
} }
h.reticulum.announceHistoryMu.Unlock() h.reticulum.announceHistoryMu.Unlock()
debugLog(DEBUG_VERBOSE, "Stored announce in history for identity %s", identity.GetHexHash()) debugLog(DEBUG_VERBOSE, "Stored %s announce in history for identity %s", recordType, identity.GetHexHash())
} }
return nil return nil
@@ -619,24 +693,33 @@ func (r *Reticulum) GetDestination() *destination.Destination {
return r.destination return r.destination
} }
func createAppData(appName, appAspect string) []byte { func (r *Reticulum) createNodeAppData() []byte {
nameString := fmt.Sprintf("%s.%s", appName, appAspect) // Create a msgpack array with 3 elements
// [Bool, Int32, Int16] for [enable, timestamp, max_transfer_size]
appData := []byte{0x93} // Array with 3 elements
// Create MessagePack array with 2 elements // Element 0: Boolean for enable/disable peer
appData := []byte{0x92} // Fix array with 2 elements if r.nodeEnabled {
appData = append(appData, 0xc3) // true
} else {
appData = append(appData, 0xc2) // false
}
// First element: name string (always use str 8 format for consistency) // Element 1: Int32 timestamp (current time)
nameBytes := []byte(nameString) // Update the timestamp when creating new announcements
appData = append(appData, 0xd9) // str 8 format r.nodeTimestamp = time.Now().Unix()
appData = append(appData, byte(len(nameBytes))) // length appData = append(appData, 0xd2) // int32 format
appData = append(appData, nameBytes...) // string data timeBytes := make([]byte, 4)
binary.BigEndian.PutUint32(timeBytes, uint32(r.nodeTimestamp))
appData = append(appData, timeBytes...)
// Second element: version string (always use str 8 format for consistency) // Element 2: Int16 max transfer size in KB
version := "0.1.0" appData = append(appData, 0xd1) // int16 format
versionBytes := []byte(version) sizeBytes := make([]byte, 2)
appData = append(appData, 0xd9) // str 8 format binary.BigEndian.PutUint16(sizeBytes, uint16(r.maxTransferSize))
appData = append(appData, byte(len(versionBytes))) // length appData = append(appData, sizeBytes...)
appData = append(appData, versionBytes...) // string data
log.Printf("[DEBUG-7] Created node appData (msgpack [enable=%v, timestamp=%d, maxsize=%d]): %x",
r.nodeEnabled, r.nodeTimestamp, r.maxTransferSize, appData)
return appData return appData
} }

View File

@@ -169,44 +169,108 @@ func (a *Announce) HandleAnnounce(data []byte) error {
log.Printf("[DEBUG-7] Handling announce packet of %d bytes", len(data)) log.Printf("[DEBUG-7] Handling announce packet of %d bytes", len(data))
// Minimum packet size validation (header(2) + desthash(16) + enckey(32) + signkey(32) + namehash(10) + // Minimum packet size validation
// randomhash(10) + signature(64) + min app data(3)) // header(2) + desthash(16) + context(1) + enckey(32) + signkey(32) + namehash(10) +
if len(data) < 169 { // randomhash(10) + signature(64) + min app data(3)
log.Printf("[DEBUG-7] Invalid announce data length: %d bytes", len(data)) if len(data) < 170 {
log.Printf("[DEBUG-7] Invalid announce data length: %d bytes (minimum 170)", len(data))
return errors.New("invalid announce data length") return errors.New("invalid announce data length")
} }
// Parse fields // Extract header and check packet type
header := data[:2] header := data[:2]
if header[0]&0x03 != PACKET_TYPE_ANNOUNCE {
return errors.New("not an announce packet")
}
// Get hop count
hopCount := header[1] hopCount := header[1]
destHash := data[2:18]
encKey := data[18:50]
signKey := data[50:82]
nameHash := data[82:92]
randomHash := data[92:102]
signature := data[102:166]
appData := data[166:]
log.Printf("[DEBUG-7] Announce fields: destHash=%x, encKey=%x, signKey=%x",
destHash, encKey, signKey)
log.Printf("[DEBUG-7] Name hash=%x, random hash=%x", nameHash, randomHash)
// Validate hop count
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)
return errors.New("announce exceeded maximum hop count") return errors.New("announce exceeded maximum hop count")
} }
// Create announced identity from public keys // Parse the packet based on header type
headerType := (header[0] & 0b01000000) >> 6
var contextByte byte
var packetData []byte
if headerType == HEADER_TYPE_2 {
// Header type 2 format: header(2) + desthash(16) + transportid(16) + context(1) + data
if len(data) < 35 {
return errors.New("header type 2 packet too short")
}
destHash := data[2:18]
transportID := data[18:34]
contextByte = data[34]
packetData = data[35:]
log.Printf("[DEBUG-7] Header type 2 announce: destHash=%x, transportID=%x, context=%d",
destHash, transportID, contextByte)
} else {
// Header type 1 format: header(2) + desthash(16) + context(1) + data
if len(data) < 19 {
return errors.New("header type 1 packet too short")
}
destHash := data[2:18]
contextByte = data[18]
packetData = data[19:]
log.Printf("[DEBUG-7] Header type 1 announce: destHash=%x, context=%d",
destHash, contextByte)
}
// Now parse the data portion according to the spec
// Public Key (32) + Signing Key (32) + Name Hash (10) + Random Hash (10) + [Ratchet] + Signature (64) + App Data
if len(packetData) < 148 { // 32 + 32 + 10 + 10 + 64
return errors.New("announce data too short")
}
// Extract the components
encKey := packetData[:32]
signKey := packetData[32:64]
nameHash := packetData[64:74]
randomHash := packetData[74:84]
// The next field could be a ratchet (32 bytes) or signature (64 bytes)
// We need to detect this somehow or use a flag
// For now, assume no ratchet
signature := packetData[84:148]
appData := packetData[148:]
log.Printf("[DEBUG-7] Announce fields: encKey=%x, signKey=%x", encKey, signKey)
log.Printf("[DEBUG-7] Name hash=%x, random hash=%x", nameHash, randomHash)
log.Printf("[DEBUG-7] Signature=%x, appDataLen=%d", signature[:8], len(appData))
// Get the destination hash from header
var destHash []byte
if headerType == HEADER_TYPE_2 {
destHash = data[2:18]
} else {
destHash = data[2:18]
}
// Combine public keys
pubKey := append(encKey, signKey...) pubKey := append(encKey, signKey...)
// Create announced identity from public keys
announcedIdentity := identity.FromPublicKey(pubKey) announcedIdentity := identity.FromPublicKey(pubKey)
if announcedIdentity == nil { if announcedIdentity == nil {
return errors.New("invalid identity public key") return errors.New("invalid identity public key")
} }
// Verify signature // Verify signature
signData := append(destHash, appData...) signedData := make([]byte, 0)
if !announcedIdentity.Verify(signData, signature) { signedData = append(signedData, destHash...)
signedData = append(signedData, encKey...)
signedData = append(signedData, signKey...)
signedData = append(signedData, nameHash...)
signedData = append(signedData, randomHash...)
signedData = append(signedData, appData...)
if !announcedIdentity.Verify(signedData, signature) {
return errors.New("invalid announce signature") return errors.New("invalid announce signature")
} }
@@ -257,8 +321,8 @@ func (a *Announce) CreatePacket() []byte {
// Create header // Create header
header := CreateHeader( header := CreateHeader(
IFAC_NONE, IFAC_NONE,
HEADER_TYPE_1, HEADER_TYPE_2, // Use header type 2 for announces
0, // No context flag 0, // No context flag
PROP_TYPE_BROADCAST, PROP_TYPE_BROADCAST,
DEST_TYPE_SINGLE, DEST_TYPE_SINGLE,
PACKET_TYPE_ANNOUNCE, PACKET_TYPE_ANNOUNCE,
@@ -270,38 +334,85 @@ func (a *Announce) CreatePacket() []byte {
// Add destination hash (16 bytes) // Add destination hash (16 bytes)
packet = append(packet, a.destinationHash...) packet = append(packet, a.destinationHash...)
// If using header type 2, add transport ID (16 bytes)
// For broadcast announces, this is filled with zeroes
transportID := make([]byte, 16)
packet = append(packet, transportID...)
// Add context byte
packet = append(packet, byte(0)) // Context byte, 0 for announces
// Add public key parts (32 bytes each) // Add public key parts (32 bytes each)
pubKey := a.identity.GetPublicKey() pubKey := a.identity.GetPublicKey()
packet = append(packet, pubKey[:32]...) // Encryption key encKey := pubKey[:32] // Encryption key
packet = append(packet, pubKey[32:]...) // Signing key signKey := pubKey[32:] // Signing key
// Start building data portion according to spec
data := make([]byte, 0, 32+32+10+10+32+64+len(a.appData))
data = append(data, encKey...) // Encryption key (32 bytes)
data = append(data, signKey...) // Signing key (32 bytes)
// Determine if this is a node announce based on appData format
var appName string
if len(a.appData) > 2 && a.appData[0] == 0x93 {
// This is a node announcement
appName = "reticulum.node"
} else if len(a.appData) > 3 && a.appData[0] == 0x92 && a.appData[1] == 0xc4 {
nameLen := int(a.appData[2])
if 3+nameLen <= len(a.appData) {
appName = string(a.appData[3 : 3+nameLen])
} else {
appName = fmt.Sprintf("%s.%s", a.config.AppName, a.config.AppAspect)
}
} else {
// Default fallback using config values
appName = fmt.Sprintf("%s.%s", a.config.AppName, a.config.AppAspect)
}
// Add name hash (10 bytes) // Add name hash (10 bytes)
nameHash := sha256.Sum256([]byte(fmt.Sprintf("%s.%s", a.config.AppName, a.config.AppAspect))) nameHash := sha256.Sum256([]byte(appName))
packet = append(packet, nameHash[:10]...) nameHash10 := nameHash[:10]
log.Printf("[DEBUG-6] Using name hash for '%s': %x", appName, nameHash10)
data = append(data, nameHash10...)
// Add random hash (10 bytes) // Add random hash (10 bytes) - 5 bytes random + 5 bytes time
randomBytes := make([]byte, 10) randomHash := make([]byte, 10)
rand.Read(randomBytes) rand.Read(randomHash[:5])
packet = append(packet, randomBytes...) timeBytes := make([]byte, 8)
binary.BigEndian.PutUint64(timeBytes, uint64(time.Now().Unix()))
copy(randomHash[5:], timeBytes[:5])
data = append(data, randomHash...)
// Add ratchet ID (32 bytes) - required in the packet format
if a.ratchetID != nil {
data = append(data, a.ratchetID...)
} else {
// If there's no ratchet, add 32 zero bytes as placeholder
data = append(data, make([]byte, 32)...)
}
// Create validation data for signature // Create validation data for signature
// Signature consists of destination hash, public keys, name hash, random hash, and app data
validationData := make([]byte, 0) validationData := make([]byte, 0)
validationData = append(validationData, a.destinationHash...) validationData = append(validationData, a.destinationHash...)
validationData = append(validationData, pubKey[:32]...) // Encryption key validationData = append(validationData, encKey...)
validationData = append(validationData, pubKey[32:]...) // Signing key validationData = append(validationData, signKey...)
validationData = append(validationData, nameHash[:10]...) validationData = append(validationData, nameHash10...)
validationData = append(validationData, randomBytes...) validationData = append(validationData, randomHash...)
validationData = append(validationData, a.appData...) validationData = append(validationData, a.appData...)
// Add signature (64 bytes) // Add signature (64 bytes)
signature := a.identity.Sign(validationData) signature := a.identity.Sign(validationData)
packet = append(packet, signature...) data = append(data, signature...)
// Add app data // Add app data
if len(a.appData) > 0 { if len(a.appData) > 0 {
packet = append(packet, a.appData...) data = append(data, a.appData...)
} }
// Combine header and data
packet = append(packet, data...)
return packet return packet
} }
@@ -398,36 +509,8 @@ func (a *Announce) GetPacket() []byte {
defer a.mutex.Unlock() defer a.mutex.Unlock()
if a.packet == nil { if a.packet == nil {
// Generate hash from announce data // Use CreatePacket to generate the packet
h := sha256.New() a.packet = a.CreatePacket()
h.Write(a.destinationHash)
h.Write(a.identity.GetPublicKey())
h.Write([]byte{a.hops})
h.Write(a.appData)
if a.ratchetID != nil {
h.Write(a.ratchetID)
}
// Construct packet
packet := make([]byte, 0)
packet = append(packet, PACKET_TYPE_ANNOUNCE)
packet = append(packet, a.destinationHash...)
packet = append(packet, a.identity.GetPublicKey()...)
packet = append(packet, a.hops)
packet = append(packet, a.appData...)
if a.ratchetID != nil {
packet = append(packet, a.ratchetID...)
}
// Add signature
signData := append(a.destinationHash, a.appData...)
if a.ratchetID != nil {
signData = append(signData, a.ratchetID...)
}
signature := a.identity.Sign(signData)
packet = append(packet, signature...)
a.packet = packet
} }
return a.packet return a.packet

View File

@@ -16,6 +16,6 @@ const (
DestinationLink = 3 DestinationLink = 3
// Minimum packet sizes // Minimum packet sizes
MinAnnounceSize = 169 // header(2) + desthash(16) + enckey(32) + signkey(32) + MinAnnounceSize = 170 // header(2) + desthash(16) + context(1) + enckey(32) + signkey(32) +
// namehash(10) + randomhash(10) + signature(64) + min appdata(3) // namehash(10) + randomhash(10) + signature(64) + min appdata(3)
) )

View File

@@ -1,7 +1,9 @@
package packet package packet
import ( import (
"crypto/rand"
"crypto/sha256" "crypto/sha256"
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"log" "log"
@@ -217,14 +219,58 @@ 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)) log.Printf("[DEBUG-7] Creating new announce packet: destHash=%x, appData=%s", destHash, fmt.Sprintf("%x", appData))
// Create combined public key // Get public key separated into encryption and signing keys
pubKey := identity.GetPublicKey() pubKey := identity.GetPublicKey()
log.Printf("[DEBUG-6] Using public key: %x", pubKey) encKey := pubKey[:32]
signKey := pubKey[32:]
log.Printf("[DEBUG-6] Using public keys: encKey=%x, signKey=%x", encKey, signKey)
// Create signed data // Parse app name from first msgpack element if possible
signedData := append(destHash, pubKey...) // For nodes, we'll use "reticulum.node" as the name hash
var appName string
if len(appData) > 2 && appData[0] == 0x93 {
// This is a node announce, use standard node name
appName = "reticulum.node"
} else if len(appData) > 3 && appData[0] == 0x92 && appData[1] == 0xc4 {
// Try to extract name from peer announce appData
nameLen := int(appData[2])
if 3+nameLen <= len(appData) {
appName = string(appData[3 : 3+nameLen])
} else {
// Default fallback
appName = "reticulum-go.node"
}
} else {
// Default fallback
appName = "reticulum-go.node"
}
// Create name hash (10 bytes)
nameHash := sha256.Sum256([]byte(appName))
nameHash10 := nameHash[:10]
log.Printf("[DEBUG-6] Using name hash for '%s': %x", appName, nameHash10)
// Create random hash (10 bytes) - 5 bytes random + 5 bytes time
randomHash := make([]byte, 10)
rand.Read(randomHash[:5])
timeBytes := make([]byte, 8)
binary.BigEndian.PutUint64(timeBytes, uint64(time.Now().Unix()))
copy(randomHash[5:], timeBytes[:5])
log.Printf("[DEBUG-6] Generated random hash: %x", randomHash)
// Prepare ratchet ID if available (not yet implemented)
var ratchetID []byte
// Prepare data for signature
// Signature consists of destination hash, public keys, name hash, random hash, and app data
signedData := make([]byte, 0, len(destHash)+len(encKey)+len(signKey)+len(nameHash10)+len(randomHash)+len(appData))
signedData = append(signedData, destHash...)
signedData = append(signedData, encKey...)
signedData = append(signedData, signKey...)
signedData = append(signedData, nameHash10...)
signedData = append(signedData, randomHash...)
signedData = append(signedData, appData...) signedData = append(signedData, appData...)
log.Printf("[DEBUG-5] Created signed data (%d bytes)", len(signedData)) log.Printf("[DEBUG-5] Created signed data (%d bytes)", len(signedData))
@@ -232,11 +278,22 @@ func NewAnnouncePacket(destHash []byte, identity *identity.Identity, appData []b
signature := identity.Sign(signedData) signature := identity.Sign(signedData)
log.Printf("[DEBUG-6] Generated signature: %x", signature) log.Printf("[DEBUG-6] Generated signature: %x", signature)
// Combine all data // Combine all fields according to spec
data := append(pubKey, appData...) // Data structure: Public Key (32) + Signing Key (32) + Name Hash (10) + Random Hash (10) + Ratchet (optional) + Signature (64) + App Data
data = append(data, signature...) data := make([]byte, 0, 32+32+10+10+64+len(appData))
data = append(data, encKey...) // Encryption key (32 bytes)
data = append(data, signKey...) // Signing key (32 bytes)
data = append(data, nameHash10...) // Name hash (10 bytes)
data = append(data, randomHash...) // Random hash (10 bytes)
if ratchetID != nil {
data = append(data, ratchetID...) // Ratchet ID (32 bytes if present)
}
data = append(data, signature...) // Signature (64 bytes)
data = append(data, appData...) // Application data (variable)
log.Printf("[DEBUG-5] Combined packet data (%d bytes)", len(data)) log.Printf("[DEBUG-5] Combined packet data (%d bytes)", len(data))
// Create the packet with header type 2 (two address fields)
p := &Packet{ p := &Packet{
HeaderType: HeaderType2, HeaderType: HeaderType2,
PacketType: PacketTypeAnnounce, PacketType: PacketTypeAnnounce,