Skip to content
C.W.K.
Stream
Lesson 02 of 06 · published

The Designated Requirement, Read and Tested

~15 min · bundle-signing, designated-requirement, codesign, identity

Level 0Bundle Opener
0 XP0/81 lessons0/17 achievements
0/100 XP to next level100 XP to go0% complete
"The same signer name does not mean the same certificate."

What the Requirement Is

Every signature carries a code requirement language. The designated requirement (DR) is the one that answers "is this future binary the same app?" If you do not write one, codesign derives it from how the code was signed: for a certificate-signed app, the bundle identifier plus a statement about the certificate chain; for ad-hoc code, the hash of the code itself. TCC, the Keychain and several system services store the DR — not the app's name, path or version — and compare new code against it.

You read it with codesign -dr - Spark.app. A leading # in the output (# designated => cdhash H"…") means the requirement was implied rather than written into the signature.

Test a Requirement Instead of Eyeballing It

codesign --verify -R='<requirement>' Spark.app checks a binary against any requirement you write and exits non-zero with test-requirement: code failed to satisfy specified code requirement(s) when it does not match. That turns "does the new build still count as the same app to TCC?" into a command a script can gate on.

Compare Before You Replace

The family's queue launchers are small signed helpers installed on every Mac. Their installer refuses to replace an installed launcher whose designated requirement differs from the newly built one, and names both. The reason is in the quote above: a certificate regenerated under the same common name produces a different leaf hash, a different requirement, and silently orphans every Accessibility grant on that Mac, while every signature "looks" like it came from the same identity. Comparing the requirement text is cheap and catches it before the swap rather than after a user notices the prompts.

A Stable Requirement Is Necessary, Not Sufficient

One more distinction the family learned the careful way: a correctly certificate-signed launcher with a stable requirement still failed Accessibility on a Mac where an earlier ad-hoc build had been installed and its permission row deleted during recovery. A stable DR means a future grant will survive rebuilds; it does not prove a grant exists right now. The only honest check of the grant is asking from the running app — the next lessons show how.

Code

Read, test and compare designated requirements before swapping an installed app·bash
INSTALLED=/Applications/Spark.app
STAGED=/Applications/.Spark.app.install.$$

codesign -dr - "$INSTALLED"
# designated => identifier "com.example.spark" and certificate leaf = H"3b81..."

# Gate on a requirement you write yourself:
codesign --verify -R='identifier "com.example.spark"' "$STAGED" || echo 'not the same identifier'

# Refuse a swap that would orphan permission grants:
old=$(codesign -dr - "$INSTALLED" 2>&1 | sed -n 's/^#* *designated => //p')
new=$(codesign -dr - "$STAGED" 2>&1 | sed -n 's/^#* *designated => //p')
if [[ -n "$old" && "$old" != "$new" ]]; then
  echo "HARD_FAIL designated requirement changed"
  echo "  installed: $old"
  echo "  new:       $new"
  echo "  every Mac would need to re-grant Accessibility and Keychain access"
  exit 1
fi

External links

Exercise

Take the Spark bundle from the previous lesson. Record its designated requirement, then write three codesign --verify -R checks: one that passes (the identifier), one that fails (a different identifier), and one that would only pass for a certificate-signed build (certificate leaf present). Add the requirement-comparison block to your build script before the swap, sign a build ad-hoc, sign the next one with a different ad-hoc build, and watch the comparison refuse the swap.
Hint
Two ad-hoc builds of different source produce different cdhash requirements, which is a perfect way to exercise the refusal without owning two certificates. Remember to print both requirements — a refusal that does not say what changed sends the reader hunting.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.