Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions 06.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ With the mint's response being of the form `GetInfoResponse`:
```json
{
"name": "Bob's Cashu mint",
"pubkey": "0283bf290884eed3a7ca2663fc0260de2e2064d6b355ea13f98dec004b7a7ead99",
"pubkey": "02d53b2972b5fcfda2861d937a40bd4dd4170718276e35ac82a84fa60c3a365046",
"signature": "efed7369effde640baee2f089681a57fd559e45ebf5bbf9a1f2200bb4137291453b617d7955f0e47426be39f21c4253e92bac8ada04f730021a3e313465334d9",
"version": "Nutshell/0.15.0",
"description": "The short mint description",
"description_long": "A description that can be a long piece of text.",
Expand Down Expand Up @@ -87,8 +88,11 @@ With the mint's response being of the form `GetInfoResponse`:
}
```

For reproducibility, the example uses the mint seed `NUT-06 example mint seed` and zero-filled auxiliary randomness for BIP-340 signing.

- (optional) `name` is the name of the mint and should be recognizable.
- (optional) `pubkey` is the hex pubkey of the mint.
- (mandatory) `pubkey` is the mint's identity public key as a lowercase hex-encoded, 33-byte compressed secp256k1 public key.
- (mandatory) `signature` is the lowercase hex encoding of the mint's 64-byte BIP-340 Schnorr signature on the response payload, constructed as specified below.
- (optional) `version` is the implementation name and the version of the software running on this mint separated with a slash "/".
- (optional) `description` is a short description of the mint that can be shown in the wallet next to the mint's name.
- (optional) `description_long` is a long description that can be shown in an additional field.
Expand All @@ -100,6 +104,30 @@ With the mint's response being of the form `GetInfoResponse`:
- (optional) `tos_url` is the URL pointing to the Terms of Service of the mint.
- (optional) `nuts` indicates each NUT specification that the mint supports and its settings. The settings are defined in each NUT separately.

## Mint identity key

The mint identity key is deterministically derived from the mint seed. Given the exact `seed` string used by the mint:

1. Encode `seed` as UTF-8.
2. Compute `secret_key = SHA256(seed)`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should use HMAC-SHA256 construction like we do in other NUTS (e.g. 12 or 13)

3. Interpret `secret_key` as a 32-byte big-endian integer. It **MUST** be a valid secp256k1 secret key in the range `1..n-1`, where `n` is the order of the secp256k1 curve.

@prusnak prusnak Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if it does not fit? Maybe introduce a counter to the HMAC above and retry with ctr = 1, 2, 3, etc.?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probability it doesn't fit should be roughly $\frac{1}{2^{128}}$

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know, but we use the same construction in NUT-12, see

nuts/12.md

Line 21 in e0a16f8

r = HMAC-SHA256(key=a, data="Cashu_DLEQ_R_v1" || A || B' || C' || ctr) # Deterministic nonce

nuts/12.md

Line 39 in e0a16f8

ctr = nonce counter byte; set to 0x00 initially, increment and retry if r == 0 or r >= n (max 256 tries)

4. Compute `pubkey = secret_key * G` and serialize it in 33-byte compressed SEC1 format.

The mint **MUST** return this public key in `GetInfoResponse.pubkey`.

## Signature

The mint **MUST** sign every `GetInfoResponse` with the identity key corresponding to `pubkey`. To construct and sign the message:

1. Start with the complete `GetInfoResponse` JSON object.
2. Remove the top-level `signature` and `time` members. The `time` member is excluded so that an otherwise unchanged response has a stable signed payload.
3. Canonicalize the resulting JSON object using the JSON Canonicalization Scheme (JCS) defined in [RFC 8785]. Encode the canonical JSON as UTF-8.
4. Compute the SHA-256 hash of those bytes.
5. Sign the 32-byte hash using [BIP-340] Schnorr over secp256k1.
6. Encode the resulting 64-byte signature as lowercase hexadecimal and return it in `GetInfoResponse.signature`.

Wallets **MUST** reject the response if `pubkey` or `signature` is missing or malformed, or if BIP-340 verification fails. For BIP-340 verification, wallets use the 32-byte x-coordinate of the compressed `pubkey`.

With curl:

```bash
Expand All @@ -119,3 +147,5 @@ curl -X GET https://mint.host:3338/v1/info
[10]: 10.md
[11]: 11.md
[12]: 12.md
[RFC 8785]: https://www.rfc-editor.org/rfc/rfc8785
[BIP-340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
Loading