NUT-20: Signature on Mint Quote¶
optional
depends on: NUT-04
This NUT defines signature-based authentication for mint quote redemption. When requesting a mint quote, clients provide a public key. The mint will then require a valid signature from the corresponding secret key to process the mint operation.
[!CAUTION]
NUT-04 mint quotes without a public key can be minted by anyone who knows the mint quote id without providing a signature.
Mint quote¶
To request a mint quote, the wallet of Alice makes a POST /v1/mint/quote/{method} request where method is the payment method requested. We present an example with the method being bolt11 here.
POST https://mint.host:3338/v1/mint/quote/bolt11
The wallet of Alice includes the following PostMintQuoteBolt11Request data in its request:
{
"amount": <int>,
"unit": <str_enum["sat"]>,
"description": <str>, // Optional
"pubkey": <str> // Optional <-- New
}
with the requested amount,unit, and description according to NUT-04.
pubkey is the compressed secp256k1 public key (33 bytes, hex-encoded) that will be required for signature verification during the minting operation. The mint will only mint ecash after receiving a valid signature from the corresponding private key in the subsequent PostMintRequest.
[!IMPORTANT]
Privacy: To prevent the mint from being able to link multiple mint quotes, wallets SHOULD generate a unique public key for each mint quote request.
The mint Bob then responds with a PostMintQuoteBolt11Response:
{
"quote": <str>,
"request": <str>,
"state": <str_enum[STATE]>,
"expiry": <int>,
"pubkey": <str> // Optional <-- New
}
The response is the same as in NUT-04 except for pubkey which has been provided by the wallet in the previous request.
Example¶
Request of Alice with curl:
curl -X POST http://localhost:3338/v1/mint/quote/bolt11 -d '{"amount": 10, "unit": "sat", "pubkey": "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac"}' -H "Content-Type: application/json"
Response of Bob:
{
"quote": "9d745270-1405-46de-b5c5-e2762b4f5e00",
"request": "lnbc100n1pj4apw9...",
"state": "UNPAID",
"expiry": 1701704757,
"pubkey": "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac"
}
Signing the mint request¶
Message aggregation¶
To provide a signature for a mint request, the owner of the signing public keys must concatenate the quote ID quote in PostMintQuoteBolt11Response and the B_ fields of all BlindedMessages in the PostMintBolt11Request (i.e., the outputs, see NUT-00) to a single message string in the order they appear in the PostMintRequest. This concatenated string is then hashed and signed (see Signature scheme).
[!NOTE]
Concatenating the quote ID and the outputs into a single message prevents maliciously replacing the outputs.
If a request has n outputs, the message to sign becomes:
msg_to_sign = quote || B_0 || ... || B_(n-1)
Where || denotes concatenation, quote is the UTF-8 quote id in PostMintQuoteBolt11Response, and each B_n is a UTF-8 encoded hex string of the outputs in the PostMintBolt11Request.
Signature scheme¶
To mint a quote where a public key was provided, the wallet includes a signature on msg_to_sign in the PostMintBolt11Request. We use a BIP340 Schnorr signature on the SHA-256 hash of the message to sign as defined above.
Minting tokens¶
After requesting a mint quote and paying the request, the wallet proceeds with minting new tokens by calling the POST /v1/mint/{method} endpoint where method is the payment method requested (here bolt11).
POST https://mint.host:3338/v1/mint/bolt11
The wallet Alice includes the following PostMintBolt11Request data in its request
{
"quote": <str>,
"outputs": <Array[BlindedMessage]>,
"signature": <str|null> <-- New
}
with the quote being the quote ID from the previous step and outputs being BlindedMessages as in NUT-04.
signature is the signature on the msg_to_sign which is the concatenated quote id and the outputs as defined above.
The mint responds with a PostMintBolt11Response as in NUT-04 if all validations are successful.
Example¶
Request of Alice with curl:
curl -X POST https://mint.host:3338/v1/mint/bolt11 -H "Content-Type: application/json" -d \
'{
"quote": "9d745270-1405-46de-b5c5-e2762b4f5e00",
"outputs": [
{
"amount": 8,
"id": "009a1f293253e41e",
"B_": "035015e6d7ade60ba8426cefaf1832bbd27257636e44a76b922d78e79b47cb689d"
},
{
"amount": 2,
"id": "009a1f293253e41e",
"B_": "0288d7649652d0a83fc9c966c969fb217f15904431e61a44b14999fabc1b5d9ac6"
}
],
"signature": "d9be080b33179387e504bb6991ea41ae0dd715e28b01ce9f63d57198a095bccc776874914288e6989e97ac9d255ac667c205fa8d90a211184b417b4ffdd24092"
}'
Response of Bob:
{
"signatures": [
{
"id": "009a1f293253e41e",
"amount": 2,
"C_": "0224f1c4c564230ad3d96c5033efdc425582397a5a7691d600202732edc6d4b1ec"
},
{
"id": "009a1f293253e41e",
"amount": 8,
"C_": "0277d1de806ed177007e5b94a8139343b6382e472c752a74e99949d511f7194f6c"
}
]
}
Errors¶
If the wallet user Alice does not include a signature on the PostMintBolt11Request but did include a pubkey in the PostMintBolt11QuoteRequest then Bob MUST respond with an error. Alice CAN repeat the request with a valid signature.
See Error Codes:
20008: Mint quote withpubkeybut no validsignatureprovided for mint request.20009: Mint quote requirespubkeybut none given or invalidpubkey.
Settings¶
The settings for this NUT indicate the support for requiring a signature before minting. They are part of the info response of the mint (NUT-06) which in this case reads
{
"20": {
"supported": <bool>,
}
}