Skip to content

Latest commit

 

History

History

spec

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

Attestation Spec

An in-toto attestation is authenticated metadata about one or more software artifacts, as per the SLSA Attestation Model. It has three layers that are independent but designed to work together:

  • Envelope: Handles authentication and serialization.
  • Statement: Binds the attestation to a particular subject and unambiguously identifies the types of the predicate.
  • Predicate: Contains arbitrary metadata about the subject, with a type-specific schema. This repo defines the following predicate types, though custom types are allowed:
    • Provenance: To describe the origins of a software artifact.
    • Link: For migration from in-toto 0.9.
    • SPDX: A Software Package Data Exchange document.

The processing model provides pseudocode showing how these layers fit together.

See the top-level README for background and examples.

Parsing rules

The following rules apply to Statement, Provenance, and other predicates that opt-in to this model.

  • Unrecognized fields: Consumers MUST ignore unrecognized fields. This is to allow minor version upgrades and extension fields. Ignoring fields is safe due to the monotonic principle.

  • Versioning: Each type has a SemVer2 version number and the TypeURI reflects the major version number. A message is always semantically correct, but possibly incomplete, when parsed as any other version with the same major version number and thus the same TypeURI. Minor version changes always follow the monotonic principle. NOTE: 0.X versions are considered major versions.

  • Extension fields: Producers MAY add extension fields to any JSON object by using a property name that is a TypeURI. The use of URI is to protect against name collisions. Consumers MAY parse and use these extensions if desired. The presence or absence of the extension field MUST NOT influence the meaning of any other field, and the field MUST follow the monotonic princple.

  • Monotonic: A policy is considered monotonic if ignoring an attestation, or a field within an attestation, will never turn a DENY decision into an ALLOW. A predicate or field follows the monotonic princple if the expected policy that consumes it is monotonic. Consumers SHOULD design policies to be monotonic. Example: instead of "deny if a 'has vulnerabilities' attestation exists", prefer "deny unless a 'no vulnerabilities' attestation exists".

See versioning rules for details and examples.

Envelope

{
  "payloadType": "application/vnd.in-toto+json",
  "payload": "<Base64(Statement)>",
  "signatures": [{"sig": "<Base64(Signature)>"}]
}

The Envelope is the outermost layer of the attestation, handling authentication and serialization. The format and protocol are defined in signing-spec and adopted by in-toto in ITE-5. It is a JSON object with the following fields:

payloadType string, required

Identifier for the encoding of the payload. Always application/vnd.in-toto+json, which indicates that it is a JSON object with a _type field indicating its schema.

payload string, required

Base64-encoded JSON Statement.

signatures array of objects, required

One or more signatures over payloadType and payload, as defined in signing-spec.

Statement

{
  "_type": "https://in-toto.io/Statement/v0.1",
  "subject": [
    {
      "name": "<NAME>",
      "digest": {"<ALGORITHM>": "<HEX_VALUE>"}
    },
    ...
  ],
  "predicateType": "<URI>",
  "predicate": { ... }
}

Version: 0.1.0 (see parsing rules)

The Statement is the middle layer of the attestation, binding it to a particular subject and unambiguously identifying the types of the predicate. It is a JSON object with the following fields:

_type string (TypeURI), required

Identifier for the schema of the Statement. Always https://in-toto.io/Statement/v0.1 for this version of the spec.

subject array of objects, required

Set of software artifacts that the attestation applies to. Each element represents a single software artifact.

IMPORTANT: Subject artifacts are matched purely by digest, regardless of content type. If this matters to you, please open a GitHub issue to discuss.

subject[*].digest object (DigestSet), required

Collection of cryptographic digests for the contents of this artifact.

Two DigestSets are considered matching if ANY of the fields match. The producer and consumer must agree on acceptable algorithms. If there are no overlapping algorithms, the subject is considered not matching.

subject[*].name string, required

Identifier to distinguish this artifact from others within the subject.

The semantics are up to the producer and consumer. Because consumers evaluate the name against a policy, the name SHOULD be stable between attestations. If the name is not meaningful, use "_". For example, a Provenance attestation might use the name to specify output filename, expecting the consumer to only considers entries with a particular name. Alternatively, a vulnerability scan attestation might use the name "_" because the results apply regardless of what the artifact is named.

MUST be non-empty and unique within subject.

predicateType string (TypeURI), required

URI identifying the type of the Predicate.

predicate object, optional

Additional parameters of the Predicate. Unset is treated the same as set-but-empty. MAY be omitted if predicateType fully describes the predicate.

Predicate

"predicateType": "<URI>",
"predicate": {
    // arbitrary object
}

The Predicate is the innermost layer of the attestation, containing arbitrary metadata about the Statement's subject.

A predicate has a requried predicateType (TypeURI) identifying what the predicate means, plus an optional predicate (object) containing additional, type-dependent parameters.

Users are expected to choose a predicate type that fits their needs, or invent a new one if no existing one satisfies. Predicate types are not registered.

Pre-defined predicates

This repo defines the following predicate types:

  • Provenance: To describe the origins of a software artifact.
  • Link: For migration from in-toto 0.9.
  • SPDX: A Software Package Data Exchange document.

Predicate conventions

We recommend the following conventions for predicates:

  • Predicates SHOULD follow and opt-in to the parsing rules, particularly the monotonic principle, and SHOULD explain what the parsing rules are.

  • Field names SHOULD use lowerCamelCase.

  • Timestamps SHOULD use RFC 3339 syntax with timezone "Z" and SHOULD clarify the meaning of the timestamp. For example, a field named timestamp is too ambiguous; a better name would be builtAt or allowedAt or scannedAt.

  • References to other artifacts SHOULD be an object that includes a digest field of type DigestSet. Consider using the same type as Provenance materials if it is a good fit.

Predicate designers are free to limit what subject types are valid for a given predicate type. For example, suppose a "Gerrit code review" predicate only applies to git commit subjects. In that case, a producer of such attestations should never use a subject other than a git commit.

Processing model

The following pseudocode shows how to verify and extract metadata about a single artifact from a single attestation. The expectation is that consumers will feed the resulting metadata into a policy engine.

TODO: Explain how to process multiple artifacts and/or multiple attestations.

Inputs:

  • artifactToVerify: blob of data
  • attestation: JSON-encoded Envelope
  • recognizedAttesters: collection of (name, publicKey) pairs
  • acceptableDigestAlgorithms: collection of acceptable cryptographic hash algorithms (usually just sha256)

Steps:

  • Envelope layer:
    • envelope := decode attestation as a JSON-encoded Envelope; reject if decoding fails
    • attesterNames := empty set of names
    • For each signature in envelope.signatures:
      • For each (name, publicKey) in recognizedAttesters:
        • Optional: skip if signature.keyid does not match publicKey
        • If signature.sig matches publicKey:
          • Add name to attesterNames
    • Reject if attesterNames is empty
  • Intermediate state: envelope.payloadType, envelope.payload, attesterNames
  • Statement layer:
    • Reject if envelope.payloadType != application/vnd.in-toto+json
    • statement := decode envelope.payload as a JSON-encoded Statement; reject if decoding fails
    • Reject if statement.type != https://in-toto.io/Statement/v0.1
    • artifactNames := empty set of names
    • For each s in statement.subject:
      • For each digest (alg, value) in s.digest:
        • If alg is in acceptableDigestAlgorithms:
          • If hash(alg, artifactToVerify) == hexDecode(value):
            • Add s.name to artifactNames
    • Reject if artifactNames is empty

Output (to be fed into policy engine):

  • statement.predicateType
  • statement.predicate
  • artifactNames
  • attesterNames