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
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -48,7 +49,7 @@ const (
|
||||
DEBUG_PACKETS = 6 // Packet-level details
|
||||
DEBUG_ALL = 7 // Everything including identity operations
|
||||
APP_NAME = "Go-Client"
|
||||
APP_ASPECT = "node"
|
||||
APP_ASPECT = "node" // Always use "node" for node announces
|
||||
)
|
||||
|
||||
type Reticulum struct {
|
||||
@@ -62,9 +63,16 @@ type Reticulum struct {
|
||||
announceHistoryMu sync.RWMutex
|
||||
identity *identity.Identity
|
||||
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 {
|
||||
timestamp int64
|
||||
appData []byte
|
||||
}
|
||||
|
||||
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
|
||||
if cfg.AppName == "" {
|
||||
cfg.AppName = "Go Client"
|
||||
cfg.AppName = APP_NAME
|
||||
}
|
||||
if cfg.AppAspect == "" {
|
||||
cfg.AppAspect = "node"
|
||||
cfg.AppAspect = APP_ASPECT // Always use "node" for node announcements
|
||||
}
|
||||
|
||||
if err := initializeDirectories(); err != nil {
|
||||
@@ -100,23 +108,16 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
|
||||
identity,
|
||||
destination.IN,
|
||||
destination.SINGLE,
|
||||
APP_NAME,
|
||||
APP_ASPECT,
|
||||
"reticulum",
|
||||
"node",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create destination: %v", err)
|
||||
}
|
||||
debugLog(DEBUG_INFO, "Created destination with hash: %x", dest.GetHash())
|
||||
|
||||
// Set default app data for announces
|
||||
appData := []byte(fmt.Sprintf(`{"app":"%s","aspect":"%s","version":"0.1.0"}`, APP_NAME, APP_ASPECT))
|
||||
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")
|
||||
// Set node metadata
|
||||
nodeTimestamp := time.Now().Unix()
|
||||
|
||||
r := &Reticulum{
|
||||
config: cfg,
|
||||
@@ -128,8 +129,19 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
|
||||
announceHistory: make(map[string]announceRecord),
|
||||
identity: identity,
|
||||
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
|
||||
for name, ifaceConfig := range cfg.Interfaces {
|
||||
if !ifaceConfig.Enabled {
|
||||
@@ -276,18 +288,6 @@ func main() {
|
||||
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
|
||||
go r.monitorInterfaces()
|
||||
|
||||
@@ -300,36 +300,45 @@ func main() {
|
||||
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
|
||||
go func() {
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
ticker := time.NewTicker(5 * time.Minute) // Adjust interval as needed
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
onlineInterfaces = append(onlineInterfaces, netIface)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
initialAnnounce, err := announce.NewAnnounce(
|
||||
r.identity,
|
||||
createAppData(r.config.AppName, r.config.AppAspect),
|
||||
r.createNodeAppData(),
|
||||
nil,
|
||||
false,
|
||||
r.config,
|
||||
@@ -474,7 +483,7 @@ func (r *Reticulum) Start() error {
|
||||
|
||||
periodicAnnounce, err := announce.NewAnnounce(
|
||||
r.identity,
|
||||
createAppData(r.config.AppName, r.config.AppAspect),
|
||||
r.createNodeAppData(),
|
||||
nil,
|
||||
false,
|
||||
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_PACKETS, "Raw announce data: %x", appData)
|
||||
|
||||
var isNode bool
|
||||
var nodeEnabled bool
|
||||
var nodeTimestamp int64
|
||||
var nodeMaxSize int16
|
||||
|
||||
// Parse msgpack array
|
||||
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
|
||||
|
||||
// Parse first element (name)
|
||||
if appData[pos] == 0xc4 { // bin 8 format
|
||||
// Parse first element (NameBytes)
|
||||
if pos+1 < len(appData) && appData[pos] == 0xc4 {
|
||||
nameLen := int(appData[pos+1])
|
||||
name := string(appData[pos+2 : pos+2+nameLen])
|
||||
pos += 2 + nameLen
|
||||
debugLog(DEBUG_VERBOSE, "Announce name: %s", name)
|
||||
if pos+2+nameLen <= len(appData) {
|
||||
nameBytes := appData[pos+2 : pos+2+nameLen]
|
||||
name := string(nameBytes)
|
||||
pos += 2 + nameLen
|
||||
debugLog(DEBUG_VERBOSE, "Peer name: %s (bytes: %x)", name, nameBytes)
|
||||
|
||||
// Parse second element (app data)
|
||||
if pos < len(appData) && appData[pos] == 0xc4 { // bin 8 format
|
||||
dataLen := int(appData[pos+1])
|
||||
data := appData[pos+2 : pos+2+dataLen]
|
||||
debugLog(DEBUG_VERBOSE, "Announce app data: %s", string(data))
|
||||
// Parse second element (TicketValue)
|
||||
if pos < len(appData) {
|
||||
ticketValue := appData[pos] // Assuming fixint for now
|
||||
debugLog(DEBUG_VERBOSE, "Peer ticket value: %d", ticketValue)
|
||||
} 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.announceHistory[identity.GetHexHash()] = announceRecord{
|
||||
// You can add fields here to store relevant announce data
|
||||
timestamp: time.Now().Unix(),
|
||||
appData: appData,
|
||||
}
|
||||
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
|
||||
@@ -619,24 +693,33 @@ func (r *Reticulum) GetDestination() *destination.Destination {
|
||||
return r.destination
|
||||
}
|
||||
|
||||
func createAppData(appName, appAspect string) []byte {
|
||||
nameString := fmt.Sprintf("%s.%s", appName, appAspect)
|
||||
func (r *Reticulum) createNodeAppData() []byte {
|
||||
// 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
|
||||
appData := []byte{0x92} // Fix array with 2 elements
|
||||
// Element 0: Boolean for enable/disable peer
|
||||
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)
|
||||
nameBytes := []byte(nameString)
|
||||
appData = append(appData, 0xd9) // str 8 format
|
||||
appData = append(appData, byte(len(nameBytes))) // length
|
||||
appData = append(appData, nameBytes...) // string data
|
||||
// Element 1: Int32 timestamp (current time)
|
||||
// Update the timestamp when creating new announcements
|
||||
r.nodeTimestamp = time.Now().Unix()
|
||||
appData = append(appData, 0xd2) // int32 format
|
||||
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)
|
||||
version := "0.1.0"
|
||||
versionBytes := []byte(version)
|
||||
appData = append(appData, 0xd9) // str 8 format
|
||||
appData = append(appData, byte(len(versionBytes))) // length
|
||||
appData = append(appData, versionBytes...) // string data
|
||||
// Element 2: Int16 max transfer size in KB
|
||||
appData = append(appData, 0xd1) // int16 format
|
||||
sizeBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(sizeBytes, uint16(r.maxTransferSize))
|
||||
appData = append(appData, sizeBytes...)
|
||||
|
||||
log.Printf("[DEBUG-7] Created node appData (msgpack [enable=%v, timestamp=%d, maxsize=%d]): %x",
|
||||
r.nodeEnabled, r.nodeTimestamp, r.maxTransferSize, 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))
|
||||
|
||||
// Minimum packet size validation (header(2) + desthash(16) + enckey(32) + signkey(32) + namehash(10) +
|
||||
// randomhash(10) + signature(64) + min app data(3))
|
||||
if len(data) < 169 {
|
||||
log.Printf("[DEBUG-7] Invalid announce data length: %d bytes", len(data))
|
||||
// Minimum packet size validation
|
||||
// header(2) + desthash(16) + context(1) + enckey(32) + signkey(32) + namehash(10) +
|
||||
// randomhash(10) + signature(64) + min app data(3)
|
||||
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")
|
||||
}
|
||||
|
||||
// Parse fields
|
||||
// Extract header and check packet type
|
||||
header := data[:2]
|
||||
if header[0]&0x03 != PACKET_TYPE_ANNOUNCE {
|
||||
return errors.New("not an announce packet")
|
||||
}
|
||||
|
||||
// Get hop count
|
||||
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 {
|
||||
log.Printf("[DEBUG-7] Announce exceeded max hops: %d", hopCount)
|
||||
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...)
|
||||
|
||||
// Create announced identity from public keys
|
||||
announcedIdentity := identity.FromPublicKey(pubKey)
|
||||
if announcedIdentity == nil {
|
||||
return errors.New("invalid identity public key")
|
||||
}
|
||||
|
||||
// Verify signature
|
||||
signData := append(destHash, appData...)
|
||||
if !announcedIdentity.Verify(signData, signature) {
|
||||
signedData := make([]byte, 0)
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -257,8 +321,8 @@ func (a *Announce) CreatePacket() []byte {
|
||||
// Create header
|
||||
header := CreateHeader(
|
||||
IFAC_NONE,
|
||||
HEADER_TYPE_1,
|
||||
0, // No context flag
|
||||
HEADER_TYPE_2, // Use header type 2 for announces
|
||||
0, // No context flag
|
||||
PROP_TYPE_BROADCAST,
|
||||
DEST_TYPE_SINGLE,
|
||||
PACKET_TYPE_ANNOUNCE,
|
||||
@@ -270,38 +334,85 @@ func (a *Announce) CreatePacket() []byte {
|
||||
// Add destination hash (16 bytes)
|
||||
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)
|
||||
pubKey := a.identity.GetPublicKey()
|
||||
packet = append(packet, pubKey[:32]...) // Encryption key
|
||||
packet = append(packet, pubKey[32:]...) // Signing key
|
||||
encKey := pubKey[:32] // Encryption 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)
|
||||
nameHash := sha256.Sum256([]byte(fmt.Sprintf("%s.%s", a.config.AppName, a.config.AppAspect)))
|
||||
packet = append(packet, nameHash[:10]...)
|
||||
nameHash := sha256.Sum256([]byte(appName))
|
||||
nameHash10 := nameHash[:10]
|
||||
log.Printf("[DEBUG-6] Using name hash for '%s': %x", appName, nameHash10)
|
||||
data = append(data, nameHash10...)
|
||||
|
||||
// Add random hash (10 bytes)
|
||||
randomBytes := make([]byte, 10)
|
||||
rand.Read(randomBytes)
|
||||
packet = append(packet, randomBytes...)
|
||||
// Add 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])
|
||||
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
|
||||
// Signature consists of destination hash, public keys, name hash, random hash, and app data
|
||||
validationData := make([]byte, 0)
|
||||
validationData = append(validationData, a.destinationHash...)
|
||||
validationData = append(validationData, pubKey[:32]...) // Encryption key
|
||||
validationData = append(validationData, pubKey[32:]...) // Signing key
|
||||
validationData = append(validationData, nameHash[:10]...)
|
||||
validationData = append(validationData, randomBytes...)
|
||||
validationData = append(validationData, encKey...)
|
||||
validationData = append(validationData, signKey...)
|
||||
validationData = append(validationData, nameHash10...)
|
||||
validationData = append(validationData, randomHash...)
|
||||
validationData = append(validationData, a.appData...)
|
||||
|
||||
// Add signature (64 bytes)
|
||||
signature := a.identity.Sign(validationData)
|
||||
packet = append(packet, signature...)
|
||||
data = append(data, signature...)
|
||||
|
||||
// Add app data
|
||||
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
|
||||
}
|
||||
|
||||
@@ -398,36 +509,8 @@ func (a *Announce) GetPacket() []byte {
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
if a.packet == nil {
|
||||
// Generate hash from announce data
|
||||
h := sha256.New()
|
||||
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
|
||||
// Use CreatePacket to generate the packet
|
||||
a.packet = a.CreatePacket()
|
||||
}
|
||||
|
||||
return a.packet
|
||||
|
||||
@@ -16,6 +16,6 @@ const (
|
||||
DestinationLink = 3
|
||||
|
||||
// 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)
|
||||
)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -217,14 +219,58 @@ 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))
|
||||
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()
|
||||
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
|
||||
signedData := append(destHash, pubKey...)
|
||||
// Parse app name from first msgpack element if possible
|
||||
// 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...)
|
||||
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)
|
||||
log.Printf("[DEBUG-6] Generated signature: %x", signature)
|
||||
|
||||
// Combine all data
|
||||
data := append(pubKey, appData...)
|
||||
data = append(data, signature...)
|
||||
// Combine all fields according to spec
|
||||
// Data structure: Public Key (32) + Signing Key (32) + Name Hash (10) + Random Hash (10) + Ratchet (optional) + Signature (64) + App Data
|
||||
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))
|
||||
|
||||
// Create the packet with header type 2 (two address fields)
|
||||
p := &Packet{
|
||||
HeaderType: HeaderType2,
|
||||
PacketType: PacketTypeAnnounce,
|
||||
|
||||
Reference in New Issue
Block a user