cuatrolabs-spa-ms / verify_encryption.py
MukeshKapoor25's picture
feat(leave,wallet): Add leave and wallet modules with JWT auth
a558c77
"""
Script to verify wallet balance encryption in the database.
This demonstrates that balances are stored encrypted and cannot be read without the key.
"""
import asyncio
import asyncpg
from decimal import Decimal
from app.core.config import settings
from app.wallet.services.service import WalletEncryption
async def verify_encryption():
"""Verify that wallet balances are encrypted in the database"""
print("\n" + "="*70)
print("Wallet Balance Encryption Verification")
print("="*70 + "\n")
# Connect to database
conn = await asyncpg.connect(
host=settings.POSTGRES_HOST,
port=settings.POSTGRES_PORT,
database=settings.POSTGRES_DB,
user=settings.POSTGRES_USER,
password=settings.POSTGRES_PASSWORD
)
try:
# Get a sample wallet
row = await conn.fetchrow(
"SELECT wallet_id, partner_id, encrypted_balance FROM trans.spa_wallets LIMIT 1"
)
if not row:
print("❌ No wallets found in database. Create a wallet first.")
return
wallet_id = row['wallet_id']
partner_id = row['partner_id']
encrypted_balance = row['encrypted_balance']
print(f"πŸ“Š Wallet Information:")
print(f" Wallet ID: {wallet_id}")
print(f" Partner ID: {partner_id}")
print(f"\nπŸ”’ Encrypted Balance (as stored in database):")
print(f" Type: {type(encrypted_balance)}")
print(f" Length: {len(encrypted_balance)} bytes")
print(f" Raw bytes (first 50): {encrypted_balance[:50]}")
print(f" Hex representation: {encrypted_balance.hex()[:100]}...")
# Try to decrypt
try:
decrypted_balance = WalletEncryption.decrypt_balance(encrypted_balance)
print(f"\nβœ… Decrypted Balance (using encryption key):")
print(f" Balance: {decrypted_balance}")
print(f" Type: {type(decrypted_balance)}")
except Exception as e:
print(f"\n❌ Decryption failed: {e}")
# Demonstrate encryption/decryption cycle
print(f"\nπŸ”„ Encryption/Decryption Cycle Test:")
test_balance = Decimal("12345.67")
print(f" Original: {test_balance}")
encrypted = WalletEncryption.encrypt_balance(test_balance)
print(f" Encrypted: {encrypted.hex()[:100]}...")
decrypted = WalletEncryption.decrypt_balance(encrypted)
print(f" Decrypted: {decrypted}")
if test_balance == decrypted:
print(f" βœ… Encryption/Decryption cycle successful!")
else:
print(f" ❌ Encryption/Decryption cycle failed!")
# Show that different values produce different encrypted data
print(f"\nπŸ” Encryption Uniqueness Test:")
balance1 = Decimal("1000.00")
balance2 = Decimal("2000.00")
encrypted1 = WalletEncryption.encrypt_balance(balance1)
encrypted2 = WalletEncryption.encrypt_balance(balance2)
print(f" Balance 1: {balance1}")
print(f" Encrypted 1: {encrypted1.hex()[:50]}...")
print(f" Balance 2: {balance2}")
print(f" Encrypted 2: {encrypted2.hex()[:50]}...")
print(f" Are they different? {encrypted1 != encrypted2} βœ…")
# Security note
print(f"\nπŸ›‘οΈ Security Notes:")
print(f" β€’ Balance is stored as binary data (BYTEA) in PostgreSQL")
print(f" β€’ Encryption uses AES via Fernet (symmetric encryption)")
print(f" β€’ Key is derived from SECRET_KEY using SHA256")
print(f" β€’ Without the encryption key, the balance cannot be read")
print(f" β€’ Even database administrators cannot see actual balances")
print(f" β€’ Each encryption produces different ciphertext (includes IV)")
# Show transaction amounts (not encrypted)
print(f"\nπŸ“ Transaction History (amounts are NOT encrypted):")
transactions = await conn.fetch(
"""
SELECT transaction_type, amount, balance_after, description, created_at
FROM trans.spa_wallet_transactions
WHERE partner_id = $1
ORDER BY created_at DESC
LIMIT 5
""",
partner_id
)
if transactions:
for txn in transactions:
print(f" {txn['created_at']} | {txn['transaction_type']:6} | "
f"β‚Ή{txn['amount']:>10} | Balance: β‚Ή{txn['balance_after']:>10} | "
f"{txn['description']}")
else:
print(f" No transactions found")
print(f"\nπŸ’‘ Why transaction amounts are not encrypted:")
print(f" β€’ Needed for reporting and analytics")
print(f" β€’ Balance history is maintained in transactions")
print(f" β€’ Only current balance in wallet table is encrypted")
print(f" β€’ Transaction amounts are historical and less sensitive")
finally:
await conn.close()
print("\n" + "="*70)
print("Verification Complete")
print("="*70 + "\n")
if __name__ == "__main__":
print("""
╔══════════════════════════════════════════════════════════════╗
β•‘ Wallet Balance Encryption Verification Script β•‘
β•‘ β•‘
β•‘ This script demonstrates that wallet balances are stored β•‘
β•‘ encrypted in the database and cannot be read without the β•‘
β•‘ encryption key. β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
""")
try:
asyncio.run(verify_encryption())
except Exception as e:
print(f"\n❌ Error: {e}")
print("\nMake sure:")
print(" 1. Database is running")
print(" 2. .env file is configured")
print(" 3. Wallet tables are created (run migration)")
print(" 4. At least one wallet exists in the database")