Skip to main content

lib/mix/tasks/stevedore.sign.ex

defmodule Mix.Tasks.Stevedore.Sign do
  @shortdoc "Sign an image and attach the signature as a referrer"

  @moduledoc """
  Sign an image with an ECDSA private key (PEM) and attach the cosign-compatible signature to it
  via the Referrers API.

      mix stevedore.sign REF --key path/to/key.pem [--scheme http|https]
  """

  use Mix.Task

  alias Stevedore.{CLI, Referrers, Sign}
  alias Stevedore.Transport.Parse

  @switches [key: :string, scheme: :string]

  @impl true
  def run(argv) do
    {opts, args, _} = OptionParser.parse(argv, switches: @switches)

    with [ref] <- args, key_path when is_binary(key_path) <- opts[:key] do
      CLI.start_app()
      key = %{private: File.read!(key_path)}
      {transport, tref} = CLI.unwrap!(Parse.parse(ref, Keyword.take(opts, [:scheme])))
      fetched = CLI.unwrap!(Stevedore.Transport.get_manifest(transport, tref))

      signature =
        CLI.unwrap!(Sign.sigstore(fetched.digest, key, subject_size: byte_size(fetched.raw)))

      digest = CLI.unwrap!(Referrers.attach(transport, fetched.digest, signature))
      Mix.shell().info("attached signature #{digest}")
    else
      _ -> Mix.raise("usage: mix stevedore.sign REF --key KEY.pem")
    end
  end
end