Skip to content

Conversation

@BitcoinErrorLog
Copy link
Owner

@BitcoinErrorLog BitcoinErrorLog commented Dec 31, 2025

Summary

Complete iOS Paykit/Pubky SDK audit fixes (Phases 1-5 + Loose Ends) based on learnings from the Android integration audit.

Changes

Phase 1: Thread Safety & Force Unwrap Elimination

Thread Safety

  • PubkyRingBridge: Added NSLock with thread-safe cache helper methods to protect sessionCache and keypairCache from race conditions
  • SpendingLimitManager: Wrapped all FFI methods with queue.sync to ensure thread-safe access to the Rust FFI manager
  • NoiseKeyCache: Fixed race condition by using barrier sync for atomic read-check-write pattern in getKey()

Force Unwrap Elimination

  • DirectoryService: Replaced force unwraps (!) with proper guard let patterns when resolving homeserver URLs
  • PaykitManager: Removed force unwraps in executor registration by using local variables

Phase 2: Security Improvements

Deep Link Validation

  • PaykitDeepLinkValidator: New utility for secure deep link validation
    • Validates scheme, host, and required parameters
    • Enforces length limits and character constraints (prevents injection attacks)
    • Integrated into MainNavView.onOpenURL handler
    • Fixed: Now requires host=payment-request for both paykit:// and bitkit:// schemes

Secure Receipt Storage

  • PaykitReceiptStore: Migrated from UserDefaults to Keychain
    • Receipts contain payment amounts and peer pubkeys (sensitive data)
    • Now uses PaykitKeychainStorage for encrypted storage at rest
    • Added: One-time migration from legacy UserDefaults storage

Phase 3: Background Service Improvements

AutoPay Evaluator Service

  • AutoPayEvaluatorService: New service for background-safe auto-pay evaluation
    • Non-MainActor service for use in BGTaskScheduler handlers
    • evaluateForBackground() treats biometric requirements as needs-approval
    • Documented biometric policy for background payments in KDoc

Retry Logic

  • SubscriptionBackgroundService: Added exponential backoff retry logic
    • 3 retries with 5s initial delay, doubling each retry
    • Maximum delay capped at 60 seconds
    • Prevents single transient failures from failing subscription payments
    • Fixed: Removed unused autoPayStorage field (dead code)

ViewModel Integration

  • AutoPayViewModel: Now delegates to AutoPayEvaluatorService for evaluation
    • Ensures consistency between foreground and background evaluation logic
    • ViewModel handles UI-specific side effects (notifications)

Phase 4: Infrastructure

Shared Network Configuration

  • PaykitNetworkConfig: New shared URLSession configuration
    • Consistent timeouts (30s request, 60s resource)
    • HTTP/2 support with proper headers
    • URL caching disabled for sensitive payment data
    • Fixed: Replaced force-cast with safe configuration copy
    • Fixed: Build User-Agent dynamically from bundle info
  • PubkyStorageAdapter: Updated to use shared session

Phase 5: Documentation

  • Updated README.md with new "Thread Safety & Security" section
    • Thread-safe services table with mechanisms
    • Secure storage documentation
    • Deep link validation usage examples
    • Biometric policy for background payments
    • Shared network configuration usage

Loose Ends Addressed

Issue Fix
Deep link host validation Both paykit:// and bitkit:// now require host=payment-request
Receipt storage migration Added one-time migration from UserDefaults to Keychain
Dead code Removed unused autoPayStorage from SubscriptionBackgroundService
Force-cast in network config Replaced with safe configuration copy method
Hardcoded User-Agent Now builds dynamically from bundle info

Testing

  • Linter passes for all modified files
  • Unable to run full build due to Xcode simulator version mismatch (environment issue, not code issue)

Related

This is the iOS equivalent of the comprehensive audit fixes applied to the Android codebase.

JOHN and others added 15 commits December 22, 2025 17:23
Added prominent link to the comprehensive Bitkit + Paykit Integration Master Guide
at the top of the README for production developers.
- Ed25519 master keys now owned exclusively by Pubky Ring
- Bitkit only stores: public key, device ID, epoch, cached X25519 keypairs
- Updated NoisePaymentService to use cached X25519 keypair
- Updated PubkyRingIntegration to retrieve cached keys only

