Skip to main content
Internal Documentation — This page contains security-sensitive implementation details. Do not share externally.

Overview

Thrust uses SSL certificate pinning to protect against man-in-the-middle attacks on critical API connections, especially banking APIs (GoCardless). The system supports dynamic hash updates via remote configuration to handle certificate rotation without app releases.

Architecture

iOS App

  • CertificatePinningManager — Validates SSL certificates
  • RemoteCertificateConfig — Fetches remote hashes

Server

  • /api/certificates.json — Current hashes endpoint
  • update-certificates.sh — Daily update script

How It Works

1

App Startup

On launch, the app calls refreshRemoteHashes() which fetches the latest hashes from thrust.finance/api/certificates.json.
2

Connection Validation

When connecting to a pinned domain, the certificate chain is validated against:
  1. Remote hashes (if available and fresh)
  2. Hardcoded fallback hashes (if remote unavailable)
3

Server Updates

A cron job runs daily at 6:00 AM to fetch fresh certificate hashes from all domains.

Pinned Domains

DomainServicePriority
bankaccountdata.gocardless.comGoCardless Banking API🔴 Critical
accounts.google.comGoogle OAuthStandard
oauth2.googleapis.comGoogle OAuth APIStandard
gmail.googleapis.comGmail APIStandard
login.microsoftonline.comMicrosoft OAuthStandard
graph.microsoft.comMicrosoft Graph APIStandard
api.coingecko.comCrypto PricesStandard
api.twelvedata.comStock PricesStandard

API Endpoint

curl https://thrust.finance/api/certificates.json

Fallback Behavior

ScenarioBehavior
Remote config availableUses remote hashes (updated daily)
Remote config unavailableUses hardcoded hashes in app
Pinning fails + graceful mode ONLogs error, allows connection
Pinning fails + graceful mode OFFBlocks connection (default)

iOS Implementation

Key Files

Thrust/Security/
├── CertificatePinningManager.swift  # Main pinning logic
└── RemoteCertificateConfig.swift    # Remote config fetcher

Startup Integration

Thrust.swift
// In runNonCriticalStartupTasks()
await CertificatePinningManager.shared.refreshRemoteHashes()

Enable Graceful Fallback

Only enable graceful fallback if you have monitoring in place!
CertificatePinningManager.shared.gracefulFallbackEnabled = true

Server Configuration

File Locations

FilePath
API Endpoint/volume1/Web/thrust/api/certificates.json
Update Script/volume1/Web/thrust/scripts/update-certificates.sh
Cron Log/volume1/Web/thrust/scripts/cron.log
Backups/volume1/Web/thrust/scripts/backups/

Cron Schedule

0 6 * * * Kondrick /volume1/Web/thrust/scripts/update-certificates.sh

Manual Update

ssh Kondrick@192.168.1.75
/volume1/Web/thrust/scripts/update-certificates.sh

Emergency Response

  1. SSH to server: ssh Kondrick@192.168.1.75
  2. Run update script: /volume1/Web/thrust/scripts/update-certificates.sh
  3. Verify endpoint: curl https://thrust.finance/api/certificates.json
  4. Users will get new hashes on next app launch
Before each release, run:
./Scripts/verify-certificates.sh
This checks that all hardcoded hashes match current certificates.

Monitoring

When pinning fails with graceful fallback enabled, errors are logged to CrashReportingManager with:
  • Host that failed
  • Actual certificate chain hashes
  • Expected hashes
Monitor these logs to detect certificate rotations early.

CertificatePinningManager.swift

Main certificate pinning implementation

RemoteCertificateConfig.swift

Remote configuration fetcher

GoCardlessAuthService.swift

Banking API authentication with pinned session

verify-certificates.sh

CI/CD certificate verification script