1+ from cryptography .fernet import Fernet
2+ from cryptography .hazmat .primitives import hashes
3+ from cryptography .hazmat .primitives .kdf .pbkdf2 import PBKDF2HMAC
4+ import base64
5+ import os
6+ import json
7+ import logging
8+ from pathlib import Path
9+ import platform
10+
11+ class TokenEncryption :
12+ def __init__ (self ):
13+ self .token_file = Path .home () / '.aria_token'
14+ self .salt_file = Path .home () / '.aria_salt'
15+ self ._ensure_salt_exists ()
16+ logging .info (f"TokenEncryption initialized on { platform .system ()} " )
17+
18+ def _ensure_salt_exists (self ):
19+ """Ensure the salt file exists and contains a valid salt."""
20+ try :
21+ if not self .salt_file .exists ():
22+ salt = os .urandom (16 )
23+ self .salt_file .write_bytes (salt )
24+ logging .info (f"Created new salt file at { self .salt_file } " )
25+ else :
26+ salt = self .salt_file .read_bytes ()
27+ if len (salt ) != 16 :
28+ salt = os .urandom (16 )
29+ self .salt_file .write_bytes (salt )
30+ logging .info ("Regenerated invalid salt file" )
31+ except Exception as e :
32+ logging .error (f"Error managing salt file: { str (e )} " )
33+ raise Exception (f"Failed to manage salt file: { str (e )} " )
34+
35+ def _get_key (self , password : str ) -> bytes :
36+ """Derive an encryption key from the password using PBKDF2."""
37+ try :
38+ salt = self .salt_file .read_bytes ()
39+ kdf = PBKDF2HMAC (
40+ algorithm = hashes .SHA256 (),
41+ length = 32 ,
42+ salt = salt ,
43+ iterations = 100000 ,
44+ )
45+ key = base64 .urlsafe_b64encode (kdf .derive (password .encode ()))
46+ return key
47+ except Exception as e :
48+ logging .error (f"Error deriving encryption key: { str (e )} " )
49+ raise Exception (f"Failed to derive encryption key: { str (e )} " )
50+
51+ def encrypt_token (self , token_data : dict , password : str ) -> None :
52+ """Encrypt and store token data."""
53+ try :
54+ key = self ._get_key (password )
55+ f = Fernet (key )
56+ encrypted_data = f .encrypt (json .dumps (token_data ).encode ())
57+ self .token_file .write_bytes (encrypted_data )
58+ logging .info (f"Successfully encrypted and stored token at { self .token_file } " )
59+ except Exception as e :
60+ logging .error (f"Error encrypting token: { str (e )} " )
61+ raise Exception (f"Failed to encrypt token: { str (e )} " )
62+
63+ def decrypt_token (self , password : str ) -> dict :
64+ """Decrypt and retrieve token data."""
65+ if not self .token_file .exists ():
66+ logging .info ("No token file found" )
67+ return None
68+
69+ try :
70+ key = self ._get_key (password )
71+ f = Fernet (key )
72+ encrypted_data = self .token_file .read_bytes ()
73+ decrypted_data = f .decrypt (encrypted_data )
74+ logging .info ("Successfully decrypted token" )
75+ return json .loads (decrypted_data )
76+ except Exception as e :
77+ logging .error (f"Error decrypting token: { str (e )} " )
78+ return None
79+
80+ def clear_token (self ) -> None :
81+ """Remove stored token data."""
82+ try :
83+ if self .token_file .exists ():
84+ self .token_file .unlink ()
85+ logging .info ("Successfully cleared token file" )
86+ except Exception as e :
87+ logging .error (f"Error clearing token file: { str (e )} " )
88+ raise Exception (f"Failed to clear token file: { str (e )} " )
0 commit comments