BREAKING: Bitkit can no longer generate or derive keys locally.
All key operations must go through Pubky Ring.
- Handle mode=secure_handoff callback from Ring
- Fetch handoff payload from homeserver via PubkySDKService
- Parse SecureHandoffPayload JSON structure
- No secrets in callback URL - more secure against logging
- Backward compatible with legacy mode
- PushRelayService stores tokens server-side, never publicly
- Deprecate DirectoryService.publishPushNotificationEndpoint()
- Deprecate DirectoryService.discoverPushNotificationEndpoint()
- Includes registration, unregistration, and wake notification APIs
- Rate limiting and signature authentication support
- HomeserverPubkey: z32 pubkey identifying a homeserver
- HomeserverURL: resolved HTTPS URL for API requests
- SessionSecret: secure wrapper for session credentials
- OwnerPubkey: z32 pubkey identifying a user
- HomeserverResolver: centralized URL resolution

Prevents confusion between pubkeys and URLs in storage code.
- Use HomeserverURL and OwnerPubkey types in DirectoryService
- Update PubkyStorageAdapter to accept HomeserverURL
- Convert to String using .value property when needed
- Maintains cache of pubkey→URL resolutions with 1-hour TTL
- Known homeservers map loaded on init
- Supports custom mappings via addMapping()
- Override support for testing/development
- Prepares for future DNS-based resolution
- Delete handoff file from homeserver after successful retrieval
- Minimizes attack window for encrypted payload
- Uses background task to avoid blocking setup result
- Added requestSignature method to PubkyRingBridge
- Added signature-result callback handler
- Updated PushRelayService to use real Ed25519 signing
- Removed placeholder signature implementation
- Added getOrRefreshKeypair method with auto-recovery
- Automatically requests from Ring when cache is empty
- Added getCurrentKeypairOrRefresh convenience method
- Improves reliability when cache is cleared
- Added checkKeyRotation method to NoisePaymentService
- Added setCurrentEpoch method to KeyManager
- Supports manual rotation from epoch 0 to epoch 1
- Prepares for time-based automatic rotation
- F7: Add missing 'await' to signMessage call in PushRelayService
- Remove unused useSecureHandoff parameter (Ring always uses secure handoff)
- Update comments to accurately describe secure handoff behavior
- Deleted PushNotificationService.swift (unimplemented stub code)
- PushRelayService is the active implementation for push notifications
Phase 1 of iOS Paykit/Pubky audit fixes:

- PubkyRingBridge: Add NSLock with thread-safe cache helpers
- SpendingLimitManager: Wrap all FFI methods with queue.sync
- NoiseKeyCache: Use barrier sync for atomic read-check-write
- DirectoryService: Replace force unwraps with guard let
- PaykitManager: Remove force unwraps in executor registration
Phase 2 of iOS Paykit/Pubky audit fixes:

- Add PaykitDeepLinkValidator for secure deep link validation
  - Validates scheme, host, required parameters
  - Enforces length limits and character constraints
  - Integrated into MainNavView.onOpenURL handler
- Migrate PaykitReceiptStore from UserDefaults to Keychain
  - Receipts contain payment amounts and peer pubkeys
  - Now uses PaykitKeychainStorage for encrypted storage
@BitcoinErrorLog BitcoinErrorLog changed the title fix: improve thread safety and eliminate force unwraps fix: iOS Paykit/Pubky audit - thread safety, security, and deep link validation Dec 31, 2025
JOHN added 3 commits December 31, 2025 08:54
Phase 3 of iOS Paykit/Pubky audit fixes:

- Add AutoPayEvaluatorService for background-safe auto-pay evaluation
  - Non-MainActor service for use in BGTaskScheduler handlers
  - evaluateForBackground() treats biometric as needsApproval
  - Documented biometric policy for background payments
- Add exponential backoff retry logic to SubscriptionBackgroundService
  - 3 retries with 5s initial delay, doubling each retry
  - Maximum delay capped at 60 seconds
- Update AutoPayViewModel to delegate to evaluator service
Phase 4-5 of iOS Paykit/Pubky audit fixes:

- Add PaykitNetworkConfig for shared URLSession configuration
  - Consistent timeouts (30s request, 60s resource)
  - HTTP/2 support and proper headers
  - URL caching disabled for sensitive payment data
- Update PubkyStorageAdapter to use shared session
- Update README with Thread Safety & Security section
  - Document thread-safe services and mechanisms
  - Document secure storage patterns
  - Document deep link validation usage
  - Document biometric policy for background payments
@BitcoinErrorLog BitcoinErrorLog changed the title fix: iOS Paykit/Pubky audit - thread safety, security, and deep link validation fix: iOS Paykit/Pubky audit - complete security and architecture fixes Dec 31, 2025
@BitcoinErrorLog BitcoinErrorLog merged commit 81cee37 into paykit-integration-complete Dec 31, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants