Package signing

Warning

This feature is experimental and subject to breaking changes. See the Conan stability section for more information.

This plugin is a Conan extension mechanism to sign and verify packages. For example using Cosign (Sigstore), OpenSSL, GPG, etc.

This capability allows users to:

  • Sign and verify packages stored in the local cache.

  • Verify packages when installed.

  • Upload signatures alongside packages to a remote server.

Configuration

To enable package signing, the plugin should be placed in the cache at extensions/plugins/sign/sign.py.

Implementation

The plugin is a Python file that must define the following two functions:

For signing packages

def sign(ref, artifacts_folder, signature_folder, **kwargs):
    """
    :param ref: The package reference being signed.
    :param artifacts_folder: Path to the folder containing the package artifacts (conaninfo.txt, conan_package.tgz, etc).
    :param signature_folder: Path to the folder where signatures should be written (metadata/sign).
    :return: A list of signature dictionaries with their signing method and provider and the signing artifacts generated that are included in the package
    """
    # Logic to compute signatures of files in artifacts_folder
    # and write them to signature_folder.
    #
    # The signature_folder should only store the files that are part of the signature.
    # These files will be uploaded with the package and downloaded when
    # it is installed (usually the metadata is not downloaded by default,
    # but the metadata/sign folder is) so the signature verification
    # is always done.
    #
    return [
      {
          "method": "openssl-dgst",  # The signing method indicates the tool used to sign the package so it can be verified later
          "provider": "my-organization",  # The signing provider indicates the organization that signed the package, so the correct public key is used for verification
          "sign_artifacts": {
              "manifest": "pkgsign-manifest.json",  # This manifest file is generated by Conan and should be added here
              "signature": "pkgsign-manifest.json.sig"  # This is the signature file, typically signing the manifest file
          }
      }
    ]

The manifest file: pkgsign-manifest.json

Conan will generate a JSON manifest file called pkgsign-manifest.json in the signature folder right before calling the sign() method of the plugin. This file is useful to check the integrity of the package and to perform the sign over it by just signing this file (recommended).

Here is an example of the contents:

<signature_folder>/pkgsign-manifest.json
 {
   "files":[
     {
       "file":"conan_export.tgz",
       "sha256":"45c89ebf7d37d05f53472535db0d347019263bb5c6b990319108db6e66b85fe3"
     },
     {
       "file":"conan_sources.tgz",
       "sha256":"e757aa62bf6ac3cc5e36ecbd3c82c04d0faff46a61223189ee07e16489ef7c99"
     },
     {
       "file":"conanfile.py",
       "sha256":"12cc1f6477f6512158914c1041833db0b417d07d8f4f2538b07e5e4661d0139b"
     },
     {
       "file":"conanmanifest.txt",
       "sha256":"8d001ac33f5ea7e818d7c2e1aa2eb920d87472ada0b3abfeb0d8b2aaa1248653"
     }
   ]
 }

The signatures file: pkgsign-signatures.json

The sign() function should return a list of signatures (see the example above) that will be automatically saved by Conan into a JSON file called pkgsign-signatures.json. This file is intended to list the signature metadata like the provider or method and the files that are part of the signature in the sign_artifacts field.

  • method: The signing method indicates the tool used to sign the package so it can be verified later. For example: gpg, cosign, x509, openssl…

  • provider: The name of the organization that signed the package. This is useful to later verify the package with a matching public key provided by the organization.

  • sign_artifacts: A dictionary of the files that are part of the signature. Normally this should include a manifest key with the manifest pkgsign-manifest.json file, and a signature key with the signature file pkgsign-manifest.json.sig (or any other signature format). This is useful to identify the files involved in the signature so that they can be used later in the verify() function.

Additionally, more than one signature is supported, in case you need to sign with different formats or transition from one signature method to another.

Here is an example of the contents:

<signature_folder>/pkgsign-signatures.json
 {
   "signatures": [
     {
       "method": "openssl-dgst",
       "provider": "my-organization",
       "sign_artifacts": {
         "manifest": "pkgsign-manifest.json",
         "signature": "pkgsign-manifest.json.sig"
       }
     }
   ]
 }

For verifying packages

The following function should be implemented in the plugin:

def verify(ref, artifacts_folder, signature_folder, files, **kwargs):
    """
    :param ref: The package reference being verified.
    :param artifacts_folder: Path to the folder containing the package artifacts.
    :param signature_folder: Path to the folder containing the signatures.
    :param files: Dictionary of names and paths of the files in the package.
    """
    # Logic to verify that files in artifacts_folder match the
    # signatures in signature_folder.
    pass

This function does not return any value. Instead, it is expected that it raises a ConanException if the verification of the signature fails.

Before calling the verify() function, Conan will perform an integrity check calculating the checksums of the package files and checking against the manifest pkgsign-manifest.json file (if the file is present).

Note

Note that the **kwargs argument in both functions is required to ensure forward compatibility. Future versions of Conan may pass additional parameters, and omitting **kwargs could break your plugin.

Commands

The conan cache sign command will trigger the sign() method of the configured signing plugin and sign the recipe and packages:

$ conan cache sign mypkg/1.0.0
[Package sign] Results:

mypkg/1.0
  revisions
    294e801a0e1da10084441487e95b80e8
      packages
        7b737f1649e252dd60b2aefeabe5b6ef5e1a4750
          revisions
            7722a3748274ae073736f20d84428fe9
        dee9f7f985eb1c20e3c41afaa8c35e2a34b5ae0b
          revisions
            829868ff6774b7da8c1eace8d76e71f1

[Package sign] Summary: OK=3, FAILED=0

And the conan cache verify command will trigger the verify() method of the plugin and verify the recipe and packages as well:

$ conan cache verify mypkg/1.0.0
[Package sign] Results:

mypkg/1.0
  revisions
    294e801a0e1da10084441487e95b80e8
      packages
        7b737f1649e252dd60b2aefeabe5b6ef5e1a4750
          revisions
            7722a3748274ae073736f20d84428fe9
        dee9f7f985eb1c20e3c41afaa8c35e2a34b5ae0b
          revisions
            829868ff6774b7da8c1eace8d76e71f1

[Package sign] Summary: OK=3, FAILED=0

Here is a usual flow for signing and verifying packages:

$ conan create --name=mypkg --version=1.0.0
$ conan cache sign mypkg/1.0.0
$ conan upload mypkg/1.0.0 --remote=myremote
...
$ conan install --requires=mypkg/1.0.0  # This will trigger verify() when the package is downloaded from a remote
# When the package is signed, the verify() can be done at anytime with:
$ conan cache verify mypkg/1.0.0

Caution

The conan upload command will not automatically sign the packages since Conan 2.26.0. Please make sure to use the conan cache sign command to sign the packages before uploading them, and update your plugin to conform to the new implementation.

Plugin implementation examples

Here you can find some implementation examples of the plugin so they can serve as guidance to develop your own: