Documentation
¶
Overview ¶
Package anchor provides transparency log anchoring for HELM's ProofGraph.
Anchoring periodically submits Merkle roots from the ProofGraph to external transparency logs (Sigstore Rekor, RFC 3161 TSA), creating externally verifiable trust anchors that prove the integrity of the proof chain without trusting HELM.
This implements the P4 ProofGraph anchoring strategy from HELM UCS v1.2.
quantum_posture: eIDAS anchoring consumes QTSP-issued classical timestamp tokens (X.509 chains, SHA-256 imprints); this file adds no cryptographic control of its own, and the receipt-encoding change keeps the DER token base64-encoded in Signature without altering any primitive.
Index ¶
- Constants
- Variables
- type AnchorBackend
- type AnchorReceipt
- type AnchorRequest
- type EIDASAnchor
- type EIDASOption
- type InMemoryReceiptStore
- func (s *InMemoryReceiptStore) GetLatestReceipt(_ context.Context) (*AnchorReceipt, error)
- func (s *InMemoryReceiptStore) GetReceiptByLamportRange(_ context.Context, from, to uint64) ([]*AnchorReceipt, error)
- func (s *InMemoryReceiptStore) StoreReceipt(_ context.Context, receipt *AnchorReceipt) error
- type LogInclusionProof
- type RFC3161Backend
- type RFC3161Option
- type ReceiptStore
- type RekorBackend
- type RekorOption
- type Service
- type ServiceConfig
Constants ¶
const (
// DefaultRekorURL is the public Sigstore Rekor v2 instance.
DefaultRekorURL = "https://rekor.sigstore.dev"
)
const ( // EIDASBackendName identifies the eIDAS/QTSP-labelled anchor backend. The // identifier alone is not proof of cryptographic or legal qualification. EIDASBackendName = "eidas-qtsp" )
Variables ¶
var ( // ErrEIDASChainNotTrusted means none of the certificates inside the // timestamp token matched a thumbprint on the EU Trusted List. ErrEIDASChainNotTrusted = errors.New("eidas: timestamp chain does not terminate at an EU Trusted List root") // ErrEIDASLOTLStale means the EU Trusted List cache has not been // refreshed inside the configured maxLOTLAge window. ErrEIDASLOTLStale = errors.New("eidas: EU Trusted List cache is stale; refresh required before verification") // ErrEIDASMissingLOTL means the anchor was constructed without a // trust.EUTrustedList instance. ErrEIDASMissingLOTL = errors.New("eidas: no EU Trusted List configured") // ErrEIDASMalformedToken means the embedded RFC 3161 token could not // be parsed (corrupt response, wrong content type, etc.). ErrEIDASMalformedToken = errors.New("eidas: malformed RFC 3161 timestamp token") )
Errors surfaced by the eIDAS anchor's verification path.
Functions ¶
This section is empty.
Types ¶
type AnchorBackend ¶
type AnchorBackend interface {
// Name returns the human-readable name of this backend (e.g., "rekor-v2", "rfc3161").
Name() string
// Anchor submits a Merkle root to the transparency log and returns an AnchorReceipt.
// The receipt contains all information necessary to independently verify the anchoring.
Anchor(ctx context.Context, req AnchorRequest) (*AnchorReceipt, error)
// Verify checks that an AnchorReceipt is valid against the transparency log.
Verify(ctx context.Context, receipt *AnchorReceipt) error
}
AnchorBackend defines the interface for transparency log backends. Implementations include Sigstore Rekor v2 and RFC 3161 timestamping authorities.
type AnchorReceipt ¶
type AnchorReceipt struct {
// Backend identifies which transparency log produced this receipt.
Backend string `json:"backend"`
// Request is the original anchor request.
Request AnchorRequest `json:"request"`
// LogID is the transparency log's unique identifier (e.g., Rekor log ID).
LogID string `json:"log_id"`
// LogIndex is the entry's position in the transparency log.
LogIndex int64 `json:"log_index"`
// IntegratedTime is the timestamp assigned by the transparency log.
IntegratedTime time.Time `json:"integrated_time"`
// Signature is the transparency log's signature over the entry.
Signature string `json:"signature"`
// InclusionProof contains the Merkle inclusion proof from the log's tree.
InclusionProof *LogInclusionProof `json:"inclusion_proof,omitempty"`
// RawResponse is the full response from the transparency log for archival.
RawResponse json.RawMessage `json:"raw_response,omitempty"`
// ReceiptHash is the SHA-256 hash of this receipt for indexing.
ReceiptHash string `json:"receipt_hash"`
}
AnchorReceipt is the proof returned by a transparency log after anchoring.
func (*AnchorReceipt) ComputeReceiptHash ¶
func (r *AnchorReceipt) ComputeReceiptHash() string
ComputeReceiptHash computes a deterministic hash of the receipt for indexing.
type AnchorRequest ¶
type AnchorRequest struct {
// MerkleRoot is the hex-encoded SHA-256 root of the ProofGraph subtree being anchored.
MerkleRoot string `json:"merkle_root"`
// FromLamport is the start of the Lamport clock range covered by this anchor.
FromLamport uint64 `json:"from_lamport"`
// ToLamport is the end of the Lamport clock range covered by this anchor.
ToLamport uint64 `json:"to_lamport"`
// NodeCount is the number of ProofGraph nodes covered by this anchor.
NodeCount int `json:"node_count"`
// HeadNodeIDs are the current DAG head node hashes at anchor time.
HeadNodeIDs []string `json:"head_node_ids"`
// Timestamp is the time the anchor request was created.
Timestamp time.Time `json:"timestamp"`
}
AnchorRequest contains the data to be anchored.
func (*AnchorRequest) ComputeDigest ¶
func (r *AnchorRequest) ComputeDigest() ([]byte, error)
ComputeDigest returns a deterministic SHA-256 digest of the anchor request for signing/submission. Uses sorted JSON keys for determinism.
type EIDASAnchor ¶
type EIDASAnchor struct {
// contains filtered or unexported fields
}
EIDASAnchor submits ProofGraph Merkle-root imprints to a configured RFC 3161 endpoint and performs a limited token-certificate inventory check against a caller-supplied EUTrustedList.
EIDASAnchor implements the same AnchorBackend interface as RFC3161Backend, adding two stages on top:
- Submit an RFC 3161 TimeStampReq and preserve the returned token.
- Extract certificates from the token and confirm that at least one certificate's SHA-256 thumbprint is present in the supplied EUTrustedList.
Verify does not validate the CMS signature, bind the token's message imprint to the receipt, build an X.509 certification path, or verify the LOTL XML signature. Those missing checks mean this type does not by itself establish cryptographic or legal eIDAS qualification.
If the LOTL cache is empty (never refreshed) or stale beyond the configured threshold, anchoring still succeeds (we have a token) but verification fails with ErrEIDASLOTLStale so callers that invoke Verify can refuse to trust the anchor. The CLI `verify --require-eidas` does not invoke this method; it performs a separate metadata-only check.
func NewEIDASAnchor ¶
func NewEIDASAnchor(qtspURL string, lotl *trust.EUTrustedList, opts ...EIDASOption) *EIDASAnchor
NewEIDASAnchor creates a new eIDAS/QTSP-labelled RFC 3161 anchor backend.
qtspURL must point to an RFC 3161 endpoint. Callers are responsible for selecting and independently validating the provider and its legal status.
lotl is the EU Trusted List validator used to gate verification; it must not be nil. Callers are responsible for invoking lotl.Refresh on a schedule (see trust.DefaultEULOTLRefreshInterval).
func (*EIDASAnchor) Anchor ¶
func (a *EIDASAnchor) Anchor(ctx context.Context, req AnchorRequest) (*AnchorReceipt, error)
Anchor submits the Merkle root to the QTSP and returns an AnchorReceipt whose Signature field is the base64-encoded TSA response. Verification against the EU Trusted List happens inside Verify, which lets callers archive an anchor that was qualified at submission time even if the LOTL later rotates.
func (*EIDASAnchor) Name ¶
func (a *EIDASAnchor) Name() string
Name returns "eidas-qtsp" — the canonical backend identifier.
func (*EIDASAnchor) QTSPURL ¶
func (a *EIDASAnchor) QTSPURL() string
QTSPURL exposes the configured QTSP endpoint (used by the trust CLI).
func (*EIDASAnchor) Verify ¶
func (a *EIDASAnchor) Verify(_ context.Context, receipt *AnchorReceipt) error
Verify performs a limited RFC 3161 certificate-inventory check and requires at least one embedded certificate thumbprint to be present in the supplied EUTrustedList. It does not validate the token signature, message imprint, certificate path, LOTL signature, or legal qualification.
Returns ErrEIDASLOTLStale if the LOTL cache is older than maxLOTLAge. Returns ErrEIDASChainNotTrusted if no embedded certificate thumbprint is present in the supplied EUTrustedList.
type EIDASOption ¶
type EIDASOption func(*EIDASAnchor)
EIDASOption configures the eIDAS anchor.
func WithEIDASAllowEmptyChain ¶
func WithEIDASAllowEmptyChain(allow bool) EIDASOption
WithEIDASAllowEmptyChain is a test-only knob that lets Verify accept a token whose embedded certificate chain is empty (e.g. when the QTSP returned a token without certReq=true). Production code should never enable this.
func WithEIDASHTTPClient ¶
func WithEIDASHTTPClient(client *http.Client) EIDASOption
WithEIDASHTTPClient supplies a custom HTTP client (mTLS, timeouts, etc.).
func WithEIDASMaxLOTLAge ¶
func WithEIDASMaxLOTLAge(d time.Duration) EIDASOption
WithEIDASMaxLOTLAge sets the maximum allowed age of the LOTL cache before Verify rejects anchors with ErrEIDASLOTLStale. Defaults to trust.DefaultEULOTLRefreshInterval.
type InMemoryReceiptStore ¶
type InMemoryReceiptStore struct {
// contains filtered or unexported fields
}
InMemoryReceiptStore is a simple in-memory receipt store for testing.
func NewInMemoryReceiptStore ¶
func NewInMemoryReceiptStore() *InMemoryReceiptStore
NewInMemoryReceiptStore creates a new in-memory receipt store.
func (*InMemoryReceiptStore) GetLatestReceipt ¶
func (s *InMemoryReceiptStore) GetLatestReceipt(_ context.Context) (*AnchorReceipt, error)
func (*InMemoryReceiptStore) GetReceiptByLamportRange ¶
func (s *InMemoryReceiptStore) GetReceiptByLamportRange(_ context.Context, from, to uint64) ([]*AnchorReceipt, error)
func (*InMemoryReceiptStore) StoreReceipt ¶
func (s *InMemoryReceiptStore) StoreReceipt(_ context.Context, receipt *AnchorReceipt) error
type LogInclusionProof ¶
type LogInclusionProof struct {
TreeSize int64 `json:"tree_size"`
RootHash string `json:"root_hash"`
LogIndex int64 `json:"log_index"`
Hashes []string `json:"hashes"`
Checkpoint string `json:"checkpoint,omitempty"`
}
LogInclusionProof proves that the anchored entry exists in the transparency log's Merkle tree.
type RFC3161Backend ¶
type RFC3161Backend struct {
// contains filtered or unexported fields
}
RFC3161Backend anchors ProofGraph Merkle roots via RFC 3161 timestamping authorities. This provides a standards-based fallback when Sigstore Rekor is unavailable.
func NewRFC3161Backend ¶
func NewRFC3161Backend(tsaURL string, opts ...RFC3161Option) *RFC3161Backend
NewRFC3161Backend creates a new RFC 3161 timestamping backend.
func (*RFC3161Backend) Anchor ¶
func (r *RFC3161Backend) Anchor(ctx context.Context, req AnchorRequest) (*AnchorReceipt, error)
Anchor submits the Merkle root hash to an RFC 3161 TSA.
func (*RFC3161Backend) Verify ¶
func (r *RFC3161Backend) Verify(_ context.Context, receipt *AnchorReceipt) error
Verify checks the RFC 3161 timestamp token. Full verification requires parsing the ASN.1 TimeStampResp and validating the TSA's certificate chain. This is a structural check.
type RFC3161Option ¶
type RFC3161Option func(*RFC3161Backend)
RFC3161Option configures the RFC 3161 backend.
func WithRFC3161HTTPClient ¶
func WithRFC3161HTTPClient(client *http.Client) RFC3161Option
WithRFC3161HTTPClient sets a custom HTTP client.
type ReceiptStore ¶
type ReceiptStore interface {
StoreReceipt(ctx context.Context, receipt *AnchorReceipt) error
GetLatestReceipt(ctx context.Context) (*AnchorReceipt, error)
GetReceiptByLamportRange(ctx context.Context, from, to uint64) ([]*AnchorReceipt, error)
}
ReceiptStore persists anchor receipts.
type RekorBackend ¶
type RekorBackend struct {
// contains filtered or unexported fields
}
RekorBackend anchors ProofGraph Merkle roots to Sigstore Rekor v2. Rekor v2 (GA Oct 2025) uses a tile-backed transparency log with sigstore-go v1.0.
func NewRekorBackend ¶
func NewRekorBackend(opts ...RekorOption) *RekorBackend
NewRekorBackend creates a new Rekor v2 anchoring backend.
func (*RekorBackend) Anchor ¶
func (r *RekorBackend) Anchor(ctx context.Context, req AnchorRequest) (*AnchorReceipt, error)
Anchor submits the ProofGraph Merkle root to Rekor as a hashedrekord entry.
func (*RekorBackend) Verify ¶
func (r *RekorBackend) Verify(ctx context.Context, receipt *AnchorReceipt) error
Verify checks the Rekor receipt against the transparency log.
type RekorOption ¶
type RekorOption func(*RekorBackend)
RekorOption configures the Rekor backend.
func WithHTTPClient ¶
func WithHTTPClient(client *http.Client) RekorOption
WithHTTPClient sets a custom HTTP client (for mTLS, timeouts, etc.)
func WithRekorURL ¶
func WithRekorURL(url string) RekorOption
WithRekorURL sets a custom Rekor URL (for staging/private instances).
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service orchestrates periodic anchoring of the ProofGraph to transparency logs. It maintains anchor state and supports multiple backends with failover.
func NewService ¶
func NewService(cfg ServiceConfig) (*Service, error)
NewService creates a new anchoring service.
func (*Service) AnchorNow ¶
func (s *Service) AnchorNow(ctx context.Context, req AnchorRequest) (*AnchorReceipt, error)
AnchorNow triggers an immediate anchoring cycle. It tries each backend in order until one succeeds (failover).
func (*Service) LastAnchoredLamport ¶
LastAnchoredLamport returns the Lamport clock value of the last successful anchor.
func (*Service) VerifyReceipt ¶
func (s *Service) VerifyReceipt(ctx context.Context, receipt *AnchorReceipt) error
VerifyReceipt verifies an anchor receipt against its transparency log.
type ServiceConfig ¶
type ServiceConfig struct {
// Backends are the transparency log backends, tried in priority order.
Backends []AnchorBackend
// Store persists anchor receipts.
Store ReceiptStore
// Interval between automatic anchoring cycles. Default: 5 minutes.
Interval time.Duration
// Logger for structured logging.
Logger *slog.Logger
}
ServiceConfig configures the anchoring service.