Repository navigation
fix(contacts): pin a share link's bundle to the user it names - #3
Merged
Merged
Conversation
A contact share link carries the user_id of the person who shared it, but
the client only checked that the fetched identity bundle was consistent with
itself. The host behind the link could therefore serve any self-signed
bundle, and the client would pin that root key as the contact.
Every bundle fetched from a share or identity-bundle URL must now carry the
user_id the URL names: the userId in a /v1/contact-share token, or the
user_id segment of /v1/shared-state/{user_id}/identity-bundle. A link that
names no user is rejected before any request. The host's HMAC is still left
to the host.
Also remove an unused desktop transport method that fetched a bundle with
no URL rules and no verification.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
koabula
added a commit
that referenced
this pull request
Nov 11, 2026
…esh (#4) * fix(corpus): spread the replicate runs so a seconds clock reads as fresh The leakage corpus ran its three replicate runs first and back to back. A KeyPackage's createdAt, notBefore and expiresAt are whole seconds (mls_adapter: now_seconds * 1000), so when those three runs fell inside one second and a later run did not, the fields held still across the replicates yet differed elsewhere, and the classifier read them as carrying every secret. only_a_payloads_size_reaches_the_host then failed on main after #3, though the same code had passed on the PR. The replicates now sit at the first run, the last and one in the middle. Runs execute in order, so a value read off a coarse clock either differs between the two ends and is fresh, or is the same in every run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * style(corpus): format matrix() as rustfmt does Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: koala <koala@koaladeMacBook-Air.local> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A contact share link names the user who shared it: the link carries that user's
user_id, which is the fingerprint of their root key. When the client imported from the link, it fetched the identity bundle from the host behind the link and checked only that the bundle was consistent with itself. So the host, or anyone serving that URL, could return its own validly signed bundle, and the client would pin that root key as the contact.Change
contact_share::contact_share_url_user_idreads the user_id from both link forms:userIdin a/v1/contact-share/{token}payload;{user_id}segment of/v1/shared-state/{user_id}/identity-bundle.It rejects malformed tokens and payloads, payloads for a service other than
contact_share, and links that name no user. The token's HMAC is still checked only by the host.contact_workflows::fetch_identity_bundle_from_urlis the single path for every import from a link: the desktop and Tauri share-link commands, and accepting a message request. It now reads the user_id before sending any request, and rejects a bundle whoseuser_iddiffers from it.Removed
DesktopTransport::fetch_identity_bundle. It was unused, and it fetched a bundle with no URL rules and no verification.Network behaviour is unchanged, except that a link naming no user is now rejected before any request.
contracts/and generated files are untouched.Other ways a bundle arrives
I checked the other paths by which a bundle reaches the client; none needed a change:
identity_bundle_refis checked against the requested user in the engine. The existing testfetched_identity_bundle_must_match_requested_usercovers this.Tests
New unit tests:
contact_share: user_id extraction from both link forms, plus the malformed, wrong-service and missing-user cases.contact_workflows, three async tests against a local server with real signed bundles:These were not built locally; CI is the first build.
🤖 Generated with Claude Code