update
This commit is contained in:
@@ -43,7 +43,7 @@ const (
|
|||||||
DEBUG_VERBOSE = 4 // Detailed information
|
DEBUG_VERBOSE = 4 // Detailed information
|
||||||
DEBUG_TRACE = 5 // Very detailed tracing
|
DEBUG_TRACE = 5 // Very detailed tracing
|
||||||
DEBUG_PACKETS = 6 // Packet-level details
|
DEBUG_PACKETS = 6 // Packet-level details
|
||||||
DEBUG_ALL = 7 // Everything
|
DEBUG_ALL = 7 // Everything including identity operations
|
||||||
)
|
)
|
||||||
|
|
||||||
type Reticulum struct {
|
type Reticulum struct {
|
||||||
@@ -336,11 +336,10 @@ func (tw *transportWrapper) GetStatus() int {
|
|||||||
|
|
||||||
func (tw *transportWrapper) Send(data []byte) interface{} {
|
func (tw *transportWrapper) Send(data []byte) interface{} {
|
||||||
p := &packet.Packet{
|
p := &packet.Packet{
|
||||||
Header: [2]byte{
|
PacketType: packet.PacketTypeData,
|
||||||
packet.PacketTypeData, // First byte
|
Hops: 0,
|
||||||
0, // Second byte (hops)
|
Data: data,
|
||||||
},
|
HeaderType: packet.HeaderType1,
|
||||||
Data: data,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := tw.Transport.SendPacket(p)
|
err := tw.Transport.SendPacket(p)
|
||||||
@@ -490,14 +489,27 @@ func (r *Reticulum) Stop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reticulum) handleAnnounce(data []byte, iface common.NetworkInterface) {
|
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{}
|
a := &announce.Announce{}
|
||||||
if err := a.HandleAnnounce(data); err != nil {
|
if err := a.HandleAnnounce(data); err != nil {
|
||||||
debugLog(1, "Error handling announce: %v", err)
|
debugLog(DEBUG_ERROR, "Error handling announce: %v", err)
|
||||||
return
|
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
|
// Check announce history
|
||||||
announceKey := fmt.Sprintf("%x", a.Hash())
|
announceKey := fmt.Sprintf("%x", a.Hash())
|
||||||
r.announceHistoryMu.Lock()
|
r.announceHistoryMu.Lock()
|
||||||
@@ -581,15 +593,29 @@ func (h *AnnounceHandler) AspectFilter() []string {
|
|||||||
return h.aspectFilter
|
return h.aspectFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *AnnounceHandler) ReceivedAnnounce(destHash []byte, identity interface{}, appData []byte) error {
|
func (h *AnnounceHandler) ReceivedAnnounce(destHash []byte, id interface{}, appData []byte) error {
|
||||||
debugLog(3, "Received announce from %x", destHash)
|
debugLog(DEBUG_INFO, "Received announce from %x", destHash)
|
||||||
|
|
||||||
if len(appData) > 0 {
|
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 {
|
// Type assert using the package path
|
||||||
debugLog(4, "Identity: %x", id)
|
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
|
return nil
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ type Destination struct {
|
|||||||
destType byte
|
destType byte
|
||||||
appName string
|
appName string
|
||||||
aspects []string
|
aspects []string
|
||||||
hash []byte
|
hashValue []byte
|
||||||
|
|
||||||
acceptsLinks bool
|
acceptsLinks bool
|
||||||
proofStrategy byte
|
proofStrategy byte
|
||||||
@@ -95,12 +95,12 @@ func New(id *identity.Identity, direction byte, destType byte, appName string, a
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate destination hash
|
// Generate destination hash
|
||||||
d.hash = d.Hash()
|
d.hashValue = d.calculateHash()
|
||||||
|
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Destination) Hash() []byte {
|
func (d *Destination) calculateHash() []byte {
|
||||||
nameHash := sha256.Sum256([]byte(d.ExpandName()))
|
nameHash := sha256.Sum256([]byte(d.ExpandName()))
|
||||||
identityHash := sha256.Sum256(d.identity.GetPublicKey())
|
identityHash := sha256.Sum256(d.identity.GetPublicKey())
|
||||||
|
|
||||||
@@ -134,8 +134,8 @@ func (d *Destination) Announce(appData []byte) error {
|
|||||||
packet := make([]byte, 0)
|
packet := make([]byte, 0)
|
||||||
|
|
||||||
// Add destination hash
|
// Add destination hash
|
||||||
packet = append(packet, d.hash...)
|
packet = append(packet, d.hashValue...)
|
||||||
log.Printf("[DEBUG-4] Added destination hash %x to announce", d.hash[:8])
|
log.Printf("[DEBUG-4] Added destination hash %x to announce", d.hashValue[:8])
|
||||||
|
|
||||||
// Add identity public key
|
// Add identity public key
|
||||||
pubKey := d.identity.GetPublicKey()
|
pubKey := d.identity.GetPublicKey()
|
||||||
@@ -386,3 +386,21 @@ func (d *Destination) GetPublicKey() []byte {
|
|||||||
func (d *Destination) GetIdentity() *identity.Identity {
|
func (d *Destination) GetIdentity() *identity.Identity {
|
||||||
return d.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"
|
"encoding/hex"
|
||||||
|
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
|
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
|
||||||
"golang.org/x/crypto/curve25519"
|
"golang.org/x/crypto/curve25519"
|
||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
@@ -35,6 +37,9 @@ const (
|
|||||||
AES128_BLOCKSIZE = 16
|
AES128_BLOCKSIZE = 16
|
||||||
HASHLENGTH = 256
|
HASHLENGTH = 256
|
||||||
SIGLENGTH = KEYSIZE
|
SIGLENGTH = KEYSIZE
|
||||||
|
|
||||||
|
RATCHET_ROTATION_INTERVAL = 1800 // Default 30 minutes in seconds
|
||||||
|
MAX_RETAINED_RATCHETS = 512 // Maximum number of retained ratchet keys
|
||||||
)
|
)
|
||||||
|
|
||||||
type Identity struct {
|
type Identity struct {
|
||||||
@@ -116,28 +121,33 @@ func New() (*Identity, error) {
|
|||||||
i := &Identity{
|
i := &Identity{
|
||||||
ratchets: make(map[string][]byte),
|
ratchets: make(map[string][]byte),
|
||||||
ratchetExpiry: make(map[string]int64),
|
ratchetExpiry: make(map[string]int64),
|
||||||
|
mutex: &sync.RWMutex{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate X25519 key pair
|
// Generate X25519 key pair
|
||||||
i.privateKey = make([]byte, curve25519.ScalarSize)
|
i.privateKey = make([]byte, curve25519.ScalarSize)
|
||||||
if _, err := io.ReadFull(rand.Reader, i.privateKey); err != nil {
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
i.publicKey, err = curve25519.X25519(i.privateKey, curve25519.Basepoint)
|
i.publicKey, err = curve25519.X25519(i.privateKey, curve25519.Basepoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("[DEBUG-1] Failed to generate X25519 public key: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate Ed25519 signing keypair
|
// Generate Ed25519 signing keypair
|
||||||
pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
|
pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("[DEBUG-1] Failed to generate Ed25519 keypair: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
i.signingKey = privKey
|
i.signingKey = privKey
|
||||||
i.verificationKey = pubKey
|
i.verificationKey = pubKey
|
||||||
|
|
||||||
|
log.Printf("[DEBUG-7] Created new identity with hash: %x", i.Hash())
|
||||||
return i, nil
|
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) {
|
func (i *Identity) Encrypt(plaintext []byte, ratchet []byte) ([]byte, error) {
|
||||||
if i.publicKey == nil {
|
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")
|
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
|
// Generate ephemeral keypair
|
||||||
ephemeralPrivKey := make([]byte, curve25519.ScalarSize)
|
ephemeralPrivKey := make([]byte, curve25519.ScalarSize)
|
||||||
if _, err := io.ReadFull(rand.Reader, ephemeralPrivKey); err != nil {
|
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, ciphertext...)
|
||||||
token = append(token, mac...)
|
token = append(token, mac...)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG-7] Encryption completed successfully")
|
||||||
return token, nil
|
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 {
|
func ValidateAnnounce(packet []byte, destHash []byte, publicKey []byte, signature []byte, appData []byte) bool {
|
||||||
if len(publicKey) != KEYSIZE/8 {
|
if len(publicKey) != KEYSIZE/8 {
|
||||||
|
log.Printf("[DEBUG-7] Invalid public key length: %d", len(publicKey))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(destHash) > TRUNCATED_HASHLENGTH/8 {
|
log.Printf("[DEBUG-7] Validating announce for destination hash: %x", destHash)
|
||||||
destHash = destHash[:TRUNCATED_HASHLENGTH/8]
|
|
||||||
}
|
|
||||||
|
|
||||||
announced := &Identity{}
|
announced := &Identity{}
|
||||||
announced.publicKey = publicKey[:KEYSIZE/16]
|
announced.publicKey = publicKey[:KEYSIZE/16]
|
||||||
@@ -285,10 +301,12 @@ func ValidateAnnounce(packet []byte, destHash []byte, publicKey []byte, signatur
|
|||||||
signedData = append(signedData, appData...)
|
signedData = append(signedData, appData...)
|
||||||
|
|
||||||
if !announced.Verify(signedData, signature) {
|
if !announced.Verify(signedData, signature) {
|
||||||
|
log.Printf("[DEBUG-7] Signature verification failed")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
Remember(packet, destHash, publicKey, appData)
|
Remember(packet, destHash, publicKey, appData)
|
||||||
|
log.Printf("[DEBUG-7] Announce validated and remembered successfully")
|
||||||
return true
|
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) {
|
func (i *Identity) Decrypt(ciphertextToken []byte, ratchets [][]byte, enforceRatchets bool, ratchetIDReceiver *common.RatchetIDReceiver) ([]byte, error) {
|
||||||
if i.privateKey == nil {
|
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")
|
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 {
|
if len(ciphertextToken) <= KEYSIZE/8/2 {
|
||||||
return nil, errors.New("decryption failed because the token size was invalid")
|
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
|
ratchetIDReceiver.LatestRatchetID = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG-7] Decryption completed successfully")
|
||||||
return plaintext[:len(plaintext)-padding], nil
|
return plaintext[:len(plaintext)-padding], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,13 +486,17 @@ func (i *Identity) tryRatchetDecryption(peerPubBytes, ciphertext, ratchet []byte
|
|||||||
// Get ratchet ID
|
// Get ratchet ID
|
||||||
ratchetPubBytes, err := curve25519.X25519(ratchetPriv, curve25519.Basepoint)
|
ratchetPubBytes, err := curve25519.X25519(ratchetPriv, curve25519.Basepoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("[DEBUG-7] Failed to generate ratchet public key: %v", err)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
ratchetID := i.GetRatchetID(ratchetPubBytes)
|
ratchetID := i.GetRatchetID(ratchetPubBytes)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG-7] Decrypting with ratchet ID: %x", ratchetID)
|
||||||
|
|
||||||
// Generate shared key
|
// Generate shared key
|
||||||
sharedKey, err := curve25519.X25519(ratchetPriv, peerPubBytes)
|
sharedKey, err := curve25519.X25519(ratchetPriv, peerPubBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("[DEBUG-7] Failed to generate shared key: %v", err)
|
||||||
return nil, nil, 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())
|
hkdfReader := hkdf.New(sha256.New, sharedKey, i.GetSalt(), i.GetContext())
|
||||||
derivedKey := make([]byte, 32)
|
derivedKey := make([]byte, 32)
|
||||||
if _, err := io.ReadFull(hkdfReader, derivedKey); err != nil {
|
if _, err := io.ReadFull(hkdfReader, derivedKey); err != nil {
|
||||||
|
log.Printf("[DEBUG-7] Failed to derive key: %v", err)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create AES cipher
|
// Create AES cipher
|
||||||
block, err := aes.NewCipher(derivedKey)
|
block, err := aes.NewCipher(derivedKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("[DEBUG-7] Failed to create cipher: %v", err)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract IV and decrypt
|
// Extract IV and decrypt
|
||||||
if len(ciphertext) < aes.BlockSize {
|
if len(ciphertext) < aes.BlockSize {
|
||||||
|
log.Printf("[DEBUG-7] Ciphertext too short")
|
||||||
return nil, nil, errors.New("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:]
|
actualCiphertext := ciphertext[aes.BlockSize:]
|
||||||
|
|
||||||
if len(actualCiphertext)%aes.BlockSize != 0 {
|
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")
|
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
|
// Remove padding
|
||||||
padding := int(plaintext[len(plaintext)-1])
|
padding := int(plaintext[len(plaintext)-1])
|
||||||
if padding > aes.BlockSize || padding == 0 {
|
if padding > aes.BlockSize || padding == 0 {
|
||||||
|
log.Printf("[DEBUG-7] Invalid padding")
|
||||||
return nil, nil, errors.New("invalid padding")
|
return nil, nil, errors.New("invalid padding")
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := len(plaintext) - padding; i < len(plaintext); i++ {
|
for i := len(plaintext) - padding; i < len(plaintext); i++ {
|
||||||
if plaintext[i] != byte(padding) {
|
if plaintext[i] != byte(padding) {
|
||||||
|
log.Printf("[DEBUG-7] Invalid padding")
|
||||||
return nil, nil, errors.New("invalid padding")
|
return nil, nil, errors.New("invalid padding")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG-7] Decrypted successfully")
|
||||||
return plaintext[:len(plaintext)-padding], ratchetID, nil
|
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 {
|
func (i *Identity) ToFile(path string) error {
|
||||||
|
log.Printf("[DEBUG-7] Saving identity %s to file: %s", i.GetHexHash(), path)
|
||||||
|
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"private_key": i.privateKey,
|
"private_key": i.privateKey,
|
||||||
"public_key": i.publicKey,
|
"public_key": i.publicKey,
|
||||||
@@ -565,26 +603,36 @@ func (i *Identity) ToFile(path string) error {
|
|||||||
|
|
||||||
file, err := os.Create(path)
|
file, err := os.Create(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("[DEBUG-1] Failed to create identity file: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
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) {
|
func RecallIdentity(path string) (*Identity, error) {
|
||||||
|
log.Printf("[DEBUG-7] Attempting to recall identity from: %s", path)
|
||||||
|
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("[DEBUG-1] Failed to open identity file: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
var data map[string]interface{}
|
var data map[string]interface{}
|
||||||
if err := json.NewDecoder(file).Decode(&data); err != nil {
|
if err := json.NewDecoder(file).Decode(&data); err != nil {
|
||||||
|
log.Printf("[DEBUG-1] Failed to decode identity data: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconstruct identity from saved data
|
|
||||||
id := &Identity{
|
id := &Identity{
|
||||||
privateKey: data["private_key"].([]byte),
|
privateKey: data["private_key"].([]byte),
|
||||||
publicKey: data["public_key"].([]byte),
|
publicKey: data["public_key"].([]byte),
|
||||||
@@ -593,8 +641,10 @@ func RecallIdentity(path string) (*Identity, error) {
|
|||||||
appData: data["app_data"].([]byte),
|
appData: data["app_data"].([]byte),
|
||||||
ratchets: make(map[string][]byte),
|
ratchets: make(map[string][]byte),
|
||||||
ratchetExpiry: make(map[string]int64),
|
ratchetExpiry: make(map[string]int64),
|
||||||
|
mutex: &sync.RWMutex{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG-7] Successfully recalled identity with hash: %s", id.GetHexHash())
|
||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,3 +736,98 @@ func NewIdentity() (*Identity, error) {
|
|||||||
|
|
||||||
return i, nil
|
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/cipher"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -16,6 +16,8 @@ import (
|
|||||||
"github.com/Sudo-Ivan/reticulum-go/pkg/destination"
|
"github.com/Sudo-Ivan/reticulum-go/pkg/destination"
|
||||||
"github.com/Sudo-Ivan/reticulum-go/pkg/identity"
|
"github.com/Sudo-Ivan/reticulum-go/pkg/identity"
|
||||||
"github.com/Sudo-Ivan/reticulum-go/pkg/packet"
|
"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/resource"
|
||||||
"github.com/Sudo-Ivan/reticulum-go/pkg/transport"
|
"github.com/Sudo-Ivan/reticulum-go/pkg/transport"
|
||||||
)
|
)
|
||||||
@@ -38,10 +40,6 @@ const (
|
|||||||
STATUS_CLOSED = 0x02
|
STATUS_CLOSED = 0x02
|
||||||
STATUS_FAILED = 0x03
|
STATUS_FAILED = 0x03
|
||||||
|
|
||||||
PACKET_TYPE_DATA = 0x00
|
|
||||||
PACKET_TYPE_LINK = 0x01
|
|
||||||
PACKET_TYPE_IDENTIFY = 0x02
|
|
||||||
|
|
||||||
PROVE_NONE = 0x00
|
PROVE_NONE = 0x00
|
||||||
PROVE_ALL = 0x01
|
PROVE_ALL = 0x01
|
||||||
PROVE_APP = 0x02
|
PROVE_APP = 0x02
|
||||||
@@ -56,6 +54,7 @@ type Link struct {
|
|||||||
lastOutbound time.Time
|
lastOutbound time.Time
|
||||||
lastDataReceived time.Time
|
lastDataReceived time.Time
|
||||||
lastDataSent time.Time
|
lastDataSent time.Time
|
||||||
|
pathFinder *pathfinder.PathFinder
|
||||||
|
|
||||||
remoteIdentity *identity.Identity
|
remoteIdentity *identity.Identity
|
||||||
sessionKey []byte
|
sessionKey []byte
|
||||||
@@ -97,6 +96,7 @@ func NewLink(dest *destination.Destination, transport *transport.Transport, esta
|
|||||||
lastOutbound: time.Time{},
|
lastOutbound: time.Time{},
|
||||||
lastDataReceived: time.Time{},
|
lastDataReceived: time.Time{},
|
||||||
lastDataSent: 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])
|
log.Printf("[DEBUG-4] Creating link request packet for destination %x", destPublicKey[:8])
|
||||||
|
|
||||||
// Create link request packet
|
p := &packet.Packet{
|
||||||
p, err := packet.NewPacket(
|
HeaderType: packet.HeaderType1,
|
||||||
packet.PACKET_TYPE_LINK,
|
PacketType: packet.PacketTypeLinkReq,
|
||||||
0x00,
|
TransportType: 0,
|
||||||
0x00,
|
Context: packet.ContextLinkIdentify,
|
||||||
destPublicKey,
|
ContextFlag: packet.FlagUnset,
|
||||||
l.linkID,
|
Hops: 0,
|
||||||
)
|
DestinationType: l.destination.GetType(),
|
||||||
if err != nil {
|
DestinationHash: l.destination.GetHash(),
|
||||||
log.Printf("[DEBUG-3] Failed to create link request packet: %v", err)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,15 +144,20 @@ func (l *Link) Identify(id *identity.Identity) error {
|
|||||||
return errors.New("link not active")
|
return errors.New("link not active")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create identify packet
|
p := &packet.Packet{
|
||||||
p, err := packet.NewPacket(
|
HeaderType: packet.HeaderType1,
|
||||||
packet.PACKET_TYPE_IDENTIFY,
|
PacketType: packet.PacketTypeData,
|
||||||
0x00,
|
TransportType: 0,
|
||||||
0x00,
|
Context: packet.ContextLinkIdentify,
|
||||||
l.destination.GetPublicKey(),
|
ContextFlag: packet.FlagUnset,
|
||||||
id.GetPublicKey(),
|
Hops: 0,
|
||||||
)
|
DestinationType: l.destination.GetType(),
|
||||||
if err != nil {
|
DestinationHash: l.destination.GetHash(),
|
||||||
|
Data: id.GetPublicKey(),
|
||||||
|
CreateReceipt: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.Pack(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,11 +476,28 @@ func (l *Link) SendPacket(data []byte) error {
|
|||||||
return err
|
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))
|
log.Printf("[DEBUG-4] Sending encrypted packet of %d bytes", len(encrypted))
|
||||||
l.lastOutbound = time.Now()
|
l.lastOutbound = time.Now()
|
||||||
l.lastDataSent = time.Now()
|
l.lastDataSent = time.Now()
|
||||||
|
|
||||||
return nil
|
return l.transport.SendPacket(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Link) HandleInbound(data []byte) error {
|
func (l *Link) HandleInbound(data []byte) error {
|
||||||
@@ -482,55 +509,24 @@ func (l *Link) HandleInbound(data []byte) error {
|
|||||||
return errors.New("link not active")
|
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
|
// Decrypt if we have a session key
|
||||||
decryptedData, err := l.decrypt(data)
|
if l.sessionKey != nil {
|
||||||
if err != nil {
|
decrypted, err := l.decrypt(data)
|
||||||
log.Printf("[DEBUG-3] Failed to decrypt packet: %v", err)
|
if err != nil {
|
||||||
return err
|
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])
|
|
||||||
}
|
}
|
||||||
}
|
data = decrypted
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
l.lastInbound = time.Now()
|
l.lastInbound = time.Now()
|
||||||
l.lastDataReceived = time.Now()
|
l.lastDataReceived = time.Now()
|
||||||
|
|
||||||
if l.packetCallback != nil {
|
if l.packetCallback != nil {
|
||||||
l.packetCallback(message, nil)
|
l.packetCallback(data, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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
|
// Helper function for min of two ints
|
||||||
func min(a, b int) int {
|
func min(a, b int) int {
|
||||||
if a < b {
|
if a < b {
|
||||||
|
|||||||
@@ -5,10 +5,6 @@ const (
|
|||||||
EncryptedMDU = 383 // Maximum size of payload data in encrypted packet
|
EncryptedMDU = 383 // Maximum size of payload data in encrypted packet
|
||||||
PlainMDU = 464 // Maximum size of payload data in unencrypted 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
|
// Propagation Types
|
||||||
PropagationBroadcast = 0
|
PropagationBroadcast = 0
|
||||||
PropagationTransport = 1
|
PropagationTransport = 1
|
||||||
|
|||||||
@@ -1,116 +1,208 @@
|
|||||||
package packet
|
package packet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Packet Types
|
// Packet Types
|
||||||
PacketTypeData = 0x00
|
PacketTypeData = 0x00
|
||||||
PacketTypeAnnounce = 0x01
|
PacketTypeAnnounce = 0x01
|
||||||
PacketTypeLink = 0x02
|
PacketTypeLinkReq = 0x02
|
||||||
PacketTypeProof = 0x03
|
PacketTypeProof = 0x03
|
||||||
PACKET_TYPE_DATA = 0x00
|
|
||||||
PACKET_TYPE_LINK = 0x01
|
|
||||||
PACKET_TYPE_IDENTIFY = 0x02
|
|
||||||
|
|
||||||
// Sizes
|
// Header Types
|
||||||
HeaderSize = 2
|
HeaderType1 = 0x00
|
||||||
AddressSize = 16
|
HeaderType2 = 0x01
|
||||||
ContextSize = 1
|
|
||||||
MaxDataSize = 465
|
// Context Types
|
||||||
RandomBlobSize = 16
|
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 {
|
type Packet struct {
|
||||||
Header [2]byte
|
HeaderType byte
|
||||||
Addresses []byte
|
PacketType byte
|
||||||
Context byte
|
TransportType byte
|
||||||
Data []byte
|
Context byte
|
||||||
AccessCode []byte
|
ContextFlag byte
|
||||||
RandomBlob []byte
|
Hops byte
|
||||||
Timestamp time.Time
|
|
||||||
|
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(destType byte, data []byte, packetType byte, context byte,
|
||||||
func NewPacket(packetType byte, flags byte, hops byte, destKey []byte, data []byte) (*Packet, error) {
|
transportType byte, headerType byte, transportID []byte, createReceipt bool,
|
||||||
if len(destKey) != AddressSize {
|
contextFlag byte) *Packet {
|
||||||
return nil, errors.New("invalid destination key length")
|
|
||||||
|
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) {
|
func (p *Packet) Serialize() ([]byte, error) {
|
||||||
totalSize := HeaderSize + len(p.Addresses) + ContextSize + len(p.Data)
|
if !p.Packed {
|
||||||
if p.AccessCode != nil {
|
if err := p.Pack(); err != nil {
|
||||||
totalSize += len(p.AccessCode)
|
return nil, fmt.Errorf("failed to pack packet: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer := make([]byte, totalSize)
|
p.Addresses = p.DestinationHash
|
||||||
offset := 0
|
|
||||||
|
|
||||||
// Write header
|
return p.Raw, nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user