summaryrefslogtreecommitdiffstats
path: root/session/session.go
diff options
context:
space:
mode:
authorgabrix73 <gabriel1@frozenstar.info>2026-04-10 16:10:48 +0200
committergabrix73 <gabriel1@frozenstar.info>2026-04-10 16:10:48 +0200
commit88375da30ea667ec25c2000874557fcc4785a558 (patch)
tree71364a43230977481c6d3280246aa8bb48562bdc /session/session.go
downloadkhimera-88375da30ea667ec25c2000874557fcc4785a558.tar.gz
khimera-88375da30ea667ec25c2000874557fcc4785a558.tar.xz
khimera-88375da30ea667ec25c2000874557fcc4785a558.zip
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
Diffstat (limited to 'session/session.go')
-rw-r--r--session/session.go202
1 files changed, 202 insertions, 0 deletions
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))
+}