SecuAAS Docs

Data Flows and Security

SecuFile — Data Flows and Security

Data Flows and Security

Data Flows and Security

Encrypted Upload Flow (E2E)

End-to-end encryption is one of SecuFile's core features. Here is the detailed flow of a client-side encrypted upload:

1. Browser (React)
   |-- Generates a random AES-256 key (Data Encryption Key = DEK)
   |-- Encrypts the file in chunks with AES-256-GCM
   |   |-- Each chunk uses a derived nonce: baseNonce XOR chunkIndex
   |   |-- Optimal chunk size calculated based on file size:
   |   |     - < 10 MB: 1 MB chunks
   |   |     - 10-100 MB: 5 MB chunks
   |   |     - > 100 MB: 10 MB chunks
   |-- Retrieves the organization's RSA-4096 public key via the API
   |-- Encrypts the DEK with RSA-OAEP (SHA-256)
   |-- Sends to the server:
       |-- The encrypted file (binary)
       |-- The encrypted DEK (encrypted_key)
       |-- Encryption metadata (nonce, chunk sizes, version)
       |-- SHA-256 checksum of the original file

2. Go Backend (API)
   |-- Receives the encrypted file + metadata
   |-- Direct streaming to OVH S3 (no in-memory buffering)
   |-- Stores metadata in PostgreSQL:
       |-- encrypted_key, key_version, encryption_metadata (JSONB)
       |-- encryption_type = "client", uses_custom_key = true
       |-- original_checksum for verification

3. OVH S3
   |-- Stores the encrypted file
   |-- The server never has access to plaintext data

Decrypted Download Flow

1. Browser (React)
   |-- Requests file download via GET /api/v1/clients/:id/files/:id/download
   |-- If uses_custom_key = true (client-side encrypted):
   |   |-- Receives the encrypted file + metadata headers
   |   |-- Retrieves the organization's RSA-4096 private key
   |   |-- Unwraps the DEK using RSA-OAEP (SHA-256)
   |   |-- Decrypts the file chunk by chunk in the browser
   |   |-- Provides the decrypted file for download
   |-- If uses_custom_key = false (server-side encrypted):
       |-- The server decrypts the file before sending
       |-- The file is sent in plaintext (over TLS)

2. Go Backend (for server-side decryption)
   |-- Loads encrypted DEK from PostgreSQL
   |-- Decrypts DEK via KMS (OVH KMS or local)
   |-- Downloads encrypted file from S3
   |-- Decrypts file with DEK (AES-256-GCM)
   |-- Streams decrypted file to client
1. Share Link Creation (authenticated user)
   |-- POST /api/v1/clients/:client_id/share-links
   |-- Generates a secure random token (64 hex characters)
   |-- Stores link configuration (password, expiry, max downloads)
   |-- Returns public URL: https://secufile.secuaas.dev/s/{token}

2. Share Link Access (anonymous user)
   |-- GET /s/{token} -> Returns link info (type, name, consent required)
   |-- If consent text configured:
   |   |-- GET /s/{token}/consent-text -> Returns Loi 25 consent text
   |   |-- POST /s/{token}/accept-consent -> Records acceptance in audit trail
   |-- If password protected:
   |   |-- Password validation required before access
   |-- For download links:
   |   |-- GET /s/{token}/download -> File download
   |   |-- If E2E encrypted: encryption info in headers for client-side decryption
   |-- For upload links:
   |   |-- POST /s/{token}/upload -> File upload (25 MB max)
   |   |-- Files stored in the organization's S3 bucket

3. Audit Trail
   |-- Every action logged: access, download, upload, consent acceptance

Key Management Flow

Organization Creation:
   |-- Generate RSA-4096 key pair (PKCS1 private, PKIX public)
   |-- Store public key as PEM in PostgreSQL (organizations.public_key)
   |-- Encrypt private key via KMS WrapKey
   |-- Store encrypted private key as Base64 in PostgreSQL (organizations.encrypted_private_key)

File Upload (client-side E2E):
   |-- Browser generates random AES-256 DEK
   |-- Browser encrypts file with DEK (AES-256-GCM, chunked)
   |-- Browser encrypts DEK with org's RSA public key (RSA-OAEP SHA-256)
   |-- Server stores encrypted DEK in PostgreSQL (files.encrypted_key)

File Download (client-side E2E):
   |-- Server returns encrypted file + encrypted DEK
   |-- Browser retrieves org's encrypted private key
   |-- Browser requests KMS UnwrapKey to decrypt private key
   |-- Browser decrypts DEK with RSA private key
   |-- Browser decrypts file with DEK

File Upload/Download (server-side):
   |-- Server generates DEK
   |-- Server encrypts file with DEK (AES-256-GCM)
   |-- Server encrypts DEK with KMS WrapKey
   |-- For download: reverse process (KMS UnwrapKey -> DEK -> decrypt file)

Storage Manager Architecture

The StorageManager provides per-organization storage isolation:

StorageManager
   |-- defaultStorage (S3Storage) -> Global fallback S3 bucket
   |-- orgStorages map[UUID]*S3Storage -> Per-organization dedicated buckets
   |-- clientStorages map[UUID]*S3Storage -> Legacy per-client buckets
   |
   |-- GetStorage(id) -> Returns org bucket if exists, else default
   |-- RegisterClientStorage(config) -> Creates new S3 connection
   |-- StartPeriodicReload(ctx, 30s) -> Reloads configs for multi-pod support

Bucket naming convention: secufile_{env}_{msp-id}_{org-id}

Each organization's bucket is auto-provisioned on creation via the OVH Cloud API with dedicated credentials (access key + secret key) encrypted and stored in PostgreSQL.

On this page