diff --git a/nut-xx.md b/nut-xx.md new file mode 100644 index 00000000..82aeec3f --- /dev/null +++ b/nut-xx.md @@ -0,0 +1,72 @@ +# NUT-XX: Remote signer communications + +`optional` + +--- + +This NUT define a standard way for Mints to communicate with a remote signer. The mint can protect its private key by isolating it to a service. + +Mints will connect to the signer and ask for validation of proofs as well signing blinded messages. This NUT does not specify the transport method needed to communicate with the mint. + +## Protocol + +The signer MUST speak the same language using the GRPC protobuf defined in `remote-signer.proto` inside the repository. + +### Authentication + +The signer and the mint can communicate with each other over different transports. +The signer MUST NOT accept any request if authentication has not occurred. + +#### Over the Network + +The Mint and the Signer MUST authenticate with each other using mTLS. + +## Seedphrase usage + +[BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) mnemonic seedphrase must be used for generation +of the keysets. + +Example: `legal winner thank year wave sausage worth useful legal winner thank yellow` + +### Canonical unit reference parsing + +Implementations **MUST** accept currency unit labels case-insensitively and ignore leading or trailing ASCII whitespace. When serializing a unit into JSON (e.g., as part of a keyset description in NUT-01 responses), implementations **SHOULD** emit the uppercase representation of the unit so that mints and wallets display consistent labels. + +Before deriving an index, the input label **MUST** be transformed as follows: + +1. Remove leading and trailing ASCII whitespace characters (space, tab, carriage return, line feed). +2. Apply Unicode Normalization Form C (NFC). +3. Convert the normalized string to uppercase using Unicode-aware semantics. + +| Input unit | Canonical form | Index | +| ------------ | -------------- | ------------ | +| `sat` | `SAT` | `1967237907` | +| `SAT` | `SAT` | `1967237907` | +| `nuts` | `NUTS` | `1502388627` | +| `USD` | `USD` | `577560378` | +| `usD` | `USD` | `577560378` | +| `café` | `CAFÉ` | `642348965` | +| `cafe\u0301` | `CAFÉ` | `642348965` | +| `eurc` | `EURC` | `1321886550` | + +NOTE: Mints **MUST** make sure that the unit_reference integer has not been repeated before for a different normalized +unit string. + +## Generating keys for the signer. + +For compatibility reasons all signers SHOULD implement the following BIP32 derivation path. + +- m = master key +- 129372' (UTF-8 for 🥜) +- unit_reference = Big endian encoded integer of the first 4 bytes of the sha256 hash of the canonical unit string reference modulo by 2^31. We modulo because we want to stay inside the `2^31 - 1` range. + ex: sha256sum('auth')[:4] = bdf49c3c = 3186924604 + 3186924604 % 2^31 = 1039440956. +- version: uint32 +- index_of_amount = index of and the amounts of the keyset as if the where laid in an array. ex: [1, 2, 4, 8, 16, ...] + +`m / 129372' / unit_reference' / version' / index_of_amount'` + +## Configuration + +The Signer can also be configured to have some rate limiting features. Limiting the amount of minting that can happen by +time or single action. diff --git a/remote-signer.proto b/remote-signer.proto new file mode 100644 index 00000000..5e2bdb36 --- /dev/null +++ b/remote-signer.proto @@ -0,0 +1,145 @@ +syntax = "proto3"; + +package signatory; + +service Signatory { + rpc BlindSign(BlindedMessages) returns (BlindSignResponse); + rpc VerifyProofs(Proofs) returns (BooleanResponse); + // returns all the keysets for the mint + rpc Keysets(EmptyRequest) returns (KeysResponse); + // rotates the keysets + rpc RotateKeyset(RotationRequest) returns (KeyRotationResponse); +} + +enum Operation { + OPERATION_UNSPECIFIED = 0; + OPERATION_MINT = 1; + OPERATION_MELT = 2; + OPERATION_SWAP = 3; +} + +message BlindSignResponse { + Error error = 1; + BlindSignatures sigs = 2; +} + +message BlindedMessages { + repeated BlindedMessage blinded_messages = 1; + Operation operation = 2; + string correlation_id = 3; +} + +// Represents a blinded message +message BlindedMessage { + uint64 amount = 1; + bytes keyset_id = 2; + bytes blinded_secret = 3; +} + +message BooleanResponse { + Error error = 1; + bool success = 2; +} + +message KeyRotationResponse { + Error error = 1; + KeySet keyset = 2; +} + +message KeysResponse { + Error error = 1; + SignatoryKeysets keysets = 2; +} + +message SignatoryKeysets { + bytes pubkey = 1; + repeated KeySet keysets = 2; +} + +message KeySet { + bytes id = 1; + CurrencyUnit unit = 2; + bool active = 3; + uint64 input_fee_ppk = 4; + Keys keys = 5; + uint32 version = 6; + optional uint64 final_expiry = 7; +} + +message Keys { + map keys = 1; +} + +message RotationRequest { + CurrencyUnit unit = 1; + uint64 input_fee_ppk = 2; + repeated uint64 amounts = 3; + // unix timestamp for expiration + optional uint64 final_expiry = 4; +} + +enum CurrencyUnitType { + CURRENCY_UNIT_TYPE_UNSPECIFIED = 0; + CURRENCY_UNIT_TYPE_SAT = 1; + CURRENCY_UNIT_TYPE_MSAT = 2; + CURRENCY_UNIT_TYPE_USD = 3; + CURRENCY_UNIT_TYPE_EUR = 4; + CURRENCY_UNIT_TYPE_AUTH = 5; +} + +message CurrencyUnit { + oneof currency_unit { + CurrencyUnitType unit = 1; + string custom_unit = 2; + } +} + +message Proofs { + repeated Proof proof = 1; + Operation operation = 3; + string correlation_id = 4; +} + +message Proof { + uint64 amount = 1; + bytes keyset_id = 2; + bytes secret = 3; + bytes c = 4; +} + +message BlindSignatures { + repeated BlindSignature blind_signatures = 1; +} + +message BlindSignature { + uint64 amount = 1; + bytes keyset_id = 2; + bytes blinded_secret = 3; + optional BlindSignatureDLEQ dleq = 4; +} + +message BlindSignatureDLEQ { + bytes e = 1; + bytes s = 2; +} + +enum ErrorCode { + ERROR_CODE_UNSPECIFIED = 0; + ERROR_CODE_AMOUNT_OUTSIDE_LIMIT = 1; + ERROR_CODE_DUPLICATE_INPUTS_PROVIDED = 2; + ERROR_CODE_DUPLICATE_OUTPUTS_PROVIDED = 3; + ERROR_CODE_KEYSET_NOT_KNOWN = 4; + ERROR_CODE_KEYSET_INACTIVE = 5; + ERROR_CODE_MINTING_DISABLED = 6; + ERROR_CODE_COULD_NOT_ROTATE_KEYSET = 7; + ERROR_CODE_INVALID_PROOF = 8; + ERROR_CODE_INVALID_BLIND_MESSAGE = 9; + ERROR_CODE_UNIT_NOT_SUPPORTED = 10; +} + +message Error { + ErrorCode code = 1; + string detail = 2; +} + +message EmptyRequest {} diff --git a/tests/xx-tests.md b/tests/xx-tests.md new file mode 100644 index 00000000..8ba873c8 --- /dev/null +++ b/tests/xx-tests.md @@ -0,0 +1,100 @@ +# NUT-XX Test Vectors + +The following are keysets corresponding to the id: `00b5a0580f75cc2f`. + +- `version` = 1 +- `unit` = sat + +```json +{ + "1": "0233501d047ff4058007722d5d24e10a8ff5c723a677be411fff46a3cee9a92cc0", + "2": "03a09803ce40118b8917fafa08409dbe6e8bb36d76c55f4c58400cd720abaf54cb", + "4": "02dac058df2e8611098286ef87ee9698f555548784ab4b1a860c79338073ad8c49", + "8": "025b66b937d65544981817aa9a053a762a7d72a7543c66a54370ea68aa53170a10", + "16": "027cf2ad5fa02b99ea37b305048562828453d89dfa7defcda1c10f6746f25f7541", + "32": "0336033cbbc044737bced1fd40b7f0cb0ce08a83aedaa882ed1ced875a1f517879", + "64": "035be95ecaadbfe67b14f07205d13bbcab5da58bb595c57dfb9b61c5e3e7e4de0e", + "128": "0232c757957a8f5a14e93a9bbe8852c273b985ad238ce9b4d5a16885d8a761462b", + "256": "02cbd889df7d38e95dca2ee0e09bc22e3ae57e95975043854a5560a464f970ac1f", + "512": "02c99a0b72ba8f01c5da765c534e75ae3e5f51e4931bfced18a91df4b9233b168f", + "1024": "0320527abb6ae3dd6db9da5041ca941be679e953b446614843af7a4393e9ac96bc", + "2048": "033f9276b0c5f73fbeb0130eab5705a8e878f4191fe251a18cbd918cda3c9e2d5e", + "4096": "03cf69ed2939be4ac35308560d4423e1a0d96cacf9fe33267c7e6a047bf438e53e", + "8192": "027c8bfff71352766c3870e9f5f577830bbb44eadfb757fdff9a8cd209c4b22d76", + "16384": "02ea21bd310828b9e46746eba2ae985626b3a2efc2468db66ae480715dc6deec8a", + "32768": "027ae7179192282d5b44ac55bff82c13e1ea916ae1edefa33ea64100be7408e015", + "65536": "028f333c1beada3445cb62108e35d72199925a055c1e7c102c742e1761770f6c62", + "131072": "03de95cae3614499a3df2d412e91aa09ddef8b8d49e8d652e3798419da86958139", + "262144": "03c7817c19b4b107eb2ccf2f32b60f9c22a59a1d4a93e492ad01f1505097a654b7", + "524288": "028aad03886b6ec6b9f628090e9c151a73f025aa949a9686dac1f0b32995a4e8df", + "1048576": "034bf50a5916d9f112b8fbfe82a5ac914b5bec792b107cf25922c9866f002473e8", + "2097152": "03d2894e1b1b7ab7497ff69e16d280b630f60ba34fe00edd7c748ae5ee73bc0d1a", + "4194304": "0285ba0ee2960927de958610b13d63fc29019407eb32c477d9a2d016fda3062a37", + "8388608": "03d7a4b4b1b8d6b9f2b5966e380a62f8efd53f79d1965e076a716d2fb75e9774a1", + "16777216": "037a033e2f1df992523df83bcb9aa02cefdadd59882d7949f4500f5493d89fa2fd", + "33554432": "03014de7af4809599cabc6d6b30e5121b4a88153eb38a7b66dd8e50e3166215ab0", + "67108864": "0240162a1d2eb1841450de53a6244a625922b14006153d5219dad0fcf0c369c497", + "134217728": "03f8c6f7b0ee71f66940a33c746c3bf8b1cba793a498dd2fdeb6857552415a4d5d", + "268435456": "02dc9de15fa1332f5a2c8f85045ea127cbc3407fb8a844b453f38e1c9cdce9ef87", + "536870912": "0291bdcb1719b5bf447b2885efc84061d1de30b9d1f583d25034059457a2fd739e", + "1073741824": "02f8a96485e3fa791f57d7f4ef279dd3617b873efbdf673815c49dbf9ce7422b0d", + "2147483648": "02ff8cf3e3de985bb2f286c98e335a175b2b53a0e0d7fa1f53d642c95a372329a2", + "4294967296": "02d96196cc54e7506bfe9fdb4a0d691eed2948ecb9b8e81d28d27225287ad5debc", + "8589934592": "03e64e5664f7ab843f41aaf4c0534d698b3318d140c23cbd2fcc33eece53400dac", + "17179869184": "034c9a4bf7b4cb8fac6ace994624e5250ddac5ac84541b6c8bd12b71d22719bb2d", + "34359738368": "0313027c2b106c7dcdee0d806c3343026260276c6793d4d1dfdf79aae30875be31", + "68719476736": "03081adca96d42cb2ac4ac94e0ea2aac4d9412265ae55ed377e3c0357aa1157253", + "137438953472": "02fdc4118761739425220ba87dee5ea9fdc1d581abfcb506fb5afabf76e172b798", + "274877906944": "031dd7cd25f761c8f80828b487bab1cef730f68e8d6f2026b443cc7223862f6c73", + "549755813888": "02da505eab15744a6fd3fa6b3257bced520d4d294ea94444528fd30d7f90948629", + "1099511627776": "02bfc54369099958275376ab030f2a085532c8a00ae4d1bbfa5031c64b42d58a47", + "2199023255552": "032241a5d4d1e988b8ae85f68a381df0e40065ae8c81b1c4f7ea31c87eab2c0d81", + "4398046511104": "03a681e41990d350cdedd30840f26ad970b4015dd6e6b5c03f7cc99b384bee8762", + "8796093022208": "033d5293a33cda29d65058d6d3a4b821472574e92414fa052c79f8bdc1cd72faba", + "17592186044416": "033ddfec40622aaf62d672f43fd05ddb396afd7ad9f00daede45102c890d3a012b", + "35184372088832": "02564bbdcbed18a8e2d79b2fdad6e5e8a9fe92e853ab23170934d84015cc4b96b0", + "70368744177664": "02170950642b94d0ed232370d5dd3630b5eb7e73791447fb961b12d8139de975de", + "140737488355328": "02b2add5a6eb5dc06f706e9dba190ba412c2c7ba240284b336b66ef38a39e51f1c", + "281474976710656": "03e3e584a4bc1d0a6399f5b6b9355bd67a10ad9f46c8a4283de96854e47eb4357c", + "562949953421312": "033821262e6a78f29dad81d3133845883a7632a47f51ab1d99a0eae4a5354eef45", + "1125899906842624": "038db672a61c70dc66b504152ea39b607527f2f59e8ebfdf8d955c38e914661534", + "2251799813685248": "03dafb9683eac036a422266ddc85b675bf13aeafe0658cad2ec1555c28f4049b28", + "4503599627370496": "0351733345d4bb491e27bdb221e382d00f2248f2ee7f04dc6f3faab2692fbd296c", + "9007199254740992": "03f930c1e6c154ca169370adbec7691fd9c11245867a37ae086f7547f5c9e8386f", + "18014398509481984": "02d700dc30d3cd6be292bddbd5f74c09df784862c785cd763ad6c829be59c21bed", + "36028797018963968": "03444b9c312900fffbd478e390aa6fdf9d3ffe230239141ecadf0bcee25e379512", + "72057594037927936": "03af7acedfcfcaf83cfdb7d171ef64723286bd6e0ab90f3629e627e77955917776", + "144115188075855872": "02e35aef647a881e8c318879fb81b6261df73e385dfbc5ff3fc0ab40f13f5ed560", + "288230376151711744": "024558ed8e986901e05839c34d17c261c8d93b8cabb5dee83ab805bb5028e5e463", + "576460752303423488": "024f60a89ba055e009d84a90a13a7860a909fb486a8ffb4315c2f59aff6fbfd929", + "1152921504606846976": "0311b2a5b91dfaebab4fb125338fd38dab72ec5671e6db5f468cb1477970ea3876", + "2305843009213693952": "02aeaa116d930767b5143cac922511c0e093beee5a2850f67490f5a5bb44a8af76", + "4611686018427387904": "02bf7003847bc8e7ad35ea5c8975e3fdde8d1c43ef540d250cf2dc75792c733647", + "9223372036854775808": "0376b06a13092fbb679f6e7a90ce877c37d5a20714a65567177a91a0479b3e86a9" +} +``` + +Keyset id: `00e1cf6079abb988`. + +- `version` = 1 +- `unit` = auth + +```json +{ + "1": "025b6c1ca8bb741a6f2321c953266df7bf3f3f2c3be8c54c0a6e41bb00976046a4" +} +``` + +This are the expected integer for unit strings: + +```json +{ + "sat" = 866057899 + "msat" = 1980671987 + "auth" = 1039440956 + "eur" = 975082952 + "usd" = 1443872135 + "SAT" = 866057899 + "cafe\u0301" = 84901316 +} +```