summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGab <24553253+gabrix73@users.noreply.github.com>2025-10-14 18:53:35 +0200
committerGitHub <noreply@github.com>2025-10-14 18:53:35 +0200
commiteed868fdfac863a5020e7cb3f5447236b6433f68 (patch)
treefebcb8c8bc5b03463a2a22ffe2d92c541ce96f92
parent96c3f07dc89bb0d6e6f378c3c8d49f7988b94313 (diff)
downloadnofuture-go-memguard-eed868fdfac863a5020e7cb3f5447236b6433f68.tar.gz
nofuture-go-memguard-eed868fdfac863a5020e7cb3f5447236b6433f68.tar.xz
nofuture-go-memguard-eed868fdfac863a5020e7cb3f5447236b6433f68.zip
Update nofuture.go
-rw-r--r--nofuture.go715
1 files changed, 327 insertions, 388 deletions
diff --git a/nofuture.go b/nofuture.go
index e4dab0d..5bc7c13 100644
--- a/nofuture.go
+++ b/nofuture.go
@@ -2,629 +2,569 @@ package main
import (
"crypto/rand"
+ "crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
+ "io"
"log"
"net/http"
- "os"
- "runtime"
"sync"
"time"
"github.com/awnumar/memguard"
+ "golang.org/x/crypto/chacha20poly1305"
+ "golang.org/x/crypto/nacl/box"
)
-// Session represents a user session with protected keys
+const (
+ VERSION = "0.5.0"
+ MAX_MESSAGE_SIZE = 1024 * 1024 // 1MB
+ SESSION_TIMEOUT = 24 * time.Hour
+ MAX_SESSIONS = 1000
+ RATE_LIMIT_WINDOW = time.Minute
+ MAX_REQUESTS = 60
+)
+
+// Session represents a user session with protected keys and plaintext
type Session struct {
ID string
- PrivateKey *memguard.LockedBuffer
- PublicKey *memguard.LockedBuffer
- SharedSecret *memguard.LockedBuffer
+ PublicKey *memguard.LockedBuffer // NaCl box public key
+ PrivateKey *memguard.LockedBuffer // NaCl box private key
+ SharedSecret *memguard.LockedBuffer // Derived shared secret
+ Nonce *memguard.LockedBuffer // Session nonce
CreatedAt time.Time
+ LastActivity time.Time
+ PairedWith string
+ mutex sync.RWMutex
}
-// PairedSession represents two connected sessions
-type PairedSession struct {
- SessionA string
- SessionB string
- PairedAt time.Time
+// SessionManager handles all sessions with rate limiting
+type SessionManager struct {
+ sessions map[string]*Session
+ mutex sync.RWMutex
+ rateLimiter map[string]*RateLimit
+ limiterMutex sync.RWMutex
}
-// StartSessionRequest represents the request to start a new session
-type StartSessionRequest struct {
- // Empty for now, could add user preferences later
+type RateLimit struct {
+ requests int
+ resetTime time.Time
}
-// StartSessionResponse represents the response from starting a session
+// Request/Response types
type StartSessionResponse struct {
SessionID string `json:"session_id"`
- PublicKey string `json:"public_key"`
Status string `json:"status"`
+ Version string `json:"version"`
}
-// PairSessionRequest represents the request to pair sessions
type PairSessionRequest struct {
SessionID string `json:"session_id"`
BuddySessionID string `json:"buddy_session_id"`
}
-// PairSessionResponse represents the response from pairing sessions
type PairSessionResponse struct {
- Status string `json:"status"`
- Message string `json:"message"`
- BuddyPublicKey string `json:"buddy_public_key,omitempty"`
+ Status string `json:"status"`
+ Message string `json:"message"`
}
-// EncryptRequest represents the request to encrypt a message
type EncryptRequest struct {
SessionID string `json:"session_id"`
Message string `json:"message"`
}
-// EncryptResponse represents the response from encrypting a message
type EncryptResponse struct {
EncryptedMessage string `json:"encrypted_message"`
- Status string `json:"status"`
+ Status string `json:"status"`
}
-// DecryptRequest represents the request to decrypt a message
type DecryptRequest struct {
SessionID string `json:"session_id"`
EncryptedMessage string `json:"encrypted_message"`
}
-// DecryptResponse represents the response from decrypting a message
type DecryptResponse struct {
DecryptedMessage string `json:"decrypted_message"`
- Status string `json:"status"`
+ Status string `json:"status"`
}
-// EndSessionRequest represents the request to end a session
type EndSessionRequest struct {
SessionID string `json:"session_id"`
}
-// EndSessionResponse represents the response from ending a session
type EndSessionResponse struct {
Status string `json:"status"`
Message string `json:"message"`
}
-// SecurityDemo represents the security demonstration data
-type SecurityDemo struct {
- Timestamp string `json:"timestamp"`
- MemguardStatus string `json:"memguard_status"`
- ProtectedPages int `json:"protected_pages"`
- LockedMemoryKB int `json:"locked_memory_kb"`
- AccessAttempts int `json:"access_attempts_blocked"`
- SecurityTests map[string]string `json:"security_tests"`
- LiveDemoResults []TestResult `json:"live_demo_results"`
-}
+var sessionManager *SessionManager
-type TestResult struct {
- TestName string `json:"test_name"`
- Status string `json:"status"`
- Result string `json:"result"`
- Timestamp string `json:"timestamp"`
- Details string `json:"details"`
+func init() {
+ sessionManager = &SessionManager{
+ sessions: make(map[string]*Session),
+ rateLimiter: make(map[string]*RateLimit),
+ }
}
-// Global variables
-var (
- sessions = make(map[string]*Session)
- pairedSessions = make(map[string]*PairedSession)
- sessionsMutex sync.RWMutex
- blockedAttempts int
- demoSecrets *memguard.LockedBuffer
-)
+// Rate limiting
+func (sm *SessionManager) checkRateLimit(ip string) bool {
+ sm.limiterMutex.Lock()
+ defer sm.limiterMutex.Unlock()
-// Initialize demo secrets in protected memory
-func initSecurityDemo() {
- demoSecrets = memguard.NewBufferFromBytes([]byte("SUPER_SECRET_DEMO_KEY_12345"))
- log.Printf("Demo secrets stored in protected memory")
-}
+ now := time.Now()
+ limit, exists := sm.rateLimiter[ip]
-// Security demonstration endpoint
-func securityDemoHandler(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Access-Control-Allow-Origin", "*")
-
- liveTests := performLiveSecurityTests()
+ if !exists || now.After(limit.resetTime) {
+ sm.rateLimiter[ip] = &RateLimit{
+ requests: 1,
+ resetTime: now.Add(RATE_LIMIT_WINDOW),
+ }
+ return true
+ }
- demo := SecurityDemo{
- Timestamp: time.Now().Format(time.RFC3339),
- MemguardStatus: getMemguardStatus(),
- ProtectedPages: getProtectedPageCount(),
- LockedMemoryKB: getLockedMemorySize(),
- AccessAttempts: blockedAttempts,
- SecurityTests: getSecurityTestResults(),
- LiveDemoResults: liveTests,
+ if limit.requests >= MAX_REQUESTS {
+ return false
}
- json.NewEncoder(w).Encode(demo)
+ limit.requests++
+ return true
}
-// Challenge endpoint
-func securityChallengeHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
- return
- }
+// Generate cryptographically secure session ID
+func generateSessionID(publicKey []byte, nonce []byte) string {
+ hash := sha256.New()
+ hash.Write(publicKey)
+ hash.Write(nonce)
+ return hex.EncodeToString(hash.Sum(nil))
+}
- var challenge struct {
- TestType string `json:"test_type"`
- }
+// Session cleanup goroutine
+func (sm *SessionManager) startCleanupRoutine() {
+ ticker := time.NewTicker(1 * time.Hour)
+ go func() {
+ for range ticker.C {
+ sm.cleanupExpiredSessions()
+ }
+ }()
+}
- if err := json.NewDecoder(r.Body).Decode(&challenge); err != nil {
- http.Error(w, "Invalid JSON", http.StatusBadRequest)
- return
- }
+func (sm *SessionManager) cleanupExpiredSessions() {
+ sm.mutex.Lock()
+ defer sm.mutex.Unlock()
- result := performSpecificTest(challenge.TestType)
-
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Access-Control-Allow-Origin", "*")
- json.NewEncoder(w).Encode(result)
+ now := time.Now()
+ for id, session := range sm.sessions {
+ if now.Sub(session.LastActivity) > SESSION_TIMEOUT {
+ session.destroy()
+ delete(sm.sessions, id)
+ log.Printf("Expired session cleaned up: %s", id)
+ }
+ }
}
-func performLiveSecurityTests() []TestResult {
- tests := []TestResult{}
- now := time.Now().Format("15:04:05")
-
- tests = append(tests, TestResult{
- TestName: "Memory Protection Check",
- Status: "ACTIVE",
- Result: "Memguard protection verified",
- Timestamp: now,
- Details: "Protected memory pages are locked and encrypted",
- })
+// Destroy session - securely wipe all sensitive data
+func (s *Session) destroy() {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
- return tests
+ if s.PrivateKey != nil {
+ s.PrivateKey.Destroy()
+ }
+ if s.PublicKey != nil {
+ s.PublicKey.Destroy()
+ }
+ if s.SharedSecret != nil {
+ s.SharedSecret.Destroy()
+ }
+ if s.Nonce != nil {
+ s.Nonce.Destroy()
+ }
}
-func performSpecificTest(testType string) TestResult {
- now := time.Now().Format("15:04:05")
- blockedAttempts++
-
- switch testType {
- case "root_access":
- return TestResult{
- TestName: "Root Access Attempt",
- Status: "BLOCKED",
- Result: "Root privileges cannot bypass memguard protection",
- Timestamp: now,
- Details: fmt.Sprintf("UID: %d attempted access - DENIED by mlock protection", os.Getuid()),
- }
- case "memory_dump":
- return TestResult{
- TestName: "Memory Dump Attempt",
- Status: "BLOCKED",
- Result: "Memory dump blocked by kernel-level protection",
- Timestamp: now,
- Details: fmt.Sprintf("Cannot access protected regions in PID %d - mlock syscall prevents core dumps", os.Getpid()),
- }
- case "debugger_attach":
- return TestResult{
- TestName: "Debugger Attach Test",
- Status: "BLOCKED",
- Result: "Debugger attachment denied",
- Timestamp: now,
- Details: "Memory protection prevents debugging access to encrypted pages",
- }
- case "process_scan":
- return TestResult{
- TestName: "Memory Pattern Scan",
- Status: "BLOCKED",
- Result: "Secret data not found in scannable memory",
- Timestamp: now,
- Details: "Protected data is encrypted and locked - pattern scanning ineffective",
- }
- case "cold_boot":
- return TestResult{
- TestName: "Cold Boot Attack Simulation",
- Status: "BLOCKED",
- Result: "Memory wiped on process termination",
- Timestamp: now,
- Details: "Memguard automatically destroys secrets on exit - no residual data",
- }
- case "swap_analysis":
- return TestResult{
- TestName: "Swap File Analysis",
- Status: "BLOCKED",
- Result: "Protected memory never swapped to disk",
- Timestamp: now,
- Details: "mlock() prevents sensitive data from reaching swap partition",
+// Middleware for CORS with restrictions
+func corsMiddleware(next http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ origin := r.Header.Get("Origin")
+
+ // Only allow same-origin or localhost for development
+ // In production, set this to your specific domain
+ if origin == "" || origin == "http://localhost:8080" || origin == "https://yourdomain.com" {
+ w.Header().Set("Access-Control-Allow-Origin", origin)
}
- default:
- return TestResult{
- TestName: "Unknown Test",
- Status: "ERROR",
- Result: "Invalid test type",
- Timestamp: now,
- Details: "Test type not recognized",
+
+ w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
+ w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
+ w.Header().Set("Access-Control-Max-Age", "3600")
+
+ if r.Method == "OPTIONS" {
+ w.WriteHeader(http.StatusOK)
+ return
}
- }
-}
-func getMemguardStatus() string {
- if demoSecrets != nil {
- return "ACTIVE"
+ next(w, r)
}
- return "INACTIVE"
}
-func getProtectedPageCount() int {
- sessionsMutex.RLock()
- defer sessionsMutex.RUnlock()
- return len(sessions)*2 + 1 // Sessions + demo secrets
-}
+// Middleware for rate limiting
+func rateLimitMiddleware(next http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ ip := r.RemoteAddr
-func getLockedMemorySize() int {
- var memStats runtime.MemStats
- runtime.ReadMemStats(&memStats)
- return int(memStats.HeapAlloc / 1024)
-}
+ if !sessionManager.checkRateLimit(ip) {
+ http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
+ return
+ }
-func getSecurityTestResults() map[string]string {
- return map[string]string{
- "memory_protection": "ENABLED",
- "root_access": "DENIED",
- "debugger_access": "BLOCKED",
- "memory_dumps": "PREVENTED",
- "cold_boot_attack": "MITIGATED",
- "swap_analysis": "PROTECTED",
+ next(w, r)
}
}
-// generateSessionID creates a secure random session ID
-func generateSessionID() (string, error) {
- bytes := make([]byte, 16)
- if _, err := rand.Read(bytes); err != nil {
- return "", err
+// Middleware for request size limiting
+func sizeLimitMiddleware(next http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ r.Body = http.MaxBytesReader(w, r.Body, MAX_MESSAGE_SIZE)
+ next(w, r)
}
- return hex.EncodeToString(bytes), nil
}
-// startSessionHandler handles starting a new session
+// Start session handler
func startSessionHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
- return
- }
-
w.Header().Set("Content-Type", "application/json")
- // Generate session ID
- sessionID, err := generateSessionID()
- if err != nil {
- http.Error(w, "Failed to generate session ID", http.StatusInternalServerError)
+ sessionManager.mutex.Lock()
+ if len(sessionManager.sessions) >= MAX_SESSIONS {
+ sessionManager.mutex.Unlock()
+ http.Error(w, "Maximum sessions reached", http.StatusServiceUnavailable)
return
}
+ sessionManager.mutex.Unlock()
- // Generate quantum-safe key pair using memguard
- privateKeyBytes := make([]byte, 32)
- if _, err := rand.Read(privateKeyBytes); err != nil {
- http.Error(w, "Failed to generate private key", http.StatusInternalServerError)
+ // Generate NaCl box key pair (Curve25519)
+ publicKey, privateKey, err := box.GenerateKey(rand.Reader)
+ if err != nil {
+ log.Printf("Key generation failed: %v", err)
+ http.Error(w, "Key generation failed", http.StatusInternalServerError)
return
}
- publicKeyBytes := make([]byte, 32)
- if _, err := rand.Read(publicKeyBytes); err != nil {
- http.Error(w, "Failed to generate public key", http.StatusInternalServerError)
+ // Generate random nonce
+ nonce := make([]byte, 24)
+ if _, err := rand.Read(nonce); err != nil {
+ log.Printf("Nonce generation failed: %v", err)
+ http.Error(w, "Nonce generation failed", http.StatusInternalServerError)
return
}
- // Store keys in protected memory
- privateKey := memguard.NewBufferFromBytes(privateKeyBytes)
- publicKey := memguard.NewBufferFromBytes(publicKeyBytes)
+ // Generate session ID from public key and nonce
+ sessionID := generateSessionID(publicKey[:], nonce)
+ // Store keys in memguard protected memory
session := &Session{
- ID: sessionID,
- PrivateKey: privateKey,
- PublicKey: publicKey,
- CreatedAt: time.Now(),
+ ID: sessionID,
+ PublicKey: memguard.NewBufferFromBytes(publicKey[:]),
+ PrivateKey: memguard.NewBufferFromBytes(privateKey[:]),
+ Nonce: memguard.NewBufferFromBytes(nonce),
+ CreatedAt: time.Now(),
+ LastActivity: time.Now(),
}
- sessionsMutex.Lock()
- sessions[sessionID] = session
- sessionsMutex.Unlock()
+ sessionManager.mutex.Lock()
+ sessionManager.sessions[sessionID] = session
+ sessionManager.mutex.Unlock()
- log.Printf("Session %s started successfully", sessionID)
+ log.Printf("Session started: %s", sessionID)
resp := StartSessionResponse{
SessionID: sessionID,
- PublicKey: hex.EncodeToString(publicKey.Bytes()),
Status: "success",
+ Version: VERSION,
}
json.NewEncoder(w).Encode(resp)
}
-// pairSessionHandler handles pairing two sessions
+// Pair session handler
func pairSessionHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
- return
- }
-
w.Header().Set("Content-Type", "application/json")
var req PairSessionRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- http.Error(w, "Invalid JSON", http.StatusBadRequest)
+ http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
if req.SessionID == "" || req.BuddySessionID == "" {
- http.Error(w, "session_id and buddy_session_id are required", http.StatusBadRequest)
+ http.Error(w, "Missing session IDs", http.StatusBadRequest)
return
}
- sessionsMutex.Lock()
- defer sessionsMutex.Unlock()
+ sessionManager.mutex.RLock()
+ session, exists1 := sessionManager.sessions[req.SessionID]
+ buddySession, exists2 := sessionManager.sessions[req.BuddySessionID]
+ sessionManager.mutex.RUnlock()
- // Check if both sessions exist
- session, exists := sessions[req.SessionID]
- if !exists {
- resp := PairSessionResponse{
- Status: "error",
- Message: "Session not found",
- }
- w.WriteHeader(http.StatusNotFound)
- json.NewEncoder(w).Encode(resp)
+ if !exists1 || !exists2 {
+ http.Error(w, "Session not found", http.StatusNotFound)
return
}
- buddySession, exists := sessions[req.BuddySessionID]
- if !exists {
- resp := PairSessionResponse{
- Status: "error",
- Message: "Buddy session not found",
- }
- w.WriteHeader(http.StatusNotFound)
- json.NewEncoder(w).Encode(resp)
- return
- }
+ // Update activity timestamps
+ session.mutex.Lock()
+ session.LastActivity = time.Now()
+ session.PairedWith = req.BuddySessionID
+ session.mutex.Unlock()
- // Create shared secret using simple XOR (in production, use proper key exchange)
- sharedSecretBytes := make([]byte, 32)
- for i := 0; i < 32; i++ {
- sharedSecretBytes[i] = session.PrivateKey.Bytes()[i] ^ buddySession.PublicKey.Bytes()[i]
- }
+ buddySession.mutex.Lock()
+ buddySession.LastActivity = time.Now()
+ buddySession.PairedWith = req.SessionID
+ buddySession.mutex.Unlock()
+
+ // Compute shared secret using NaCl box
+ var sharedSecret [32]byte
+ var myPrivate [32]byte
+ var theirPublic [32]byte
- // Store shared secret in both sessions
- session.SharedSecret = memguard.NewBufferFromBytes(sharedSecretBytes)
- buddySession.SharedSecret = memguard.NewBufferFromBytes(sharedSecretBytes)
+ copy(myPrivate[:], session.PrivateKey.Bytes())
+ copy(theirPublic[:], buddySession.PublicKey.Bytes())
- // Create pairing record
- pairKey := fmt.Sprintf("%s-%s", req.SessionID, req.BuddySessionID)
- pairedSessions[pairKey] = &PairedSession{
- SessionA: req.SessionID,
- SessionB: req.BuddySessionID,
- PairedAt: time.Now(),
+ box.Precompute(&sharedSecret, &theirPublic, &myPrivate)
+
+ // Store shared secret in memguard
+ session.mutex.Lock()
+ session.SharedSecret = memguard.NewBufferFromBytes(sharedSecret[:])
+ session.mutex.Unlock()
+
+ buddySession.mutex.Lock()
+ buddySession.SharedSecret = memguard.NewBufferFromBytes(sharedSecret[:])
+ buddySession.mutex.Unlock()
+
+ // Zero out stack variables
+ for i := range myPrivate {
+ myPrivate[i] = 0
+ }
+ for i := range theirPublic {
+ theirPublic[i] = 0
+ }
+ for i := range sharedSecret {
+ sharedSecret[i] = 0
}
- log.Printf("Sessions %s and %s paired successfully", req.SessionID, req.BuddySessionID)
+ log.Printf("Sessions paired: %s <-> %s", req.SessionID, req.BuddySessionID)
resp := PairSessionResponse{
- Status: "success",
- Message: "Sessions paired successfully",
- BuddyPublicKey: hex.EncodeToString(buddySession.PublicKey.Bytes()),
+ Status: "success",
+ Message: "Sessions paired successfully",
}
json.NewEncoder(w).Encode(resp)
}
-// Simple XOR encryption (for demonstration - use proper encryption in production)
-func xorEncrypt(data, key []byte) []byte {
- result := make([]byte, len(data))
- for i := 0; i < len(data); i++ {
- result[i] = data[i] ^ key[i%len(key)]
- }
- return result
-}
-
-// encryptHandler handles message encryption
+// Encrypt handler
func encryptHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
- return
- }
-
w.Header().Set("Content-Type", "application/json")
var req EncryptRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- http.Error(w, "Invalid JSON", http.StatusBadRequest)
+ http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
if req.SessionID == "" || req.Message == "" {
- http.Error(w, "session_id and message are required", http.StatusBadRequest)
+ http.Error(w, "Missing required fields", http.StatusBadRequest)
return
}
- sessionsMutex.RLock()
- session, exists := sessions[req.SessionID]
- sessionsMutex.RUnlock()
+ sessionManager.mutex.RLock()
+ session, exists := sessionManager.sessions[req.SessionID]
+ sessionManager.mutex.RUnlock()
if !exists {
http.Error(w, "Session not found", http.StatusNotFound)
return
}
+ session.mutex.Lock()
+ defer session.mutex.Unlock()
+
if session.SharedSecret == nil {
http.Error(w, "Session not paired", http.StatusBadRequest)
return
}
+ session.LastActivity = time.Now()
+
+ // Store plaintext in memguard protected buffer
+ plaintextBuffer := memguard.NewBufferFromBytes([]byte(req.Message))
+ defer plaintextBuffer.Destroy()
+
+ // Create AEAD cipher with XChaCha20-Poly1305
+ aead, err := chacha20poly1305.NewX(session.SharedSecret.Bytes())
+ if err != nil {
+ log.Printf("Cipher creation failed: %v", err)
+ http.Error(w, "Encryption failed", http.StatusInternalServerError)
+ return
+ }
+
+ // Generate random nonce
+ nonce := make([]byte, aead.NonceSize())
+ if _, err := rand.Read(nonce); err != nil {
+ log.Printf("Nonce generation failed: %v", err)
+ http.Error(w, "Encryption failed", http.StatusInternalServerError)
+ return
+ }
+
// Encrypt the message
- encrypted := xorEncrypt([]byte(req.Message), session.SharedSecret.Bytes())
- encryptedHex := hex.EncodeToString(encrypted)
+ ciphertext := aead.Seal(nonce, nonce, plaintextBuffer.Bytes(), nil)
- log.Printf("Message encrypted for session %s", req.SessionID)
+ // Encode to hex for transmission
+ encryptedHex := hex.EncodeToString(ciphertext)
resp := EncryptResponse{
EncryptedMessage: encryptedHex,
- Status: "success",
+ Status: "success",
}
json.NewEncoder(w).Encode(resp)
}
-// decryptHandler handles message decryption
+// Decrypt handler
func decryptHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
- return
- }
-
w.Header().Set("Content-Type", "application/json")
var req DecryptRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- http.Error(w, "Invalid JSON", http.StatusBadRequest)
+ http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
if req.SessionID == "" || req.EncryptedMessage == "" {
- http.Error(w, "session_id and encrypted_message are required", http.StatusBadRequest)
+ http.Error(w, "Missing required fields", http.StatusBadRequest)
return
}
- sessionsMutex.RLock()
- session, exists := sessions[req.SessionID]
- sessionsMutex.RUnlock()
+ sessionManager.mutex.RLock()
+ session, exists := sessionManager.sessions[req.SessionID]
+ sessionManager.mutex.RUnlock()
if !exists {
http.Error(w, "Session not found", http.StatusNotFound)
return
}
+ session.mutex.Lock()
+ defer session.mutex.Unlock()
+
if session.SharedSecret == nil {
http.Error(w, "Session not paired", http.StatusBadRequest)
return
}
- // Decode and decrypt the message
- encryptedBytes, err := hex.DecodeString(req.EncryptedMessage)
+ session.LastActivity = time.Now()
+
+ // Decode from hex
+ ciphertext, err := hex.DecodeString(req.EncryptedMessage)
+ if err != nil {
+ http.Error(w, "Invalid ciphertext format", http.StatusBadRequest)
+ return
+ }
+
+ // Create AEAD cipher
+ aead, err := chacha20poly1305.NewX(session.SharedSecret.Bytes())
if err != nil {
- http.Error(w, "Invalid encrypted message format", http.StatusBadRequest)
+ log.Printf("Cipher creation failed: %v", err)
+ http.Error(w, "Decryption failed", http.StatusInternalServerError)
+ return
+ }
+
+ // Extract nonce and ciphertext
+ if len(ciphertext) < aead.NonceSize() {
+ http.Error(w, "Invalid ciphertext", http.StatusBadRequest)
return
}
- decrypted := xorEncrypt(encryptedBytes, session.SharedSecret.Bytes())
+ nonce := ciphertext[:aead.NonceSize()]
+ ciphertext = ciphertext[aead.NonceSize():]
- log.Printf("Message decrypted for session %s", req.SessionID)
+ // Decrypt the message
+ plaintext, err := aead.Open(nil, nonce, ciphertext, nil)
+ if err != nil {
+ log.Printf("Decryption failed: %v", err)
+ http.Error(w, "Decryption failed - authentication failed", http.StatusBadRequest)
+ return
+ }
+
+ // Store plaintext in memguard protected buffer
+ plaintextBuffer := memguard.NewBufferFromBytes(plaintext)
+ defer plaintextBuffer.Destroy()
+
+ // Zero out plaintext slice
+ for i := range plaintext {
+ plaintext[i] = 0
+ }
resp := DecryptResponse{
- DecryptedMessage: string(decrypted),
- Status: "success",
+ DecryptedMessage: string(plaintextBuffer.Bytes()),
+ Status: "success",
}
json.NewEncoder(w).Encode(resp)
}
-// endSessionHandler handles ending a session
+// End session handler
func endSessionHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
- return
- }
-
w.Header().Set("Content-Type", "application/json")
var req EndSessionRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- http.Error(w, "Invalid JSON", http.StatusBadRequest)
+ http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
if req.SessionID == "" {
- http.Error(w, "session_id is required", http.StatusBadRequest)
+ http.Error(w, "Missing session ID", http.StatusBadRequest)
return
}
- sessionsMutex.Lock()
- defer sessionsMutex.Unlock()
+ sessionManager.mutex.Lock()
+ defer sessionManager.mutex.Unlock()
- // Check if session exists
- session, exists := sessions[req.SessionID]
+ session, exists := sessionManager.sessions[req.SessionID]
if !exists {
- resp := EndSessionResponse{
- Status: "error",
- Message: "Session not found",
- }
- w.WriteHeader(http.StatusNotFound)
- json.NewEncoder(w).Encode(resp)
+ http.Error(w, "Session not found", http.StatusNotFound)
return
}
- // Securely wipe session keys from memory using memguard
- if session.PrivateKey != nil {
- session.PrivateKey.Destroy()
- }
- if session.PublicKey != nil {
- session.PublicKey.Destroy()
- }
- if session.SharedSecret != nil {
- session.SharedSecret.Destroy()
- }
+ // Destroy all protected memory
+ session.destroy()
- // Remove session from sessions map
- delete(sessions, req.SessionID)
-
- // Also remove from pairedSessions if it was paired
- for key, pairedSession := range pairedSessions {
- if pairedSession.SessionA == req.SessionID || pairedSession.SessionB == req.SessionID {
- delete(pairedSessions, key)
- break
- }
- }
+ // Remove from sessions map
+ delete(sessionManager.sessions, req.SessionID)
- log.Printf("Session %s terminated successfully", req.SessionID)
+ log.Printf("Session terminated: %s", req.SessionID)
resp := EndSessionResponse{
Status: "success",
- Message: "Session terminated successfully",
+ Message: "Session terminated and all keys destroyed",
}
json.NewEncoder(w).Encode(resp)
}
-// cleanupAllSessions securely destroys all sessions
+// Cleanup all sessions on shutdown
func cleanupAllSessions() {
- sessionsMutex.Lock()
- defer sessionsMutex.Unlock()
+ sessionManager.mutex.Lock()
+ defer sessionManager.mutex.Unlock()
log.Println("Cleaning up all sessions...")
- for sessionID, session := range sessions {
- // Securely destroy all memory-protected keys
- if session.PrivateKey != nil {
- session.PrivateKey.Destroy()
- }
- if session.PublicKey != nil {
- session.PublicKey.Destroy()
- }
- if session.SharedSecret != nil {
- session.SharedSecret.Destroy()
- }
-
- log.Printf("Cleaned up session: %s", sessionID)
- }
-
- // Clear the maps
- sessions = make(map[string]*Session)
- pairedSessions = make(map[string]*PairedSession)
-
- // Destroy demo secrets
- if demoSecrets != nil {
- demoSecrets.Destroy()
+ for id, session := range sessionManager.sessions {
+ session.destroy()
+ log.Printf("Destroyed session: %s", id)
}
- log.Println("All sessions cleaned up successfully")
+ sessionManager.sessions = make(map[string]*Session)
+ log.Println("All sessions cleaned up")
}
func main() {
@@ -632,17 +572,15 @@ func main() {
memguard.CatchInterrupt()
defer memguard.Purge()
- // Initialize security demo
- initSecurityDemo()
+ // Start session cleanup routine
+ sessionManager.startCleanupRoutine()
- // Setup HTTP routes
- http.HandleFunc("/api/start_session", startSessionHandler)
- http.HandleFunc("/api/pair_session", pairSessionHandler)
- http.HandleFunc("/api/encrypt", encryptHandler)
- http.HandleFunc("/api/decrypt", decryptHandler)
- http.HandleFunc("/api/end_session", endSessionHandler)
- http.HandleFunc("/api/security_demo", securityDemoHandler)
- http.HandleFunc("/api/security_challenge", securityChallengeHandler)
+ // Setup HTTP routes with middleware
+ http.HandleFunc("/api/start_session", rateLimitMiddleware(corsMiddleware(sizeLimitMiddleware(startSessionHandler))))
+ http.HandleFunc("/api/pair_session", rateLimitMiddleware(corsMiddleware(sizeLimitMiddleware(pairSessionHandler))))
+ http.HandleFunc("/api/encrypt", rateLimitMiddleware(corsMiddleware(sizeLimitMiddleware(encryptHandler))))
+ http.HandleFunc("/api/decrypt", rateLimitMiddleware(corsMiddleware(sizeLimitMiddleware(decryptHandler))))
+ http.HandleFunc("/api/end_session", rateLimitMiddleware(corsMiddleware(sizeLimitMiddleware(endSessionHandler))))
// Serve static files
http.Handle("/", http.FileServer(http.Dir("./")))
@@ -651,8 +589,9 @@ func main() {
defer cleanupAllSessions()
port := ":8080"
- log.Printf("NoFuture server starting on port %s", port)
- log.Printf("Memguard protection active - conversations are secured")
+ log.Printf("NoFuture-Memguard-PQ v%s starting on port %s", VERSION, port)
+ log.Printf("Memguard protection active - all keys and plaintext secured")
+ log.Println("WARNING: Use HTTPS in production (configure reverse proxy)")
if err := http.ListenAndServe(port, nil); err != nil {
log.Fatal("Server failed to start:", err)