Back to list
co-labs-co

trust-certificate-authentication

by co-labs-co

Central skills repository for ContextHarness - curated agent skills and workflows

0🍴 0📅 Jan 12, 2026

SKILL.md


name: trust-certificate-authentication version: 0.1.0 author: cmtzco description: Secure device-to-device trust relationships using cryptographic certificates on iOS. Use this skill when establishing trusted P2P connections, implementing device authentication without servers, building QR code pairing flows, or creating persistent trust relationships stored in Keychain.

Skill: Trust Certificate Authentication (iOS/Swift)

Implements secure device-to-device trust relationships using cryptographic certificates on iOS. Covers secure token generation, Keychain storage with appropriate accessibility levels, QR code exchange for trust establishment, bidirectional trust models, and timing-attack-resistant certificate validation.

When to Use

  • Establishing trusted peer-to-peer connections between iOS devices
  • Implementing device authentication without a central server
  • Building secure pairing flows using QR codes
  • Creating persistent trust relationships that survive app restarts
  • Protecting authentication flows from timing attacks

Key Concepts

Keychain Accessibility Levels

LevelWhen AccessibleiCloud SyncUse Case
.whenUnlockedDevice unlockedYesGeneral secrets (synced)
.afterFirstUnlockAfter first unlockYesBackground app tokens
.whenUnlockedThisDeviceOnlyDevice unlockedNoDevice-specific credentials
.afterFirstUnlockThisDeviceOnlyAfter first unlockNoBackground + device-only

Recommendation: Use .whenUnlockedThisDeviceOnly for trust certificates.

Bidirectional Trust Model

Device A                          Device B
┌──────────────┐                  ┌──────────────┐
│ TrustedDevices: │              │ TrustedDevices: │
│  - Device B  │  ←── mutual ──→ │  - Device A  │
│    (cert_B)  │      trust      │    (cert_A)  │
└──────────────┘                  └──────────────┘

Either device can initiate connection.
Both store each other's certificates.

Constant-Time Comparison

Prevents timing attacks by ensuring comparison time is independent of match position:

// ❌ DANGEROUS: Early exit leaks timing information
func vulnerableCompare(_ a: String, _ b: String) -> Bool {
    for (charA, charB) in zip(a, b) {
        if charA != charB { return false }  // Early exit!
    }
    return true
}

// ✅ SECURE: Constant-time comparison
func constantTimeCompare(_ a: String, _ b: String) -> Bool {
    let aData = Data(a.utf8)
    let bData = Data(b.utf8)
    
    guard aData.count == bData.count else { return false }
    
    var result: UInt8 = 0
    for (aByte, bByte) in zip(aData, bData) {
        result |= aByte ^ bByte  // XOR accumulates differences
    }
    return result == 0  // Only true if ALL bytes matched
}

Implementation Guide

Step 1: Set Up KeychainAccess

import KeychainAccess

final class TrustCertificateManager {
    private let keychain: Keychain
    
    init(service: String = Bundle.main.bundleIdentifier ?? "com.app") {
        self.keychain = Keychain(service: service)
            .accessibility(.whenUnlockedThisDeviceOnly)
            .label("App Trust Certificates")
    }
}

Step 2: Generate Secure Tokens

import Security

func generateSecureToken() -> String? {
    var bytes = [UInt8](repeating: 0, count: 32)
    let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
    
    guard status == errSecSuccess else { return nil }
    
    return Data(bytes).base64EncodedString()
}

Step 3: Define Trust Models

struct TrustedDevice: Codable, Identifiable {
    let deviceId: String
    let deviceName: String
    let certificate: String      // Shared secret (base64)
    let trustedAt: Date
    var lastConnected: Date?
    
    var id: String { deviceId }
}

struct TrustInvitation: Codable {
    let version: Int
    let deviceId: String
    let deviceName: String
    let certificate: String
    
    static let currentVersion = 2
}

Step 4: Generate Trust Invitations

func generateTrustInvitation() -> TrustInvitation? {
    guard let certificate = generateSecureToken() else { return nil }
    
    return TrustInvitation(
        version: TrustInvitation.currentVersion,
        deviceId: deviceId,
        deviceName: deviceName,
        certificate: certificate
    )
}

static func encodeInvitation(_ invitation: TrustInvitation) -> String? {
    guard let jsonData = try? JSONEncoder().encode(invitation),
          let base64 = jsonData.base64EncodedString()
              .addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
        return nil
    }
    return "myapp://trust/" + base64
}

Step 5: Validate Credentials (Constant-Time)

func validateDeviceCredential(_ credential: DeviceCredential) -> Bool {
    guard let trusted = trustedDevices.first(where: { 
        $0.deviceId == credential.deviceId 
    }) else {
        return false
    }
    
    // CRITICAL: Use constant-time comparison
    return constantTimeCompare(credential.certificate, trusted.certificate)
}

private func constantTimeCompare(_ a: String, _ b: String) -> Bool {
    let aData = Data(a.utf8)
    let bData = Data(b.utf8)
    
    guard aData.count == bData.count else { return false }
    
    var result: UInt8 = 0
    for (aByte, bByte) in zip(aData, bData) {
        result |= aByte ^ bByte
    }
    return result == 0
}

Step 6: QR Code Generation (Optimized)

import CoreImage
import Metal

/// Reusable Metal-backed CIContext (expensive to create)
private lazy var ciContext: CIContext = {
    if let device = MTLCreateSystemDefaultDevice() {
        return CIContext(mtlDevice: device, options: [.cacheIntermediates: true])
    }
    return CIContext(options: [.useSoftwareRenderer: false])
}()

@MainActor
func generateQRCodeAsync(for invitation: TrustInvitation) async -> UIImage? {
    guard let payload = Self.encodeInvitation(invitation) else { return nil }
    
    return await Task.detached(priority: .userInitiated) {
        let filter = CIFilter.qrCodeGenerator()
        filter.setValue(payload.data(using: .ascii), forKey: "inputMessage")
        
        guard let outputImage = filter.outputImage else { return nil }
        
        let scale: CGFloat = 8
        let scaled = outputImage.transformed(by: CGAffineTransform(scaleX: scale, y: scale))
        
        guard let cgImage = self.ciContext.createCGImage(scaled, from: scaled.extent) else {
            return nil
        }
        
        return UIImage(cgImage: cgImage)
    }.value
}

Common Pitfalls

1. Using Standard String Comparison for Secrets

// ❌ WRONG: Vulnerable to timing attacks
if credential.certificate == trusted.certificate { ... }

// ✅ CORRECT: Constant-time comparison
constantTimeCompare(credential.certificate, trusted.certificate)

2. Weak Keychain Accessibility

// ❌ WRONG: Data syncs to iCloud
.accessibility(.afterFirstUnlock)

// ✅ CORRECT: Device-specific, no sync
.accessibility(.whenUnlockedThisDeviceOnly)

3. Creating CIContext Per QR Code

// ❌ WRONG: CIContext creation is expensive (~100-300ms)
func generateQR() {
    let context = CIContext()  // Created each time!
}

// ✅ CORRECT: Lazy-initialized, reused
private lazy var ciContext: CIContext = { ... }()

4. Storing Plain Secrets in UserDefaults

// ❌ WRONG: UserDefaults is not encrypted
UserDefaults.standard.set(certificate, forKey: "cert")

// ✅ CORRECT: Always use Keychain
keychain.set(certificate, key: "cert")

References


Derived from CarSeet project - TrustCertificateManager.swift

Score

Total Score

60/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon