Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1c9e785
add new how-tos and concept
morrison-sap Feb 24, 2026
a3b0fb1
adopt existing guides to how-to design and move to different folder
morrison-sap Feb 24, 2026
001e4d2
rework tutorial in diataxis format
morrison-sap Feb 24, 2026
f19bed5
add guide to configure crendentials for OCM controllers
morrison-sap Feb 25, 2026
70a82af
correct linter issues
morrison-sap Feb 25, 2026
b5a98ef
correct linter issues
morrison-sap Feb 25, 2026
8f92710
correct linter issues
morrison-sap Feb 25, 2026
4721578
correct spellcheck issues
morrison-sap Feb 26, 2026
b20b464
correct linter issues
morrison-sap Feb 26, 2026
fb37a0d
merge from upstream/main
morrison-sap Mar 9, 2026
51f5b25
adopt better to diataxis: move best practices from concept to tutoria…
morrison-sap Mar 9, 2026
7b303e6
rebase
morrison-sap Mar 9, 2026
1d5870b
correct broken links
morrison-sap Mar 9, 2026
3e276de
correct broken links
morrison-sap Mar 9, 2026
88d688c
update hugo
morrison-sap Mar 9, 2026
8bd0cf1
correct linter issues
morrison-sap Mar 9, 2026
08b3fed
correct linter issues
morrison-sap Mar 9, 2026
a0cd980
Merge branch 'main' into create-new-security-docs
matthiasbruns Mar 10, 2026
863a944
Merge remote-tracking branch 'upstream/main' into create-new-security…
morrison-sap Mar 11, 2026
4ef9aae
correct order of docs, now starting with weight=1 gor the first doc o…
morrison-sap Mar 11, 2026
0df5847
Merge branch 'create-new-security-docs' of github.com:morri-son/ocm-w…
morrison-sap Mar 11, 2026
294479d
restore key pair creation
morrison-sap Mar 11, 2026
a6bcfdc
add callout for sigstore support and change version for jsonNormalisa…
morrison-sap Mar 11, 2026
b80de4d
change order
morrison-sap Mar 11, 2026
0b28a10
Merge remote-tracking branch 'upstream/main' into create-new-security…
morrison-sap Mar 12, 2026
1ae9c35
remove hugo update from this PR
morrison-sap Mar 12, 2026
c2697e6
remove hugo update from this PR
morrison-sap Mar 12, 2026
494ce80
replace `~` with $HOME everwhere. Use example component from getting …
morrison-sap Mar 12, 2026
62d2ad2
correct docs and integrate feedback from reviewers.
morrison-sap Mar 12, 2026
0aca291
move tutorial to correct ocm cli v2 commands and test.
morrison-sap Mar 12, 2026
5b1ff09
add back link to json normalization in the ocm spec.
morrison-sap Mar 12, 2026
ae43a39
add reference for consumer identities and link it from the other docs.
morrison-sap Mar 13, 2026
465cd05
update with decriptor
morrison-sap Mar 13, 2026
9221dd9
add signatureEncodingPolicy section and explain Plain vs. PEM
morrison-sap Mar 13, 2026
51ca1eb
Apply suggestions from code review
morri-son Mar 13, 2026
27c7c6f
hide large decriptor example behind {{< details >}} section
morrison-sap Mar 13, 2026
b5b2ca6
Merge branch 'main' into create-new-security-docs
morri-son Mar 13, 2026
89b8d0c
Merge remote-tracking branch 'upstream/main' into create-new-security…
morrison-sap Mar 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions .github/config/wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -459,11 +459,20 @@ diataxis
callouts
shortcode
shortcodes
href
dockerconfigjson
rhel
misattributed
whitespace
hsms
helloworld
centos
wsl
auditable
hasmermaid
XDG
xdg
subpaths
IAM
iam
ocirepository
pullable
descriptor's
2 changes: 1 addition & 1 deletion content/docs/concepts/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title : "OCM Components"
description: "Learn about OCM components and their elements."
icon: "🔩"
weight: 41
weight: 1
toc: true
---

Expand Down
2 changes: 1 addition & 1 deletion content/docs/concepts/coordinates.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title : "OCM Coordinates"
description: "Discover how OCM components and artifacts are identified."
icon: "🧭"
weight: 42
weight: 2
toc: true
---

Expand Down
8 changes: 6 additions & 2 deletions content/docs/concepts/credential-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: "Credential System"
description: "Why OCM manages credentials centrally and how its resolution model works."
icon: "🔑"
weight: 43
weight: 6
toc: true
---

Expand Down Expand Up @@ -77,7 +77,7 @@ flowchart TB

