signing

package
v0.1.0-dev.20260815011518 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package signing signs and verifies the two signable op artifacts — graphs and execution traces — per the settled signing design (docs/plans/extract-starlark-from-op/phase-8/signing-options.md and graph-signing.md; phase-8 step 46).

The model is publisher identity with verifier-side trust: a raw ssh-ed25519 signature over the artifact's namespace-prefixed canonical bytes (NamespaceGraph / NamespaceTrace give domain separation), the publisher's key riding the document in OpenSSH wire format, and trust resolved against an OpenSSH `allowed_signers` file the verifier owns. No envelope, no hash options — the algorithm names the whole ciphersuite.

This package implements the DEFAULT custody tier: the developer's SSH key (`~/.ssh/id_ed25519`) with a generated local Ed25519 keyfile as the fallback. The ssh-agent, cloud-KMS, and keyless tiers are chartered opt-ins (the support matrix in signing-options.md) and are deliberately absent so their dependency weight stays out of the default build.

What a consumer does with a verification outcome is governed by Policy — the settled four-tier ladder — through one enforcement point, [Judge]. Like its prior art (PowerShell's ExecutionPolicy), the policy is a safety feature, not a security boundary.

Index

Constants

View Source
const (
	// NamespaceGraph is the domain-separation prefix signed ahead of a graph's canonical bytes.
	NamespaceGraph = "devlore.graph.v1"

	// NamespaceTrace is the domain-separation prefix signed ahead of a trace's canonical bytes.
	NamespaceTrace = "devlore.trace.v1"

	// AlgorithmEd25519 is the default ciphersuite — Ed25519 under its OpenSSH key-type name.
	AlgorithmEd25519 = "ssh-ed25519"
)

Variables

This section is empty.

Functions

func CanonicalDocument

func CanonicalDocument(data []byte) ([]byte, error)

CanonicalDocument returns a serialized document's canonical bytes: the generically-decoded document with its integrity fields (`checksum`, `signature`) removed, re-marshaled.

This is the verify-side dual of the live artifacts' CanonicalContent for document-form canonicalization (traces): decoding into the typed struct can be lossy (custom unmarshalers), so verification canonicalizes the bytes it was handed. yaml key ordering is stable, so the result matches the sign-time canonical.

Parameters:

  • `data`: the document bytes (YAML).

Returns:

  • `[]byte`: the canonical bytes.
  • `error`: non-nil when the document does not decode or re-marshal.

func External

func External(documentPath, storeHome string) bool

External reports whether a document path lies outside `storeHome` — the settled externality marker (a document under this machine's own store was produced by its own runs; anything else is external).

Parameters:

  • `documentPath`: the document's path as given.
  • `storeHome`: the machine's store root (the devlore state home).

Returns:

  • `bool`: true when the document is external to the store.

Types

type Outcome

type Outcome int

Outcome classifies one artifact's verification result.

const (
	// OutcomeValid means the signature verifies and the publisher key resolved to a trusted principal.
	OutcomeValid Outcome = iota

	// OutcomeUnsigned means the artifact carries no signature — a finding of absence, not a failure.
	OutcomeUnsigned

	// OutcomeInvalid means a signature is present but does not verify over the canonical bytes (the artifact
	// was altered after signing, or the signature is malformed).
	OutcomeInvalid

	// OutcomeUntrusted means the signature verifies but the publisher key resolved to no trusted principal in
	// the verifier's `allowed_signers`.
	OutcomeUntrusted
)

func (Outcome) String

func (o Outcome) String() string

String returns the outcome's lowercase label.

Returns:

  • `string`: "valid", "unsigned", "invalid", or "untrusted".

type Policy

type Policy int

Policy governs what a consumer does with a verification Verdict — the settled four-tier ladder (phase-8 step 46, question 4; prior art: PowerShell ExecutionPolicy, pacman SigLevel, Kubernetes admission enforce/warn/audit).

Like its prior art, the policy is a safety feature, not a security boundary: it prevents accidents and surfaces facts; it does not stop a local adversary who can change the policy.

const (
	// PolicyReport verifies and reports findings — both unsigned (absence) and invalid/untrusted (failure) —
	// but refuses nothing. The floor: unsigned stores keep working while signature state becomes visible.
	PolicyReport Policy = iota

	// PolicyIgnore performs no verification at all.
	PolicyIgnore

	// PolicyRejectExternal rejects unsigned, invalid, or untrusted documents from OUTSIDE this machine's own
	// store; own-store documents behave as [PolicyReport]. The store boundary is the externality marker.
	PolicyRejectExternal

	// PolicyReject rejects every document that is not valid — unsigned, invalid, or untrusted alike.
	PolicyReject
)

func ParsePolicy

func ParsePolicy(value string) (Policy, error)

ParsePolicy parses a config/flag policy value.

Parameters:

  • `value`: "ignore", "report", "reject_external", or "reject"; "" parses as the PolicyReport floor.

Returns:

  • `Policy`: the parsed policy.
  • `error`: non-nil for any other value.

func (Policy) Judge

func (p Policy) Judge(verdict Verdict, external bool) error

Judge applies the policy to a verdict: the one enforcement point every consumer shares.

Parameters:

  • `verdict`: the artifact's verification result.
  • `external`: whether the document came from outside this machine's own store (see External).

Returns:

  • `error`: the rejection when the policy refuses this verdict; nil to proceed (the caller reports the verdict regardless — except under PolicyIgnore, where there is nothing to report).

func (Policy) String

func (p Policy) String() string

String returns the policy's config/flag value (snake_case per the config convention).

Returns:

  • `string`: "ignore", "report", "reject_external", or "reject".

type Signer

type Signer interface {

	// Sign produces the signature over `namespace ‖ canonical`.
	//
	// Parameters:
	//   - `namespace`: the artifact-kind domain separator ([NamespaceGraph] or [NamespaceTrace]).
	//   - `canonical`: the artifact's canonical bytes.
	//
	// Returns:
	//   - `*op.Signature`: the algorithm, the publisher key (OpenSSH wire format), and the raw signature.
	//   - `error`: non-nil when signing fails.
	Sign(namespace string, canonical []byte) (*op.Signature, error)
}

Signer signs canonical artifact bytes under a namespace, producing the document's op.Signature.

func DefaultSigner

func DefaultSigner() (Signer, error)

DefaultSigner resolves the default-tier signer: the developer's SSH key, else the generated local key.

Resolution order (signing-options.md): `~/.ssh/id_ed25519` when present and parseable without a passphrase; otherwise the generated local Ed25519 keyfile under the user config directory (`<config>/devlore/signing/ed25519`, created on first use with its `.pub` in authorized_keys format for `allowed_signers` seeding). The generated path honors `XDG_CONFIG_HOME` on every platform (the devlore XDG convention).

Returns:

  • `Signer`: the resolved signer.
  • `error`: non-nil when no key can be loaded or generated.

type Verdict

type Verdict struct {

	// Outcome is the classification.
	Outcome Outcome

	// Principal is the trusted identity the publisher key resolved to; "" unless the outcome is valid.
	Principal string

	// Detail elaborates non-valid outcomes for human readers.
	Detail string
}

Verdict is one artifact's verification result: the outcome, the trusted principal when valid, and the human-readable detail otherwise.

func Verify

func Verify(signature *op.Signature, namespace string, canonical []byte, allowedSignersPath string) Verdict

Verify checks an artifact's signature over its namespace-prefixed canonical bytes and resolves the publisher against the trust list.

Parameters:

  • `signature`: the artifact's signature, or nil for unsigned.
  • `namespace`: the artifact-kind domain separator (NamespaceGraph or NamespaceTrace).
  • `canonical`: the artifact's canonical bytes.
  • `allowedSignersPath`: the verifier's trust list; "" resolves to the default (`<config>/devlore/allowed_signers`).

Returns:

  • `Verdict`: the classification, with the trusted principal on validity.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL