fix: iOS Paykit/Pubky audit - complete security and architecture fixes - #8

Merged
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety
Dec 31, 2025
Merged

fix: iOS Paykit/Pubky audit - complete security and architecture fixes#8
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety

Conversation

@BitcoinErrorLog

@BitcoinErrorLogBitcoinErrorLog commented Dec 31, 2025

Copy link
Copy Markdown
Owner

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

IssueFix
Deep link host validationBoth paykit:// and bitkit:// now require host=payment-request
Receipt storage migrationAdded one-time migration from UserDefaults to Keychain
Dead codeRemoved unused autoPayStorage from SubscriptionBackgroundService
Force-cast in network configReplaced with safe configuration copy method
Hardcoded User-AgentNow 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.

JOHNand 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: improve thread safety and eliminate force unwrapsfix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationDec 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationfix: iOS Paykit/Pubky audit - complete security and architecture fixesDec 31, 2025
@BitcoinErrorLog
BitcoinErrorLog merged commit 81cee37 into paykit-integration-completeDec 31, 2025
Sign up for freeto 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.

1 participant

@BitcoinErrorLog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix: iOS Paykit/Pubky audit - complete security and architecture fixes - #8

Merged
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety
Dec 31, 2025
Merged

fix: iOS Paykit/Pubky audit - complete security and architecture fixes#8
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety

Conversation

@BitcoinErrorLog

@BitcoinErrorLogBitcoinErrorLog commented Dec 31, 2025

Copy link
Copy Markdown
Owner

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

IssueFix
Deep link host validationBoth paykit:// and bitkit:// now require host=payment-request
Receipt storage migrationAdded one-time migration from UserDefaults to Keychain
Dead codeRemoved unused autoPayStorage from SubscriptionBackgroundService
Force-cast in network configReplaced with safe configuration copy method
Hardcoded User-AgentNow 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.

JOHNand 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: improve thread safety and eliminate force unwrapsfix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationDec 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationfix: iOS Paykit/Pubky audit - complete security and architecture fixesDec 31, 2025
@BitcoinErrorLog
BitcoinErrorLog merged commit 81cee37 into paykit-integration-completeDec 31, 2025
Sign up for freeto 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.

1 participant

@BitcoinErrorLog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: iOS Paykit/Pubky audit - complete security and architecture fixes - #8

Merged
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety
Dec 31, 2025
Merged

fix: iOS Paykit/Pubky audit - complete security and architecture fixes#8
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety

Conversation

@BitcoinErrorLog

@BitcoinErrorLogBitcoinErrorLog commented Dec 31, 2025

Copy link
Copy Markdown
Owner

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

IssueFix
Deep link host validationBoth paykit:// and bitkit:// now require host=payment-request
Receipt storage migrationAdded one-time migration from UserDefaults to Keychain
Dead codeRemoved unused autoPayStorage from SubscriptionBackgroundService
Force-cast in network configReplaced with safe configuration copy method
Hardcoded User-AgentNow 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.

JOHNand 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: improve thread safety and eliminate force unwrapsfix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationDec 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationfix: iOS Paykit/Pubky audit - complete security and architecture fixesDec 31, 2025
@BitcoinErrorLog
BitcoinErrorLog merged commit 81cee37 into paykit-integration-completeDec 31, 2025
Sign up for freeto 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.

1 participant

@BitcoinErrorLog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: iOS Paykit/Pubky audit - complete security and architecture fixes - #8

Merged
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety
Dec 31, 2025
Merged

fix: iOS Paykit/Pubky audit - complete security and architecture fixes#8
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety

Conversation

@BitcoinErrorLog

@BitcoinErrorLogBitcoinErrorLog commented Dec 31, 2025

Copy link
Copy Markdown
Owner

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

IssueFix
Deep link host validationBoth paykit:// and bitkit:// now require host=payment-request
Receipt storage migrationAdded one-time migration from UserDefaults to Keychain
Dead codeRemoved unused autoPayStorage from SubscriptionBackgroundService
Force-cast in network configReplaced with safe configuration copy method
Hardcoded User-AgentNow 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.

JOHNand 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: improve thread safety and eliminate force unwrapsfix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationDec 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationfix: iOS Paykit/Pubky audit - complete security and architecture fixesDec 31, 2025
@BitcoinErrorLog
BitcoinErrorLog merged commit 81cee37 into paykit-integration-completeDec 31, 2025
Sign up for freeto 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.

1 participant

@BitcoinErrorLog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix: iOS Paykit/Pubky audit - complete security and architecture fixes - #8

Merged
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety
Dec 31, 2025
Merged

