pub struct TrustedRoot {
pub media_type: String,
pub tlogs: Vec<TransparencyLogInstance>,
pub certificate_authorities: Value,
pub ctlogs: Value,
pub timestamp_authorities: Value,
}Expand description
Parsed Sigstore trusted_root.json (v0.1 protobuf-go JSON shape).
Only the fields cortex needs are deserialised. Unknown fields are
preserved as serde_json::Value so a refresh from a slightly newer
schema still round-trips.
Fields§
§media_type: StringMedia type from the Sigstore protobuf descriptor.
Example: application/vnd.dev.sigstore.trustedroot+json;version=0.1.
tlogs: Vec<TransparencyLogInstance>Rekor transparency log declarations. Cortex enforces a non-empty list at parse time; an empty array fails closed.
Fulcio certificate authority declarations. Cortex does not consume these on the trust path today; they are kept so the cached file is a faithful copy and so a future signature verifier can use them.
ctlogs: ValueCertificate transparency log declarations. Same posture as
certificateAuthorities above.
RFC 3161 timestamp authority declarations.
Implementations§
Source§impl TrustedRoot
impl TrustedRoot
Sourcepub fn embedded() -> Result<Self, TrustedRootParseError>
pub fn embedded() -> Result<Self, TrustedRootParseError>
Parse the embedded snapshot. Panics only when the embedded JSON is structurally invalid — which is a build-time bug, not a runtime failure mode.
Operators MUST NOT rely on the embedded root staying fresh; it is the fail-closed floor that exists so verifying with a brand-new data directory does not silently degrade. Refresh is an operator action.
Sourcepub fn parse_bytes(bytes: &[u8]) -> Result<Self, TrustedRootParseError>
pub fn parse_bytes(bytes: &[u8]) -> Result<Self, TrustedRootParseError>
Parse a trusted_root.json payload supplied as raw bytes.
Sourcepub fn load_cached(
path: impl AsRef<Path>,
) -> Result<Option<Self>, TrustedRootIoError>
pub fn load_cached( path: impl AsRef<Path>, ) -> Result<Option<Self>, TrustedRootIoError>
Read a cached trusted_root.json off disk. Missing files return
Ok(None) so callers can fall back to Self::embedded; parse
failures fail closed.
Sourcepub fn validate(&self) -> Result<(), TrustedRootParseError>
pub fn validate(&self) -> Result<(), TrustedRootParseError>
Validate the structural invariants cortex enforces on every
trusted_root.json:
mediaTypeis non-empty and starts with the Sigstoreapplication/vnd.dev.sigstore.trustedroot+jsonprefix so a misrouted file (e.g.targets.json,timestamp.json) cannot be silently accepted.- At least one tlog is declared; an empty list cannot witness anything and is treated as a hard parse failure rather than as “trust nothing”.
- Every tlog has a parseable activation start.
Sourcepub fn metadata_signed_at(&self) -> Option<DateTime<Utc>>
pub fn metadata_signed_at(&self) -> Option<DateTime<Utc>>
Latest tlog activation timestamp across every transparency log in the root. Used as the proxy for “this trust root was signed at” — a fresher activation entry means the trust root saw at least one signing-key rotation event after that date.
Returns None only when validation was skipped or every tlog
lacked a start (which validate would have rejected).
Sourcepub fn is_stale_at(
&self,
now: DateTime<Utc>,
max_age: Duration,
anchor: TrustRootStalenessAnchor<'_>,
) -> Result<bool, TrustRootStalenessError>
pub fn is_stale_at( &self, now: DateTime<Utc>, max_age: Duration, anchor: TrustRootStalenessAnchor<'_>, ) -> Result<bool, TrustRootStalenessError>
Return true when now - anchor exceeds max_age, where the
anchor is the explicit freshness datum the caller selected via
TrustRootStalenessAnchor.
Bug J + 2026-05-15 portfolio-extension fix: callers MUST pass an
explicit anchor source rather than letting the implementation
fall through to Self::metadata_signed_at — the latter is the
Sigstore tlog signing-key activation date, which rotates rarely
(months/years) and would make every release immediately stale
under a 30-day policy. The correct anchors are documented on
TrustRootStalenessAnchor.
Returns Err(TrustRootStalenessError) only when the anchor
source cannot be resolved (e.g. cache file unreadable,
embedded snapshot date constant is malformed). I/O failures on
the cache file are reported verbatim so callers can map them to
the correct CLI exit code; they are NOT silently treated as
stale.
Sourcepub fn is_stale(
&self,
now: DateTime<Utc>,
anchor: TrustRootStalenessAnchor<'_>,
) -> Result<bool, TrustRootStalenessError>
pub fn is_stale( &self, now: DateTime<Utc>, anchor: TrustRootStalenessAnchor<'_>, ) -> Result<bool, TrustRootStalenessError>
Convenience alias for Self::is_stale_at with the default
operator policy (DEFAULT_MAX_TRUST_ROOT_AGE). Used by the
Rekor live adapter (council Decision #2) to keep the call sites
readable.
Sourcepub fn from_embedded() -> Result<Self, TrustedRootParseError>
pub fn from_embedded() -> Result<Self, TrustedRootParseError>
Convenience alias for Self::embedded. Used by the Rekor live
adapter (council Decision #2) which was authored against the
pre-merge shim API before Decision #1 landed.
Sourcepub fn signed_at(&self) -> DateTime<Utc>
pub fn signed_at(&self) -> DateTime<Utc>
Best-effort signed-at accessor for the staleness gate.
Falls back to the Unix epoch when no tlog activation is parseable —
trust-root validation refuses that case earlier, so the fallback is
only reachable when the operator deliberately constructs a trust
root with no tlog. Callers that need to distinguish “no activation”
from a real timestamp should use Self::metadata_signed_at.
Sourcepub fn rekor_verifying_key(
&self,
receipt_log_id: &str,
) -> Result<VerifyingKey, TrustedRootKeyError>
pub fn rekor_verifying_key( &self, receipt_log_id: &str, ) -> Result<VerifyingKey, TrustedRootKeyError>
Return the parsed ECDSA P-256 verifying key for the Rekor
SignedEntryTimestamp signature bound to the receipt’s
body.logID.
BH-3 fix (Finding 3 in
docs/reviews/BUG_HUNT_2026-05-12_post_8f43450.md,
Cosign GHSA-whqx-f9j3-ch6m class). The selector finds the tlog
whose logId.keyId byte-decodes to the same value as the
receipt’s body.logID, then returns that tlog’s ECDSA P-256
public key. It does not silently fall back to a “latest
activation” tlog when the logId is unknown — Sigstore’s
trusted_root.json declares multiple historical tlogs during
rotation, each with a distinct logId and key; an entry signed
by an older tlog (or a newly-added attacker-controlled tlog
that wins the activation-date race) would otherwise be verified
against the wrong key.
Encoding: Sigstore’s logId.keyId is base64 of the raw bytes
(typically SHA-256(public_key_der)); Rekor’s REST API returns
body.logID as the same bytes in lowercase hex. The match
function decodes both sides to bytes (preferring hex then
base64 for the receipt, base64 then raw for the tlog id) and
compares any pair for equality. Fixture log-ids that are
neither hex nor base64 (e.g. the literal "fixture-log-id"
used in unit tests) are matched on raw byte equality so the
fixture surface keeps working without inflating the production
matching rules.
Fails closed with TrustedRootKeyError::TlogLogIdNoMatch
(carrying the stable invariant
REKOR_TRUSTED_ROOT_TLOG_LOGID_NO_MATCH_INVARIANT) when no
declared tlog matches the receipt’s logId, and with the
existing TrustedRootKeyError variants when the matched
tlog’s rawBytes is missing or does not decode as DER
SubjectPublicKeyInfo.
Sourcepub fn write_atomic(
&self,
path: impl AsRef<Path>,
) -> Result<(), TrustedRootIoError>
pub fn write_atomic( &self, path: impl AsRef<Path>, ) -> Result<(), TrustedRootIoError>
Atomically install this trusted root at path.
Writes to a sibling temp file then renames into place so a partial write cannot leave a half-written trust root on disk for the next verify call to load.
Trait Implementations§
Source§impl Clone for TrustedRoot
impl Clone for TrustedRoot
Source§fn clone(&self) -> TrustedRoot
fn clone(&self) -> TrustedRoot
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for TrustedRoot
impl Debug for TrustedRoot
Source§impl<'de> Deserialize<'de> for TrustedRoot
impl<'de> Deserialize<'de> for TrustedRoot
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for TrustedRoot
impl PartialEq for TrustedRoot
Source§fn eq(&self, other: &TrustedRoot) -> bool
fn eq(&self, other: &TrustedRoot) -> bool
self and other values to be equal, and is used by ==.