subgraph repos ["Repositories (fallback)"]
direction TB
docker["DockerConfig/v1<br>~/.docker/config.json"]
docker["DockerConfig/v1<br>$HOME/.docker/config.json"]
end

```
Expand All @@ -91,3 +91,7 @@ To see resolution in action, try the [Understand Credential Resolution]({{< relr
- [Tutorial: Credential Resolution]({{< relref "/docs/tutorials/credential-resolution.md" >}}) — Learn how OCM picks the right credentials by experimenting with a config
- [How-To: Configure Credentials for Multiple Registries]({{< relref "/docs/how-to/configure-multiple-credentials.md" >}}) — Quick task-oriented setup
- [Tutorial: Credentials for OCM Controllers]({{< relref "/docs/tutorials/configure-credentials-for-controllers.md" >}}) — How to provide credentials in Kubernetes environments

## Related Documentation

- [Reference: Consumer Identities]({{< relref "/docs/reference/credential-consumer-identities.md" >}}) — Complete list of identity types, attributes, and credential properties
2 changes: 1 addition & 1 deletion content/docs/concepts/ocm-controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: OCM Controllers
description: "Learn about the OCM controllers and their capabilities."
icon: "🏁"
weight: 43
weight: 3
toc: true
---

Expand Down
2 changes: 1 addition & 1 deletion content/docs/concepts/resolvers.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: "Resolvers"
description: "Learn how OCM resolvers map component name patterns to repositories for recursive resolution."
icon: "🔍"
weight: 44
weight: 7
toc: true
---

Expand Down
261 changes: 261 additions & 0 deletions content/docs/concepts/signing-and-verification-concept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
---
title: "Signing and Verification"
description: "Understanding how OCM ensures component integrity and authenticity through cryptographic signatures."
weight: 5
toc: true
---

OCM uses cryptographic signatures to guarantee that component versions are authentic (created by a trusted party) and have not been tampered with during storage or transfer.

## Why Sign Components?

Software supply chains involve multiple stages: development, build, packaging, distribution, and deployment. At each stage, components could potentially be:

- **Modified** — malicious actors could inject code or alter resources
- **Replaced** — components could be swapped for compromised versions
- **Misattributed** — components could falsely claim to come from a trusted source

Signing addresses these risks by creating a cryptographic proof of:

1. **Integrity**: The component has not changed since it was signed
2. **Authenticity**: The signature was created by someone with access to the private key
3. **Provenance**: The signer cannot deny having signed the component

## How OCM Signing Works

```mermaid
flowchart TB
subgraph sign ["Sign (Producer)"]
direction TB
A[Component Version] --> B[Normalize & Hash]
B --> C[Sign with Private Key]
C --> D["Signature embedded in CV"]
end

sign --> T["Transfer Component Version"]

T --> verify