fix: iOS Paykit/Pubky audit - complete security and architecture fixes#8
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety

Conversation

@BitcoinErrorLog

@BitcoinErrorLogBitcoinErrorLog commented Dec 31, 2025

Copy link
Copy Markdown
Owner

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

IssueFix
Deep link host validationBoth paykit:// and bitkit:// now require host=payment-request
Receipt storage migrationAdded one-time migration from UserDefaults to Keychain
Dead codeRemoved unused autoPayStorage from SubscriptionBackgroundService
Force-cast in network configReplaced with safe configuration copy method
Hardcoded User-AgentNow 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.

JOHNand 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: improve thread safety and eliminate force unwrapsfix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationDec 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationfix: iOS Paykit/Pubky audit - complete security and architecture fixesDec 31, 2025
@BitcoinErrorLog
BitcoinErrorLog merged commit 81cee37 into paykit-integration-completeDec 31, 2025
Sign up for freeto 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.

1 participant

@BitcoinErrorLog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: iOS Paykit/Pubky audit - complete security and architecture fixes - #8

Merged
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety
Dec 31, 2025
Merged

fix: iOS Paykit/Pubky audit - complete security and architecture fixes#8
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety

Conversation

@BitcoinErrorLog

@BitcoinErrorLogBitcoinErrorLog commented Dec 31, 2025

Copy link
Copy Markdown
Owner

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

IssueFix
Deep link host validationBoth paykit:// and bitkit:// now require host=payment-request
Receipt storage migrationAdded one-time migration from UserDefaults to Keychain
Dead codeRemoved unused autoPayStorage from SubscriptionBackgroundService
Force-cast in network configReplaced with safe configuration copy method
Hardcoded User-AgentNow 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.

JOHNand 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: improve thread safety and eliminate force unwrapsfix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationDec 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationfix: iOS Paykit/Pubky audit - complete security and architecture fixesDec 31, 2025
@BitcoinErrorLog
BitcoinErrorLog merged commit 81cee37 into paykit-integration-completeDec 31, 2025
Sign up for freeto 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.

1 participant

@BitcoinErrorLog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: iOS Paykit/Pubky audit - complete security and architecture fixes - #8

Merged
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety
Dec 31, 2025
Merged

fix: iOS Paykit/Pubky audit - complete security and architecture fixes#8
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety

Conversation

@BitcoinErrorLog

@BitcoinErrorLogBitcoinErrorLog commented Dec 31, 2025

Copy link
Copy Markdown
Owner

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

IssueFix
Deep link host validationBoth paykit:// and bitkit:// now require host=payment-request
Receipt storage migrationAdded one-time migration from UserDefaults to Keychain
Dead codeRemoved unused autoPayStorage from SubscriptionBackgroundService
Force-cast in network configReplaced with safe configuration copy method
Hardcoded User-AgentNow 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.

JOHNand 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: improve thread safety and eliminate force unwrapsfix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationDec 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationfix: iOS Paykit/Pubky audit - complete security and architecture fixesDec 31, 2025
@BitcoinErrorLog
BitcoinErrorLog merged commit 81cee37 into paykit-integration-completeDec 31, 2025
Sign up for freeto 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.

1 participant

@BitcoinErrorLog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix: iOS Paykit/Pubky audit - complete security and architecture fixes - #8

Merged
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety
Dec 31, 2025
Merged

fix: iOS Paykit/Pubky audit - complete security and architecture fixes#8
BitcoinErrorLog merged 18 commits into
paykit-integration-completefrom
fix/ios-paykit-phase1-thread-safety

Conversation

@BitcoinErrorLog

@BitcoinErrorLogBitcoinErrorLog commented Dec 31, 2025

Copy link
Copy Markdown
Owner

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

IssueFix
Deep link host validationBoth paykit:// and bitkit:// now require host=payment-request
Receipt storage migrationAdded one-time migration from UserDefaults to Keychain
Dead codeRemoved unused autoPayStorage from SubscriptionBackgroundService
Force-cast in network configReplaced with safe configuration copy method
Hardcoded User-AgentNow 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.

JOHNand 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: improve thread safety and eliminate force unwrapsfix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationDec 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
@BitcoinErrorLogBitcoinErrorLog changed the title fix: iOS Paykit/Pubky audit - thread safety, security, and deep link validationfix: iOS Paykit/Pubky audit - complete security and architecture fixesDec 31, 2025
@BitcoinErrorLog
BitcoinErrorLog merged commit 81cee37 into paykit-integration-completeDec 31, 2025
Sign up for freeto 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.

1 participant

@BitcoinErrorLog