Signing Chalk artifacts

Chalk can automatically sign (or attest) chalked artifacts.

Configure

To configure attestation, use the setup command. This will create an attestation key and will print out the password used to encrypt the private key:

$ chalk setup
------------------------------------------
CHALK_PASSWORD=91-qmuffjZlKOWSh-5T2RA==
------------------------------------------
Write this down. In future chalk commands, you will need
to provide it via CHALK_PASSWORD environment variable.

$ ls chalk.{key,pub}
󰌆 chalk.key  󰌆 chalk.pub

Save this password as well as the key. We recommend to save it as a secret in your CI/CD.

Use standalone

Once you’ve run chalk setup, running chalk insert will automatically sign the artifact if the CHALK_PASSWORD environment variable is set.

Use in CI/CD

In order to use attestation in CI/CD, you will need to reference the secrets created earlier.

GitHub Actions

- name: Set up Chalk
  uses: crashappsec/setup-chalk-action@main
  with:
    password: ${{ secrets.CHALK_PASSWORD }}
    public_key: ${{ secrets.CHALK_PUBLIC_KEY }} # content of chalk.pub
    private_key: ${{ secrets.CHALK_PRIVATE_KEY }} # content of chalk.key

Other

Similarly attestation can be enabled via setup.sh:

$ CHALK_PASSWORD=<password> \
  sh <(curl -fsSL https://crashoverride.run/setup.sh) \
    --public-key=./chalk.pub \
    --private-key=./chalk.key

Verifying Attestation

Once an artifact is chalked, the extract command will verify its attestation. Chalk will report whether the artifact attestation signature was successfully validated. This works for both files and docker images:

$ chalk extract ./myapp # file
$ chalk extract docker.io/example/image # docker image

Here’s an example of signing a binary with Chalk (where the password and signature will differ based on your run of chalk setup).

$ unalias ls
$ cp $(which ls) ls
$ CHALK_PASSWORD=91-qmuffjZlKOWSh-5T2RA== chalk insert ./ls 2>/dev/null | jq -r '.[]|._CHALKS[]|.SIGNATURE'
MEYCIQCz8IUfWOgjhMVjdsmkE2T/u83UIu5Y8ZJszmReanodjgIhAN62niXkNYYwnHdluKwUeMnp1V6pW4ys1syyXmZZFKMb
$ chalk extract ./ls 2>/dev/null | jq -r '.[]|._CHALKS[]|._VALIDATED_SIGNATURE'

And if we omitted the CHALK_PASSWORD (or we didn’t run chalk setup) neither of these fields will exist:

$ unalias ls
$ cp $(which ls) ls
$ chalk insert ./ls 2>/dev/null | jq -r '.[]|._CHALKS[]|.SIGNATURE'
null
./chalk extract ./ls 2>/dev/null | jq -r '.[]|._CHALKS[]|._VALIDATED_SIGNATURE'
null

How does this differ from gpg?

You can always sign your own binaries yourself, whether you chalk mark them or not. In that scenario you will manage and distributed and verify the signature separately, or not at all.

When signing with Chalk (when dealing with binaries) the signature gets baked into the binary itself and is then validated every time you chalk extract.

(Separately, note that even if Chalk is set up with signing keys it doesn’t do anything with chalk exec-ed binaries that have not been marked by Chalk.)