spec: tighten ifc-fides framing and evidenceRef canonicalization (review @Rul1an)
ifc-fides: - lead Label semantics with the load-bearing wire/host split (wire markers are advisory hints; reader-set semantics are host-resolved) - state the join asymmetry as principled, not incidental: integrity join is total/wire-computable (small closed lattice); confidentiality join is partial because public is the only wire-computable case (reader set = top), while private join is the intersection the opaque markers don't carry - frame wire opaqueness as a property of the security model, not a spec limitation - add fail-closed handling when resolution is unavailable (ref absent, digest-only, source unreachable): never treat opaque labels as equal or as public; unknown/ mixed provenance classifies private; resolved reader sets are not durable grants trust-annotations: - show jcs/rfc8785 alongside cbor/rfc8949 so canonicalization reads as a per-reference envelope choice rather than a default - note type/digest/canonicalization as the local-re-derivation minimum Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
b799af5bea
commit
4967a9eccc
2 changed files with 47 additions and 13 deletions
|
|
@ -86,12 +86,24 @@ be inlined by deployments that accept the wire cost) has the shape:
|
|||
|
||||
### Label semantics
|
||||
|
||||
The load-bearing distinction is between the wire and the host: **wire markers are
|
||||
advisory hints; reader-set semantics are host-resolved.** The asymmetry between
|
||||
the two joins below follows from that one cut.
|
||||
|
||||
- **Join on accumulation.** As a session ingests labeled results, the context
|
||||
label is the *join* of what it has seen: integrity degrades toward
|
||||
`untrusted`, confidentiality narrows toward the smallest permitted reader set.
|
||||
The **integrity join is total and computable from the wire values alone**
|
||||
(`untrusted` dominates). The **confidentiality join is NOT computable from the
|
||||
opaque wire markers alone** — see [Reader-set resolution](#reader-set-resolution).
|
||||
The two joins differ in *where* they can be computed, and the difference is
|
||||
principled rather than incidental:
|
||||
- **Integrity join is total and wire-computable.** The integrity lattice is
|
||||
small and closed (`trusted ⊑ untrusted`), so `untrusted` dominates and the
|
||||
join needs nothing beyond the wire values.
|
||||
- **Confidentiality join is partial and host-resolved.** Reader sets are open
|
||||
and host-knowledge-dependent. `public` is the one wire-computable case,
|
||||
because its reader set is universal (`⊤`): `public ⊔ anything = public`.
|
||||
`private ⊔ private`, by contrast, is the *intersection* of two reader sets
|
||||
that the opaque markers don't carry, so it is **not** computable from the
|
||||
wire — see [Reader-set resolution](#reader-set-resolution).
|
||||
- **Policy check before egress.** Before a write/egress tool call, the host
|
||||
checks whether the current context label may flow to the call's target. When
|
||||
a label is absent, the host falls back to its default (trusted-action)
|
||||
|
|
@ -103,13 +115,16 @@ be inlined by deployments that accept the wire cost) has the shape:
|
|||
|
||||
### Reader-set resolution
|
||||
|
||||
`"private"` is intentionally opaque on the wire. Two distinct `"private"`
|
||||
markers (e.g. file contents from two different private repositories) are **not
|
||||
equal**, and their confidentiality join is **not** the same `"private"` token:
|
||||
data derived from both may flow only to principals who can read *both* sources —
|
||||
the intersection of their reader sets. The opaque marker cannot express this
|
||||
intersection, so a host that needs to make a precise cross-source flow decision
|
||||
MUST resolve each `"private"` marker to a concrete reader set before joining.
|
||||
`"private"` is intentionally opaque on the wire — and that opaqueness is a
|
||||
property of the security model, not a limitation of the spec. A reader set is not
|
||||
transmissible without policy context, so the wire shape correctly declines to
|
||||
carry it. Two distinct `"private"` markers (e.g. file contents from two different
|
||||
private repositories) are **not equal**, and their confidentiality join is **not**
|
||||
the same `"private"` token: data derived from both may flow only to principals who
|
||||
can read *both* sources — the intersection of their reader sets. The opaque marker
|
||||
cannot express this intersection, so a host that needs to make a precise
|
||||
cross-source flow decision MUST resolve each `"private"` marker to a concrete
|
||||
reader set before joining.
|
||||
|
||||
Resolution is a host-side concern, performed at policy-decision time:
|
||||
|
||||
|
|
@ -121,8 +136,19 @@ Resolution is a host-side concern, performed at policy-decision time:
|
|||
3. The host computes the flow decision over the resolved sets (intersection for
|
||||
a join of multiple private sources) and then discards them.
|
||||
|
||||
Keeping resolution host-side is what lets the wire stay at `public` / `private`
|
||||
while still supporting precise multi-source decisions.
|
||||
**When resolution is unavailable** — the `ref` is absent, the label is
|
||||
digest-only, or the originating system is unreachable at decision time — the host
|
||||
MUST NOT treat two opaque labels as equal, and MUST NOT treat `"private"` as
|
||||
`"public"`. It denies, prompts, or applies its configured fail-closed policy. Two
|
||||
`"private"` labels are equal only once resolution proves their sources are; until
|
||||
a source is established, unknown or mixed provenance classifies as `"private"`,
|
||||
never defaulted to `"public"` from a repository-level shortcut.
|
||||
|
||||
The resolved reader set is a decision-time read performed under the host's own
|
||||
credentials. It is not a durable grant: a host SHOULD NOT cache it as one or
|
||||
serialize it back into annotations or evidence unless a deployment explicitly opts
|
||||
in. This keeps the wire free of user identities while still letting the host make a
|
||||
precise decision when it holds source provenance plus its own credentials.
|
||||
|
||||
### Relationship to `trust-annotations`
|
||||
|
||||
|
|
@ -164,3 +190,4 @@ the IFC label is the precise, host-checkable evidence behind them.
|
|||
| ---------- | ------------------------------------------------------------ |
|
||||
| 2026-06-10 | Initial draft skeleton. Reframed from a top-level `ifc` extension to a `trust-annotations` `evidenceRef` profile (`ifc.fides.v1`). |
|
||||
| 2026-06-15 | Confidentiality limited to `public` / `private` on the wire (dropped reader-list); added Reader-set resolution section; emitter classifies per resource, not per repository. (Review: @JoannaaKL.) |
|
||||
| 2026-06-16 | Lead the semantics with the wire-hint / host-resolved split; state the integrity-total vs confidentiality-partial asymmetry as principled (`public` = `⊤` is wire-computable, `private ⊔ private` is not); add fail-closed handling when resolution is unavailable and a no-durable-grant rule for resolved sets. (Review: @Rul1an.) |
|
||||
|
|
|
|||
|
|
@ -101,6 +101,12 @@ the SEP-1913 discussion):
|
|||
This single slot subsumes the previously separate `attestationChainRef` /
|
||||
`policyDecisionRef` ideas — both become `type` values.
|
||||
|
||||
`canonicalization` is per-reference precisely so different evidence producers can
|
||||
be re-derived independently. `cbor/rfc8949` and `jcs/rfc8785` (JSON
|
||||
Canonicalization Scheme) are both valid envelope choices — neither is the default,
|
||||
and the `type`/`digest`/`canonicalization` triple is the minimum a client needs
|
||||
for local re-derivation regardless of which is used.
|
||||
|
||||
### Coarse vs. rich classification (DataClass)
|
||||
|
||||
SEP-1913 carried a four-level data classification
|
||||
|
|
@ -114,7 +120,7 @@ is recovered as an `evidenceRef` profile:
|
|||
"type": "data-class.v1",
|
||||
// record resolves to e.g. { "class": "highly_confidential", "regulatory": ["hipaa"] }
|
||||
"digest": "sha256:…",
|
||||
"canonicalization": "cbor/rfc8949"
|
||||
"canonicalization": "jcs/rfc8785" // a JSON-canonicalized record; CBOR is equally valid
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -174,3 +180,4 @@ and `evidenceRef` is how that claim is made checkable.
|
|||
| Date | Change |
|
||||
| ---------- | ------------------------------------------------------------- |
|
||||
| 2026-06-10 | Initial draft skeleton. Narrowed to `sensitive` + `untrusted` + `evidenceRef`; DataClass demoted to a profile; `requires_review` moved to `action-metadata`. |
|
||||
| 2026-06-16 | Show `jcs/rfc8785` alongside `cbor/rfc8949` so canonicalization reads as a per-reference envelope choice, not a default; note the `type`/`digest`/`canonicalization` triple as the local-re-derivation minimum. (Review: @Rul1an.) |
|
||||
|
|
|
|||
Loading…
Reference in a new issue