Veqtrx security whitepaper

Veqtrx security whitepaper

Version 1.0 — 2026-05-18 Author: Vaibhav Iyer (Founder, Veqtrx) Contact: contact@veqtrx.com

Veqtrx is a UK affordability-assessment platform for consumer credit and debt advice. This whitepaper documents how Veqtrx handles, encrypts, and protects customer data. It is intended for bank, lender, and regulator due-diligence reviews.


1. Executive summary

Veqtrx is built around a single technical commitment: the server cannot read customer data, even with full database access. This is enforced by client-side envelope encryption rather than by policy.

Verifiability: the cryptographic code is public at github.com/vaibhavkiyer24/veqtrx-crypto-reference under MIT licence. Banks and security researchers can read, test, and diff it against what runs in production. No “trust us” required — verify yourself.

Compliance roadmap (summary):

Standard Target Notes
UK GDPR ✅ Compliant at launch Privacy policy + Vanish Protocol (Article 17) live
ICO registration Q2 2026 In progress
Cyber Essentials Q3 2026 Self-assessment phase
Cyber Essentials Plus Q4 2026 After first external audit
SOC 2 Type I Q1 2027 Target for first paying B2B partner
SOC 2 Type II Q3 2027 Target for first regulated lender partner
FCA authorisation TBD Only if regulated credit decisions become in-scope

2. Technical architecture

2.1 Data flow diagram

┌─────────────────────────────────────────────────────────────────────────┐
│                          BROWSER (trusted client)                       │
│                                                                         │
│   User password ──► PBKDF2-SHA256 (600,000 iter, 32-byte salt)         │
│                                  │                                      │
│                                  ▼                                      │
│                         KEK (256-bit AES key, non-extractable)         │
│                                                                         │
│   Per-budget data:                                                      │
│       ┌──────────────┐         ┌──────────────────────┐                │
│       │  Plaintext   │ ───►    │  AES-GCM encrypt    │ ────► ciphertext│
│       │  budget JSON │         │  (random nonce)      │                │
│       └──────────────┘         └──────────────────────┘                │
│                                          ▲                              │
│                                          │                              │
│                                       fresh DEK                         │
│                                       (256-bit)                         │
│                                          │                              │
│                            wrap DEK 3 ways for access control:          │
│                                                                         │
│           AES-GCM(KEK)             RSA-OAEP(agent pubkey)     AES-GCM   │
│           = customer wrap          = agent wrap               (share    │
│                                                               key)      │
│                                                               = creditor│
│                                                               wrap      │
│                                                                         │
└─────────┬──────────────────────────┬──────────────────────────┬─────────┘
          │  ciphertext              │  wrapped DEKs            │
          │  (opaque to server)      │  (opaque to server)      │
          ▼                          ▼                          ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                       SERVER (untrusted) — Render                       │
│                                                                         │
│   PostgreSQL:                                                           │
│     • customers (email, salt, kek_iter — never the password)            │
│     • budget_reports (encrypted_blob, integrity_hash, metadata)         │
│     • wrapped_keys (DEK wrapped for each principal)                     │
│     • identities (joined to customer via customer_id; encrypted)        │
│                                                                         │
│   What the server never has:                                            │
│     ❌ Plaintext password                                               │
│     ❌ KEK                                                              │
│     ❌ DEK (only wrapped versions)                                      │
│     ❌ Decrypted budget JSON                                            │
│     ❌ Agent's private RSA key (only wrapped under agent KEK)          │
│     ❌ Creditor share token (only the wrapped DEK)                     │
└─────────────────────────────────────────────────────────────────────────┘

2.2 Encryption layer

