pub struct TransparencyLog { /* private fields */ }Expand description
The transparency log: the push-attestation log, plus the evidence that it has not been rewritten.
§What it adds, and what it deliberately does not
It adds nothing to the attestation format. The Merkle tree is a pure
function of the lines already on disk — leaf i is
SHA-256(0x00 ‖ line_i) over the exact bytes of line i, no
canonicalisation, no re-serialisation — so there is no second encoding to
keep in step and no chance of hashing something other than what is stored.
A signed tree head at size N pins every line up to N. Rewriting line 3
changes the root, so every head issued since stops verifying against the
file: the tampering is detected by any holder of any later head, including
one who never saw the original line. That is the property, and
TransparencyLog::audit is the check.
What it does not add is a consistency proof between two heads (RFC 6962’s second proof type). A verifier with two heads can today detect that the file changed under them; it cannot yet be shown that head M is a prefix-extension of head N without recomputing from the records. That is a real gap and it is named rather than papered over.
Implementations§
Source§impl TransparencyLog
impl TransparencyLog
Sourcepub fn over(records: impl Into<PathBuf>, heads: impl Into<PathBuf>) -> Self
pub fn over(records: impl Into<PathBuf>, heads: impl Into<PathBuf>) -> Self
Any append-only JSONL file, with its own heads beside it.
The Merkle machinery cares about lines, not about what is in them, so
the same log covers push attestations and build provenance without a
second implementation (one implementation). What is record-specific is exactly two
methods — Self::records and Self::prove_pack — and they say so.
Sourcepub fn records(&self) -> AttestationLog
pub fn records(&self) -> AttestationLog
This log’s records, read as push attestations.
Only meaningful for the push log; a build log’s lines are a different shape and this would refuse them. Everything else on this type is record-agnostic.
Sourcepub fn records_path(&self) -> &Path
pub fn records_path(&self) -> &Path
Where the records are.
Sourcepub fn heads_path(&self) -> &Path
pub fn heads_path(&self) -> &Path
Where the signed heads are.
Sourcepub fn lines_of(&self) -> Result<Vec<String>>
pub fn lines_of(&self) -> Result<Vec<String>>
The record lines, for a caller that decodes them itself.
pub because the build log’s records are not PushAttestations and
have to be parsed by whoever knows their shape.
pub fn is_empty(&self) -> Result<bool>
Sourcepub fn issue_head(
&self,
key: &PrivateKey,
at_unix_ms: i64,
) -> Result<SignedTreeHead>
pub fn issue_head( &self, key: &PrivateKey, at_unix_ms: i64, ) -> Result<SignedTreeHead>
Issue a signed tree head over everything the log currently holds, and append it.
Returns the head. Issuing one over an empty log is allowed and meaningful: it is how a verifier learns the log exists and held nothing at that moment, which is the only way “this server has never accepted a push” can be evidence rather than an absence.
Sourcepub fn heads(&self) -> Result<Vec<SignedTreeHead>>
pub fn heads(&self) -> Result<Vec<SignedTreeHead>>
Every signed head that has been issued, oldest first.
Sourcepub fn prove(&self, index: usize) -> Result<Option<Inclusion>>
pub fn prove(&self, index: usize) -> Result<Option<Inclusion>>
The inclusion proof for the record at index.
None when the log does not hold that many records — a proof for a
record that is not there is not an empty proof.
Sourcepub fn prove_pack(&self, pack_hash: &str) -> Result<Option<Inclusion>>
pub fn prove_pack(&self, pack_hash: &str) -> Result<Option<Inclusion>>
Find the record for a pack hash, and prove it.
The lookup a verifier actually makes: it holds an artifact, the artifact names a pack, and it wants to know the push that landed it. Scans, which is honest about the cost — an index over the log is a later problem and pretending one exists would be worse than the scan.
Sourcepub fn audit(&self, key: &PublicKey) -> Result<AuditVerdict>
pub fn audit(&self, key: &PublicKey) -> Result<AuditVerdict>
Check every signed head against the records on disk.
This is what the log is for. It answers three different questions, and keeping them apart is the point:
- a head that does not verify means somebody wrote a head they could not sign — or the key rotated and the verifier was not told;
- a head that does verify over a root the records no longer produce means the records were changed after they were signed for, which is tampering and nothing else;
- a head covering more records than exist means lines were removed.
The first verdict wins and is returned; an operator fixes one thing at a time.
Trait Implementations§
Source§impl Clone for TransparencyLog
impl Clone for TransparencyLog
Auto Trait Implementations§
impl Freeze for TransparencyLog
impl RefUnwindSafe for TransparencyLog
impl Send for TransparencyLog
impl Sync for TransparencyLog
impl Unpin for TransparencyLog
impl UnsafeUnpin for TransparencyLog
impl UnwindSafe for TransparencyLog
Blanket Implementations§
impl<T> Allocation for T
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request