refactor: move clipboard to frontend, fix cipher type mapping, add context menu
This commit is contained in:
+5
-46
@@ -15,21 +15,6 @@ from cryptography.hazmat.primitives import hashes
|
||||
|
||||
log = logging.getLogger("decky-vaultwarden")
|
||||
|
||||
_DEBUG_LOG = "/tmp/decky-vaultwarden-debug.log"
|
||||
|
||||
|
||||
def _debug(msg: str):
|
||||
"""Write debug info to file for easy inspection."""
|
||||
import datetime
|
||||
ts = datetime.datetime.now().isoformat()
|
||||
line = f"[{ts}] {msg}\n"
|
||||
try:
|
||||
with open(_DEBUG_LOG, "a") as f:
|
||||
f.write(line)
|
||||
f.flush()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
from crypto import BitwardenCrypto, PinCrypto
|
||||
|
||||
|
||||
@@ -125,9 +110,7 @@ class BitwardenClient:
|
||||
method, url, headers=headers, json=data, params=params, ssl=_get_ssl_context()
|
||||
) as resp:
|
||||
text = await resp.text()
|
||||
_debug(f"[REQUEST] {method} {url} -> {resp.status}")
|
||||
if resp.status >= 400:
|
||||
_debug(f"[REQUEST] error body: {text[:500]}")
|
||||
try:
|
||||
error_data = json.loads(text)
|
||||
message = error_data.get("error_model", {}).get(
|
||||
@@ -144,10 +127,7 @@ class BitwardenClient:
|
||||
"""Get KDF settings for the user."""
|
||||
url = f"{self._get_api_url()}/accounts/prelogin"
|
||||
data = {"email": email}
|
||||
_debug(f"[PRELOGIN] POST {url}")
|
||||
_debug(f"[PRELOGIN] request data: {json.dumps(data)}")
|
||||
result = await self._request("POST", url, data=data)
|
||||
_debug(f"[PRELOGIN] response: {json.dumps(result)}")
|
||||
|
||||
self.kdf_type = result.get("kdf") or result.get("Kdf", 0)
|
||||
self.kdf_iterations = result.get("kdfIterations") or result.get("KdfIterations", 600000)
|
||||
@@ -155,7 +135,6 @@ class BitwardenClient:
|
||||
self.kdf_parallelism = result.get("kdfParallelism") or result.get("KdfParallelism")
|
||||
self.email = email
|
||||
|
||||
_debug(f"[PRELOGIN] parsed kdf_type={self.kdf_type}, kdf_iterations={self.kdf_iterations}, kdf_memory={self.kdf_memory}, kdf_parallelism={self.kdf_parallelism}")
|
||||
|
||||
return result
|
||||
|
||||
@@ -163,7 +142,6 @@ class BitwardenClient:
|
||||
self, email: str, password: str, two_factor_token: Optional[str] = None
|
||||
) -> dict:
|
||||
"""Login with email and master password."""
|
||||
_debug(f"[LOGIN] Starting password login for: {email}")
|
||||
await self.prelogin(email)
|
||||
|
||||
# Derive master key
|
||||
@@ -182,7 +160,6 @@ class BitwardenClient:
|
||||
else:
|
||||
raise ValueError(f"Unsupported KDF type: {self.kdf_type}")
|
||||
|
||||
_debug(f"[LOGIN] master_key (first 8 bytes b64): {b64encode(master_key[:8]).decode()}")
|
||||
|
||||
# Hash master password for auth (Bitwarden uses PBKDF2 with 1 iteration, NOT HMAC)
|
||||
_pwd_hash_kdf = PBKDF2HMAC(
|
||||
@@ -194,7 +171,6 @@ class BitwardenClient:
|
||||
)
|
||||
master_password_hash = b64encode(_pwd_hash_kdf.derive(master_key)).decode("utf-8")
|
||||
|
||||
_debug(f"[LOGIN] master_password_hash: {master_password_hash}")
|
||||
|
||||
# Build auth request
|
||||
url = f"{self.identity_url}/connect/token"
|
||||
@@ -217,9 +193,6 @@ class BitwardenClient:
|
||||
data["two_factor_provider"] = "0" # Authenticator
|
||||
data["two_factor_remember"] = "1"
|
||||
|
||||
_debug(f"[LOGIN] POST {url}")
|
||||
_debug(f"[LOGIN] form data (excl password): {json.dumps({k: v for k, v in data.items() if k != 'password'}, indent=2)}")
|
||||
_debug(f"[LOGIN] password field (master_password_hash): {data['password']}")
|
||||
|
||||
# Use form data instead of JSON
|
||||
async with aiohttp.ClientSession() as session:
|
||||
@@ -227,12 +200,9 @@ class BitwardenClient:
|
||||
url, data=data, headers=headers, ssl=_get_ssl_context()
|
||||
) as resp:
|
||||
text = await resp.text()
|
||||
_debug(f"[LOGIN] response status: {resp.status}")
|
||||
_debug(f"[LOGIN] response body: {text[:500]}")
|
||||
if resp.status >= 400:
|
||||
try:
|
||||
error_data = json.loads(text)
|
||||
_debug(f"[LOGIN] error response: {json.dumps(error_data, indent=2)}")
|
||||
# Check if 2FA is required
|
||||
if error_data.get("error") == "invalid_grant" and "twoFactor" in text:
|
||||
return {"two_factor_required": True}
|
||||
@@ -240,32 +210,20 @@ class BitwardenClient:
|
||||
"message", text
|
||||
)
|
||||
except (json.JSONDecodeError, KeyError):
|
||||
_debug(f"[LOGIN] raw error text: {text}")
|
||||
message = text
|
||||
raise Exception(f"Login failed: {message}")
|
||||
result = json.loads(text)
|
||||
|
||||
_debug("[LOGIN] SUCCESS - token received")
|
||||
self.access_token = result.get("access_token")
|
||||
self.refresh_token = result.get("refresh_token")
|
||||
self.user_id = result.get("Profile", {}).get("id") or result.get("sub")
|
||||
|
||||
# Get encrypted user key
|
||||
enc_user_key = result.get("Key")
|
||||
_debug(f"[LOGIN] enc_user_key present: {enc_user_key is not None}")
|
||||
if enc_user_key:
|
||||
_debug(f"[LOGIN] enc_user_key (first 40): {enc_user_key[:40]}...")
|
||||
_debug(f"[LOGIN] attempting to decrypt user key with master_key...")
|
||||
try:
|
||||
self.enc_key, self.mac_key = self.crypto.decrypt_user_key(
|
||||
enc_user_key, master_key
|
||||
)
|
||||
_debug(f"[LOGIN] user key decrypted successfully!")
|
||||
_debug(f"[LOGIN] enc_key (first 8 b64): {b64encode(self.enc_key[:8]).decode()}")
|
||||
_debug(f"[LOGIN] mac_key (first 8 b64): {b64encode(self.mac_key[:8]).decode()}")
|
||||
except Exception as e:
|
||||
_debug(f"[LOGIN] user key decryption FAILED: {e}")
|
||||
raise
|
||||
self.enc_key, self.mac_key = self.crypto.decrypt_user_key(
|
||||
enc_user_key, master_key
|
||||
)
|
||||
return {
|
||||
"success": True,
|
||||
"master_key": master_key,
|
||||
@@ -383,7 +341,8 @@ class BitwardenClient:
|
||||
# Decrypt ciphers
|
||||
decrypted_ciphers = []
|
||||
for cipher in ciphers:
|
||||
if cipher.get("type") in [0, 1, 2, 3]: # Login, Note, Card, Identity
|
||||
ctype = cipher.get("type")
|
||||
if ctype in [1, 2, 3, 4]: # Login, Note, Card, Identity
|
||||
decrypted = self.crypto.decrypt_cipher(
|
||||
cipher, enc_key, mac_key
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user