---
# System prepended metadata

title: signtool

---

---
robots: noindex, nofollow
---

# `signtool` Command-Line Tool

Blockchain Commons
Wolf McNally, Lead Researcher
October 26, 2024

**Note:** This is an early draft working document. There is no implementation of this at this time.

## Overview

`signtool` is a command-line utility for secure, multiparty signing of [Gordian Envelopes](https://www.blockchaincommons.com/introduction/Envelope-Intro/) using two leading cryptographic protocols: [MuSig2](https://eprint.iacr.org/2020/1261) and [FROST](https://eprint.iacr.org/2020/852). Both protocols leverage [Schnorr signatures](https://en.wikipedia.org/wiki/Schnorr_signature), and (by default) [BIP-340 Schnorr signatures on the secp256k1 curve](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki), ensuring compatibility with Bitcoin and other systems using the same cryptographic standards. By integrating both n-of-n (MuSig2) and m-of-n threshold (FROST) signing methods into a single, unified tool, `signtool` allows users to select the optimal signing approach for their collaborative or decentralized applications built on Gordian Envelope.

**Note:** The existing [`envelope` command line tool](https://crates.io/crates/bc-envelope-cli) provides support for a variety of single-signer and non-aggregated multisignature schemes for Envelope.

## Key Features

### Session Context

A Session Context (`SESSION`) is an immutable data structure that contains all persistent attributes of the session, including:

- Unique session identifier (SID)
- Participant Identifiers (XIDs or XID documents)
- Threshold (m) for FROST sessions
- Envelope to be signed

This structured approach ensures that all necessary information is preserved and used consistently throughout the signing process.

A `SESSION` is not associated with partial keys or nonces. It can be thought of as an invitation to participate in a signing session.

### Participant Identifiers

Participant Identifiers (XIDs) are embedded within data structures, such as partial keys, nonces, and partial signatures, to ensure that each participant’s contributions are uniquely identified and ordered. XIDs also provide a cryptographic foundation for identity and privacy in the signing process.

### Unified Interface

With top-level `musig2` and `frost` subcommands, `signtool` provides a coherent experience across both protocols. A single, algorithm-agnostic `verify` command (essentially the same as the `envelope` tool's `verify` command) provides signature validation, allowing any generated signature to be authenticated regardless of the signing method used.

## `musig2` Subcommand

Implements the MuSig2 multisignature protocol (n-of-n signing).

### `musig2` Overview

```
signtool musig2 <command> [options]
```

| **Step**                       | **Command**               | **Description** |
|--------------------------------|---------------------------|-----------------|
| **Session Setup**              | `musig2 setup-session`    | Initializes the `SESSION` with the message and participant XIDs. |
| **Key Pair Generation**        | `musig2 genkey`           | Participants generate their partial keys. |
| **Group Public Key**           | `musig2 aggregate-pubkey` | Computes the group public key from all partial public keys. |
| **Nonce Generation**           | `musig2 gen-nonce`        | Each participant generates a nonce pair. |
| **Partial Signature Creation** | `musig2 partial-sign`     | Creates each participant's partial signature using their nonce and partial key. |
| **Aggregate Signing**          | `musig2 aggregate-sign`   | Aggregates all partial signatures into a final signature for the message. |

### 1.1. `musig2 setup-session`

Initialize a new MuSig2 signing session and output the SESSION.

```
signtool musig2 setup-session \
  --message <ur:envelope> \
  --xid <ur:xid> [--xid <ur:xid>]...
```

- `--message <ur:envelope>`: The envelope to be signed.
- `--xid <ur:xid>`: Participant Identifier XID or XID document. Repeat for each participant.

Output:

- Session Context (`ur:musig2-session`)

### 1.2. `musig2 genkey`

Generate a partial key pair with an embedded XID.

```
signtool musig2 genkey \
  --xid <ur:xid>
```

- `--xid <ur:xid>`: Your Participant Identifier (`ur:xid`).

Output:

- Partial private key: <ur:musig2-partial-private-key>
- Partial public key: <ur:musig2-partial-public-key>

The partial key pair may be used across multiple sessions, or may be ephemeral for a single session.

### 1.3. `musig2 aggregate-pubkey`

Compute the group public key.

```
signtool musig2 aggregate-pubkey \
  --public-key <ur:musig2-partial-public-key> \
  [--public-key <ur:musig2-partial-public-key>]...
```

- `--public-key <ur:musig2-partial-public-key>`: Partial public key. All participants must provide their partial public keys. Keys may be provided in any order, as XIDs are inherently ordered and embedded in the keys.

Output:

- Group public key: <ur:signing-public-key>

### 1.4. `musig2 gen-nonce`

Generate a nonce pair for signing.

```
signtool musig2 gen-nonce \
  --session <ur:musig2-session> \
  --private-key <ur:musig2-partial-private-key>
```

- `--session <ur:musig2-session>`: The Session Context.
- `--private-key <ur:musig2-partial-private-key>`: Your private key.

Output:

- Private Nonce: <ur:musig2-private-nonce>
- Public Nonce: <ur:musig2-public-nonce>

### 1.5. `musig2 partial-sign`

Create a partial signature.

```
signtool musig2 partial-sign \
  --session <ur:musig2-session> \
  --private-key <ur:musig2-partial-private-key> \
  --private-nonce <ur:musig2-private-nonce> \
  --aggregated-pubkey <ur:signing-public-key> \
  --public-key <ur:musig2-partial-public-key> \
  [--public-key <ur:musig2-partial-public-key>]... \
  --public-nonce <ur:musig2-public-nonce> \
  [--public-nonce <ur:musig2-public-nonce>]...
```

- `--session <ur:musig2-session>`: The Session Context.
- `--private-key <ur:musig2-partial-private-key>`: Your partial private key.
- `--private-nonce <ur:musig2-private-nonce>`: Your private nonce.
- `--aggregated-pubkey <ur:signing-public-key>`: Group public key.
- `--public-key <ur:musig2-partial-public-key>`: Partial public keys of all participants.
- `--public-nonce <ur:musig2-public-nonce>`: Public nonces of all participants.

Output:

- Partial Signature: <ur:musig2-partial-signature>

### 1.6. `musig2 aggregate-sign`

Produce a final signed envelope by aggregating partial signatures into the final signature. This signature can be verified using the signed envelope and the group public key.

```
signtool musig2 aggregate-sign \
  --session <ur:musig2-session> \
  --aggregated-pubkey <ur:signing-public-key> \
  --partial-signature <ur:musig2-partial-signature> \
  [--partial-signature <ur:musig2-partial-signature>]... \
  --public-nonce <ur:musig2-public-nonce> \
  [--public-nonce <ur:musig2-public-nonce>]...
```

- `--session <ur:musig2-session>`: The Session Context.
- `--aggregated-pubkey <ur:signing-public-key>`: Group public key.
- `--partial-signature <ur:musig2-partial-signature>`: Partial signatures with embedded PIDs.
- `--public-nonce <ur:musig2-public-nonce>`: Public nonces with embedded PIDs.

Output:

- Final Signature: <ur:signature>

## `frost` Subcommand

Implements the FROST threshold signature protocol (m-of-n signing).

### `frost` Overview

```
signtool frost <command> [options]
```

| **Step**                       | **Command**                | **Description** |
|--------------------------------|----------------------------|-----------------|
| **Session Setup**              | `frost setup-session`      | Initializes the `SESSION` with the message, threshold, and participant XIDs. |
| **Key Share Generation**       | `frost gen-key-share`      | Participants generate secret key shares and public commitments. |
| **Combine Key Shares**         | `frost combine-key-shares` | Computes the group public key by combining all participants’ commitments. |
| **Nonce Generation**           | `frost gen-nonce`          | Each participant generates a nonce pair. |
| **Partial Signature Creation** | `frost partial-sign`       | Creates each participant's partial signature using their nonce and key share. |
| **Aggregate Signing**          | `frost aggregate-sign`     | Aggregates all partial signatures into a final signature for the message. |

### 2.1. `frost setup-session`

Initialize a new FROST signing session and output the SESSION.

```
signtool frost setup-session \
  --message <ur:envelope> \
  --threshold <t> \
  --xid <ur:xid> [--xid <ur:xid>]...
```

- `--message <ur:envelope>`: The envelope to be signed.
- `--threshold <t>`: Threshold value t, where t is the number of signatures required for the final signature and must be less than or equal to the number of participants.
- `--xid <ur:xid>`: Participant Identifier XID or XID document. Repeat for each participant.

Output:

- Session Context (`ur:frost-session`)

### 2.2. `frost genkey`

Generate a partial key pair with an embedded XID.

```
signtool frost genkey \
  --xid <ur:xid>
```

- `--xid <ur:xid>`: Your Participant Identifier (`ur:xid`).

Output:

- Partial private key: <ur:frost-partial-private-key>
- Partial public key: <ur:frost-partial-public-key>

The partial key pair may be used across multiple sessions, or may be ephemeral for a single session.

### 2.3. `frost gen-key-share`

Generate your secret key share and public commitments.

```
signtool frost gen-key-share \
  --session <ur:frost-session> \
  --private-key <ur:frost-partial-private-key>
```

- `--session <ur:frost-session>`: The Session Context.
- `--private-key <ur:frost-partial-private-key>`: Your Partial Private Key.

Output:

- Secret Key Share: <ur:frost-secret-key-share>
- Verification Commitments with PID: <ur:frost-commitments>

### 2.4. `frost combine-key-shares`

Validates that each commitment is correctly derived and aligns with the session and and its participants to prevent malformed or incorrect contributions. Then combines key shares to compute the group public key.

The set of participants in this step MUST be a subset of the participants in the session, and this subset MUST be the same for all subsequent steps.

```
signtool frost combine-key-shares \
  --session <ur:frost-session> \
  --commitments <ur:frost-commitments> \
  [--commitments <ur:frost-commitments>]...
```

- `--session <ur:frost-session>`: The Session Context.
- `--commitments <ur:frost-commitments>`: The commitments of all participants.

Output:

- Group Public Key: <ur:signing-public-key>

### 2.5. `frost gen-nonce`

Generate a nonce pair for signing.

```
signtool frost gen-nonce \
  --session <ur:frost-session>
```

- `--session <ur:frost-session>`: The Session Context.

Output:

- Private Nonce: <ur:frost-private-nonce>
- Public Nonce: <ur:frost-public-nonce>

### 2.6. `frost partial-sign`

Create a partial signature.

The set of participants in this step MUST be the same as the set of participants in the `combine-key-shares` step.

```
signtool frost partial-sign \
  --session <ur:frost-session> \
  --secret-key-share <ur:frost-secret-key-share> \
  --private-nonce <ur:frost-private-nonce> \
  --public-nonce <ur:frost-public-nonce> \
  [--public-nonce <ur:frost-public-nonce>]... \
  --group-public-key <ur:signing-public-key>
```

- `--session <ur:frost-session>`: The Session Context.
- `--secret-key-share <ur:frost-secret-key-share>`: Your secret key share.
- `--private-nonce <ur:frost-private-nonce>`: Your private nonce.
- `--public-nonce <ur:frost-public-nonce>`: Public nonces for all participants.
- `--group-public-key <ur:signing-public-key>`: The group public key.

Output:

- Your partial signature: <ur:frost-partial-signature>

### 2.7. `frost aggregate-sign`

Produce a final signed envelope by aggregating partial signatures into the final signature. This signature can be verified using the signed envelope and the group public key.

The set of participants in this step MUST be the same as the set of participants in the `combine-key-shares` step.

```
signtool frost aggregate-sign \
  --session <ur:frost-session> \
  --group-public-key <ur:signing-public-key> \
  --partial-signature <ur:frost-partial-signature> \
  [--partial-signature <ur:frost-partial-signature>]... \
  --public-nonce <ur:frost-public-nonce> \
  [--public-nonce <ur:frost-public-nonce>]...
```

- `--session <ur:frost-session>`: The Session Context.
- `--group-public-key <ur:signing-public-key>`: Group public key.
- `--partial-signature <ur:frost-partial-signature>`: Partial signatures from all participants.
- `--public-nonce <ur:frost-public-nonce>`: Public nonces from all participants.

Output:

- Final Signature: <ur:signature>

## `verify` Subcommand

An algorithm-agnostic command to verify signatures produced by musig2, FROST, or other signing protocols.

```
signtool verify \
  --message <ur:envelope> \
  --public-key <ur:signing-public-key>
  -s --silent
```

- `--message <ur:envelope>`: The signed envelope.
- `--public-key <ur:signing-public-key>`: The verifying public key, which may be a group public key generated by musig2 or FROST, or a single public key.
- `-s`, `--silent`: Don't output the envelope's UR on success.

Output:

- If the signature is valid, the tool will output the envelope's UR (or nothing if the `--silent` option is present).
- If the signature is invalid, the tool will output an error message and return a non-zero exit code.

## Musig2 Example

In this example, we demonstrate how to perform an n-of-n multisignature using the signtool musig2 subcommands. Assume there are three participants: Alice, Bob, and Carol. Each participant has a unique Participant Identifier (XID). The goal is to collectively sign a message (an Envelope) using the MuSig2 protocol.

### Step 1: Session Setup

Alice initializes a new MuSig2 session by specifying the message to be signed and the XIDs of all participants.

```
$ signtool musig2 setup-session \
  --message ur:envelope/unsigned \
  --xid ur:xid/alice \
  --xid ur:xid/bob \
  --xid ur:xid/carol

ur:musig2-session
```

### Step 2: Generate Partial Keys

Each participant generates their own partial key pair with their embedded XID.

For Alice:

```
$ signtool musig2 genkey \
  --xid ur:xid/alice

ur:musig2-partial-private-key/alice
ur:musig2-partial-public-key/alice
```

### Step 3: Compute Group Public Key

Participants exchange their partial public keys. Any participant can compute the group public key.

```
$ signtool musig2 aggregate-pubkey \
  --public-key ur:musig2-partial-public-key/alice \
  --public-key ur:musig2-partial-public-key/bob \
  --public-key ur:musig2-partial-public-key/carol

ur:signing-public-key
```

### Step 4: Generate Nonces

Each participant generates a nonce pair for signing using their partial private key and the session context.

For Alice:

```
$ signtool musig2 gen-nonce \
  --session ur:musig2-session \
  --private-key ur:musig2-partial-private-key/alice

ur:musig2-private-nonce/alice
ur:musig2-public-nonce/alice
```

### Step 5: Exchange Public Nonces

Participants exchange their public nonces. Each participant should now have all public nonces:

```
ur:musig2-public-nonce/alice
ur:musig2-public-nonce/bob
ur:musig2-public-nonce/carol
```

### Step 6: Create Partial Signatures

Each participant creates their partial signature using their private key, private nonce, the group public key, all partial public keys, and all public nonces.

For Alice:

```
$ signtool musig2 partial-sign \
  --session ur:musig2-session \
  --private-key ur:musig2-partial-private-key/alice \
  --private-nonce ur:musig2-private-nonce/alice \
  --aggregated-pubkey ur:signing-public-key \
  --public-key ur:musig2-partial-public-key/alice \
  --public-key ur:musig2-partial-public-key/bob \
  --public-key ur:musig2-partial-public-key/carol \
  --public-nonce ur:musig2-public-nonce/alice \
  --public-nonce ur:musig2-public-nonce/bob \
  --public-nonce ur:musig2-public-nonce/carol

ur:musig2-partial-signature/alice
```

### Step 7: Aggregate Partial Signatures

Any participant (or a designated aggregator) can aggregate the partial signatures to produce the final signature.

```
$ signtool musig2 aggregate-sign \
  --session ur:musig2-session \
  --aggregated-pubkey ur:signing-public-key \
  --partial-signature ur:musig2-partial-signature/alice \
  --partial-signature ur:musig2-partial-signature/bob \
  --partial-signature ur:musig2-partial-signature/carol \
  --public-nonce ur:musig2-public-nonce/alice \
  --public-nonce ur:musig2-public-nonce/bob \
  --public-nonce ur:musig2-public-nonce/carol

ur:envelope/signed
```

### Step 8: Verify the Signature

Anyone can verify the signature using the message (envelope) and the group public key. If the signature is valid, the tool outputs the envelope's UR, facilitating command pipelining.

```
$ signtool verify \
  --message ur:envelope/signed \
  --public-key ur:signing-public-key

ur:envelope/signed
```

## FROST Example

In this example, we demonstrate how to perform an m-of-n threshold signature using the signtool frost subcommands. Assume there are five participants: Alice, Bob, Carol, Dave, and Eve. Each participant has a unique Participant Identifier (XID). The threshold is set to 3, meaning any combination of 3 participants can collaboratively sign a message (an Envelope) using the FROST protocol.

### Step 1: Session Setup

Alice initializes a new FROST session by specifying the message to be signed, the threshold value, and the XIDs of all participants.

```
$ signtool frost setup-session \
  --message ur:envelope/unsigned \
  --threshold 3 \
  --xid ur:xid/alice \
  --xid ur:xid/bob \
  --xid ur:xid/carol \
  --xid ur:xid/dave \
  --xid ur:xid/eve

ur:frost-session
```

### Step 2: Generate Partial Keys

Each participant generates their own partial key pair with their embedded XID.

For Alice:

```
$ signtool frost genkey \
  --xid ur:xid/alice

ur:frost-partial-private-key/alice
ur:frost-partial-public-key/alice
```

### Step 3: Generate Key Shares and Commitments

Each participant generates their secret key share and public commitments using their partial private key and the session context.

For Alice:

```
$ signtool frost gen-key-share \
  --session ur:frost-session \
  --private-key ur:frost-partial-private-key/alice

ur:frost-secret-key-share/alice
ur:frost-commitments/alice
```

### Step 4: Combine Key Shares

Participants exchange their commitments. Any participant can combine the key shares to compute the group public key.

```
$ signtool frost combine-key-shares \
  --session ur:frost-session \
  --commitments ur:frost-commitments/alice \
  --commitments ur:frost-commitments/bob \
  --commitments ur:frost-commitments/carol \
  --commitments ur:frost-commitments/dave \
  --commitments ur:frost-commitments/eve

ur:signing-public-key
```

### Step 5: Generate Nonces

Participants who are part of the signing group generate nonce pairs using the session context. Since the threshold is 3, let’s assume Alice, Bob, and Carol decide to sign the message.

For Alice:

```
$ signtool frost gen-nonce \
  --session ur:frost-session

ur:frost-private-nonce/alice
ur:frost-public-nonce/alice
```

### Step 6: Exchange Public Nonces

The signing participants exchange their public nonces. Each signer should now have all public nonces.

```
ur:frost-public-nonce/alice
ur:frost-public-nonce/bob
ur:frost-public-nonce/carol
```

### Step 7: Create Partial Signatures

Each signer creates their partial signature using their secret key share, private nonce, group public key, and the public nonces of all signers.

For Alice:

```
$ signtool frost partial-sign \
  --session ur:frost-session \
  --secret-key-share ur:frost-secret-key-share/alice \
  --private-nonce ur:frost-private-nonce/alice \
  --public-nonce ur:frost-public-nonce/alice \
  --public-nonce ur:frost-public-nonce/bob \
  --public-nonce ur:frost-public-nonce/carol \
  --group-public-key ur:signing-public-key

ur:frost-partial-signature/alice
```

### Step 8: Aggregate Partial Signatures

Any participant (or a designated aggregator) can aggregate the partial signatures to produce the final signature.

```
$ signtool frost aggregate-sign \
  --session ur:frost-session \
  --group-public-key ur:signing-public-key \
  --partial-signature ur:frost-partial-signature/alice \
  --partial-signature ur:frost-partial-signature/bob \
  --partial-signature ur:frost-partial-signature/carol \
  --public-nonce ur:frost-public-nonce/alice \
  --public-nonce ur:frost-public-nonce/bob \
  --public-nonce ur:frost-public-nonce/carol

ur:envelope/signed
```

### Step 9: Verify the Signature

Anyone can verify the signature using the signed envelope and the group public key. If the signature is valid, the tool outputs the envelope’s UR, facilitating command pipelining.

```
$ signtool verify \
  --message ur:envelope/signed \
  --public-key ur:signing-public-key

ur:envelope/signed
```