Primitive Choice Rationale
Password hashing for KEK PBKDF2-SHA256, 600,000 iterations OWASP 2026 recommendation. Argon2 is stronger but not yet in Web Crypto.
KEK, DEK AES-GCM 256-bit NIST-approved authenticated encryption. AEAD prevents ciphertext tampering.
Agent asymmetric RSA-OAEP 2048-bit + SHA-256 Only widely-available asymmetric option in Web Crypto. Curve25519 deferred.
Salt 32 random bytes (crypto.getRandomValues) Exceeds NIST SP 800-132 minimum of 16. Stored alongside encrypted blob.
Share token (creditor) 32 random bytes, base64url, in URL fragment 256 bits of entropy. No KDF needed. URL fragment never sent to server.
TLS TLS 1.3 (provider-managed) Render + Netlify edge. HSTS enabled.
Database-at-rest AES-256 (provider-managed) Render Postgres encrypted volumes. Our own encryption layer is independent.

All client-side cryptography uses the browser’s Web Crypto API (crypto.subtle). No third-party crypto libraries are imported. The audit surface is exactly one file (src/zero-knowledge.ts, ~360 lines) plus the host browser’s implementation.

2.3 Key management

Key Where it lives When it exists What guards it
Customer password Browser memory only User types it at login; discarded after KEK derivation User’s responsibility (we recommend strong passwords)
Customer KEK Browser memory only Lifetime of the logged-in tab Non-extractable CryptoKey (PBKDF2-derived)
Per-budget DEK Browser memory; ciphertext wraps on server Generated at budget save; reconstructed at decrypt KEK (customer), RSA private key (agent), share token (creditor)
Agent RSA private key Browser memory; wrapped on server under agent KEK Lifetime of logged-in agent tab Agent KEK (PBKDF2 from agent password)
Creditor share token URL fragment (#...) on share link Browser memory of anyone holding the URL URL secrecy + revocation = delete server-side wrapped DEK row
Admin token (operational) Server env var + bcrypt hash in DB Server lifetime Render env var (set out-of-band; not in git)

Critically: there is no server-side master key, KMS key, HSM key, or admin override that can decrypt customer data. If every Veqtrx employee, every server, and every backup were compromised simultaneously, customer financial data would remain ciphertext.

2.4 Storage architecture


3. Threat model

3.1 What we defend against (and how)

Threat Defence
Server-side data breach Encrypted blobs are opaque. Attacker who exfiltrates the entire budget_reports table gets ciphertext + integrity hashes. No useable customer data.
TLS interception by malicious proxy TLS 1.3 + HSTS. The actual data on the wire is already ciphertext from the browser; even a downgrade attack yields nothing useful.
Insider access at Veqtrx No employee path to plaintext. Engineers cannot click through to a decrypted budget — the database admin panel can only show metadata + blob hashes.
Government subpoena for cleartext data We can produce ciphertext but cannot decrypt it. Lawful-access requests for plaintext are technically impossible to fulfil.
Stolen admin credentials Admin can edit metadata, suspend accounts, see analytics — but cannot decrypt financial data. Admin SQL console exists (audited tool); IP allowlist post-launch.
Replay / tampering of encrypted blobs AES-GCM AEAD detects tampering. Plus an SHA-256 integrity hash of the original plaintext is stored separately for end-to-end verification.
Cross-tenant data leakage in partner API Every assessment lookup is scoped to partner.id. Cross-tenant returns 404 (not 403) to prevent enumeration.
CSRF on the customer flow JWT in Authorization header, not cookies. allow_credentials: false in CORS. CSRF not exploitable in this design.
Brute force on customer password PBKDF2 600k iter ≈ 600-900 ms per attempt on a fast device. Rate-limited 5/min per IP at the login endpoint.

3.2 What we do NOT defend against (acknowledged)

Threat Why not Mitigation if it matters to you
Client device compromise Malware in the user’s browser can read decrypted data while they view it. Same as every web app. Customer-side endpoint security is the user’s responsibility.
Weak customer passwords A user choosing password123 defeats PBKDF2. We require ≥8 chars + show a strength meter, but cannot force higher. Future: server-side password policy + breach-check via HIBP.
Phishing A user typing their password into a fake site reveals it. Standard browser security applies. We don’t currently offer hardware-key MFA.
Web Crypto bugs We depend on crypto.subtle being implemented correctly. We have no way to verify the browser’s implementation from JS. Trust shifts to the browser vendor. Same as every web app using crypto.
Bugs in our own crypto code The code is published (veqtrx-crypto repo). Independent review welcomed. Bug bounty programme planned post-launch.
Timing attacks on login Our login endpoint may leak the existence of an email via response-time difference. Flagged in pre-launch audit; constant-time comparison planned.
Sophisticated nation-state actors We are not (yet) designed against $1M+ targeted attacks. Out of scope for an early-stage product; reconsidered at scale.

3.3 Mitigation strategies for residual risks


4. Audit trail

4.1 What we log

Event Data captured Retention
Customer login timestamp, IP country, user-agent (not full IP) 90 days
Budget save timestamp, blob hash, customer_id, principals granted 90 days
Budget access by agent timestamp, agent_id, principal route, ref_id 365 days
Budget access by creditor timestamp, IP country, ref_id, share token prefix (not full token) 365 days
Admin action timestamp, admin email, endpoint, JSON request body 730 days
Vanish Protocol (GDPR Article 17) timestamp, customer_id, what was scrubbed Indefinite (proof of erasure)

Logs are structured JSON (structlog), aggregated via Render’s log pipeline. PII-scrubbed at SDK level.

4.2 How to verify the log

For a partner / regulator wanting to confirm we accessed only what they expect:

  1. Request a log dump for the customer / time window in question.
  2. We export the relevant rows from agent_access_logs, admin_actions, creditor_views.
  3. Each row includes the principal, the endpoint, the timestamp, and the ref_id.
  4. Cross-reference against your records of authorisation. Any access without matching authorisation is a finding.

We commit to producing this export within 5 business days of a written request from a contracted partner.

4.3 Partner / bank access to logs

For B2B partners on the Architecture B (white-label portal) tier: - A webhook fires on every customer completion, providing real-time access notification. - Quarterly the partner can request a CSV of all access events scoped to their partner_id. - For incident response, an emergency log export is available within 4 hours.

For B2B partners on Architecture A (direct API): - The same webhooks and quarterly CSV exports apply. - API call logs are scoped to the partner’s API key — they see only their own usage.


5. Roadmap

5.1 Short term (next 90 days)

5.2 Mid term (6-12 months)

5.3 Long term (12+ months)

5.4 External audits planned

Date Activity Vendor Scope
Q3 2026 Pen test #1 TBD (shortlist: NCC, ThreatScout, F-Secure) Application + crypto repo
Q4 2026 Cyber Essentials Plus UK CE accredited body Infrastructure
Q1 2027 SOC 2 Type I gap assessment TBD Customer-data handling controls
Q2 2027 Independent crypto review TBD veqtrx-crypto repo + production diff
Q3 2027 SOC 2 Type II audit period begins Same auditor as Type I 6-month observation window

This document will be updated after each milestone.


Appendices

A. Glossary

Term Meaning
ZK Zero-knowledge — server cannot read customer data
KEK Key Encryption Key — derived from password, wraps DEKs
DEK Data Encryption Key — per-budget AES-GCM key, wraps the actual data
PBKDF2 Password-Based Key Derivation Function 2 — slow KDF resistant to brute force
AEAD Authenticated Encryption with Associated Data — encryption that also detects tampering
AES-GCM Galois/Counter Mode — NIST-standard AEAD construction
RSA-OAEP Optimal Asymmetric Encryption Padding — RSA with secure padding
JWK JSON Web Key — standard format for public-key transport

B. References

C. Contact


This document is published verbatim at docs/security-whitepaper.md in the application repo. To convert to PDF for distribution: pandoc security-whitepaper.md -o security-whitepaper.pdf --pdf-engine=xelatex.