PairCrypt Logo
PairCrypt Protocol Architecture Lifecycle
Rendering Protocol Diagram...
sequenceDiagram
    autonumber
    participant Marry as Marry
    participant Store as Apple / Google Play Store
    participant Cloud as Server
    participant David as David

    Note over Marry: 1. Launch App & Anonymous Identity Generation
    Marry->>Marry: Generate PairCrypt ID (quota_id_marry_9982) locally
    Marry->>Marry: Store PairCrypt ID in Device (OS Secure Storage)

    Note over Marry, Cloud: 2. Privacy-Preserving Subscription (Two-Signal VOPRF)
    Marry->>Store: Buy Subscription / Pay via App Store / Google Play
    Store-->>Marry: Return Store Purchase Receipt

    Note over Marry: Signal 1: Receipt Verification & Voucher Unblinding
    Marry->>Marry: Generate secret blinding factor (r) & random voucher (v) in RAM
    Marry->>Marry: Compute Blinded Point P = r * HashToCurveP256(v || ":" || month_anchor)
    Marry->>Cloud: Send P + Store Receipt (NO PairCrypt ID sent)
    Cloud->>Store: Verify Receipt with Apple/Google
    Store-->>Cloud: Receipt Valid
    Cloud->>Cloud: Sign Blinded Point Q = d * P (using server private key d)
    Cloud-->>Marry: Return Signed Point Q + Authoritative server month_anchor
    Marry->>Marry: Unblind Q using r^-1 ➔ Saves VOPRF Signature S = r^-1 * Q and server-authoritative month_anchor

    Note over Marry: Signal 2: Voucher Redemption & Activation (Tap ACTIVATE)
    Marry->>Cloud: Send Unblinded Voucher (v, S, month_anchor) + PoW Nonce + PairCrypt ID (NO Receipt sent)
    Cloud->>Cloud: Verify PoW Nonce & VOPRF: d * HashToCurveP256(v || ":" || month_anchor) == S
    Cloud->>Cloud: Grant Active Status to (quota_id_marry_9982) in subscription_entitlements
    Cloud-->>Marry: Confirmation ➔ PairCrypt ID becomes Premium Active!

    Note over Marry: 3. Create Room "Secret HQ", Relay Secret & Mask ID Generation
    Marry->>Marry: Generate (masterkey1) = 8F3C19E4...
    Marry->>Marry: Derive relaySecret = HKDF-SHA256(masterkey1, "paircrypt-relay-v1")
    Marry->>Marry: Compute Mask ID_Marry = SHA256(quota_id_marry + relaySecret)
    Marry->>Marry: Compute Header x-relay-auth = SHA256(relaySecret)
    Marry->>Cloud: Call RPC publish_room(p_quota_id = quota_id_marry, Mask ID, x-relay-auth)
    Cloud->>Cloud: Lookup quota_id_marry in subscription_entitlements ➔ Active!
    Cloud-->>Marry: Room Created & Connected

    Note over Marry: 4. Send Message "Hello World!"
    Marry->>Marry: Encrypt "Hello World!" with (masterkey1) via AES-256-GCM
    Marry->>Cloud: Broadcast Ciphertext payload to Channel_x99
    Note over Cloud: The relay receives encrypted message payloads and processes limited routing metadata (room ID, sequence number) needed for delivery. It does not possess the decryption keys.

    Note over Marry, David: 5. Add Device & Join Code Exchange
    Marry->>Marry: Generate (joinCode)K9X2M4P8Q7W1 ➔ Computes (hashedCode) = PBKDF2(joinCode, 15,000)
    Marry->>Marry: Generate (marryPrivKey) & (marryPubKey) via X25519
    Marry->>Marry: Encrypt (marryPubKey) using AES-256-GCM(key = PBKDF2(joinCode))
    Marry->>Cloud: Publish (hashedCode) + Encrypted (marryPubKey)
    David->>David: Generate PairCrypt ID (quota_id_david_4412) locally
    David->>David: Install App & Type (joinCode) ➔ Computes (hashedCode)
    David->>Cloud: Fetch & Decrypt (marryPubKey) using PBKDF2(joinCode)
    David->>David: Generate (davidPrivKey) & (davidPubKey) via X25519
    David->>Cloud: Send (davidPubKey) + HMAC Signature using (joinCode)

    Note over Marry, David: 6. Key Exchange & (sharedSecret) Calculation
    Cloud->>Marry: Relay (davidPubKey)
    Cloud->>David: Relay (marryPubKey)
    Marry->>Marry: MATH: X25519(marryPrivKey, davidPubKey) ➔ CREATES (sharedSecret)
    David->>David: MATH: X25519(davidPrivKey, marryPubKey) ➔ CREATES (sharedSecret)
    Note over Marry, David: Both devices compute identical 256-bit (sharedSecret) bound to this pairing session!

    Note over Marry, David: 7. SAS Verification & Master Key Transfer
    Marry->>Marry: Compute 6-digit (sasCode)481920 via PBKDF2(sharedSecret, 50,000)
    David->>David: Compute 6-digit (sasCode)481920 via PBKDF2(sharedSecret, 50,000)
    Note over Marry, David: Both screens display 6-digit "(sasCode)481920" ➔ Tap "CONFIRM MATCH"
    Marry->>Marry: Package (masterkey1) encrypted using AES-256-GCM(key = sharedSecret)
    Marry->>Cloud: Send Encrypted Key Bundle
    Cloud->>David: Relay Encrypted Key Bundle
    David->>David: Decrypt (masterkey1) using (sharedSecret) & store in Device
    Marry->>Marry: DELETE (sharedSecret) from RAM
    David->>David: DELETE (sharedSecret) from RAM

    Note over Marry, David: 8. History Sync (Transfer Past Messages)
    David->>Cloud: Send history_request (maxSeq=0, isInvitee=true)
    Cloud->>Marry: Relay history_request
    Marry->>Marry: Check myMaxSeq > 0 ➔ Becomes DONOR
    Marry->>Cloud: Send history_offer
    Cloud->>David: Relay history_offer
    David->>Cloud: Send history_accept
    Cloud->>Marry: Relay history_accept
    Marry->>Cloud: Send Encrypted History Chunks from local vault
    Cloud->>David: Relay History Chunks
    David->>David: Decrypt past messages using (masterkey1) & render on screen

    Note over Marry, David: 9. Kick Device & Access Revocation
    Marry->>Cloud: Broadcast Kick Signal + RPC reset_room_after_kick
    Cloud->>Cloud: Server invalidates old x-relay-auth & resets room authorization
    Cloud->>David: Relay Kick Signal
    David->>David: Client wipes room & deletes (masterkey1) from local vault
    Marry->>Marry: Retires (masterkey1) [is_active=0] locally (retained to read past history)
    Marry->>Marry: Generates NEW active (masterkey2) for all future messages
    Note over Marry, David: David's room access is revoked on the relay. The server rejects his previous authorization, and future messages are protected by masterkey2. Previously received messages on David's device cannot be remotely erased if his client is modified.