From 88375da30ea667ec25c2000874557fcc4785a558 Mon Sep 17 00:00:00 2001 From: gabrix73 Date: Fri, 10 Apr 2026 16:10:48 +0200 Subject: Initial commit: Veilith v0.9 - P2P Encrypted Messenger Military-grade secure messenger with: - End-to-End Encryption (Noise Protocol XX) - Forward Secrecy (automatic session rotation) - Anti-Traffic Analysis (padding + cover traffic) - Secure Deletion (DOD 5220.22-M) - Tor Integration (embedded with bridge support) - Portable Mode (no installation required) Generated with Claude Code --- session/session.go | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 session/session.go (limited to 'session') diff --git a/session/session.go b/session/session.go new file mode 100644 index 0000000..688321d --- /dev/null +++ b/session/session.go @@ -0,0 +1,202 @@ +// Package session manages secure communication sessions with Forward Secrecy +package session + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "sync" + "time" + + "github.com/flynn/noise" +) + +// Session represents a secure communication session +type Session struct { + ID string + RemotePubKey string // Remote peer's public key (hex) + HandshakeState *noise.HandshakeState + CipherState *noise.CipherState + CreatedAt time.Time + LastActivity time.Time + IsInitiator bool +} + +// SessionManager manages multiple sessions +type SessionManager struct { + sessions map[string]*Session // key = session ID + mu sync.RWMutex +} + +// NewSessionManager creates a new session manager +func NewSessionManager() *SessionManager { + return &SessionManager{ + sessions: make(map[string]*Session), + } +} + +// CreateSession creates a new Noise Protocol session +func (sm *SessionManager) CreateSession(remotePubKey string, isInitiator bool) (*Session, error) { + sm.mu.Lock() + defer sm.mu.Unlock() + + // Generate session ID + sessionID := generateSessionID() + + // Create Noise handshake configuration + // Using Noise_XX_25519_ChaChaPoly_BLAKE2b pattern for mutual authentication + var hs *noise.HandshakeState + var err error + + config := noise.Config{ + CipherSuite: noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashBLAKE2b), + Pattern: noise.HandshakeXX, + Initiator: isInitiator, + } + + // Generate ephemeral keypair + keypair, err := noise.DH25519.GenerateKeypair(rand.Reader) + if err != nil { + return nil, fmt.Errorf("failed to generate keypair: %w", err) + } + + config.KeyPair = keypair + + hs, err = noise.NewHandshakeState(config) + if err != nil { + return nil, fmt.Errorf("failed to create handshake state: %w", err) + } + + session := &Session{ + ID: sessionID, + RemotePubKey: remotePubKey, + HandshakeState: hs, + CreatedAt: time.Now(), + LastActivity: time.Now(), + IsInitiator: isInitiator, + } + + sm.sessions[sessionID] = session + return session, nil +} + +// GetSession retrieves a session by ID +func (sm *SessionManager) GetSession(sessionID string) (*Session, bool) { + sm.mu.RLock() + defer sm.mu.RUnlock() + + session, exists := sm.sessions[sessionID] + return session, exists +} + +// RemoveSession removes a session and securely wipes it +func (sm *SessionManager) RemoveSession(sessionID string) { + sm.mu.Lock() + defer sm.mu.Unlock() + + if session, exists := sm.sessions[sessionID]; exists { + session.SecureWipe() + delete(sm.sessions, sessionID) + } +} + +// SecureWipeAll securely wipes all sessions +func (sm *SessionManager) SecureWipeAll() { + sm.mu.Lock() + defer sm.mu.Unlock() + + for _, session := range sm.sessions { + session.SecureWipe() + } + sm.sessions = make(map[string]*Session) +} + +// ListSessions returns all active sessions +func (sm *SessionManager) ListSessions() []*Session { + sm.mu.RLock() + defer sm.mu.RUnlock() + + sessions := make([]*Session, 0, len(sm.sessions)) + for _, session := range sm.sessions { + sessions = append(sessions, session) + } + return sessions +} + +// CleanupExpiredSessions removes sessions older than the specified duration +func (sm *SessionManager) CleanupExpiredSessions(maxAge time.Duration) int { + sm.mu.Lock() + defer sm.mu.Unlock() + + now := time.Now() + removed := 0 + + for id, session := range sm.sessions { + if now.Sub(session.LastActivity) > maxAge { + session.SecureWipe() + delete(sm.sessions, id) + removed++ + } + } + + return removed +} + +// UpdateActivity updates the last activity timestamp +func (sm *SessionManager) UpdateActivity(sessionID string) { + sm.mu.Lock() + defer sm.mu.Unlock() + + if session, exists := sm.sessions[sessionID]; exists { + session.LastActivity = time.Now() + } +} + +// Count returns the number of active sessions +func (sm *SessionManager) Count() int { + sm.mu.RLock() + defer sm.mu.RUnlock() + return len(sm.sessions) +} + +// SecureWipe zeros out sensitive session data +func (s *Session) SecureWipe() { + // Wipe cipher state if exists + if s.CipherState != nil { + // Noise library doesn't expose key wiping, but we can nil the reference + s.CipherState = nil + } + + // Wipe handshake state + if s.HandshakeState != nil { + s.HandshakeState = nil + } + + // Zero out session ID + s.ID = "" + s.RemotePubKey = "" +} + +// generateSessionID generates a random session ID +func generateSessionID() string { + id := make([]byte, 16) + rand.Read(id) + return hex.EncodeToString(id) +} + +// String returns a safe string representation +func (s *Session) String() string { + role := "responder" + if s.IsInitiator { + role = "initiator" + } + return fmt.Sprintf("Session(id=%s, role=%s, remote=%s...)", + s.ID[:8], role, s.RemotePubKey[:16]) +} + +// String returns session manager info +func (sm *SessionManager) String() string { + sm.mu.RLock() + defer sm.mu.RUnlock() + return fmt.Sprintf("SessionManager(active=%d)", len(sm.sessions)) +} -- cgit v1.2.3