Improve decryption error handling

This commit is contained in:
2026-04-02 15:32:13 +03:00
Unverified
parent be7da286d4
commit 579b55477e
3 changed files with 66 additions and 9 deletions
@@ -14,7 +14,9 @@ import time
from datetime import datetime
from pathlib import Path
from typing import Optional
import httpx
from nacl.exceptions import CryptoError
from fastapi import APIRouter, Depends, HTTPException, status, Request
from sqlalchemy.orm import Session
from pydantic import BaseModel, Field
@@ -376,6 +378,20 @@ async def send_encrypted_message(
except HTTPException:
raise
except CryptoError as e:
logger.warning(
"DM send: transport NaCl decrypt failed (message/file key mismatch or corrupt ciphertext): %s",
e,
)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=(
"Transport decryption failed: the encrypted message and each attachment must be "
"encrypted with the same client ephemeral keypair. For resumable uploads, the "
"ciphertext bytes on the server must match the transport fields in this request—"
"re-encrypt on the client or abort the upload session and start over."
),
) from e
except Exception as e:
logger.exception("Error sending encrypted message: %s", e)
raise HTTPException(
+16 -1
View File
@@ -10,6 +10,9 @@ import os
import logging
import json
import httpx
from fastapi import HTTPException, status
# Import request models for in-process calls
logger = logging.getLogger("uvicorn.error")
@@ -418,11 +421,23 @@ async def process_message_with_files_in_messaging_service(
"files": transport_files,
}
try:
import httpx
async with httpx.AsyncClient(timeout=timeout) as client:
r = await client.post(url, json=payload)
r.raise_for_status()
return r.json()
except httpx.HTTPStatusError as e:
if e.response.status_code == status.HTTP_400_BAD_REQUEST:
try:
body = e.response.json()
detail = body.get("detail", str(body)) if isinstance(body, dict) else str(body)
except Exception:
detail = (e.response.text or "").strip() or str(e)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=detail,
) from e
logger.error("Failed to process message+files in messaging service: %s", e)
raise
except Exception as e:
logger.error("Failed to process message+files in messaging service: %s", e)
raise
+28 -2
View File
@@ -11,17 +11,21 @@ API Endpoints:
"""
import logging
import sys
import time
import base64
import os
from typing import Dict, Any
from fastapi import FastAPI, HTTPException, status
from nacl.exceptions import CryptoError
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
from pydantic import BaseModel
logger = logging.getLogger("uvicorn.error")
_B64_DECODE_KW = {"validate": True} if sys.version_info >= (3, 11) else {}
# Import encryption modules
from .encryption import generate_nonce, TRANSPORT_NONCE_SIZE, decrypt_transport_blob, decrypt_transport_message
from .processor import process_encrypted_message, process_encrypted_message_and_files
@@ -371,9 +375,14 @@ async def process_message_with_files(
plaintext_files: list[bytes] = []
filenames: list[str] = []
for tf in transport_files:
for idx, tf in enumerate(transport_files):
enc_b64 = tf.get("encrypted_file_data_b64", "")
transport_blob = base64.b64decode(enc_b64)
try:
transport_blob = base64.b64decode(enc_b64, **_B64_DECODE_KW)
except Exception as e:
logger.error("Invalid base64 for transport file index=%s filename=%r: %s", idx, tf.get("filename"), e)
raise
try:
plaintext_files.append(
decrypt_transport_blob(
client_public_key_b64=client_public_key_b64,
@@ -381,6 +390,14 @@ async def process_message_with_files(
ephemeral_private_key=private_key,
)
)
except Exception as e:
logger.error(
"Transport file decrypt failed index=%s filename=%r (check same ephemeral as message): %s",
idx,
tf.get("filename"),
e,
)
raise
filenames.append(tf.get("filename", "file"))
return process_encrypted_message_and_files(
@@ -417,6 +434,15 @@ async def process_message_with_files_http(request: ProcessMessageWithFilesReques
recipient_public_key_b64=request.recipient_public_key_b64,
transport_files=transport_files,
)
except CryptoError as e:
logger.warning("process-with-files: transport CryptoError: %s", e)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=(
"Transport decryption failed: message and files must use the same client ephemeral "
"key as when file ciphertext was produced."
),
) from e
except Exception as e:
logger.exception("Failed to process message with files: %s", e)
raise