0.2.3
This commit is contained in:
@@ -9,12 +9,15 @@ import (
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/Sudo-Ivan/reticulum-go/internal/config"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/identity"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/transport"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/interfaces"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/announce"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -27,6 +30,7 @@ type Client struct {
|
||||
config *common.ReticulumConfig
|
||||
transport *transport.Transport
|
||||
interfaces []common.NetworkInterface
|
||||
identity *identity.Identity
|
||||
}
|
||||
|
||||
func NewClient(cfg *common.ReticulumConfig) (*Client, error) {
|
||||
@@ -43,10 +47,16 @@ func NewClient(cfg *common.ReticulumConfig) (*Client, error) {
|
||||
return nil, fmt.Errorf("failed to initialize transport: %v", err)
|
||||
}
|
||||
|
||||
id, err := identity.New()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create identity: %v", err)
|
||||
}
|
||||
|
||||
return &Client{
|
||||
config: cfg,
|
||||
transport: t,
|
||||
interfaces: make([]common.NetworkInterface, 0),
|
||||
identity: id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -69,7 +79,7 @@ func (c *Client) Start() error {
|
||||
}
|
||||
|
||||
callback := common.PacketCallback(func(data []byte, iface interface{}) {
|
||||
c.transport.HandlePacket(data, iface)
|
||||
c.handlePacket(data, iface)
|
||||
})
|
||||
client.SetPacketCallback(callback)
|
||||
iface = client
|
||||
@@ -87,7 +97,7 @@ func (c *Client) Start() error {
|
||||
}
|
||||
|
||||
callback := common.PacketCallback(func(data []byte, iface interface{}) {
|
||||
c.transport.HandlePacket(data, iface)
|
||||
c.handlePacket(data, iface)
|
||||
})
|
||||
udp.SetPacketCallback(callback)
|
||||
iface = udp
|
||||
@@ -106,9 +116,110 @@ func (c *Client) Start() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Start periodic announce after interfaces are set up
|
||||
go func() {
|
||||
// Initial delay to allow interfaces to connect
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Send first announce
|
||||
c.sendAnnounce()
|
||||
|
||||
// Set up periodic announces
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
c.sendAnnounce()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
log.Printf("Client started with %d interfaces", len(c.interfaces))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handlePacket(data []byte, iface interface{}) {
|
||||
if len(data) < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
packetType := data[0]
|
||||
switch packetType {
|
||||
case 0x04: // Announce packet
|
||||
c.handleAnnounce(data[1:])
|
||||
default:
|
||||
c.transport.HandlePacket(data, iface)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) handleAnnounce(data []byte) {
|
||||
if len(data) < 42 { // 32 bytes hash + 8 bytes timestamp + 1 byte hops + 1 byte flags
|
||||
log.Printf("Received malformed announce packet (too short)")
|
||||
return
|
||||
}
|
||||
|
||||
destHash := data[:32]
|
||||
timestamp := binary.BigEndian.Uint64(data[32:40])
|
||||
hops := data[40]
|
||||
flags := data[41]
|
||||
|
||||
log.Printf("Received announce from %x", destHash)
|
||||
log.Printf(" Timestamp: %d", timestamp)
|
||||
log.Printf(" Hops: %d", hops)
|
||||
log.Printf(" Flags: %x", flags)
|
||||
|
||||
if len(data) > 42 {
|
||||
// Extract app data if present
|
||||
dataLen := binary.BigEndian.Uint16(data[42:44])
|
||||
if len(data) >= 44+int(dataLen) {
|
||||
appData := data[44:44+dataLen]
|
||||
log.Printf(" App Data: %s", string(appData))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) sendAnnounce() {
|
||||
// Create announce packet following RNS protocol
|
||||
announceData := make([]byte, 0, 128)
|
||||
announceData = append(announceData, 0x04) // Announce packet type
|
||||
announceData = append(announceData, c.identity.Hash()...) // Identity hash (32 bytes)
|
||||
|
||||
// Add timestamp (8 bytes, big-endian)
|
||||
timestamp := time.Now().Unix()
|
||||
timeBytes := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(timeBytes, uint64(timestamp))
|
||||
announceData = append(announceData, timeBytes...)
|
||||
|
||||
// Add hops (1 byte)
|
||||
announceData = append(announceData, 0x00) // Initial hop count
|
||||
|
||||
// Add flags (1 byte)
|
||||
announceData = append(announceData, byte(announce.ANNOUNCE_IDENTITY)) // Using identity announce type
|
||||
|
||||
// Add app data with length prefix
|
||||
appData := []byte("RNS.Go.Client")
|
||||
lenBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(lenBytes, uint16(len(appData)))
|
||||
announceData = append(announceData, lenBytes...)
|
||||
announceData = append(announceData, appData...)
|
||||
|
||||
// Sign the announce packet
|
||||
signature := c.identity.Sign(announceData)
|
||||
announceData = append(announceData, signature...)
|
||||
|
||||
log.Printf("Sending announce packet, length: %d bytes", len(announceData))
|
||||
|
||||
for _, iface := range c.interfaces {
|
||||
if err := iface.Send(announceData, ""); err != nil {
|
||||
log.Printf("Failed to send announce on interface %s: %v", iface.GetName(), err)
|
||||
} else {
|
||||
log.Printf("Sent announce on interface %s", iface.GetName())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Stop() {
|
||||
for _, iface := range c.interfaces {
|
||||
iface.Detach()
|
||||
|
||||
@@ -2,13 +2,12 @@ package announce
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/identity"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/transport"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -64,7 +63,7 @@ func New(dest *identity.Identity, appData []byte, pathResponse bool) (*Announce,
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (a *Announce) Propagate(interfaces []transport.Interface) error {
|
||||
func (a *Announce) Propagate(interfaces []common.NetworkInterface) error {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
@@ -89,7 +88,7 @@ func (a *Announce) Propagate(interfaces []transport.Interface) error {
|
||||
|
||||
// Propagate to all interfaces
|
||||
for _, iface := range interfaces {
|
||||
if err := iface.SendAnnounce(packet, a.pathResponse); err != nil {
|
||||
if err := iface.Send(packet, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -125,21 +124,34 @@ func (a *Announce) HandleAnnounce(data []byte) error {
|
||||
|
||||
// Extract fields
|
||||
destHash := data[:16]
|
||||
pubKey := data[16:48]
|
||||
hops := data[48]
|
||||
publicKey := data[16:48]
|
||||
hopCount := data[48]
|
||||
|
||||
// Validate hop count
|
||||
if hopCount > MAX_HOPS {
|
||||
return errors.New("announce exceeded maximum hop count")
|
||||
}
|
||||
|
||||
// Extract app data and signature
|
||||
appData := data[49 : len(data)-64]
|
||||
signature := data[len(data)-64:]
|
||||
|
||||
// Create announced identity from public key
|
||||
announcedIdentity := identity.FromPublicKey(publicKey)
|
||||
if announcedIdentity == nil {
|
||||
return errors.New("invalid identity public key")
|
||||
}
|
||||
|
||||
// Verify signature
|
||||
signData := append(destHash, appData...)
|
||||
if !a.identity.Verify(signData, signature) {
|
||||
if !announcedIdentity.Verify(signData, signature) {
|
||||
return errors.New("invalid announce signature")
|
||||
}
|
||||
|
||||
// Process announce with registered handlers
|
||||
for _, handler := range a.handlers {
|
||||
if handler.ReceivePathResponses() || !a.pathResponse {
|
||||
if err := handler.ReceivedAnnounce(destHash, a.identity, appData); err != nil {
|
||||
if err := handler.ReceivedAnnounce(destHash, announcedIdentity, appData); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -148,7 +160,7 @@ func (a *Announce) HandleAnnounce(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Announce) RequestPath(destHash []byte, onInterface transport.Interface) error {
|
||||
func (a *Announce) RequestPath(destHash []byte, onInterface common.NetworkInterface) error {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
@@ -158,7 +170,7 @@ func (a *Announce) RequestPath(destHash []byte, onInterface transport.Interface)
|
||||
packet = append(packet, byte(0)) // Initial hop count
|
||||
|
||||
// Send path request
|
||||
if err := onInterface.SendPathRequest(packet); err != nil {
|
||||
if err := onInterface.Send(packet, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -435,4 +435,26 @@ func (i *Identity) Hash() []byte {
|
||||
|
||||
func (i *Identity) Hex() string {
|
||||
return hex.EncodeToString(i.Hash())
|
||||
}
|
||||
|
||||
func FromPublicKey(publicKey []byte) *Identity {
|
||||
if len(publicKey) != curve25519.PointSize {
|
||||
return nil
|
||||
}
|
||||
|
||||
i := &Identity{
|
||||
publicKey: append([]byte{}, publicKey...),
|
||||
ratchets: make(map[string][]byte),
|
||||
ratchetExpiry: make(map[string]int64),
|
||||
}
|
||||
|
||||
// Generate Ed25519 verification key from the X25519 public key
|
||||
hash := sha256.New()
|
||||
hash.Write(publicKey)
|
||||
seed := hash.Sum(nil)
|
||||
|
||||
// Use the first 32 bytes of the hash as the verification key
|
||||
i.verificationKey = ed25519.PublicKey(seed[:32])
|
||||
|
||||
return i
|
||||
}
|
||||
Reference in New Issue
Block a user