subgraph verify ["Verify (Consumer)"]
direction TB
E[Component Version] --> F[Extract Signature]
E --> G[Normalize & Hash]
F --> H[Verify with Public Key]
G --> H
H --> I{Valid?}
I -->|Yes| VALID["✓ Trusted"]
I -->|No| INVALID["✗ Rejected"]
end
```

### Normalization and Digest Calculation

OCM uses a two-layer approach to ensure consistent and reproducible digests:

#### Component Descriptor Normalization

Before hashing, the component descriptor is normalized into a canonical form, eliminating any ambiguities
that could cause the same logical descriptor to produce different digests. The default normalization
algorithm ([`jsonNormalisation/v4alpha1`](https://github.com/open-component-model/ocm-spec/blob/main/doc/04-extensions/04-algorithms/component-descriptor-normalization-algorithms.md#normalization-algorithms)) defines exactly how this canonical form is derived, ensuring
identical component descriptors always yield the same digest.

#### Artifact Digest Normalization

Each artifact's digest is calculated using a type-specific normalization algorithm:

| Artifact Type | Algorithm | Description |
|---------------|-----------|-------------|
| OCI artifact | `ociArtifactDigest/v1` | Digest of the OCI manifest (used for container images, Helm charts, and other OCI-native content) |
| Generic blob | `genericBlobDigest/v1` | Direct hash of blob content (used for executables, blueprints, and other non-OCI content) |

This allows OCM to use the most appropriate digest mechanism for each artifact type.
OCI artifacts use their manifest digest rather than re-hashing the blob,
improving performance and ensuring consistency with OCI registry behavior. Generic blobs are hashed directly.

#### Recursive Component References

When a component references other components, their digests are calculated recursively and embedded:

```yaml
references:
- componentName: ocm.software/helper
name: helper
version: 1.0.0
digest:
hashAlgorithm: SHA-256
normalisationAlgorithm: jsonNormalisation/v4alpha1
value: 01c211f5c9cfd7c40e5b84d66a2fb7d19cb0...
```

This creates a **complete integrity chain** — verifying the root component automatically verifies all transitive dependencies.

### What Gets Signed?

OCM signs a **digest** of the component descriptor, which includes:

- Component metadata (name, version, provider)
- Resource declarations with their digests
- Source references
- Component references

The signature does **not** cover the raw resource content directly — instead, it covers the **digests** of those resources as recorded in the component descriptor. Crucially, the `access` field (which describes *where* a resource is stored) is **excluded** from the signed digest by the normalization process. This is a key design principle:

- **Location-independent integrity** — a component version can be transferred to a different registry (changing all `access` references) without invalidating its signature. The digest remains stable because it depends only on *what* the artifacts contain, not *where* they are stored.
- Any change to resource content changes its digest, invalidating the signature.
- Signature verification is fast (no need to re-hash large binaries).

This separation of content identity from storage location is what enables secure delivery across environments: a producer signs a component version once, and consumers can verify it after any number of transfers — even into air-gapped environments with completely different registries.

The following example shows a signed component descriptor. Notice that each resource has both an `access` field (storage location) and a `digest` field (content hash). Only the `digest` is included in the signature — the `access` can change freely during transfers:

{{< details "Example Signed Component Descriptor" >}}
```yaml
component:
name: github.com/acme.org/helloworld
version: 1.0.0
provider: acme.org
resources:
- name: mylocalfile
type: blob
version: 1.0.0
relation: local
access: # NOT included in signature
type: localBlob
localReference: sha256:70a257...
mediaType: text/plain; charset=utf-8
digest: # Included in signature
hashAlgorithm: SHA-256
normalisationAlgorithm: genericBlobDigest/v1
value: 70a2577d7b649574cbbba99a2f2ebdf27904a4abf80c9729923ee67ea8d2d9d8
- name: image
type: ociImage
version: 1.0.0
relation: external
access: # NOT included in signature
type: ociArtifact
imageReference: ghcr.io/stefanprodan/podinfo:6.9.1@sha256:262578cd...
digest: # Included in signature
hashAlgorithm: SHA-256
normalisationAlgorithm: genericBlobDigest/v1
value: 262578cde928d5c9eba3bce079976444f624c13ed0afb741d90d5423877496cb
signatures:
- name: default
digest:
hashAlgorithm: SHA-256
normalisationAlgorithm: jsonNormalisation/v4alpha1
value: 91dd197868907487e62872695db1fa7b397fde300bcbae23e24abc188fb147ad
signature:
algorithm: RSASSA-PSS
mediaType: application/vnd.ocm.signature.rsa.pss
value: 7feb449229c6ffe368144995432befd1505d2d29...
```
{{< /details >}}

### Signature Storage

Signatures are stored as part of the component version:

```yaml
signatures:
- name: acme-release-signing
digest:
hashAlgorithm: SHA-256
normalisationAlgorithm: jsonNormalisation/v4alpha1
value: abc123...
signature:
algorithm: RSASSA-PSS
mediaType: application/vnd.ocm.signature.rsa
value: <base64-encoded-signature>
```

A component version can have **multiple signatures** from different parties, enabling:

- Separation of build and release signing
- Multiple approval workflows
- Cross-organizational trust chains

## Supported Signing Algorithms

OCM currently only supports RSA-based signing algorithms:

| Algorithm | Type | Characteristics |
|----------------------|------|-----------------|
| RSASSA-PSS (default) | Asymmetric | Probabilistic, stronger security guarantees, recommended for new implementations |
| RSA-PKCS#1 v1.5 | Asymmetric | Deterministic, widely supported, compatible with legacy systems |

To override the default signing algorithm or encoding policy, see the --signer-spec flag in the [CLI reference]({{< relref "/docs/reference/ocm-cli/ocm_sign_component-version.md" >}}).
The signer spec file configures only the algorithm and encoding policy — credentials are always resolved separately via the [`.ocmconfig`]({{< relref "configure-multiple-credentials.md" >}}) file.

For key management, OCM uses PEM-encoded key files configured in the `.ocmconfig`:

- **Private keys**: Used by producers to sign component versions
- **Public keys**: Distributed to consumers for verification

See [How-to: Generate Signing Keys]({{< relref "docs/how-to/generate-signing-keys.md" >}}) for creating RSA key pairs.

{{< callout context="tip" title="Upcoming Sigstore Support" icon="outline/bulb" >}}
We are planning to add support for [Sigstore](https://www.sigstore.dev/) and Cosign as an additional signing mechanism.
This will enable keyless signing workflows and improved supply chain security. Stay tuned for updates.
{{< /callout >}}

### Signature Encoding Policies

The `signatureEncodingPolicy` in the [signer spec]({{< relref "/docs/reference/ocm-cli/ocm_sign_component-version.md" >}}) controls how the **signature output** is serialized and stored. It does **not** affect the format of key input files, which are always PEM-encoded.

| Policy | Signature Format | Media Type | Certificate Chain | Verification Requires |
|--------|-----------------|------------|-------------------|-----------------------|
| **Plain** (default) | Hex-encoded raw bytes | `application/vnd.ocm.signature.rsa.pss` | Not embedded | Externally supplied public key |
| **PEM** (experimental) | PEM `SIGNATURE` block + `CERTIFICATE` blocks | `application/x-pem-file` | Embedded in signature | Valid certificate chain in signature |

#### Plain Encoding (Default)

The raw RSA signature bytes are hex-encoded and stored directly. This is the most compact representation. Verification always requires the public key to be provided separately via `.ocmconfig` credentials.

Example signature in a component descriptor:

```yaml
signature:
algorithm: RSASSA-PSS
mediaType: application/vnd.ocm.signature.rsa.pss
value: d1ea6e0cd850c8dbd0d20cd39b9c7954...
```

#### PEM Encoding (Experimental)

The signature is wrapped in a PEM block of type `SIGNATURE`, optionally followed by the signer's X.509 certificate chain. This makes the signature **self-contained**: verifiers can extract and validate the public key from the embedded chain without needing a separately distributed key.

Example of a PEM-encoded signature value:

```text
-----BEGIN SIGNATURE-----
Signature Algorithm: RSASSA-PSS
<base64-encoded signature bytes>
-----END SIGNATURE-----
-----BEGIN CERTIFICATE-----
<leaf certificate>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<intermediate CA>
-----END CERTIFICATE-----
```

{{< callout context="caution" title="PEM encoding is experimental" icon="outline/alert-triangle" >}}
This encoding policy may change or be deprecated in future versions. For production use, prefer the default Plain encoding.
{{< /callout >}}

{{< callout context="note" title="Key files vs. signature encoding" icon="outline/info-circle" >}}
A common source of confusion: "PEM" in `signatureEncodingPolicy` refers to the **signature output** format, not the key input format. Input keys are **always** PEM-encoded files (e.g. `-----BEGIN RSA PRIVATE KEY-----`), regardless of which encoding policy is selected.

When using PEM encoding for signing, the credential referenced by `public_key_pem` / `public_key_pem_file` must contain **X.509 certificates** (not bare public keys), because the certificate chain is embedded into the signature for self-contained verification.
{{< /callout >}}

## Next Steps

- [How-to: Generate Signing Keys]({{< relref "generate-signing-keys.md" >}}) - Step-by-step creating RSA key pairs.
- [How-to: Configure Signing Credentials]({{< relref "configure-signing-credentials.md" >}}) - Set up OCM to use your keys for signing and verification
- [How-to: Sign a Component Version]({{< relref "sign-component-version.md" >}}) - Step-by-step signing instructions
- [How-to: Verify a Component Version]({{< relref "verify-component-version.md" >}}) - Step-by-step verification instructions

## Related Documentation

- [Concept: Component Versions]({{< relref "components.md" >}}) - Understanding component structure
2 changes: 1 addition & 1 deletion content/docs/concepts/transfer-concept.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Transfer and Transport"
description: "Understand how OCM moves component versions between repositories while preserving identity, integrity, and signatures."
weight: 44
weight: 4
toc: true
hasMermaid: true
---
Expand Down
8 changes: 3 additions & 5 deletions content/docs/how-to/air-gap-transfer.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
---
title: "Transfer Components Across an Air Gap"
title: "Transfer Components across an Air Gap"
description: "Transfer a signed OCM component version into an air-gapped registry via a CTF archive."
weight: 10
weight: 1
toc: true
---

## Goal

Transfer a signed component version from a source registry into an air-gapped target registry using a CTF archive as the transport medium. An air-gapped environment is a network that is physically isolated from untrusted networks such as the public internet.

{{< callout context="note" title="You will end up with" >}}
## You'll end up with

- A verified, signed component version available in your air-gapped registry
- All resource artifacts (container images, Helm charts) copied into the target registry

{{< /callout >}}

**Estimated time:** ~10 minutes

## Prerequisites
Expand Down
Loading
Loading