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:
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user