Device Linking Sequence

Interaction Type: 🤝 IN-PERSON (Proximity Required)

User links a new device to their existing identity. The new device receives the master seed and syncs all data. A confirmation code and proximity verification prevent unauthorized remote linking.

Participants

  • User - Person owning both devices
  • Device A (Primary) - Existing device with identity
  • Device B (New) - New device to be linked

Sequence Diagram

    accTitle: Device Linking Sequence
    accDescr: Shows how a user links a new device to their existing identity with proximity verification
sequenceDiagram
    autonumber
    participant U as User
    participant DA as Device A (Primary)
    participant DB as Device B (New)

    Note over U: IN-PERSON: User has both devices physically present

    %% Generate Link QR
    U->>DA: Settings > Devices > Link New Device
    activate DA
    DA->>DA: Generate ephemeral link_key (32 bytes)
    DA->>DA: Sign QR fields with identity Ed25519 key
    DA->>DA: Create QR: WBDL | version | identity_pubkey | link_key | timestamp | signature
    DA->>U: Display QR code (expires in 5 minutes)
    deactivate DA

    %% New Device Scans
    U->>DB: "Link to Existing Identity"
    activate DB
    DB->>DB: Scan QR code from Device A
    DB->>DB: Validate WBDL magic, version, signature, expiry
    DB->>DB: Create DeviceLinkRequest (device_name, random nonce, timestamp)
    DB->>DB: Encrypt request with link_key (ChaCha20-Poly1305)
    DB->>DA: Send encrypted request
    deactivate DB

    Note over DA,DB: CONFIRMATION & PROXIMITY VERIFICATION

    %% Confirmation Code
    activate DA
    DA->>DA: Decrypt request using link_key
    DA->>DA: Derive confirmation code: HMAC-SHA256(link_key, nonce) → XXX-XXX
    DA->>U: Show: "Link device 'Device B'? Code: XXX-XXX"
    activate DB
    DB->>DB: Derive same confirmation code from link_key + nonce
    DB->>U: Show: "Confirmation code: XXX-XXX"
    U->>U: Verify codes match on both screens
    U->>DA: Confirm link
    DA->>DA: Set proximity verified
    deactivate DB

    Note over DA,DB: IDENTITY TRANSFER

    %% Build and send response
    DA->>DA: Derive new device keys from master_seed + device_index
    DA->>DA: Add Device B to registry, re-sign
    DA->>DA: Build response: master_seed + display_name + device_index + registry + sync_payload
    DA->>DA: Encrypt response with link_key (ChaCha20-Poly1305)
    DA->>DB: Send encrypted response
    deactivate DA

    %% New device processes
    activate DB
    DB->>DB: Decrypt response
    DB->>DB: Extract master_seed, registry, sync_payload
    DB->>DB: Derive own device keys from master_seed + device_index
    DB->>DB: Store identity locally
    DB->>DB: Apply sync payload (contacts, card)
    deactivate DB

    %% Success
    DA->>U: "Device B linked successfully"
    DB->>U: "Welcome back, [Your Name]"

    Note over DA,DB: Both devices now share the same identity

Data Exchanged

Binary format with WBDL magic bytes, base64-encoded for QR:

WBDL              (4 bytes magic)
version           (1 byte, currently 1)
identity_pubkey   (32 bytes, Ed25519 public key)
link_key          (32 bytes, random ephemeral key)
timestamp         (8 bytes, big-endian u64 unix seconds)
signature         (64 bytes, Ed25519 over all preceding fields)
─────────────────
Total: 141 bytes  (before base64 encoding)

The QR expires after 300 seconds (5 minutes). Signature is verified by the new device using the embedded identity public key.

Confirmation Code

Derived independently by both devices:

HMAC-SHA256(link_key, request_nonce)
  → first 4 bytes as big-endian u32
  → modulo 1,000,000
  → formatted as XXX-XXX

Both devices display the same code. User verifies they match.

Proximity Challenge

For external proximity verification (NFC, ultrasonic, etc.):

HKDF(ikm=link_key, info="vauchi-device-link-proximity-v1", len=16)
  → 16-byte challenge

Both devices derive the same challenge from the shared link key.

DeviceLinkRequest (New → Existing)

Encrypted with ChaCha20-Poly1305 using link_key:

device_name_len   (4 bytes, little-endian u32)
device_name       (variable, UTF-8)
nonce             (32 bytes, random)
timestamp         (8 bytes, little-endian u64)

DeviceLinkResponse (Existing → New)

Encrypted with ChaCha20-Poly1305 using link_key:

master_seed       (32 bytes, zeroized after use)
display_name_len  (4 bytes, little-endian u32)
display_name      (variable, UTF-8)
device_index      (4 bytes, little-endian u32)
registry_json_len (4 bytes, little-endian u32)
registry_json     (variable, signed DeviceRegistry)
sync_payload_len  (4 bytes, little-endian u32)
sync_payload_json (variable, contacts + card)

Security Properties

PropertyMechanism
Seed EncryptionChaCha20-Poly1305 with ephemeral link_key
QR AuthenticationEd25519 signature over QR fields
Confirmation CodeHMAC-SHA256(link_key, nonce) displayed on both devices
Proximity VerificationHKDF-derived 16-byte challenge; enforced before confirm
Replay PreventionRandom 32-byte nonce in each request
Token ExpiryQR expires after 5 minutes
Registry IntegrityEd25519 signature over version + device list
Memory SafetyMaster seed zeroized on Drop
Device LimitMaximum 10 devices per identity

Numeric Code Fallback (No Camera)

    accTitle: Device Linking Sequence (2)
    accDescr: Shows how a user links a new device to their existing identity with proximity verification
sequenceDiagram
    participant U as User
    participant DA as Device A
    participant DB as Device B (Desktop/CLI)

    Note over U: Device B has no camera

    U->>DA: Generate link code
    DA->>U: Show QR code + data string

    U->>DB: "Link to Existing Identity"
    U->>DB: Paste data string from Device A
    DB->>DB: Parse WBDL data, validate signature + expiry

    Note over DA,DB: Same confirmation code flow as above
    DA->>U: "Code: XXX-XXX"
    DB->>U: "Code: XXX-XXX"
    U->>DA: Confirm

    DA->>DB: Encrypted identity bundle
    DB->>DB: Complete linking

Revoking a Device

    accTitle: Device Linking Sequence (3)
    accDescr: Shows how a user links a new device to their existing identity with proximity verification
sequenceDiagram
    participant U as User
    participant DA as Device A
    participant DB as Device B
    participant R as Relay

    Note over U: REMOTE: Can revoke from any device

    U->>DA: Settings > Devices > Revoke Device B
    U->>DA: Confirm revocation

    activate DA
    DA->>DA: Mark Device B as revoked in registry
    DA->>DA: Re-sign registry with identity key
    DA->>DA: Increment registry version
    DA->>R: Push updated registry (encrypted)
    R-->>DB: Forward revocation
    DB->>DB: Receive revocation notice
    DB->>DB: Wipe all identity data
    DB->>DB: Return to welcome screen
    deactivate DA

    DA->>U: "Device B revoked"
    DB->>U: "This device has been unlinked"

Platform Implementation Status

PlatformStatusNotes
Core APICompleteFull protocol with tests
CLIComplete7 commands: list, link, join, complete, finish, revoke, info
Desktop (native)CompleteNative UI (SwiftUI/GTK/Qt) with QR display, confirmation overlay
TUICompleteratatui UI with QR overlay, vim-style navigation
iOSPlannedAwaiting mobile bindings
AndroidPlannedAwaiting mobile bindings