Repository navigation
Expand file tree
/
Copy pathfreebsd-keychain-port-plan.html
More file actions
385 lines (346 loc) · 36.1 KB
/
Copy pathfreebsd-keychain-port-plan.html
File metadata and controls
385 lines (346 loc) · 36.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>FreeBSD Keychain — porting / alternatives plan</title>
<style>
:root {
--bg: #fbfbf8;
--fg: #1a1a1a;
--muted: #555;
--accent: #b03000;
--accent2: #0a4d68;
--ok: #1f7a1f;
--warn: #b06800;
--bad: #b00020;
--code-bg: #f0ece4;
--rule: #d6cfc0;
--card: #fff;
}
html { -webkit-text-size-adjust: 100%; }
body { margin: 0 auto; max-width: 1080px; padding: 2.5rem 1.5rem 6rem;
font: 16px/1.55 -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
color: var(--fg); background: var(--bg); }
h1 { font-size: 2rem; line-height: 1.2; margin: 0 0 .25rem; }
h2 { font-size: 1.4rem; margin: 2.5rem 0 .75rem; padding-bottom: .25rem; border-bottom: 2px solid var(--rule); }
h3 { font-size: 1.15rem; margin: 1.75rem 0 .5rem; color: var(--accent2); }
h4 { margin: 1.25rem 0 .35rem; }
.subtitle { color: var(--muted); font-size: 1.05rem; margin: 0 0 2rem; }
code, pre, kbd { font-family: "SF Mono", Menlo, Consolas, monospace; }
code { background: var(--code-bg); padding: 1px 5px; border-radius: 3px; font-size: .9em; }
pre { background: var(--code-bg); padding: .85rem 1rem; border-radius: 6px;
overflow-x: auto; font-size: .82rem; line-height: 1.45;
border-left: 3px solid var(--accent2); }
pre code { background: none; padding: 0; }
pre.shell { border-left-color: var(--ok); }
pre.objc { border-left-color: var(--accent); }
a { color: var(--accent2); }
a:hover { color: var(--accent); }
.tldr { background: var(--card); border: 1px solid var(--rule); border-left: 4px solid var(--accent2);
padding: 1rem 1.25rem; border-radius: 6px; margin-bottom: 2rem; }
.tldr h3 { margin-top: 0; color: var(--accent2); }
.pill { display: inline-block; font-size: .72rem; padding: 1px 8px; border-radius: 999px;
background: #eee; color: #333; margin-left: .35rem; vertical-align: middle;
font-weight: 600; letter-spacing: .02em; }
.pill.ok { background: #d8efd8; color: var(--ok); }
.pill.warn { background: #f6e4cb; color: var(--warn); }
.pill.bad { background: #f5d0d6; color: var(--bad); }
.pill.info { background: #d6e6f3; color: var(--accent2); }
table { border-collapse: collapse; width: 100%; margin: 1rem 0; font-size: .92rem; }
th, td { text-align: left; padding: .5rem .65rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
th { background: #eee5d6; }
tr:nth-child(even) td { background: #faf6ed; }
table.compact td, table.compact th { padding: .35rem .55rem; font-size: .88rem; }
.nav { position: sticky; top: 0; background: var(--bg); margin: -2.5rem -1.5rem 2rem;
padding: .75rem 1.5rem; border-bottom: 1px solid var(--rule);
font-size: .88rem; z-index: 10; }
.nav a { margin-right: .9rem; text-decoration: none; }
.gap { background: #fff5f5; border-left: 4px solid var(--bad); padding: .8rem 1rem; margin: 1rem 0; border-radius: 0 6px 6px 0; }
.gap strong { color: var(--bad); }
.resolved { background: #ecf7ec; border-left: 4px solid var(--ok); padding: .8rem 1rem; margin: 1rem 0; border-radius: 0 6px 6px 0; }
.resolved strong { color: var(--ok); }
.open-q { background: #fff8d6; border: 1px solid #e5d76b; padding: .8rem 1rem; margin: 1rem 0; border-radius: 6px; font-size: .92rem; }
.open-q strong { color: #7a5e00; }
.ascii-diagram { font-family: "SF Mono", Menlo, Consolas, monospace; font-size: .82rem; line-height: 1.3; white-space: pre; background: var(--code-bg); padding: 1rem; border-radius: 6px; overflow-x: auto; }
.yesno { font-weight: 600; }
.yesno.yes { color: var(--ok); }
.yesno.no { color: var(--bad); }
.yesno.partial { color: var(--warn); }
.rec { background: #eef4f8; border: 1px solid #bcd4e2; border-left: 4px solid var(--accent2); padding: 1rem 1.25rem; margin: 1.25rem 0; border-radius: 6px; }
.rec strong { color: var(--accent2); }
</style>
</head>
<body>
<nav class="nav">
<a href="#tldr">TL;DR</a>
<a href="#what">What keychain is</a>
<a href="#oss">Open source status</a>
<a href="#need">What we need</a>
<a href="#alts">Alternatives</a>
<a href="#rec">Recommended</a>
<a href="#crypto">Crypto & storage</a>
<a href="#api">API surface</a>
<a href="#open">Open questions</a>
<a href="#refs">References</a>
</nav>
<h1>FreeBSD Keychain — porting / alternatives plan <span class="pill info">Scoping</span></h1>
<p class="subtitle">What macOS Keychain actually is, what's open vs closed, and the full set of options for giving NextBSD/gershwin a local secret store with Apple's <code>SecItem</code> API — from porting Apple's <code>Security.framework</code> wholesale, to wrapping <code>gnome-keyring</code>, to a clean-room <code>secd</code>-equivalent. Companion to the <a href="nextbsd-wlan-research.html">WLAN management</a>, PAM port, and <a href="nextbsd-configd-plan.html">configd</a> plans.</p>
<section id="tldr" class="tldr">
<h3>TL;DR</h3>
<ul>
<li><strong>Keychain isn't one thing.</strong> On macOS it's a library (<code>Security.framework</code>) talking over Mach IPC to a <strong>launchd-managed daemon</strong> (<code>securityd</code> / <code>secd</code>) that owns the encrypted keychain files, with the hardware root of trust in the <strong>Secure Enclave (SEP)</strong> on modern Macs.</li>
<li><strong>The source is published but not buildable.</strong> <a href="https://github.com/apple-oss-distributions/Security">apple-oss-distributions/Security</a> + <a href="https://github.com/apple-oss-distributions/securityd">securityd</a> are real source, but the tree doesn't build standalone (missing internal headers), depends on <code>corecrypto</code> (restrictive license) and <strong>SEP firmware that is closed and hardware-specific</strong>, and carries decades of CDSA/iCloud baggage we don't want. It's a <em>reference</em>, not a vendorable codebase.</li>
<li><strong>We don't need most of it.</strong> No iCloud Keychain sync, no SEP/biometric gating, no code-signing trust evaluation (separate concern). What we need is a <em>local</em> secret store exposing the public <code>SecItem</code> (and legacy <code>SecKeychain</code>) C API, so Apple-derived apps and our own daemons (<code>wland</code>, configd, a future mail/browser) have somewhere safe to put passwords and keys.</li>
<li><strong>Recommendation: clean-room <code>secd</code>-equivalent.</strong> A launchd-managed Mach/<a href="freebsd-libxpc-plan.html">DO</a> daemon implementing the <code>SecItem</code> facade over an encrypted file store, using base crypto (<code>libcrypto</code> / libsodium), optionally TPM2-backed. Per the shipped system's convention it keeps Apple's own name — launchd job label <em>and</em> Mach service both <strong><code>com.apple.securityd</code></strong> (modern alt <code>com.apple.secd</code>), exactly like the running <code>com.apple.configd</code> / <code>com.apple.notifyd</code> — see <a href="#naming">§5.1</a>. Reuses the project's existing launchd + CoreFoundation + Mach substrate. Estimated <strong>~2–3 person-months for a usable per-user subset</strong>; more for full ACL/access-group fidelity.</li>
<li><strong>Alternatives considered & why they lose:</strong> full <code>Security.framework</code> port (closed crypto + SEP + enormous); wrapping <code>gnome-keyring</code>/<code>libsecret</code> (drags in D-Bus, LGPL, and a non-Apple API shape); KWallet (Qt/KDE weight); <code>pass</code>/gpg-agent (no daemon API, CLI-shaped); plaintext <code>Network.plist</code> (status quo, no protection).</li>
<li><strong>Not a commitment.</strong> Sequenced behind a real consumer. Until one lands, secrets stay inline in plaintext <code>Network.plist</code> (the WLAN plan's <a href="nextbsd-wlan-plan.html#openq">Q2</a>). This plan exists so the option space is settled and we don't relitigate.</li>
</ul>
</section>
<h2 id="what">1. What the macOS keychain actually is</h2>
<p>Four layers, only the top two of which are app-visible:</p>
<table>
<thead>
<tr><th>Layer</th><th>Role</th><th>launchd?</th></tr>
</thead>
<tbody>
<tr>
<td><code>Security.framework</code> (library)</td>
<td>The in-process API apps link against: modern <code>SecItem*</code> (<code>SecItemAdd</code>/<code>CopyMatching</code>/<code>Update</code>/<code>Delete</code>) and legacy <code>SecKeychain*</code>. Does <em>no</em> storage itself — marshals every request over Mach IPC to the daemon.</td>
<td>No — loaded into each client process</td>
</tr>
<tr>
<td><code>securityd</code> (legacy) / <code>secd</code> (modern data-protection keychain) / <code>trustd</code></td>
<td>The daemon that owns the keychain files, holds unlocked keys in memory, enforces access control, and (on modern Macs) talks to the SEP. <code>trustd</code> handles certificate trust evaluation (a related but separable job).</td>
<td><strong>Yes</strong> — launchd jobs advertising Mach service names; started on demand when a client does a bootstrap lookup</td>
</tr>
<tr>
<td>Keychain files</td>
<td>On-disk encrypted databases. Legacy: <code>~/Library/Keychains/login.keychain-db</code> (per-user, unlocked by login password) and <code>/Library/Keychains/System.keychain</code> (system-wide, e.g. WLAN). Modern: SQLite-backed data-protection keychain.</td>
<td>n/a</td>
</tr>
<tr>
<td>Secure Enclave (SEP)</td>
<td>Hardware coprocessor (T2 / Apple Silicon) that wraps the keychain's master keys and gates them on biometrics/passcode. The actual root of trust on modern hardware.</td>
<td>n/a — closed firmware</td>
</tr>
</tbody>
</table>
<p>Critically for us, the daemon half is a <strong>textbook launchd-on-demand Mach service</strong>, the same pattern as configd / notifyd: a <code>.plist</code> declares a Mach service name, the library does a <code>bootstrap_look_up</code>, launchd starts the daemon on first use. That maps directly onto the infrastructure the <a href="freebsd-launchd-plan.html">launchd</a> and <a href="freebsd-libxpc-plan.html">libxpc/DO</a> work already provides.</p>
<h3>1.1 The architecture</h3>
<div class="ascii-diagram"> app / wland / configd
|
v SecItemCopyMatching(...)
+--------------------+
| Security.framework | (library, in-process; no storage)
+--------------------+
|
v Mach IPC (bootstrap_look_up "com.apple.securityd")
+--------------------+ +------------------+
| securityd / secd |<----->| trustd (certs) |
| (launchd Mach job) | +------------------+
+--------------------+
| |
v v
+--------------------+ +-------------------+
| keychain files | | Secure Enclave |
| (~/Library/Key...) | | (key wrap, closed)|
+--------------------+ +-------------------+</div>
<h3>1.2 What a "keychain item" is</h3>
<p>Each item has a <strong>class</strong> (<code>kSecClassGenericPassword</code>, <code>kSecClassInternetPassword</code>, <code>kSecClassCertificate</code>, <code>kSecClassKey</code>, <code>kSecClassIdentity</code>), a set of <strong>attributes</strong> (account, service, server, protocol, label, …) used as search keys, an encrypted <strong>secret payload</strong>, and an <strong>access-control</strong> policy (which apps may read it, whether unlock/biometric is required). WLAN passwords, for example, are <code>kSecClassGenericPassword</code> / <code>kSecClassInternetPassword</code> items in the System keychain. Replicating <em>this</em> data model is the real work — the crypto is the easy part.</p>
<h2 id="oss">2. Open-source status — published, not buildable</h2>
<table>
<thead>
<tr><th>Component</th><th>Status</th><th>Notes</th></tr>
</thead>
<tbody>
<tr>
<td><a href="https://github.com/apple-oss-distributions/Security">Security</a></td>
<td><span class="yesno partial">Source published</span></td>
<td>The framework: <code>SecItem</code> / <code>SecKeychain</code> APIs, on-disk format logic, ACL logic. <strong>Does not build standalone</strong> — Apple's own README notes missing internal headers since ~2016.</td>
</tr>
<tr>
<td><a href="https://github.com/apple-oss-distributions/securityd">securityd</a></td>
<td><span class="yesno partial">Source published</span></td>
<td>The daemon + <code>SecurityTool</code>. Mach/MIG IPC server. Same build caveat.</td>
</tr>
<tr>
<td><code>corecrypto</code></td>
<td><span class="yesno no">Effectively closed</span></td>
<td>Apple's FIPS-validated crypto core. Source is published but under a <strong>restrictive license</strong> that bars free reuse — not vendorable. Security.framework's crypto bottoms out here.</td>
</tr>
<tr>
<td>Secure Enclave firmware</td>
<td><span class="yesno no">Closed</span></td>
<td>Hardware root of trust. Unportable by definition; FreeBSD has no SEP.</td>
</tr>
<tr>
<td>iCloud Keychain (sync/escrow server)</td>
<td><span class="yesno no">Closed</span></td>
<td>The CloudKit sync, SOS circle, and escrow mechanisms. We don't want this anyway.</td>
</tr>
<tr>
<td><code>CommonCrypto</code>, <code>libDER</code>, CoreFoundation</td>
<td><span class="yesno yes">Open / available</span></td>
<td><code>CommonCrypto</code> is a thin API (can sit on <code>libcrypto</code>); <code>libDER</code> is open; CoreFoundation we <a href="freebsd-libcorefoundation-icu-audit.html">already have</a>.</td>
</tr>
</tbody>
</table>
<div class="gap"><strong>Binding constraint:</strong> the parts that make Apple's keychain <em>secure</em> — <code>corecrypto</code> and the SEP — are exactly the parts you cannot take. So "port Apple's keychain" really means "port Apple's keychain <em>data model and API</em>, and supply your own crypto + (optional) hardware backing." Once that's accepted, a from-scratch daemon stops looking so different from a port.</p></div>
<h2 id="need">3. What we actually need (and what we can drop)</h2>
<table class="compact">
<thead><tr><th>Feature</th><th>Need it?</th><th>Why</th></tr></thead>
<tbody>
<tr><td>Local secret storage + <code>SecItem</code> CRUD</td><td><span class="yesno yes">Yes</span></td><td>The whole point — give apps/daemons a place to keep secrets behind a known API.</td></tr>
<tr><td>Per-user keychain unlocked by login password</td><td><span class="yesno yes">Yes</span></td><td>Matches user expectation; integrates with the PAM port for unlock-at-login.</td></tr>
<tr><td>System keychain (for WLAN etc.)</td><td><span class="yesno yes">Yes</span></td><td><code>wland</code> needs a root-owned store for network creds available before login.</td></tr>
<tr><td>Per-app / access-group access control</td><td><span class="yesno partial">Partial</span></td><td>A simplified uid/path-based policy is enough initially; full code-signing-identity ACLs are a later fidelity bump.</td></tr>
<tr><td>iCloud Keychain sync</td><td><span class="yesno no">No</span></td><td>No Apple-ID infra; out of scope permanently.</td></tr>
<tr><td>Secure Enclave / biometric gating</td><td><span class="yesno no">No</span></td><td>No SEP hardware. TPM2 is the only analog and it's optional (see §6).</td></tr>
<tr><td>Certificate trust evaluation (<code>trustd</code>)</td><td><span class="yesno no">No (separate)</span></td><td>Different problem; the base system / OpenSSL already do TLS trust. Don't conflate.</td></tr>
<tr><td>CDSA / legacy <code>SecKeychain</code> full fidelity</td><td><span class="yesno partial">Shim only</span></td><td>Provide thin legacy shims mapping onto the modern store; don't reimplement CDSA.</td></tr>
</tbody>
</table>
<h2 id="alts">4. Alternatives</h2>
<p>Seven options, from most-Apple to least-effort. Verdicts summarized in §4.8.</p>
<h3>4.A — Port Apple's <code>Security.framework</code> + <code>securityd</code> wholesale</h3>
<p>Vendor the published source, reconstruct the missing build glue, swap <code>corecrypto</code> for <code>libcrypto</code>, stub the SEP.</p>
<ul>
<li><strong>Pros:</strong> Real API/ABI fidelity — every <code>SecItem</code>/<code>SecKeychain</code> caller works unchanged; closest to "it's literally macOS."</li>
<li><strong>Cons:</strong> The tree doesn't build; <code>corecrypto</code> is unusable and threaded throughout; SEP paths must be torn out; decades of CDSA/iCloud/code-signing code comes along for the ride; deep CoreFoundation + Security-Transforms + MIG coupling. Realistically a <strong>6–12+ person-month</strong> slog with permanent maintenance of a non-buildable upstream. You'd end up rewriting the crypto and storage layers anyway — i.e. most of option B — while carrying all of Apple's legacy.</li>
</ul>
<p><span class="pill bad">Rejected</span> Highest fidelity, worst effort/maintenance. The closed crypto + SEP mean you can't actually ship Apple's security guarantees regardless.</p>
<h3>4.B — Clean-room native <code>secd</code>-equivalent <span class="pill ok">recommended</span></h3>
<p>Write a new launchd-managed Mach/DO daemon (<code>com.apple.securityd</code>) exposing the public <code>SecItem</code> API, backed by our own encrypted store and base crypto. Implement the published API <em>contract</em>, not Apple's implementation.</p>
<ul>
<li><strong>Pros:</strong> Scope we control; BSD-licensable; reuses the project's launchd + CoreFoundation + Mach substrate; gives apps the real Apple API surface; no closed deps. Honest about its threat model (software-only unless TPM2 added).</li>
<li><strong>Cons:</strong> We own the security design (storage format, key hierarchy, ACL semantics); must track the <code>SecItem</code> attribute vocabulary carefully for compat; no hardware backing by default.</li>
</ul>
<p><span class="pill ok">Chosen</span> Best balance. Detail in §5.</p>
<h3>4.C — Wrap <code>gnome-keyring</code> / <code>libsecret</code> (freedesktop Secret Service)</h3>
<p>Run <code>gnome-keyring-daemon</code> as the storage backend; put a <code>SecItem</code>→Secret-Service translation shim in our <code>Security</code>-shim library.</p>
<ul>
<li><strong>Pros:</strong> A mature, audited secret store for nearly free; the Secret Service item/collection model is conceptually close to keychain items.</li>
<li><strong>Cons:</strong> Drags in <strong>D-Bus</strong> — a foreign IPC the project deliberately avoided in favor of Mach/DO — plus glib. <strong>LGPL-2.1</strong> (linkable, but not the base-system fit BSD code is). Impedance mismatch translating ACLs/access-groups onto Secret Service. You inherit someone else's daemon lifecycle, not launchd-native. Ends up being "option B's shim layer anyway, but on top of a D-Bus stack we don't otherwise want."</li>
</ul>
<p><span class="pill warn">Fallback only</span> Reasonable if a working store is needed <em>fast</em> and D-Bus is already present; otherwise the dependency cost outweighs the saved daemon work.</p>
<h3>4.D — Wrap KWallet</h3>
<ul>
<li><strong>Pros:</strong> Mature, good UX, encrypted store.</li>
<li><strong>Cons:</strong> Qt/KDE + D-Bus weight, even further from the Cocoa/CoreFoundation world the desktop targets. All of C's downsides, heavier.</li>
</ul>
<p><span class="pill bad">Rejected</span> Strictly worse fit than C for this project.</p>
<h3>4.E — <code>pass</code> / <code>gpg-agent</code> backend</h3>
<ul>
<li><strong>Pros:</strong> Dead simple, file-per-secret, gpg-audited crypto, trivially scriptable.</li>
<li><strong>Cons:</strong> CLI/file-shaped, not a daemon with an API; no attribute search model, no per-app ACLs, no Apple API surface. Fine for a developer's personal secrets, not a system keychain apps can call.</li>
</ul>
<p><span class="pill bad">Rejected</span> Wrong shape — can't present <code>SecItem</code> semantics.</p>
<h3>4.F — PKCS#11 (<code>p11-kit</code> + SoftHSM / OpenSC)</h3>
<ul>
<li><strong>Pros:</strong> Standard token API; good for certs/keys and smartcards; could back the <code>kSecClassKey</code>/<code>Identity</code> classes.</li>
<li><strong>Cons:</strong> PKCS#11 is a <em>key/token</em> API, not a general password store; awkward for <code>GenericPassword</code> items; another semantic translation layer. Better seen as an <em>optional backend</em> for the key classes under option B than as the store itself.</li>
</ul>
<p><span class="pill info">Complementary</span> Not a standalone answer; a possible plug-in backend for cert/key items later.</p>
<h3>4.G — Status quo: plaintext <code>Network.plist</code> / flat files</h3>
<ul>
<li><strong>Pros:</strong> Zero work; already how WLAN creds would land today.</li>
<li><strong>Cons:</strong> No encryption at rest, no API, no access control. Acceptable as an interim, not as a destination.</li>
</ul>
<p><span class="pill info">Interim</span> The honest current state until a consumer justifies B.</p>
<h3 id="alt-summary">4.8 Verdict matrix</h3>
<table class="compact">
<thead><tr><th>Option</th><th>Apple API</th><th>License fit</th><th>IPC fit</th><th>Effort</th><th>Verdict</th></tr></thead>
<tbody>
<tr><td>A. Port Security.framework</td><td><span class="yesno yes">Exact</span></td><td><span class="yesno partial">corecrypto blocked</span></td><td><span class="yesno yes">Mach</span></td><td><span class="yesno no">6–12+ mo</span></td><td><span class="pill bad">Rejected</span></td></tr>
<tr><td><strong>B. Clean-room secd</strong></td><td><span class="yesno yes">Public API</span></td><td><span class="yesno yes">BSD</span></td><td><span class="yesno yes">Mach/DO</span></td><td><span class="yesno partial">2–3 mo</span></td><td><span class="pill ok">Chosen</span></td></tr>
<tr><td>C. gnome-keyring/libsecret</td><td><span class="yesno partial">Shimmed</span></td><td><span class="yesno partial">LGPL</span></td><td><span class="yesno no">D-Bus</span></td><td><span class="yesno yes">3–5 wk</span></td><td><span class="pill warn">Fallback</span></td></tr>
<tr><td>D. KWallet</td><td><span class="yesno partial">Shimmed</span></td><td><span class="yesno partial">LGPL</span></td><td><span class="yesno no">D-Bus/Qt</span></td><td><span class="yesno partial">4–6 wk</span></td><td><span class="pill bad">Rejected</span></td></tr>
<tr><td>E. pass / gpg-agent</td><td><span class="yesno no">None</span></td><td><span class="yesno yes">GPL tool</span></td><td><span class="yesno no">CLI</span></td><td><span class="yesno yes">days</span></td><td><span class="pill bad">Rejected</span></td></tr>
<tr><td>F. PKCS#11</td><td><span class="yesno partial">Keys only</span></td><td><span class="yesno yes">BSD/LGPL</span></td><td><span class="yesno partial">lib</span></td><td><span class="yesno partial">varies</span></td><td><span class="pill info">Backend</span></td></tr>
<tr><td>G. plaintext plist</td><td><span class="yesno no">None</span></td><td>—</td><td>—</td><td><span class="yesno yes">0</span></td><td><span class="pill info">Interim</span></td></tr>
</tbody>
</table>
<h2 id="rec">5. Recommended design — <code>com.apple.securityd</code></h2>
<div class="rec"><strong>Shape:</strong> a launchd-managed Mach/DO daemon presenting the public <code>SecItem</code> keychain API, backed by an encrypted per-user (and per-system) file store, with crypto from base <code>libcrypto</code>/libsodium and an optional TPM2 key-wrapping backend. The <code>Security</code>-shim library does a <code>bootstrap_look_up</code> and forwards calls — identical client/daemon pattern to configd and notifyd.</p></div>
<ol>
<li><strong>Daemon.</strong> Launchd job labelled <code>com.apple.securityd</code> (file <code>com.apple.securityd.plist</code>) — the same string for the job <code>Label</code> and the advertised Mach service (legacy alias <code>com.apple.SecurityServer</code>); started on demand. Per-user instances in the user's launchd domain (keychains are per-user); one system instance (root) for the System keychain. Mirrors the per-user/per-session domain handling the launchd port already supports.</li>
<li><strong>Library.</strong> A <code>Security</code>-compatible shim exporting <code>SecItemAdd/CopyMatching/Update/Delete</code> (+ legacy <code>SecKeychain*</code> shims) that marshal CFDictionary queries over DO/Mach to <code>secd</code>. CoreFoundation types are <a href="freebsd-libcorefoundation-icu-audit.html">already available</a>.</li>
<li><strong>Store.</strong> One encrypted SQLite (or flat) DB per keychain: <code>~/Library/Keychains/login.keychain-db</code> and <code>/Library/Keychains/System.keychain</code>, matching Apple paths so muscle memory / tooling line up. Items carry class + attributes (searchable, plaintext) + encrypted secret blob + access policy.</li>
<li><strong>Key hierarchy.</strong> A per-keychain master key derived from the unlock secret (login password) via Argon2/scrypt; the master key wraps per-item content-encryption keys (AES-256-GCM). Locking drops the master key from memory; unlocking re-derives it. (See §6.)</li>
<li><strong>Unlock.</strong> A PAM module (<code>pam_secd</code>, analog of <code>pam_keychain</code>/<code>pam_gnome_keyring</code>) unlocks the login keychain at login using the same password — direct tie-in to the just-completed PAM port. Manual lock/unlock via a CLI (<code>security(1)</code>-equivalent) and an Agent callback for GUI prompts when an app needs an item from a locked keychain.</li>
<li><strong>Access control.</strong> v1: uid + executable-path policy ("which program asked"). v2: stronger identity (code-signing/Mach audit-token-based, if/when code signing exists). Never weaker than gnome-keyring's model.</li>
</ol>
<h3 id="naming">5.1 Naming — follow the shipped system's convention</h3>
<p>This is settled by what the running image actually does, not by what earlier draft plans guessed. A live <code>launchctl list</code> on a NextBSD box shows the system daemons keeping Apple's <em>exact</em> labels — the job <code>Label</code>, the <code>.plist</code> filename, and the advertised Mach service are all the one <code>com.apple.*</code> string:</p>
<pre class="shell"><code>launchd% list
PID Status Label
54 - com.apple.syslogd
53 - com.apple.notifyd
52 - com.apple.mDNSResponder
51 - com.apple.kextd
50 - com.apple.hostnamed
48 - com.apple.configd
47 - com.apple.aslmanager
46 - com.apple.IPConfiguration
45 - com.apple.DiskArbitration
55 - com.openssh.sshd # third-party → upstream's own reverse-DNS</code></pre>
<p>So a daemon that <em>models or replaces an Apple service</em> takes Apple's own name outright. For the keychain that is <strong><code>com.apple.securityd</code></strong> (modern data-protection alt: <code>com.apple.secd</code>; legacy bootstrap alias: <code>com.apple.SecurityServer</code>). A clean-room <code>Security.framework</code> has <code>bootstrap_look_up("com.apple.securityd")</code> baked in, and using Apple's label for the job too keeps everything aligned with the rest of the running stack. <em>(This is the convention as shipped. It is not permanent, and it does not extend to daemons we write both ends of — see the direction-of-travel note below.)</em></p>
<div class="resolved"><strong>The project's namespace layering (read off the shipped image):</strong>
<table class="compact">
<thead><tr><th>Layer</th><th>Namespace</th><th>Examples</th></tr></thead>
<tbody>
<tr><td>Userland daemon whose label Apple-derived code <strong>looks up</strong> (<code>bootstrap_look_up</code>), or that already ships under it</td><td><strong><code>com.apple.*</code></strong> (Apple's exact label; job = service)</td><td><code>com.apple.configd</code>, <code>com.apple.notifyd</code>, <code>com.apple.IPConfiguration</code>, <code>com.apple.DiskArbitration</code>, <code>com.apple.securityd</code></td></tr>
<tr><td>Userland daemon where <strong>we write both ends</strong> — no Apple-derived consumer, nothing shipped yet</td><td><strong><code>org.nextbsd.*</code></strong></td><td><code>org.nextbsd.wland</code> / <code>org.nextbsd.wlan</code> — see below</td></tr>
<tr><td>Third-party daemon</td><td>upstream's own reverse-DNS</td><td><code>com.openssh.sshd</code></td></tr>
<tr><td>Kernel kexts (loaded by <code>com.apple.kextd</code>)</td><td><strong><code>org.nextbsd.*</code></strong></td><td><code>org.nextbsd.driver.*</code>, <code>org.nextbsd.kpi.*</code>, <code>org.nextbsd.filesystems.*</code></td></tr>
<tr><td>Desktop / GUI layer</td><td><strong><code>org.gershwin.*</code></strong></td><td><code>org.gershwin.loginwindow</code>, <code>org.gershwin.dshelper</code></td></tr>
</tbody>
</table>
<strong>Not <code>org.nextbsd.secd</code></strong> — and the reason is <em>not</em> "that namespace is kernel-only" (an earlier draft said so; it was wrong, see below). It is that a clean-room <code>Security.framework</code> carries <code>bootstrap_look_up("com.apple.securityd")</code> in Apple-derived source. The label is a <strong>compatibility surface</strong> here, not a branding choice. Also <strong>not <code>org.freebsd.secd</code></strong> (the <code>org.freebsd.*</code> labels in older plan drafts don't match the shipped image — see the audit note in the Bluetooth companion).</div>
<div class="open-q"><strong>Direction of travel: <code>com.apple.*</code> → <code>org.nextbsd.*</code>.</strong> The table above describes <em>the shipped image as it is today</em>, not an endpoint we're defending. The intent is to move these labels to <code>org.nextbsd.*</code> across the board — they are our daemons, not Apple's, and the reverse-DNS should say so. What blocks it is cost, not principle: the labels are baked into running jobs, <code>.plist</code> filenames, and <code>bootstrap_look_up()</code> call sites in already-shipping code, so a sweep is a real code-breaking migration and has to be scheduled as one. Until then, <strong>existing</strong> daemons keep <code>com.apple.*</code>.
<p style="margin:.6rem 0 0"><strong>So the real test is not "is it new?" — it is "does Apple-derived code look the label up?"</strong> Where some Apple-shaped consumer hardcodes <code>bootstrap_look_up("com.apple.X")</code>, the label is a compatibility surface and we are stuck with it however greenfield the daemon is. <code>securityd</code> is exactly that case: <code>Security.framework</code> is Apple's API and we would be porting its source. Where <em>we</em> write both ends, the label is free — and a free label should be ours.</p>
<p style="margin:.6rem 0 0"><strong><code>wland</code> is the first daemon where both ends are ours.</strong> Nothing Apple-derived looks it up: we write the <code>wlan</code> CLI, we write <code>SCWLANBackend</code>, and the CoreWLAN-shaped ObjC API sits above the Mach line in Gershwin. So it takes <strong><code>org.nextbsd.wland</code></strong> (job) advertising Mach service <strong><code>org.nextbsd.wlan</code></strong>, with no shipped code to break and no migration to pay for later.</p>
<p style="margin:.6rem 0 0">The rename settles it independently, too. The old rule was <em>inherit Apple's exact label</em> — which presupposes there is one, and Apple's daemon is <code>wifid</code>, not <code>wland</code>. <code>com.apple.wland</code> would have been a <em>fabricated</em> Apple label for a daemon Apple never shipped: the worst of both worlds. There was nothing left to inherit.</p></div>
<h2 id="crypto">6. Crypto & storage — the honest threat model</h2>
<ul>
<li><strong>No <code>corecrypto</code>.</strong> Use base <code>libcrypto</code> (OpenSSL) or libsodium — AES-256-GCM for item secrets, Argon2id/scrypt for password-derived keys, HKDF for subkeys.</li>
<li><strong>No SEP.</strong> Default deployment is <strong>software-only</strong>: the master key lives in <code>secd</code>'s (ideally <code>mlock</code>'d, non-swappable) memory while unlocked. This is the <em>same</em> guarantee gnome-keyring/KWallet give on commodity Linux — root, or a debugger on the unlocked process, can read live secrets. State this plainly; don't imply Apple-equivalent hardware protection.</li>
<li><strong>Optional TPM2 backing.</strong> FreeBSD has <code>tpm(4)</code> and <code>security/tpm2-tss</code> in ports. The keychain master key can be <em>sealed</em> to the TPM (optionally to PCR state), so the on-disk DB can't be decrypted off-box without the TPM. This is the closest analog to SEP key-wrapping and is the recommended hardening once the core works — but strictly optional and gated on TPM presence.</li>
<li><strong>At rest:</strong> attributes may be plaintext (needed for search) but secret payloads are always encrypted; whole-DB encryption optional on top.</li>
</ul>
<h2 id="api">7. API surface to implement (v1)</h2>
<pre class="objc"><code>/* Modern data-protection API — the priority surface */
OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result);
OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result);
OSStatus SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
OSStatus SecItemDelete(CFDictionaryRef query);
/* Classes to support first */
kSecClassGenericPassword // app/daemon secrets, WLAN PSKs
kSecClassInternetPassword // server creds (has protocol/server/port attrs)
kSecClassKey / kSecClassCertificate / kSecClassIdentity // later; PKCS#11 backend option
/* Common attributes (search keys) */
kSecAttrAccount kSecAttrService kSecAttrServer kSecAttrProtocol
kSecAttrLabel kSecAttrAccessGroup kSecAttrAccessible
/* Legacy shims (thin, mapped onto the modern store) */
SecKeychainAddGenericPassword(...);
SecKeychainFindGenericPassword(...);
SecKeychainAddInternetPassword(...);</code></pre>
<p>Getting <code>GenericPassword</code> + <code>InternetPassword</code> CRUD correct covers WLAN creds, network shares, and the vast majority of app needs. Key/cert/identity classes and a possible PKCS#11 (§4.F) backend come later.</p>
<h2 id="open">8. Open questions</h2>
<div class="open-q"><strong>Q1. First consumer?</strong> The plan only starts when something needs it. Likely candidates: <code>wland</code> network creds, a future mail/browser, or network-share mounts. Until then, plaintext <code>Network.plist</code> stands (WLAN plan <a href="nextbsd-wlan-plan.html#openq">Q2</a>).</div>
<div class="open-q"><strong>Q2. Per-user vs system scope on day one?</strong> The System keychain (root, pre-login) is what <code>wland</code> would need first; the per-user login keychain is what desktop apps need. Probably ship the system instance first since WLAN is the nearer consumer.</div>
<div class="open-q"><strong>Q3. TPM2 from the start or as a follow-up?</strong> Software-only is simpler and works everywhere; TPM2 sealing is the meaningful hardening but adds a conditional dependency and PCR-policy complexity. Lean: ship software-only, add TPM2 as opt-in phase 2.</div>
<div class="open-q"><strong>Q4. How faithful must access control be?</strong> Full Apple ACLs are tied to code-signing identities we don't have. Start with uid+path; revisit if/when a code-signing story exists. Document the gap so nobody assumes Apple-grade app isolation.</div>
<div class="open-q"><strong>Q5. Reuse the on-disk format or invent our own?</strong> Apple's <code>.keychain-db</code> format is undocumented/SEP-entangled in modern form. Inventing a clean format (encrypted SQLite) is simpler and we control it; Apple-path filenames give familiarity without binary-format compatibility (which buys us nothing without macOS interop).</div>
<h2 id="refs">9. References</h2>
<ul>
<li><strong>Apple Security framework source:</strong> <a href="https://github.com/apple-oss-distributions/Security">github.com/apple-oss-distributions/Security</a></li>
<li><strong>Apple securityd source:</strong> <a href="https://github.com/apple-oss-distributions/securityd">github.com/apple-oss-distributions/securityd</a></li>
<li><strong>Keychain Services API:</strong> <a href="https://developer.apple.com/documentation/security/keychain_services">developer.apple.com/documentation/security/keychain_services</a></li>
<li><strong>freedesktop Secret Service / libsecret:</strong> <a href="https://specifications.freedesktop.org/secret-service/latest/">specifications.freedesktop.org/secret-service</a></li>
<li><strong>FreeBSD TPM2 stack:</strong> <a href="https://www.freshports.org/security/tpm2-tss/">freshports.org/security/tpm2-tss</a>, <code>man 4 tpm</code></li>
<li><strong>Companion plans:</strong> <a href="nextbsd-wlan-research.html">WLAN management</a>, PAM port, OpenDirectory scoping, <a href="nextbsd-configd-plan.html">configd</a>, <a href="freebsd-launchd-plan.html">launchd</a>, <a href="freebsd-libcorefoundation-icu-audit.html">libCoreFoundation</a></li>
</ul>
</body>
</html>