Skip to content

Sync exchange v2: Fix password deriation mismatch#9014

Merged
CDRussell merged 1 commit into
developfrom
feature/craig/sync_exchange_v2_upgrade_flow_use_same_hashed_password
Jun 26, 2026
Merged

Sync exchange v2: Fix password deriation mismatch#9014
CDRussell merged 1 commit into
developfrom
feature/craig/sync_exchange_v2_upgrade_flow_use_same_hashed_password

Conversation

@CDRussell

@CDRussell CDRussell commented Jun 25, 2026

Copy link
Copy Markdown
Member

Task/Issue URL: https://app.asana.com/1/137249556945/task/1216049977533939?focus=true
Tech Design URL (if applicable):

Description

Fixes a password derivation mismatch in the native 3party → ddg upgrade flow (joinAccountFromThirdPartyRecoveryCode).

When minting the new DDG credential on POST /access-credentials/ddg, credentialHashedPassword now uses newDdgKeys.passwordHash (libsodium/Argon2, same as signup) instead of an HKDF-derived hash from the new primary key. That keeps server-side stored hashes aligned with what performLogin sends via prepareForLogin.

Steps to test this PR

Note

  • Fresh install internal build from this branch
  • You'll need two emulators/devices to test
  • When I say Sync screen I mean Settings -> Sync & Backup

Native to native pairing

  • Both devices:: Go to Sync screen and choose Sync With Another Device
  • Scan one barcode from the other (or copy/paste code between them)
  • Both devices: On confirmation dialog that appears, choose Sync Now
  • Both devices: Verify both devices appear in the Synced Devices list (might take a few seconds)

Adding in 3rd party browser

  • On a 3rd party desktop browser (e.g., Firefox/Chrome), visit https://duck.ai/
  • Tap Settings & More (bottom left) then Settings and tap Turn On Sync & Backup -> Sync With Another Device
  • On Android device/emulator, visit Sync screen and tap Sync With Another Device
  • From Android, scan the 3p browser's barcode (copy / paste between them)
  • When prompted on Android, choose Sync Now
  • Verify you now have three devices in your device list; the two native Android ones and your 3rd party browser.

Important

Log out of sync on all devices (e.g., Sync screen -> Turn Off Sync & Backup...

3rd party code first

  • Confirm you are not signed in to sync with the native Android, nor on 3rd party browser
  • In 3rd party browser: Tap Settings & More, Settings and tap Turn On Sync & Backup -> Sync With Another Device
  • In Android: Sync screen then Sync with Another Device
  • Use Android app to scan 3rd party browser's code (or copy from 3p to Android app)
  • In Android: When you see user confirmation tap Sync Now
  • In Android: Verify you see two devices in your list; the current Android device and the 3rd party browser
  • In Android: Tap Sync with Another Device
  • In the other Android: Tap Sync with Another Device too
  • Pair these together (one scans the other's code or copy paste), and tap Sync Now on both devices when prompted
  • Verify you now have 3 devices all signed into sync

Note

High Risk
Changes authentication hashing and the post-upgrade login/commit path for joining sync accounts; a regression could block 3party upgrades or leave credentials in a bad state until TTL expiry.

Overview
Fixes a password derivation mismatch in the native 3party → ddg upgrade flow (joinAccountFromThirdPartyRecoveryCode).

When minting the new DDG credential on POST /access-credentials/ddg, credentialHashedPassword now uses newDdgKeys.passwordHash (libsodium/Argon2, same as signup) instead of an HKDF-derived hash from the new primary key. That keeps server-side stored hashes aligned with what performLogin sends via prepareForLogin.

Step 7 no longer uses the removed performDdgLoginForUpgrade plus a manual SyncStore write. It calls performLogin (with retries), so committing the credential, persisting the unrestricted ddg token, decrypting protected_encryption_key, setting credentialId, scoped password handling, and post-login sync all follow the normal login path.

Interface/docs now describe three network steps, with the third unscoped /sync/login going through performLogin. Tests mock prepareForLogin, decrypt, and return protected_encryption_key on the post-upgrade login response.

Reviewed by Cursor Bugbot for commit 994d6c4. Configure here.

CDRussell commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

@CDRussell CDRussell force-pushed the feature/craig/sync_exchange_v2_upgrade_flow_use_same_hashed_password branch 2 times, most recently from 931b74c to 994d6c4 Compare June 25, 2026 16:57
@CDRussell CDRussell marked this pull request as ready for review June 25, 2026 16:59

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 994d6c4. Configure here.

@CDRussell CDRussell force-pushed the feature/craig/sync_exchange_v2_upgrade_flow_use_same_hashed_password branch from 994d6c4 to 3264782 Compare June 25, 2026 22:21

@CrisBarreiro CrisBarreiro left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than not seeing "sync now"dialog when pairing 2 Android devices, it works as expected

@CDRussell

Copy link
Copy Markdown
Member Author

@CrisBarreiro , re #9014 (review)

since writing the testing steps the remote flag for canShowV2ConnectCode was disabled so you'd have been scanning a v1 code at that point, hence why not seeing the "Sync Now" confirmation.

I'll do a final round of testing to check it all works before merging this.

CDRussell commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

Merge activity

  • Jun 26, 4:45 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jun 26, 4:45 PM UTC: @CDRussell merged this pull request with Graphite.

@CDRussell CDRussell merged commit 7f3442d into develop Jun 26, 2026
55 checks passed
@CDRussell CDRussell deleted the feature/craig/sync_exchange_v2_upgrade_flow_use_same_hashed_password branch June 26, 2026 16:45
CDRussell added a commit that referenced this pull request Jun 26, 2026
Task/Issue URL: https://app.asana.com/1/137249556945/project/1203822806345703/task/1216036247181808?focus=true
Tech Design URL (if applicable): 

### Description
Drop the direct `POST /sync/keys/.../set-if-absent` call from the 3party credential creation flow which previously existed as its own distinct step. Removing this also means we can remove some now-redundant dev tooling for it.

### Steps to test this PR
- Exact same as #9014


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes the sync v2 crypto path for first-time `ai_chats` keys and 3party credential setup; mistakes could break scoped login or key consistency, though behavior is localized to the 3party credential flow behind the v2 feature flag.
> 
> **Overview**
> **3party credential creation** no longer calls `POST /sync/keys/.../set-if-absent` or uses `ProtectedKeyManager`. When the account has no ddg-wrapped `ai_chats` key, the client mints an RSA keypair via **`mintDdgWrappedProtectedKey`** and includes **both** ddg and 3party `ProtectedKeyEntry` rows (same `kid`) in the **`POST /access-credentials/3party`** `keys[]` payload, alongside re-wrapped existing ddg keys.
> 
> The **`setProtectedKeyIfAbsent`** Retrofit/API surface, **`SyncAccountRepository.createProtectedKey`**, **`ProtectedKeyManager`**, and related tests/dev UI (“Create Protected Key”) are removed. **`ThirdPartyCredentialManager`** adds bounded retries on **`concurrent_modification`** 409s for that credential POST and broadens 409 handling messaging before the adopt-existing-credential path.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit b0288d5. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants