# NUT-00: Notation, Utilization, and Terminology `mandatory` --- This document details the notation and models used throughout the specification and lays the groundwork for understanding the basic cryptography used in the Cashu protocol. - Sending user: `Alice` - Receiving user: `Carol` - Mint: `Bob` # Blind Diffie-Hellmann key exchange (BDHKE) ## Variables - `G` elliptic curve generator point ### Bob (mint) - `k` private key of mint (one for each amount) - `K` public key corresponding to `k` - `C_` blind signature (on `B_`) ### Alice (user) - `x` UTF-8-encoded random string (secret message), corresponds to point `Y = hash_to_curve(x)` on curve - `r` blinding factor (scalar) - `B_` blinded message (curve point) - `C` unblinded signature (curve point) ### `hash_to_curve(x: bytes) -> curve point Y` Deterministically maps a message to a public key point on the secp256k1 curve, utilizing a domain separator to ensure uniqueness. `Y = PublicKey('02' || SHA256(msg_hash || counter))` where `msg_hash` is `SHA256(DOMAIN_SEPARATOR || x)` - `Y` derived public key - `DOMAIN_SEPARATOR` constant byte string `b"Secp256k1_HashToCurve_Cashu_"` - `x` message to hash - `counter` uint32 counter(byte order little endian) incremented from 0 until a point is found that lies on the curve ## Protocol - Mint `Bob` publishes public key `K = kG` - `Alice` picks secret `x` and computes `Y = hash_to_curve(x)` - `Alice` sends to `Bob`: `B_ = Y + rG` with `r` being a random blinding factor (**blinding**) - `Bob` sends back to `Alice` blinded key: `C_ = kB_` (these two steps are the DH key exchange) (**signing**) - `Alice` can calculate the unblinded key as `C_ - rK = kY + krG - krG = kY = C` (**unblinding**) - Alice can take the pair `(x, C)` as a token and can send it to `Carol`. - `Carol` can send `(x, C)` to `Bob` who then checks that `k*hash_to_curve(x) == C` (**verification**), and if so treats it as a valid spend of a token, adding `x` to the list of spent secrets. ## Notation And Conventions These specifications use elliptic curve public key cryptography over the field and curve parameters defined by [secp256k1](http://www.secg.org/sec2-v2.pdf). ### Types Values in these specification are one of: - Scalars: integers modulo the curve order `n` - Curve points: elements of the elliptic curve group (including the identity point at infinity) - Byte sequences ### Byte Operations And Encodings - Concatenation `||` appends one byte sequence to another. - All operations such as point addition, scalar multiplication, hashing, are performed on the underlying mathematical values, not on their serialised byte encodings. - When a curve point is serialised as `hex_str`, it is the hex encoding of the SEC1 compressed public key bytes, unless a NUT specifies a different encoding. - Integer byte order and fixed widths are defined by each NUT where relevant, if a NUT is silent, it must not be assumed. ### Hashing - `SHA256(m)` denotes the SHA 256 hash of byte sequence `m`. - Any hash to curve function must specify its domain separator and exact procedure, including how counters are encoded and incremented. ## 0.1 - Models ### `BlindedMessage` An encrypted ("blinded") secret and an amount is sent from `Alice` to `Bob` for [minting tokens][04] or for [swapping tokens][03]. A `BlindedMessage` is also called an _output_. ```json { "amount": int, "id": hex_str, "B_": hex_str } ``` `amount` is the value for the requested `BlindSignature`, `id` is the requested keyset ID from which we expect a signature, and `B_` is the blinded secret message generated by `Alice`. An array `[BlindedMessage]` is also referred to as `BlindedMessages`. ### `BlindSignature` A `BlindSignature` is sent from `Bob` to `Alice` after [minting tokens][04] or after [swapping tokens][03]. A `BlindSignature` is also called a _promise_. ```json { "amount": int, "id": hex_str, "C_": hex_str } ``` `amount` is the value of the blinded token, `id` is the [keyset id][02] of the mint keys that signed the token, and `C_` is the blinded signature on the secret message `B_` sent in the previous step. ### `Proof` A `Proof` is also called an _input_ and is generated by `Alice` from a `BlindSignature` it received. An array `[Proof]` is called `Proofs`. `Alice` sends `Proofs` to `Bob` for [melting tokens][05]. [Serialized](#serialization-of-tokens) `Proofs` can also be sent from `Alice` to `Carol`. Upon receiving the token, `Carol` deserializes it and requests a [swap][03] from `Bob` to receive new `Proofs`. ```json { "amount": int, "id": hex_str, "secret": str, "C": hex_str, } ``` `amount` is the amount of the `Proof`, `secret` is the secret message and is a utf-8 encoded string (the use of a 64 character hex string generated from 32 random bytes is recommended to prevent fingerprinting), `C` is the unblinded signature on `secret` (hex string), `id` is the [keyset id][02] of the mint public keys that signed the token (hex string). ## 0.2 - Protocol ### Errors In case of an error, mints respond with the HTTP status code `400` and include the following data in their response: ```json { "detail": "oops", "code": 1337 } ``` Here, `detail` is the error message, and `code` is the error code. Error codes are to be defined in the documents concerning the use of a certain API endpoint. ## 0.3 - Methods ### Serialization of tokens Tokens can be serialized to send them between users `Alice` and `Carol`. Serialized tokens have a Cashu token prefix, a versioning flag, and the token. Optionally, a URI prefix for making tokens clickable on the web. We use the following format for token serialization: ```sh cashu[version][token] ``` `cashu` is the Cashu token prefix. `[version]` is a single `base64_urlsafe` character to denote the token format version. ##### URI tags To make Cashu tokens clickable on the web, we use the URI scheme `cashu:`. An example of a serialized token with URI tag is ```sh cashu:cashuAeyJwcm9vZn... ``` ### V3 tokens > _V3 tokens are deprecated and the use of the more space-efficient V4 tokens is encouraged._ ##### Version This token format has the `[version]` value `A`. ##### Format V3 tokens are base64-encoded JSON objects. The token format supports tokens from multiple mints. The JSON is serialized with a `base64_urlsafe` (base64 encoding with `/` replaced by `_` and `+` by `-`). `base64_urlsafe` strings may have padding characters (usually `=`) at the end which can be omitted. Clients need to be able to decode both cases. ```sh cashuA[base64_token_json] ``` `[base64_token_json]` is the token JSON serialized in `base64_urlsafe`. `[base64_token_json]` should be cleared of any whitespace before serializing. ##### Token format The deserialized `base64_token_json` is ```json { "token": [ { "mint": str, "proofs": Proofs }, ... ], "unit": str , "memo": str } ``` `mint` is the mint URL. The mint URL must be stripped of any trailing slashes (`/`). `Proofs` is an array of `Proof` objects. The next two elements are only for displaying the receiving user appropriate information: `unit` is the currency unit of the token keysets (see [Keysets][01] for supported units), and `memo` is an optional text memo from the sender. ##### Example Below is a TokenV3 JSON before `base64_urlsafe` serialization. ```json { "token": [ { "mint": "https://8333.space:3338", "proofs": [ { "amount": 2, "id": "009a1f293253e41e", "secret": "407915bc212be61a77e3e6d2aeb4c727980bda51cd06a6afc29e2861768a7837", "C": "02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea" }, { "amount": 8, "id": "009a1f293253e41e", "secret": "fe15109314e61d7756b0f8ee0f23a624acaa3f4e042f61433c728c7057b931be", "C": "029e8e5050b890a7d6c0968db16bc1d5d5fa040ea1de284f6ec69d61299f671059" } ] } ], "unit": "sat", "memo": "Thank you." } ``` When serialized, this becomes: ``` cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6IjQwNzkxNWJjMjEyYmU2MWE3N2UzZTZkMmFlYjRjNzI3OTgwYmRhNTFjZDA2YTZhZmMyOWUyODYxNzY4YTc4MzciLCJDIjoiMDJiYzkwOTc5OTdkODFhZmIyY2M3MzQ2YjVlNDM0NWE5MzQ2YmQyYTUwNmViNzk1ODU5OGE3MmYwY2Y4NTE2M2VhIn0seyJhbW91bnQiOjgsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmUiLCJDIjoiMDI5ZThlNTA1MGI4OTBhN2Q2YzA5NjhkYjE2YmMxZDVkNWZhMDQwZWExZGUyODRmNmVjNjlkNjEyOTlmNjcxMDU5In1dfV0sInVuaXQiOiJzYXQiLCJtZW1vIjoiVGhhbmsgeW91LiJ9 ``` ### V4 tokens V4 tokens are a space-efficient way of serializing tokens using the CBOR binary format. All keys are single characters and hex strings are encoded in binary. V4 tokens can only hold proofs from a single mint. ##### Version This token format has the `[version]` value `B`. ##### Format Wallets serialize tokens in a `base64_urlsafe` format (base64 encoding with `/` replaced by `_` and `+` by `-`). `base64_urlsafe` strings may have padding characters (usually `=`) at the end which can be omitted. Clients need to be able to decode both cases. ```sh cashuB[base64_token_cbor] ``` ##### Token format For readability, the structure of a TokenV4 object is shown below in its equivalent JSON form. ```json { "m": str, // mint URL "u": str, // currency unit "d": str , // optional memo "t": [ { "i": bytes, // keyset ID (short or long form) "p": [ // proofs with this keyset ID { "a": int, // amount "s": str, // secret "c": bytes, // signature "d": { // optional DLEQ proof "e": bytes, "s": bytes, "r": bytes }, "w": str // optional witness }, ... ] }, ... ], } ``` `m` is the mint URL. The mint URL **MUST** be normalized by stripping any trailing slashes (`/`). `u` is the currency unit of the token keysets. Supported units are defined in [Keysets][01]. `d` is an optional, human-readable memo provided by the sender. Within the `t` (token) array, `i` denotes the keyset ID associated with the proofs contained in `p` which are grouped by `i`. `p` is an array of `Proof` objects with the original keyset ID field `id` omitted. All proofs in the corresponding `p` array MUST belong to the same keyset ID. Unless otherwise stated, fields of type `bytes` represent byte strings in the CBOR encoding. In the original JSON representation of `Proof` objects, these values are encoded as hexadecimal strings. Implementations MUST convert between hex strings and raw byte arrays when translating between JSON and CBOR representations. Optional fields MAY be omitted if not present. Receivers MUST ignore unknown fields to preserve forward compatibility. #### Short keyset ID To reduce the size of the `i` field and the overall Token encoding, wallets **MAY** use the short keyset ID representation (`s_id`). The short keyset ID is defined as the first 8 bytes of the full 33-byte keyset ID: - Byte form: `s_id = id_bytes[:8]` - Hex form: `s_id = id_hex_str[:16]` Wallets receiving a Token **MUST** support both short and full keyset ID representations. When a short keyset ID is encountered, the wallet **MUST** resolve it to the corresponding full keyset ID before processing the contained `Proof` objects. If a short keyset ID resolves to more than one known full keyset ID, the identifier is considered ambiguous. In this case, the wallet **MUST** fail token parsing and return an error. The mint is unaware of the `s_id`. All API endpoints exposed by the mint use the full keyset ID. ##### Example Below is a TokenV4 JSON before CBOR and `base64_urlsafe` serialization. ```json { "t": [ { "i": h'00ffd48b8f5ecf80', "p": [ { "a": 1, "s": "acc12435e7b8484c3cf1850149218af90f716a52bf4a5ed347e48ecc13f77388", "c": h'0244538319de485d55bed3b29a642bee5879375ab9e7a620e11e48ba482421f3cf', }, ], }, { "i": h'00ad268c4d1f5826', "p": [ { "a": 2, "s": "1323d3d4707a58ad2e23ada4e9f1f49f5a5b4ac7b708eb0d61f738f48307e8ee", "c": h'023456aa110d84b4ac747aebd82c3b005aca50bf457ebd5737a4414fac3ae7d94d', }, { "a": 1, "s": "56bcbcbb7cc6406b3fa5d57d2174f4eff8b4402b176926d3a57d3c3dcbb59d57", "c": h'0273129c5719e599379a974a626363c333c56cafc0e6d01abe46d5808280789c63', }, ], }, ], "m": "http://localhost:3338", "u": "sat", } ``` The `h''` values are `bytes` but displayed as hex strings here. We serialize this JSON using CBOR which can be seen [here](https://cbor.nemo157.com/#type=hex&value=a3617482a261694800ffd48b8f5ecf80617081a36161016173784061636331323433356537623834383463336366313835303134393231386166393066373136613532626634613565643334376534386563633133663737333838616358210244538319de485d55bed3b29a642bee5879375ab9e7a620e11e48ba482421f3cfa261694800ad268c4d1f5826617082a3616102617378403133323364336434373037613538616432653233616461346539663166343966356135623461633762373038656230643631663733386634383330376538656561635821023456aa110d84b4ac747aebd82c3b005aca50bf457ebd5737a4414fac3ae7d94da36161016173784035366263626362623763633634303662336661356435376432313734663465666638623434303262313736393236643361353764336333646362623539643537616358210273129c5719e599379a974a626363c333c56cafc0e6d01abe46d5808280789c63616d75687474703a2f2f6c6f63616c686f73743a33333338617563736174). The resulting bytes are then serialized to a string using `base64_urlsafe` and the prefix `cashuB` is added. This leaves us with the following serialized TokenV4: ``` cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA ``` #### Binary Token Token can be transmitted in a binary format when applicable (for example when transmitting via NFC). For this the serialised token is prepended with a prefix and a version byte. ``` utf8("craw") || utf8() || ``` - Binary Encoding V4: `utf8("craw") || utf8("B") || cbor(token_v4_object)` [00]: 00.md [01]: 01.md [02]: 02.md [03]: 03.md [04]: 04.md [05]: 05.md [06]: 06.md [07]: 07.md [08]: 08.md [09]: 09.md [10]: 10.md [11]: 11.md [12]: 12.md