Browse Source

use a kdf

boyska 1 year ago
parent
commit
e070a96fb6
1 changed files with 32 additions and 5 deletions
  1. 32 5
      tresetter.py

+ 32 - 5
tresetter.py

@@ -6,10 +6,12 @@ import json
 import uuid
 from subprocess import Popen, CalledProcessError, check_output
 from typing import Optional
+import hashlib
+import base64
 
 import redis
 from fastapi import FastAPI, APIRouter, HTTPException, Cookie, Request
-from fastapi.responses import Response, JSONResponse, RedirectResponse
+from fastapi.responses import Response, RedirectResponse
 from fastapi.staticfiles import StaticFiles
 from pydantic import BaseModel, BaseSettings
 
@@ -201,22 +203,47 @@ async def logout(session_id: str = Cookie(None)) -> BaseModel:
     delete_session(session_id)
     return BaseModel()
 
+KDF_SALT_SIZE = 16
+
+
+def kdf_gen(password, salt=None) -> str:
+    if salt is None:
+        salt = random.randbytes(KDF_SALT_SIZE)
+    if hasattr(password, 'encode'):
+        password = password.encode('utf8')
+    raw = hashlib.scrypt(password, n=2, r=1, p=1, salt=salt)
+    with_salt = salt + raw
+    return base64.b64encode(with_salt).decode('ascii')
+
+def kdf_get_salt(hashed: str):
+    hashed_str = hashed.decode('ascii') if hasattr(hashed, 'decode') else hashed
+    with_salt = base64.b64decode(hashed_str)
+    salt = with_salt[:KDF_SALT_SIZE]
+    return salt
+
+def kdf_verify(hashed: str, password: str) -> bool:
+    salt = kdf_get_salt(hashed)
+    hashed2 = kdf_gen(password, salt=salt)
+    return hashed == hashed2
+
 
 @router.post("/generate", tags=["password"])
 async def generate(session_id: str = Cookie(None)):
     session = get_session(session_id)
-    session["proposed_password"] = password_generate()
+    proposed_password = password_generate()
+    session["proposed_password_hash"] = kdf_gen(proposed_password)
     set_session(session_id, session)
 
-    return ChangeData(password=session["proposed_password"])
+    return ChangeData(password=proposed_password)
 
 
 @router.post("/change", tags=["password"])
 async def change(req: ChangeData, session_id: str = Cookie(None)) -> SuccessData:
     session = get_session(session_id)
-    if "proposed_password" not in session:
+    if "proposed_password_hash" not in session:
         raise HTTPException(status_code=400, detail="You must generate it first")
-    if req.password != session["proposed_password"]:
+    hashed = session["proposed_password_hash"]
+    if not kdf_verify(hashed, req.password):
         raise HTTPException(status_code=409)
 
     success = change_password(session["username"], req.password)