Skip to content

Sign a TLK share's extra record fields when verifying it - #4

Open
gregakespret wants to merge 1 commit into
findmy-export-supportfrom
fix/tlkshare-signature-extra-fields
Open

Sign a TLK share's extra record fields when verifying it#4
gregakespret wants to merge 1 commit into
findmy-export-supportfrom
fix/tlkshare-signature-extra-fields

Conversation

@gregakespret

Copy link
Copy Markdown
Owner

fetch_shares_for now verifies a TLK share over the same bytes Apple signs. -[CKKSTLKShare dataForSigning:] (apple-oss-distributions/Security, keychain/ckks/CKKSTLKShare.m) signs the seven positional fields, then every record field it doesn't know, except server_*, sorted by key. data_for_signing takes the share's record fields and appends those extras the way Apple serializes them:

  • strings as UTF-8, bytes raw
  • dates as ISO 8601 whole seconds in UTC, counted from CloudKit's 2001 epoch
  • numbers as eight little-endian bytes of unsignedLongLongValue
  • references, lists, assets and locations skipped

Without the extras, a share carrying any extra field fails its check, and the ? aborts the keychain join on the first one. A beta account hits this on every attempt, in a different zone each time (Entering on key Health, then Backstop), after its bottle has already decrypted.

A failed check still aborts the join, but it first logs a warn! listing the record's field names (never values), so the next mismatch shows which field caused it. That also confirms the diagnosis for this account: its field names haven't been seen yet.

Tests: six unit tests in keychain.rs, with expected payloads built by hand from Apple's source rather than from this code:

  • a P-384 share signed over an extra field verifies
  • a record without extras signs only the positional fields
  • key order is followed and server_* is left out
  • dates use the 2001 epoch
  • negative integers and doubles serialize like NSNumber
  • the unsigned kinds are skipped

Each test was checked against a targeted mutation of the code. Run them with cargo test --release --features 'macos-validation-data,remote-anisette-v3' --package rustpush --lib (7 pass). CI runs tests only once #3 lands.

Related: OpenBubbles#34 fixes the same payload but also skips shares that fail; this PR keeps the abort. Out of scope: the CKKSItem AAD code (~line 147) also reads a CloudKit date as Unix time.

🤖 Generated with Claude Code

https://claude.ai/code/session_01S8kaPMUXu4LSU1K91q6FDN

-[CKKSTLKShare dataForSigning:] signs the seven positional fields and then
every record field it doesn't know (bar server_*), sorted by key. We signed
only the positional part, so a share carrying any extra field failed its
check, and the ? in fetch_shares_for aborted the keychain join on the first
one. A beta account fails this way on every attempt, in a different zone
each time.

Extras are serialized as Apple does: strings as UTF-8, bytes raw, dates as
ISO 8601 whole seconds UTC counted from CloudKit's 2001 epoch, numbers as
eight little-endian bytes of unsignedLongLongValue. References, lists,
assets and locations are skipped.

A failed check still aborts the join; it now first logs the record's field
names (never values) so the next mismatch can be pinned from the logs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8kaPMUXu4LSU1K91q6FDN
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.

1 participant