lp-signing

A service for storing keys and signing messages.

Development environment

Create a noble LXD container:

$ lxc launch ubuntu:noble lp-signing-noble

(You may want to use a profile to bind-mount your home directory as well.)

From now on, instructions will assume you’re inside the project’s LXD container, unless otherwise noted.

Install dependencies in the container:

$ sudo ./setup-container

Bootstrap the project:

$ make bootstrap

Run the tests:

$ make test

Generate a key pair, and add the private half to service_private_keys (JSON-encoded) in the [auth] section of service.conf:

$ env/bin/lp-signing generate-key-pair

Generate another key pair, and add the private half to private_keys (JSON-encoded) in the [key_storage] section of service.conf:

$ env/bin/lp-signing generate-key-pair

If you want to communicate with the lp-signing service via a client application, you need a keypair for it. The keypair can be generated using the env/bin/lp-signing generate-keypair command, which prints the public and private keys of the generated keypair. The public key from that output can be registered using the env/bin/lp-signing register-client <client-name> <client-public-key> command, where <client-public-key> is a base64-encoded NaCl public key, and the private key has to be used in the client application. Please check the example client code below in this document for more details.

Note

In the case of the Launchpad development instance acting as a client, the keypair can be obtained from configs/development/launchpad-lazr.conf in the Launchpad repository and the public key registered using this command.

Start the server:

$ make run

Using the service

An example client is in client/lp-signing-client. It requires the requests and pynacl libraries.

To use it, add the URL of your deployment and the private part of the key registered with register-client above to client/lp-signing.conf.

Then the client can be used to generate keys and sign files:

$ client/lp-signing-client --config client/lp-signing.conf generate \
   --key-type OPENPGP --description "test key" --length 4096
Generated openpgp key
Fingerprint: ...
Public key saved to signing-key.pub
$ mkdir -m 700 gnupg-tmp
$ gpg --homedir gnupg-tmp --import signing-key.pub
gpg: keybox '/home/mwhudson/src/ubuntu/lp-signing/gnupg-tmp/pubring.kbx' created
gpg: /home/mwhudson/src/ubuntu/lp-signing/gnupg-tmp/trustdb.gpg: trustdb created
gpg: key ...: public key "test key" imported
gpg: Total number processed: 1
gpg:               imported: 1
$ FINGERPRINT=$(gpg --homedir gnupg-tmp --with-colons --list-keys | awk -F: '$1 == "fpr" {print $10}')

$ head -c 1024 /dev/random | base64 > data
$ client/lp-signing-client --config client/lp-signing.conf sign \
   --key-type OPENPGP --fingerprint $FINGERPRINT data > data.asc
$ gpg --homedir gnupg-tmp --verify data.asc data
gpg: Signature made Thu 27 Nov 2025 09:20:54 PM NZDT
gpg:                using RSA key ...
gpg: Good signature from "test key" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: ...

You can also specify the `--fingerprint` argument multiple times with additional
fingerprints for multi-signing. This is only supported for OpenPGP keys.

You can also inject a keypair generated outside of the signing service into the signing service using this example client. Note that this has only been tested and verified for OpenPGP keys.

$ client/lp-signing-client –config client/lp-signing.conf inject

–key-type OPENPGP –public-key-file /path/to/ascii-armored/public/key/export/file –private-key-file /path/to/ascii-armored/private/key/export/file –description “Description of the keypair to be stored in the signing service”

On successful injection, the fingerprint of the injected keypair will be printed. You can then sign files using the injected key by using the invocation mentioned previously.

How-to guides