From 6cdc02346fa6504a2700c1ef09dab0d1c90f1166 Mon Sep 17 00:00:00 2001 From: Sudo-Ivan Date: Wed, 1 Jan 2025 03:12:26 -0600 Subject: [PATCH] update --- cmd/reticulum-go/main.go | 52 ++++-- pkg/destination/destination.go | 28 +++- pkg/identity/identity.go | 155 +++++++++++++++++- pkg/link/link.go | 191 ++++++++++++++-------- pkg/packet/constants.go | 4 - pkg/packet/packet.go | 278 ++++++++++++++++++++++----------- 6 files changed, 522 insertions(+), 186 deletions(-) diff --git a/cmd/reticulum-go/main.go b/cmd/reticulum-go/main.go index eea3335..6e647fa 100644 --- a/cmd/reticulum-go/main.go +++ b/cmd/reticulum-go/main.go @@ -43,7 +43,7 @@ const ( DEBUG_VERBOSE = 4 // Detailed information DEBUG_TRACE = 5 // Very detailed tracing DEBUG_PACKETS = 6 // Packet-level details - DEBUG_ALL = 7 // Everything + DEBUG_ALL = 7 // Everything including identity operations ) type Reticulum struct { @@ -336,11 +336,10 @@ func (tw *transportWrapper) GetStatus() int { func (tw *transportWrapper) Send(data []byte) interface{} { p := &packet.Packet{ - Header: [2]byte{ - packet.PacketTypeData, // First byte - 0, // Second byte (hops) - }, - Data: data, + PacketType: packet.PacketTypeData, + Hops: 0, + Data: data, + HeaderType: packet.HeaderType1, } err := tw.Transport.SendPacket(p) @@ -490,14 +489,27 @@ func (r *Reticulum) Stop() error { } func (r *Reticulum) handleAnnounce(data []byte, iface common.NetworkInterface) { - debugLog(2, "Received announce packet on interface %s (%d bytes)", iface.GetName(), len(data)) + debugLog(DEBUG_INFO, "Received announce packet on interface %s (%d bytes)", iface.GetName(), len(data)) a := &announce.Announce{} if err := a.HandleAnnounce(data); err != nil { - debugLog(1, "Error handling announce: %v", err) + debugLog(DEBUG_ERROR, "Error handling announce: %v", err) return } + // Log announce details + debugLog(DEBUG_ALL, "Announce details:") + debugLog(DEBUG_ALL, " Hash: %x", a.Hash()) + + // Get fields using packet data + packet := a.GetPacket() + if len(packet) > 2 { + destHash := packet[2:18] + hops := packet[50] + debugLog(DEBUG_ALL, " Destination Hash: %x", destHash) + debugLog(DEBUG_ALL, " Hops: %d", hops) + } + // Check announce history announceKey := fmt.Sprintf("%x", a.Hash()) r.announceHistoryMu.Lock() @@ -581,15 +593,29 @@ func (h *AnnounceHandler) AspectFilter() []string { return h.aspectFilter } -func (h *AnnounceHandler) ReceivedAnnounce(destHash []byte, identity interface{}, appData []byte) error { - debugLog(3, "Received announce from %x", destHash) +func (h *AnnounceHandler) ReceivedAnnounce(destHash []byte, id interface{}, appData []byte) error { + debugLog(DEBUG_INFO, "Received announce from %x", destHash) if len(appData) > 0 { - debugLog(3, "Announce contained app data: %s", string(appData)) + debugLog(DEBUG_VERBOSE, "Announce app data: %s", string(appData)) } - if id, ok := identity.([]byte); ok { - debugLog(4, "Identity: %x", id) + // Type assert using the package path + if identity, ok := id.(*identity.Identity); ok { + debugLog(DEBUG_ALL, "Identity details:") + debugLog(DEBUG_ALL, " Hash: %s", identity.GetHexHash()) + debugLog(DEBUG_ALL, " Public Key: %x", identity.GetPublicKey()) + + ratchets := identity.GetRatchets() + debugLog(DEBUG_ALL, " Active Ratchets: %d", len(ratchets)) + + if len(ratchets) > 0 { + ratchetKey := identity.GetCurrentRatchetKey() + if ratchetKey != nil { + ratchetID := identity.GetRatchetID(ratchetKey) + debugLog(DEBUG_ALL, " Current Ratchet ID: %x", ratchetID) + } + } } return nil diff --git a/pkg/destination/destination.go b/pkg/destination/destination.go index 09e014c..cf9484f 100644 --- a/pkg/destination/destination.go +++ b/pkg/destination/destination.go @@ -50,7 +50,7 @@ type Destination struct { destType byte appName string aspects []string - hash []byte + hashValue []byte acceptsLinks bool proofStrategy byte @@ -95,12 +95,12 @@ func New(id *identity.Identity, direction byte, destType byte, appName string, a } // Generate destination hash - d.hash = d.Hash() + d.hashValue = d.calculateHash() return d, nil } -func (d *Destination) Hash() []byte { +func (d *Destination) calculateHash() []byte { nameHash := sha256.Sum256([]byte(d.ExpandName())) identityHash := sha256.Sum256(d.identity.GetPublicKey()) @@ -134,8 +134,8 @@ func (d *Destination) Announce(appData []byte) error { packet := make([]byte, 0) // Add destination hash - packet = append(packet, d.hash...) - log.Printf("[DEBUG-4] Added destination hash %x to announce", d.hash[:8]) + packet = append(packet, d.hashValue...) + log.Printf("[DEBUG-4] Added destination hash %x to announce", d.hashValue[:8]) // Add identity public key pubKey := d.identity.GetPublicKey() @@ -386,3 +386,21 @@ func (d *Destination) GetPublicKey() []byte { func (d *Destination) GetIdentity() *identity.Identity { return d.identity } + +func (d *Destination) GetType() byte { + return d.destType +} + +func (d *Destination) GetHash() []byte { + d.mutex.RLock() + defer d.mutex.RUnlock() + if d.hashValue == nil { + d.mutex.RUnlock() + d.mutex.Lock() + defer d.mutex.Unlock() + if d.hashValue == nil { + d.hashValue = d.calculateHash() + } + } + return d.hashValue +} diff --git a/pkg/identity/identity.go b/pkg/identity/identity.go index 9822745..f8fac50 100644 --- a/pkg/identity/identity.go +++ b/pkg/identity/identity.go @@ -17,6 +17,8 @@ import ( "encoding/hex" + "log" + "github.com/Sudo-Ivan/reticulum-go/pkg/common" "golang.org/x/crypto/curve25519" "golang.org/x/crypto/hkdf" @@ -35,6 +37,9 @@ const ( AES128_BLOCKSIZE = 16 HASHLENGTH = 256 SIGLENGTH = KEYSIZE + + RATCHET_ROTATION_INTERVAL = 1800 // Default 30 minutes in seconds + MAX_RETAINED_RATCHETS = 512 // Maximum number of retained ratchet keys ) type Identity struct { @@ -116,28 +121,33 @@ func New() (*Identity, error) { i := &Identity{ ratchets: make(map[string][]byte), ratchetExpiry: make(map[string]int64), + mutex: &sync.RWMutex{}, } // Generate X25519 key pair i.privateKey = make([]byte, curve25519.ScalarSize) if _, err := io.ReadFull(rand.Reader, i.privateKey); err != nil { + log.Printf("[DEBUG-1] Failed to generate X25519 private key: %v", err) return nil, err } var err error i.publicKey, err = curve25519.X25519(i.privateKey, curve25519.Basepoint) if err != nil { + log.Printf("[DEBUG-1] Failed to generate X25519 public key: %v", err) return nil, err } // Generate Ed25519 signing keypair pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) if err != nil { + log.Printf("[DEBUG-1] Failed to generate Ed25519 keypair: %v", err) return nil, err } i.signingKey = privKey i.verificationKey = pubKey + log.Printf("[DEBUG-7] Created new identity with hash: %x", i.Hash()) return i, nil } @@ -162,9 +172,15 @@ func (i *Identity) Verify(data []byte, signature []byte) bool { func (i *Identity) Encrypt(plaintext []byte, ratchet []byte) ([]byte, error) { if i.publicKey == nil { + log.Printf("[DEBUG-1] Encryption failed: identity has no public key") return nil, errors.New("encryption failed: identity does not hold a public key") } + log.Printf("[DEBUG-7] Starting encryption for identity %s", i.GetHexHash()) + if ratchet != nil { + log.Printf("[DEBUG-7] Using ratchet for encryption") + } + // Generate ephemeral keypair ephemeralPrivKey := make([]byte, curve25519.ScalarSize) if _, err := io.ReadFull(rand.Reader, ephemeralPrivKey); err != nil { @@ -232,6 +248,7 @@ func (i *Identity) Encrypt(plaintext []byte, ratchet []byte) ([]byte, error) { token = append(token, ciphertext...) token = append(token, mac...) + log.Printf("[DEBUG-7] Encryption completed successfully") return token, nil } @@ -270,12 +287,11 @@ func Remember(packet []byte, destHash []byte, publicKey []byte, appData []byte) func ValidateAnnounce(packet []byte, destHash []byte, publicKey []byte, signature []byte, appData []byte) bool { if len(publicKey) != KEYSIZE/8 { + log.Printf("[DEBUG-7] Invalid public key length: %d", len(publicKey)) return false } - if len(destHash) > TRUNCATED_HASHLENGTH/8 { - destHash = destHash[:TRUNCATED_HASHLENGTH/8] - } + log.Printf("[DEBUG-7] Validating announce for destination hash: %x", destHash) announced := &Identity{} announced.publicKey = publicKey[:KEYSIZE/16] @@ -285,10 +301,12 @@ func ValidateAnnounce(packet []byte, destHash []byte, publicKey []byte, signatur signedData = append(signedData, appData...) if !announced.Verify(signedData, signature) { + log.Printf("[DEBUG-7] Signature verification failed") return false } Remember(packet, destHash, publicKey, appData) + log.Printf("[DEBUG-7] Announce validated and remembered successfully") return true } @@ -369,9 +387,15 @@ func (i *Identity) GetCurrentRatchetKey() []byte { func (i *Identity) Decrypt(ciphertextToken []byte, ratchets [][]byte, enforceRatchets bool, ratchetIDReceiver *common.RatchetIDReceiver) ([]byte, error) { if i.privateKey == nil { + log.Printf("[DEBUG-1] Decryption failed: identity has no private key") return nil, errors.New("decryption failed because identity does not hold a private key") } + log.Printf("[DEBUG-7] Starting decryption for identity %s", i.GetHexHash()) + if len(ratchets) > 0 { + log.Printf("[DEBUG-7] Attempting decryption with %d ratchets", len(ratchets)) + } + if len(ciphertextToken) <= KEYSIZE/8/2 { return nil, errors.New("decryption failed because the token size was invalid") } @@ -450,6 +474,7 @@ func (i *Identity) Decrypt(ciphertextToken []byte, ratchets [][]byte, enforceRat ratchetIDReceiver.LatestRatchetID = nil } + log.Printf("[DEBUG-7] Decryption completed successfully") return plaintext[:len(plaintext)-padding], nil } @@ -461,13 +486,17 @@ func (i *Identity) tryRatchetDecryption(peerPubBytes, ciphertext, ratchet []byte // Get ratchet ID ratchetPubBytes, err := curve25519.X25519(ratchetPriv, curve25519.Basepoint) if err != nil { + log.Printf("[DEBUG-7] Failed to generate ratchet public key: %v", err) return nil, nil, err } ratchetID := i.GetRatchetID(ratchetPubBytes) + log.Printf("[DEBUG-7] Decrypting with ratchet ID: %x", ratchetID) + // Generate shared key sharedKey, err := curve25519.X25519(ratchetPriv, peerPubBytes) if err != nil { + log.Printf("[DEBUG-7] Failed to generate shared key: %v", err) return nil, nil, err } @@ -475,17 +504,20 @@ func (i *Identity) tryRatchetDecryption(peerPubBytes, ciphertext, ratchet []byte hkdfReader := hkdf.New(sha256.New, sharedKey, i.GetSalt(), i.GetContext()) derivedKey := make([]byte, 32) if _, err := io.ReadFull(hkdfReader, derivedKey); err != nil { + log.Printf("[DEBUG-7] Failed to derive key: %v", err) return nil, nil, err } // Create AES cipher block, err := aes.NewCipher(derivedKey) if err != nil { + log.Printf("[DEBUG-7] Failed to create cipher: %v", err) return nil, nil, err } // Extract IV and decrypt if len(ciphertext) < aes.BlockSize { + log.Printf("[DEBUG-7] Ciphertext too short") return nil, nil, errors.New("ciphertext too short") } @@ -493,6 +525,7 @@ func (i *Identity) tryRatchetDecryption(peerPubBytes, ciphertext, ratchet []byte actualCiphertext := ciphertext[aes.BlockSize:] if len(actualCiphertext)%aes.BlockSize != 0 { + log.Printf("[DEBUG-7] Ciphertext is not a multiple of block size") return nil, nil, errors.New("ciphertext is not a multiple of block size") } @@ -504,15 +537,18 @@ func (i *Identity) tryRatchetDecryption(peerPubBytes, ciphertext, ratchet []byte // Remove padding padding := int(plaintext[len(plaintext)-1]) if padding > aes.BlockSize || padding == 0 { + log.Printf("[DEBUG-7] Invalid padding") return nil, nil, errors.New("invalid padding") } for i := len(plaintext) - padding; i < len(plaintext); i++ { if plaintext[i] != byte(padding) { + log.Printf("[DEBUG-7] Invalid padding") return nil, nil, errors.New("invalid padding") } } + log.Printf("[DEBUG-7] Decrypted successfully") return plaintext[:len(plaintext)-padding], ratchetID, nil } @@ -555,6 +591,8 @@ func (i *Identity) DecryptWithHMAC(data []byte, key []byte) ([]byte, error) { } func (i *Identity) ToFile(path string) error { + log.Printf("[DEBUG-7] Saving identity %s to file: %s", i.GetHexHash(), path) + data := map[string]interface{}{ "private_key": i.privateKey, "public_key": i.publicKey, @@ -565,26 +603,36 @@ func (i *Identity) ToFile(path string) error { file, err := os.Create(path) if err != nil { + log.Printf("[DEBUG-1] Failed to create identity file: %v", err) return err } defer file.Close() - return json.NewEncoder(file).Encode(data) + if err := json.NewEncoder(file).Encode(data); err != nil { + log.Printf("[DEBUG-1] Failed to encode identity data: %v", err) + return err + } + + log.Printf("[DEBUG-7] Identity saved successfully") + return nil } func RecallIdentity(path string) (*Identity, error) { + log.Printf("[DEBUG-7] Attempting to recall identity from: %s", path) + file, err := os.Open(path) if err != nil { + log.Printf("[DEBUG-1] Failed to open identity file: %v", err) return nil, err } defer file.Close() var data map[string]interface{} if err := json.NewDecoder(file).Decode(&data); err != nil { + log.Printf("[DEBUG-1] Failed to decode identity data: %v", err) return nil, err } - // Reconstruct identity from saved data id := &Identity{ privateKey: data["private_key"].([]byte), publicKey: data["public_key"].([]byte), @@ -593,8 +641,10 @@ func RecallIdentity(path string) (*Identity, error) { appData: data["app_data"].([]byte), ratchets: make(map[string][]byte), ratchetExpiry: make(map[string]int64), + mutex: &sync.RWMutex{}, } + log.Printf("[DEBUG-7] Successfully recalled identity with hash: %s", id.GetHexHash()) return id, nil } @@ -686,3 +736,98 @@ func NewIdentity() (*Identity, error) { return i, nil } + +func (i *Identity) RotateRatchet() ([]byte, error) { + i.mutex.Lock() + defer i.mutex.Unlock() + + log.Printf("[DEBUG-7] Rotating ratchet for identity %s", i.GetHexHash()) + + // Generate new ratchet key + newRatchet := make([]byte, RATCHETSIZE/8) + if _, err := io.ReadFull(rand.Reader, newRatchet); err != nil { + log.Printf("[DEBUG-1] Failed to generate new ratchet: %v", err) + return nil, err + } + + // Get public key for ratchet ID + ratchetPub, err := curve25519.X25519(newRatchet, curve25519.Basepoint) + if err != nil { + log.Printf("[DEBUG-1] Failed to generate ratchet public key: %v", err) + return nil, err + } + + ratchetID := i.GetRatchetID(ratchetPub) + expiry := time.Now().Unix() + RATCHET_EXPIRY + + // Store new ratchet + i.ratchets[string(ratchetID)] = newRatchet + i.ratchetExpiry[string(ratchetID)] = expiry + + log.Printf("[DEBUG-7] New ratchet generated with ID: %x, expiry: %d", ratchetID, expiry) + + // Cleanup old ratchets if we exceed max retained + if len(i.ratchets) > MAX_RETAINED_RATCHETS { + var oldestID string + oldestTime := time.Now().Unix() + + for id, exp := range i.ratchetExpiry { + if exp < oldestTime { + oldestTime = exp + oldestID = id + } + } + + delete(i.ratchets, oldestID) + delete(i.ratchetExpiry, oldestID) + log.Printf("[DEBUG-7] Cleaned up oldest ratchet with ID: %x", []byte(oldestID)) + } + + log.Printf("[DEBUG-7] Current number of active ratchets: %d", len(i.ratchets)) + return newRatchet, nil +} + +func (i *Identity) GetRatchets() [][]byte { + i.mutex.RLock() + defer i.mutex.RUnlock() + + log.Printf("[DEBUG-7] Getting ratchets for identity %s", i.GetHexHash()) + + ratchets := make([][]byte, 0, len(i.ratchets)) + now := time.Now().Unix() + expired := 0 + + // Return only non-expired ratchets + for id, expiry := range i.ratchetExpiry { + if expiry > now { + ratchets = append(ratchets, i.ratchets[id]) + } else { + // Clean up expired ratchets + delete(i.ratchets, id) + delete(i.ratchetExpiry, id) + expired++ + } + } + + log.Printf("[DEBUG-7] Retrieved %d active ratchets, cleaned up %d expired", len(ratchets), expired) + return ratchets +} + +func (i *Identity) CleanupExpiredRatchets() { + i.mutex.Lock() + defer i.mutex.Unlock() + + log.Printf("[DEBUG-7] Starting ratchet cleanup for identity %s", i.GetHexHash()) + + now := time.Now().Unix() + cleaned := 0 + for id, expiry := range i.ratchetExpiry { + if expiry <= now { + delete(i.ratchets, id) + delete(i.ratchetExpiry, id) + cleaned++ + } + } + + log.Printf("[DEBUG-7] Cleaned up %d expired ratchets, %d remaining", cleaned, len(i.ratchets)) +} diff --git a/pkg/link/link.go b/pkg/link/link.go index a1c20a3..0d361ea 100644 --- a/pkg/link/link.go +++ b/pkg/link/link.go @@ -5,7 +5,7 @@ import ( "crypto/cipher" "crypto/ed25519" "crypto/rand" - "crypto/sha256" + "encoding/hex" "errors" "fmt" "io" @@ -16,6 +16,8 @@ import ( "github.com/Sudo-Ivan/reticulum-go/pkg/destination" "github.com/Sudo-Ivan/reticulum-go/pkg/identity" "github.com/Sudo-Ivan/reticulum-go/pkg/packet" + "github.com/Sudo-Ivan/reticulum-go/pkg/pathfinder" + "github.com/Sudo-Ivan/reticulum-go/pkg/resolver" "github.com/Sudo-Ivan/reticulum-go/pkg/resource" "github.com/Sudo-Ivan/reticulum-go/pkg/transport" ) @@ -38,10 +40,6 @@ const ( STATUS_CLOSED = 0x02 STATUS_FAILED = 0x03 - PACKET_TYPE_DATA = 0x00 - PACKET_TYPE_LINK = 0x01 - PACKET_TYPE_IDENTIFY = 0x02 - PROVE_NONE = 0x00 PROVE_ALL = 0x01 PROVE_APP = 0x02 @@ -56,6 +54,7 @@ type Link struct { lastOutbound time.Time lastDataReceived time.Time lastDataSent time.Time + pathFinder *pathfinder.PathFinder remoteIdentity *identity.Identity sessionKey []byte @@ -97,6 +96,7 @@ func NewLink(dest *destination.Destination, transport *transport.Transport, esta lastOutbound: time.Time{}, lastDataReceived: time.Time{}, lastDataSent: time.Time{}, + pathFinder: pathfinder.NewPathFinder(), } } @@ -117,16 +117,21 @@ func (l *Link) Establish() error { log.Printf("[DEBUG-4] Creating link request packet for destination %x", destPublicKey[:8]) - // Create link request packet - p, err := packet.NewPacket( - packet.PACKET_TYPE_LINK, - 0x00, - 0x00, - destPublicKey, - l.linkID, - ) - if err != nil { - log.Printf("[DEBUG-3] Failed to create link request packet: %v", err) + p := &packet.Packet{ + HeaderType: packet.HeaderType1, + PacketType: packet.PacketTypeLinkReq, + TransportType: 0, + Context: packet.ContextLinkIdentify, + ContextFlag: packet.FlagUnset, + Hops: 0, + DestinationType: l.destination.GetType(), + DestinationHash: l.destination.GetHash(), + Data: l.linkID, + CreateReceipt: true, + } + + if err := p.Pack(); err != nil { + log.Printf("[DEBUG-3] Failed to pack link request packet: %v", err) return err } @@ -139,15 +144,20 @@ func (l *Link) Identify(id *identity.Identity) error { return errors.New("link not active") } - // Create identify packet - p, err := packet.NewPacket( - packet.PACKET_TYPE_IDENTIFY, - 0x00, - 0x00, - l.destination.GetPublicKey(), - id.GetPublicKey(), - ) - if err != nil { + p := &packet.Packet{ + HeaderType: packet.HeaderType1, + PacketType: packet.PacketTypeData, + TransportType: 0, + Context: packet.ContextLinkIdentify, + ContextFlag: packet.FlagUnset, + Hops: 0, + DestinationType: l.destination.GetType(), + DestinationHash: l.destination.GetHash(), + Data: id.GetPublicKey(), + CreateReceipt: true, + } + + if err := p.Pack(); err != nil { return err } @@ -466,11 +476,28 @@ func (l *Link) SendPacket(data []byte) error { return err } + p := &packet.Packet{ + HeaderType: packet.HeaderType1, + PacketType: packet.PacketTypeData, + TransportType: 0, + Context: packet.ContextNone, + ContextFlag: packet.FlagUnset, + Hops: 0, + DestinationType: l.destination.GetType(), + DestinationHash: l.destination.GetHash(), + Data: encrypted, + CreateReceipt: false, + } + + if err := p.Pack(); err != nil { + return err + } + log.Printf("[DEBUG-4] Sending encrypted packet of %d bytes", len(encrypted)) l.lastOutbound = time.Now() l.lastDataSent = time.Now() - return nil + return l.transport.SendPacket(p) } func (l *Link) HandleInbound(data []byte) error { @@ -482,55 +509,24 @@ func (l *Link) HandleInbound(data []byte) error { return errors.New("link not active") } - log.Printf("[DEBUG-7] Received encrypted packet of %d bytes", len(data)) + // Decode and log packet details + l.decodePacket(data) - // Decrypt data using session key - decryptedData, err := l.decrypt(data) - if err != nil { - log.Printf("[DEBUG-3] Failed to decrypt packet: %v", err) - return err - } - - // Split message and HMAC - if len(decryptedData) < sha256.Size { - log.Printf("[DEBUG-3] Received data too short: %d bytes", len(decryptedData)) - return errors.New("received data too short") - } - - message := decryptedData[:len(decryptedData)-sha256.Size] - messageHMAC := decryptedData[len(decryptedData)-sha256.Size:] - - // Log packet details - log.Printf("[DEBUG-7] Decrypted packet details:") - log.Printf("[DEBUG-7] - Size: %d bytes", len(message)) - log.Printf("[DEBUG-7] - First 16 bytes: %x", message[:min(16, len(message))]) - if len(message) > 0 { - log.Printf("[DEBUG-7] - Type: 0x%02x", message[0]) - switch message[0] { - case packet.PacketData: - log.Printf("[DEBUG-7] - Type: Data Packet") - case packet.PacketAnnounce: - log.Printf("[DEBUG-7] - Type: Announce Packet") - case packet.PacketLinkRequest: - log.Printf("[DEBUG-7] - Type: Link Request") - case packet.PacketProof: - log.Printf("[DEBUG-7] - Type: Proof Request") - default: - log.Printf("[DEBUG-7] - Type: Unknown (0x%02x)", message[0]) + // Decrypt if we have a session key + if l.sessionKey != nil { + decrypted, err := l.decrypt(data) + if err != nil { + log.Printf("[DEBUG-3] Failed to decrypt packet: %v", err) + return err } - } - - // Verify HMAC - if !l.destination.GetIdentity().ValidateHMAC(l.hmacKey, message, messageHMAC) { - log.Printf("[DEBUG-3] Invalid HMAC for packet") - return errors.New("invalid message authentication code") + data = decrypted } l.lastInbound = time.Now() l.lastDataReceived = time.Now() if l.packetCallback != nil { - l.packetCallback(message, nil) + l.packetCallback(data, nil) } return nil @@ -737,6 +733,69 @@ func (l *Link) HandleProofRequest(packet *packet.Packet) bool { } } +func (l *Link) decodePacket(data []byte) { + if len(data) < 1 { + log.Printf("[DEBUG-7] Invalid packet: zero length") + return + } + + packetType := data[0] + log.Printf("[DEBUG-7] Packet Analysis:") + log.Printf("[DEBUG-7] - Size: %d bytes", len(data)) + log.Printf("[DEBUG-7] - Type: 0x%02x", packetType) + + switch packetType { + case packet.PacketTypeData: + log.Printf("[DEBUG-7] - Type Description: Data Packet") + if len(data) > 1 { + log.Printf("[DEBUG-7] - Payload Size: %d bytes", len(data)-1) + } + + case packet.PacketTypeLinkReq: + log.Printf("[DEBUG-7] - Type Description: Link Management") + if len(data) > 32 { + log.Printf("[DEBUG-7] - Link ID: %x", data[1:33]) + } + + case packet.PacketTypeAnnounce: + log.Printf("[DEBUG-7] - Type Description: RNS Announce") + if len(data) > 33 { + destHash := data[1:17] + pubKey := data[17:49] + log.Printf("[DEBUG-7] - Destination Hash: %x", destHash) + log.Printf("[DEBUG-7] - Public Key: %x", pubKey) + + if len(data) > 81 { + signature := data[49:81] + appData := data[81:] + if identity.ValidateAnnounce(data, destHash, pubKey, signature, appData) { + log.Printf("[DEBUG-7] - Announce signature valid") + + if path, ok := l.pathFinder.GetPath(hex.EncodeToString(destHash)); ok { + log.Printf("[DEBUG-7] - Updated path: Interface=%s, Hops=%d", + path.Interface, path.HopCount) + } + } + } + } + + case packet.PacketTypeProof: + log.Printf("[DEBUG-7] - Type Description: RNS Discovery") + if len(data) > 17 { + searchHash := data[1:17] + log.Printf("[DEBUG-7] - Searching for Hash: %x", searchHash) + + if id, err := resolver.ResolveIdentity(hex.EncodeToString(searchHash)); err == nil { + log.Printf("[DEBUG-7] - Found matching identity: %s", id.GetHexHash()) + } + } + + default: + log.Printf("[DEBUG-7] - Type Description: Unknown (0x%02x)", packetType) + log.Printf("[DEBUG-7] - Raw Hex: %x", data) + } +} + // Helper function for min of two ints func min(a, b int) int { if a < b { diff --git a/pkg/packet/constants.go b/pkg/packet/constants.go index 690ef02..ed9206f 100644 --- a/pkg/packet/constants.go +++ b/pkg/packet/constants.go @@ -5,10 +5,6 @@ const ( EncryptedMDU = 383 // Maximum size of payload data in encrypted packet PlainMDU = 464 // Maximum size of payload data in unencrypted packet - // Header Types - HeaderType1 = 0 // Two byte header, one 16 byte address field - HeaderType2 = 1 // Two byte header, two 16 byte address fields - // Propagation Types PropagationBroadcast = 0 PropagationTransport = 1 diff --git a/pkg/packet/packet.go b/pkg/packet/packet.go index 1eb561d..f9598e6 100644 --- a/pkg/packet/packet.go +++ b/pkg/packet/packet.go @@ -1,116 +1,208 @@ package packet import ( + "crypto/sha256" "errors" + "fmt" "time" ) const ( // Packet Types - PacketTypeData = 0x00 - PacketTypeAnnounce = 0x01 - PacketTypeLink = 0x02 - PacketTypeProof = 0x03 - PACKET_TYPE_DATA = 0x00 - PACKET_TYPE_LINK = 0x01 - PACKET_TYPE_IDENTIFY = 0x02 + PacketTypeData = 0x00 + PacketTypeAnnounce = 0x01 + PacketTypeLinkReq = 0x02 + PacketTypeProof = 0x03 - // Sizes - HeaderSize = 2 - AddressSize = 16 - ContextSize = 1 - MaxDataSize = 465 - RandomBlobSize = 16 + // Header Types + HeaderType1 = 0x00 + HeaderType2 = 0x01 + + // Context Types + ContextNone = 0x00 + ContextResource = 0x01 + ContextResourceAdv = 0x02 + ContextResourceReq = 0x03 + ContextResourceHMU = 0x04 + ContextResourcePRF = 0x05 + ContextResourceICL = 0x06 + ContextResourceRCL = 0x07 + ContextCacheReq = 0x08 + ContextRequest = 0x09 + ContextResponse = 0x0A + ContextPathResponse = 0x0B + ContextCommand = 0x0C + ContextCmdStatus = 0x0D + ContextChannel = 0x0E + ContextKeepalive = 0xFA + ContextLinkIdentify = 0xFB + ContextLinkClose = 0xFC + ContextLinkProof = 0xFD + ContextLRRTT = 0xFE + ContextLRProof = 0xFF + + // Flag Values + FlagSet = 0x01 + FlagUnset = 0x00 + + // Header sizes + HeaderMaxSize = 64 + MTU = 500 + + AddressSize = 32 // Size of address/hash fields in bytes ) -// Header flags and types -const ( - // First byte flags - IFACFlag = 0x80 - HeaderTypeFlag = 0x40 - ContextFlag = 0x20 - PropagationFlags = 0x18 - DestinationFlags = 0x06 - PacketTypeFlags = 0x01 - - // Second byte - HopsField = 0xFF -) - -// Packet represents a network packet in the Reticulum protocol type Packet struct { - Header [2]byte - Addresses []byte - Context byte - Data []byte - AccessCode []byte - RandomBlob []byte - Timestamp time.Time + HeaderType byte + PacketType byte + TransportType byte + Context byte + ContextFlag byte + Hops byte + + DestinationType byte + DestinationHash []byte + TransportID []byte + Data []byte + + Raw []byte + Packed bool + Sent bool + CreateReceipt bool + FromPacked bool + + SentAt time.Time + PacketHash []byte + RatchetID []byte + + RSSI *float64 + SNR *float64 + Q *float64 + + Addresses []byte // Add this field for address storage } -// NewPacket creates a new packet with the specified parameters -func NewPacket(packetType byte, flags byte, hops byte, destKey []byte, data []byte) (*Packet, error) { - if len(destKey) != AddressSize { - return nil, errors.New("invalid destination key length") +func NewPacket(destType byte, data []byte, packetType byte, context byte, + transportType byte, headerType byte, transportID []byte, createReceipt bool, + contextFlag byte) *Packet { + + return &Packet{ + HeaderType: headerType, + PacketType: packetType, + TransportType: transportType, + Context: context, + ContextFlag: contextFlag, + Hops: 0, + DestinationType: destType, + Data: data, + TransportID: transportID, + CreateReceipt: createReceipt, + Packed: false, + Sent: false, + FromPacked: false, } - - p := &Packet{ - Header: [2]byte{flags, hops}, - Addresses: make([]byte, AddressSize), - Data: data, - Timestamp: time.Now(), - } - - // Set packet type in flags - p.Header[0] |= packetType & PacketTypeFlags - - // Copy destination address - copy(p.Addresses, destKey) - - return p, nil } -// Serialize converts the packet into a byte slice +func (p *Packet) Pack() error { + if p.Packed { + return nil + } + + flags := (p.HeaderType << 6) | (p.ContextFlag << 5) | + (p.TransportType << 4) | (p.DestinationType << 2) | p.PacketType + + header := make([]byte, 0) + header = append(header, flags) + header = append(header, p.Hops) + + if p.HeaderType == HeaderType2 && p.TransportID != nil { + header = append(header, p.TransportID...) + header = append(header, p.DestinationHash...) + } else if p.HeaderType == HeaderType1 { + header = append(header, p.DestinationHash...) + } else { + return errors.New("invalid header configuration") + } + + header = append(header, p.Context) + p.Raw = append(header, p.Data...) + + if len(p.Raw) > MTU { + return errors.New("packet size exceeds MTU") + } + + p.Packed = true + p.updateHash() + return nil +} + +func (p *Packet) Unpack() error { + if len(p.Raw) < 3 { + return errors.New("packet too short") + } + + flags := p.Raw[0] + p.Hops = p.Raw[1] + + p.HeaderType = (flags & 0b01000000) >> 6 + p.ContextFlag = (flags & 0b00100000) >> 5 + p.TransportType = (flags & 0b00010000) >> 4 + p.DestinationType = (flags & 0b00001100) >> 2 + p.PacketType = flags & 0b00000011 + + dstLen := 16 // Truncated hash length + + if p.HeaderType == HeaderType2 { + if len(p.Raw) < 2*dstLen+3 { + return errors.New("packet too short for header type 2") + } + p.TransportID = p.Raw[2 : dstLen+2] + p.DestinationHash = p.Raw[dstLen+2 : 2*dstLen+2] + p.Context = p.Raw[2*dstLen+2] + p.Data = p.Raw[2*dstLen+3:] + } else { + if len(p.Raw) < dstLen+3 { + return errors.New("packet too short for header type 1") + } + p.TransportID = nil + p.DestinationHash = p.Raw[2 : dstLen+2] + p.Context = p.Raw[dstLen+2] + p.Data = p.Raw[dstLen+3:] + } + + p.Packed = false + p.updateHash() + return nil +} + +func (p *Packet) GetHash() []byte { + hashable := p.getHashablePart() + hash := sha256.Sum256(hashable) + return hash[:] +} + +func (p *Packet) getHashablePart() []byte { + hashable := []byte{p.Raw[0] & 0b00001111} + if p.HeaderType == HeaderType2 { + hashable = append(hashable, p.Raw[18:]...) + } else { + hashable = append(hashable, p.Raw[2:]...) + } + return hashable +} + +func (p *Packet) updateHash() { + p.PacketHash = p.GetHash() +} + func (p *Packet) Serialize() ([]byte, error) { - totalSize := HeaderSize + len(p.Addresses) + ContextSize + len(p.Data) - if p.AccessCode != nil { - totalSize += len(p.AccessCode) + if !p.Packed { + if err := p.Pack(); err != nil { + return nil, fmt.Errorf("failed to pack packet: %w", err) + } } - buffer := make([]byte, totalSize) - offset := 0 + p.Addresses = p.DestinationHash - // Write header - copy(buffer[offset:], p.Header[:]) - offset += HeaderSize - - // Write access code if present - if p.AccessCode != nil { - copy(buffer[offset:], p.AccessCode) - offset += len(p.AccessCode) - } - - // Write addresses - copy(buffer[offset:], p.Addresses) - offset += len(p.Addresses) - - // Write context - buffer[offset] = p.Context - offset += ContextSize - - // Write data - copy(buffer[offset:], p.Data) - - return buffer, nil -} - -type AnnouncePacket struct { - Header [2]byte - DestHash []byte - PublicKey []byte - AppData []byte - RandomBlob []byte - Signature []byte - HopCount byte - Timestamp time.Time + return p.Raw, nil }