ocibuild_sign (ocibuild v0.10.4)

View Source

Cosign-compatible image signing using ECDSA P-256.

Implements the cosign simplesigning v1 format for container image signatures. Signatures are pushed as OCI artifacts via the referrers API.

Key Format

Supports PEM-encoded ECDSA P-256 private keys (cosign default):

# Generate with cosign
cosign generate-key-pair

# Or with openssl
openssl ecparam -genkey -name prime256v1 -noout -out cosign.key

Usage

{ok, PrivateKey} = ocibuild_sign:load_key("/path/to/cosign.key"),
{ok, PayloadJson, Signature} = ocibuild_sign:sign(ManifestDigest, DockerRef, PrivateKey).

Verification

After pushing the signature, verify with cosign:

cosign verify --key cosign.pub ghcr.io/myorg/myapp:latest

Summary

Functions

Get the artifact type for cosign signatures.

Build the cosign simplesigning payload.

Build a cosign signature manifest for tag-based discovery.

Load an ECDSA P-256 private key from a PEM file.

Sign a manifest digest and return the payload and signature.

Types

private_key()

-type private_key() ::
          #'ECPrivateKey'{version :: term(),
                          privateKey :: term(),
                          parameters :: term(),
                          publicKey :: term(),
                          attributes :: term()}.

sign_payload()

-type sign_payload() :: #{binary() => #{binary() => binary() | #{binary() => binary()}}}.

Functions

artifact_type()

-spec artifact_type() -> binary().

Get the artifact type for cosign signatures.

Returns the cosign simplesigning v1 media type used as the artifact type in OCI referrer manifests.

build_payload(ManifestDigest, DockerRef)

-spec build_payload(ManifestDigest :: binary(), DockerRef :: binary()) -> sign_payload().

Build the cosign simplesigning payload.

Creates the JSON payload that will be signed. The payload contains:

  • critical.identity.docker-reference: The full image reference
  • critical.image.docker-manifest-digest: The manifest digest being signed
  • critical.type: Always "cosign container image signature"
  • optional: Empty map (reserved for future extensions)

Example:

Payload = ocibuild_sign:build_payload(
    ~"sha256:abc123...",
    ~"ghcr.io/myorg/myapp:v1"
).

build_referrer_manifest(PayloadDigest, PayloadSize, Signature, SubjectDigest, SubjectSize)

-spec build_referrer_manifest(PayloadDigest :: binary(),
                              PayloadSize :: non_neg_integer(),
                              Signature :: binary(),
                              SubjectDigest :: binary(),
                              SubjectSize :: non_neg_integer()) ->
                                 map().

Build an OCI referrer manifest for a cosign signature.

Creates a manifest following the OCI referrers API specification that attaches a signature to a subject image manifest. The manifest structure follows the cosign simplesigning format:

  • Config blob contains the simplesigning payload
  • Layer references the same payload with signature in annotation
  • Subject points to the signed image manifest

Parameters:

  • PayloadDigest: SHA256 digest of the payload JSON
  • PayloadSize: Size of the payload JSON in bytes
  • Signature: The DER-encoded ECDSA signature
  • SubjectDigest: SHA256 digest of the image manifest being signed
  • SubjectSize: Size of the image manifest in bytes

Example:

Manifest = ocibuild_sign:build_referrer_manifest(
    PayloadDigest, PayloadSize, Signature, SubjectDigest, SubjectSize
).

build_signature_manifest(PayloadDigest, PayloadSize, Signature)

-spec build_signature_manifest(PayloadDigest :: binary(),
                               PayloadSize :: non_neg_integer(),
                               Signature :: binary()) ->
                                  map().

Build a cosign signature manifest for tag-based discovery.

Creates a manifest that cosign will find at the signature tag (sha256-<digest>.sig). This is the default discovery mechanism used by cosign verify.

The manifest structure follows the cosign simplesigning format:

  • Config blob contains the simplesigning payload
  • Layer references the same payload with signature in annotation

Note: Unlike build_referrer_manifest/5, this does NOT include a subject field since tag-based discovery doesn't use the OCI referrers API.

Parameters:

  • PayloadDigest: SHA256 digest of the payload JSON
  • PayloadSize: Size of the payload JSON in bytes
  • Signature: The DER-encoded ECDSA signature

Example:

Manifest = ocibuild_sign:build_signature_manifest(PayloadDigest, PayloadSize, Signature).

load_key(Path)

-spec load_key(Path :: file:filename()) -> {ok, private_key()} | {error, term()}.

Load an ECDSA P-256 private key from a PEM file.

Supports PEM-encoded EC private keys as generated by cosign or openssl. The key must use the P-256 (secp256r1/prime256v1) curve.

Returns {ok, PrivateKey} on success, or {error, Reason} on failure.

Example:

{ok, Key} = ocibuild_sign:load_key("cosign.key").

sign(ManifestDigest, DockerRef, PrivateKey)

-spec sign(ManifestDigest :: binary(), DockerRef :: binary(), PrivateKey :: private_key()) ->
              {ok, PayloadJson :: binary(), Signature :: binary()}.

Sign a manifest digest and return the payload and signature.

Builds the simplesigning payload, encodes it as JSON, and signs it with the ECDSA P-256 private key using SHA-256.

Returns {ok, PayloadJson, Signature} where:

  • PayloadJson is the JSON-encoded payload (binary)
  • Signature is the DER-encoded ECDSA signature (binary)

Example:

{ok, Key} = ocibuild_sign:load_key("cosign.key"),
{ok, PayloadJson, Signature} = ocibuild_sign:sign(
    ~"sha256:abc123...",
    ~"ghcr.io/myorg/myapp:v1",
    Key
).