OTA Connect Developer Guide

Sign metadata with an externally generated signature

For safety reasons, you may want to sign your metadata with the signature generated by your own PKI. For more information on where to find your metadata files, see the Find the unsigned Root and Targets metadata.

Sign Root metadata

To sign root.json with your own generated signature, specify the name of the key that you used to sign the file the last time. You can use multiple signatures for signing. For verification purposes, for each signature, specify the name that you used to generate the key.

garage-sign root sign \
        --repo <repository-name> \
        --old-root-alias <key-name> \
        --signatures <key-name=signature>

Sign Targets metadata

To sign targets.json with your own generated signature, specify the name that you used to generate the key. You can use multiple signatures for signing. For verification purposes, for each signature, specify its key name.

If you pass a signature, you cannot pass a version.
garage-sign targets sign \
        --repo <repository-name> \
        --signatures <key-name=signature>

Example script

This example includes a flow that uses OpenSSL as a stand-in for an external Key Management Service (KMS).

This script calls garage-sign move-offline, which makes irreversible changes to the namespace. If you want to run it to see the result, make sure you run it with a dispensable account first.
#!/bin/bash

set -x
set -euo pipefail

online_credentials="${1}"
shift
root_pem_path="${1}"
shift
targets_pem_path="${1}"
shift
length="${1}"
shift
filename="${1}"
shift
version="${1}"
shift
sha="${1}"
shift
hardwareid="${1}"

garage_sign init --repo example_repo --credentials ${online_credentials}

# Generate keys for signing.
openssl genpkey -algorithm RSA -out root_private.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in root_private.pem -out root_public.pem
openssl genpkey -algorithm RSA -out targets_private.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in targets_private.pem -out targets_public.pem

# Perform initial pulling of the unsigned metadata.
garage_sign targets pull --repo example_repo
garage_sign move-offline --repo example_repo --old-root-alias origroot
garage_sign root pull --repo example_repo

# Import your Root and Targets public keys.
garage_sign user-keys importpub --repo example_repo --key-name root_public --input ${root_pem_path}
garage_sign root key add --repo example_repo --key-name root_public
garage_sign user-keys importpub --repo example_repo --key-name targets_public --input ${targets_pem_path}
garage_sign root targets-key add --repo example_repo --key-name targets_public

# Sign and push the Root metadata.
garage_sign root increment-version --repo example_repo
garage_sign root get-unsigned --repo example_repo > root.canonical.json
signature=$(openssl dgst -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -sign root_private.pem root.canonical.json | base64 -w0)
garage_sign root sign --repo example_repo --old-root-alias origroot --signatures root_public=${signature}
garage_sign root push --repo example_repo

# Sign and push the Targets metadata.
garage_sign targets increment-version --repo example_repo
garage_sign targets get-unsigned --repo example_repo > targets.canonical.json
signature=$(openssl dgst -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -sign targets_private.pem targets.canonical.json | base64 -w0)
garage_sign targets sign --repo example_repo --signatures targets_public=${signature}
garage_sign targets push --repo example_repo

garage_sign targets pull --repo example_repo

# Sign and push the Targets metadata once more.
garage_sign targets increment-version --repo example_repo
garage_sign targets add --length ${length} --name ${filename} --version ${version} --sha256 ${sha} --hardwareids ${hardwareid} --repo example_repo
garage_sign targets get-unsigned --repo example_repo > targets.canonical.json
signature=$(openssl dgst -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -sign targets_private.pem targets.canonical.json | base64 -w0)
garage_sign targets sign --repo example_repo --signatures targets_public=${signature}
garage_sign targets push --repo example_repo