From 12dcc31a0cf8e53ea73b207d713f458df6b7f67f Mon Sep 17 00:00:00 2001 From: Suman Biswas Date: Thu, 23 Jul 2026 00:37:19 +0530 Subject: [PATCH] e2ee reset --- server/controllers/e2ee.go | 26 ++++++++++++++++++++++++++ server/models/e2ee.go | 4 ++++ server/repository/e2ee.go | 29 +++++++++++++++++++++++++++++ server/routes/e2ee.go | 2 ++ server/services/e2ee.go | 24 ++++++++++++++++++++++++ 5 files changed, 85 insertions(+) diff --git a/server/controllers/e2ee.go b/server/controllers/e2ee.go index 2bd37fd..55743b1 100644 --- a/server/controllers/e2ee.go +++ b/server/controllers/e2ee.go @@ -109,3 +109,29 @@ func HandleGetVerificationStatus(w http.ResponseWriter, r *http.Request) { _ = json.NewEncoder(w).Encode(models.JSONResponse{Success: true, Data: resp}) } + +func HandleResetKeys(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + uid, _ := r.Context().Value(middlewares.UserIDKey).(string) + + var req models.ResetKeysReq + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + w.WriteHeader(http.StatusBadRequest) + _ = json.NewEncoder(w).Encode(models.JSONResponse{Success: false, Error: "Invalid JSON request payload"}) + return + } + + svc := services.NewE2EEService() + if err := svc.ResetKeys(r.Context(), uid, req.Password); err != nil { + w.WriteHeader(http.StatusUnauthorized) + _ = json.NewEncoder(w).Encode(models.JSONResponse{Success: false, Error: err.Error()}) + return + } + + _ = json.NewEncoder(w).Encode(models.JSONResponse{ + Success: true, + Data: map[string]interface{}{ + "message": "All encryption keys wiped successfully. Please re-generate and upload a new prekey bundle.", + }, + }) +} diff --git a/server/models/e2ee.go b/server/models/e2ee.go index 192424c..2f3e30d 100644 --- a/server/models/e2ee.go +++ b/server/models/e2ee.go @@ -33,3 +33,7 @@ type VerificationStatusResp struct { VerifiedUserID string `json:"verified_user_id"` IsVerified bool `json:"is_verified"` } + +type ResetKeysReq struct { + Password string `json:"password"` +} diff --git a/server/repository/e2ee.go b/server/repository/e2ee.go index 956cbe3..620b396 100644 --- a/server/repository/e2ee.go +++ b/server/repository/e2ee.go @@ -154,3 +154,32 @@ func (r *E2EERepository) InvalidateUserVerifications(ctx context.Context, uid st _, err := config.DB.Exec(ctx, query, uid) return err } + +func (r *E2EERepository) ResetUserKeys(ctx context.Context, uid string) error { + tx, err := config.DB.Begin(ctx) + if err != nil { + return err + } + defer tx.Rollback(ctx) + + _, err = tx.Exec(ctx, `DELETE FROM user_devices WHERE user_id = $1::uuid;`, uid) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, `DELETE FROM one_time_prekeys WHERE user_id = $1::uuid;`, uid) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, ` + UPDATE user_verifications + SET is_verified = FALSE, updated_at = NOW() + WHERE verified_user_id = $1::uuid; + `, uid) + if err != nil { + return err + } + + return tx.Commit(ctx) +} diff --git a/server/routes/e2ee.go b/server/routes/e2ee.go index 1f0ae3e..c8a056d 100644 --- a/server/routes/e2ee.go +++ b/server/routes/e2ee.go @@ -14,4 +14,6 @@ func E2EERoute(router chi.Router) { router.Post("/verify", controllers.HandleSetVerification) router.Get("/verify/{userId}", controllers.HandleGetVerificationStatus) + + router.Post("/reset", controllers.HandleResetKeys) } diff --git a/server/services/e2ee.go b/server/services/e2ee.go index 225c8a1..20f8c03 100644 --- a/server/services/e2ee.go +++ b/server/services/e2ee.go @@ -4,6 +4,7 @@ import ( "context" "errors" + "github.com/commandlinecoding/elephant/server/config" "github.com/commandlinecoding/elephant/server/models" "github.com/commandlinecoding/elephant/server/repository" ) @@ -63,3 +64,26 @@ func (s *E2EEService) GetVerification(ctx context.Context, uid, targetUID string IsVerified: isVerified, }, nil } + +func (s *E2EEService) ResetKeys(ctx context.Context, uid string, password string) error { + + var passwordHash string + query := `SELECT password_hash FROM users WHERE id = $1::uuid;` + err := config.DB.QueryRow(ctx, query, uid).Scan(&passwordHash) + if err != nil { + return errors.New("user account context not found") + } + + if passwordHash != "" { + if password == "" { + return errors.New("password required for re-authentication") + } + + match, err := VerifyPassword(password, passwordHash) + if err != nil || !match { + return errors.New("invalid password re-authentication attempt") + } + } + + return s.repo.ResetUserKeys(ctx, uid) +}