update
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
191
pkg/link/link.go
191
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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user