Skip to main content

TrustedRoot

Struct TrustedRoot 

Source
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: String

Media 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.

§certificate_authorities: Value

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: Value

Certificate transparency log declarations. Same posture as certificateAuthorities above.

§timestamp_authorities: Value

RFC 3161 timestamp authority declarations.

Implementations§

Source§

impl TrustedRoot

Source

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.

Source

pub fn parse_bytes(bytes: &[u8]) -> Result<Self, TrustedRootParseError>

Parse a trusted_root.json payload supplied as raw bytes.

Source

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.

Source

pub fn validate(&self) -> Result<(), TrustedRootParseError>

Validate the structural invariants cortex enforces on every trusted_root.json:

  • mediaType is non-empty and starts with the Sigstore application/vnd.dev.sigstore.trustedroot+json prefix 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.
Source

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).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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

Source§

fn clone(&self) -> TrustedRoot

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TrustedRoot

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for TrustedRoot

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for TrustedRoot

Source§

fn eq(&self, other: &TrustedRoot) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for TrustedRoot

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl Eq for TrustedRoot

Source§

impl StructuralPartialEq for TrustedRoot

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,