|
@@ -0,0 +1,276 @@
|
|
|
|
+This crate is centred around the concept of _credentials_. A credential
|
|
|
|
+contains:
|
|
|
|
+
|
|
|
|
+ - A number of _attributes_ (each of a type called `Scalar`)
|
|
|
|
+ - A _Message Authentication Code (MAC)_, which is two values of type
|
|
|
|
+ `Point`
|
|
|
|
+
|
|
|
|
+Credentials are held by _clients_, and are both issued and validated by
|
|
|
|
+an _issuer_. With CMZ credentials (the kind used in this crate), the
|
|
|
|
+issuer is the only entity that can check whether a given credential is
|
|
|
|
+valid. (Checking the credential requires the same secret key as is used
|
|
|
|
+to create the credential.)
|
|
|
|
+
|
|
|
|
+Your application can have multiple different kinds of credentials, each
|
|
|
|
+with its own set of attributes. All of the credentials in your
|
|
|
|
+application should use the _same_ `Scalar` and `Point` types. You get
|
|
|
|
+these from a mathematical _group_, which must satisfy the trait
|
|
|
|
+[group::prime::PrimeGroup](https://docs.rs/group/0.13.0/group/prime/trait.PrimeGroup.html).
|
|
|
|
+A typical such group would be
|
|
|
|
+[curve25519\_dalek::ristretto::RistrettoPoint](https://docs.rs/curve25519-dalek/4.1.3/curve25519_dalek/ristretto/struct.RistrettoPoint.html).
|
|
|
|
+
|
|
|
|
+To declare a credential type, use the `CMZ!` macro at the top level of
|
|
|
|
+your crate or module (outside of any function):
|
|
|
|
+
|
|
|
|
+```rust
|
|
|
|
+ CMZ! { Lox<RistrettoPoint> :
|
|
|
|
+ id,
|
|
|
|
+ bucket,
|
|
|
|
+ trust_level,
|
|
|
|
+ level_since,
|
|
|
|
+ invites_remaining,
|
|
|
|
+ blockages
|
|
|
|
+ }
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+This declares a _credential type_ called `Lox` using the mathematical
|
|
|
|
+group `RistrettoPoint`. The credential has six attributes, with the
|
|
|
|
+names `id`, `bucket`, etc.
|
|
|
|
+
|
|
|
|
+If you omit the `<RistrettoPoint>`, a default of `<G>` will be assumed,
|
|
|
|
+so you will need to have a group called `G` in scope. For example:
|
|
|
|
+
|
|
|
|
+`use curve25519_dalek::ristretto::RistrettoPoint as G;`
|
|
|
|
+
|
|
|
|
+Note that this macro declares a _type_ for a credential. Your
|
|
|
|
+application may have any number (zero or more) actual credentials of
|
|
|
|
+this type.
|
|
|
|
+
|
|
|
|
+The attribute fields of this credential are of type `Option<Scalar>`.
|
|
|
|
+The field values could be `None` if, for example, a credential is
|
|
|
|
+incomplete (in the process of being issued, and the attributes are not
|
|
|
|
+fully filled in yet), or if an attribute is being hidden from the issuer
|
|
|
|
+(in which case the issuer will see a credential with some of the fields
|
|
|
|
+being `None`).
|
|
|
|
+
|
|
|
|
+## CMZ Protocols
|
|
|
|
+
|
|
|
|
+A _protocol_ is executed by a client and the issuer, and involves:
|
|
|
|
+
|
|
|
|
+ - Proving possession of ("showing") zero or more credentials, which
|
|
|
|
+ may be of the same or different credential types
|
|
|
|
+ - Requesting zero or more new credentials to be issued, which may be
|
|
|
|
+ of the same or different credential types
|
|
|
|
+
|
|
|
|
+Importantly, when a client shows a credential and/or requests for a new
|
|
|
|
+credential to be issued, the attributes of those credentials _are not
|
|
|
|
+necessarily revealed to the issuer_. The protocol defines which
|
|
|
|
+attributes are revealed, and which are hidden. (There are also a few
|
|
|
|
+more options, described below.) For the attributes that are hidden, the
|
|
|
|
+client can nonetheless prove that certain facts about them are true,
|
|
|
|
+using a _zero-knowledge proof_ (which will be automatically created
|
|
|
|
+and checked by the modules generated by this crate).
|
|
|
|
+
|
|
|
|
+### Example
|
|
|
|
+
|
|
|
|
+Suppose we have a credential type called `Wallet`, with two attributes
|
|
|
|
+`randid` (a random id number for the wallet) and `balance` (the amount
|
|
|
|
+of funds in the wallet). We also have a second credential type called
|
|
|
|
+`Item`, representing items that can be purchased, with two attributes
|
|
|
|
+`serialno` (the serial number of the item), and `price` (the price of
|
|
|
|
+the item):
|
|
|
|
+
|
|
|
|
+```rust
|
|
|
|
+CMZ! { Wallet: randid, balance }
|
|
|
|
+CMZ! { Item: serialno, price }
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+Now we want to implement a zero-knowledge protocol by which a client
|
|
|
|
+who holds a wallet with a given balance can buy an item and be issued a
|
|
|
|
+new wallet with the remaining balance. The balance, however, is _not_
|
|
|
|
+revealed to the issuer. To avoid double-spending (using an old wallet
|
|
|
|
+with a larger balance after having spent some of that balance already),
|
|
|
|
+the random id of the wallet will be revealed in each transaction, and
|
|
|
|
+the issuer will reject attempts to use the same random id two or more
|
|
|
|
+times. The new wallet will be created with a fresh random id that is
|
|
|
|
+also unknown to the issuer, so that the issuer cannot track clients from
|
|
|
|
+transaction to transaction. Items for purchase are represented by
|
|
|
|
+credentials that anyone can download from the issuer's website.
|
|
|
|
+
|
|
|
|
+The primary way to create a protocol is with the `CMZProtocol!` macro.
|
|
|
|
+
|
|
|
|
+```rust
|
|
|
|
+ CMZProtocol! { wallet_spend,
|
|
|
|
+ [ W: Wallet { randid: R, balance: H },
|
|
|
|
+ I: Item { serialno: H, price: H } ],
|
|
|
|
+ N: Wallet { randid: J, balance: H },
|
|
|
|
+ N.balance >= 0,
|
|
|
|
+ N.balance + I.price = W.balance
|
|
|
|
+ }
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+The parameters to the macro call are:
|
|
|
|
+
|
|
|
|
+ - an identifier for the protocol
|
|
|
|
+ - a list of zero or more specifications for credentials that will be shown
|
|
|
|
+ - a list of zero or more specifications for credentials that will be issued
|
|
|
|
+ - zero or more statements relating the attributes in the credentials
|
|
|
|
+
|
|
|
|
+Each credential specification list can be:
|
|
|
|
+
|
|
|
|
+ - empty
|
|
|
|
+ - a single credential specification
|
|
|
|
+ - a square-bracketed list of credential specifications
|
|
|
|
+
|
|
|
|
+Each credential specification is:
|
|
|
|
+
|
|
|
|
+ - an identifier for the credential
|
|
|
|
+ - a type for the credential, previously defined with the `CMZ!` macro
|
|
|
|
+ - a braced list of the attributes of the credential (as defined in
|
|
|
|
+ the `CMZ!` macro), annotated with the attribute specification
|
|
|
|
+
|
|
|
|
+An attribute specification for a credential to be shown is one of:
|
|
|
|
+
|
|
|
|
+ - H (hide)
|
|
|
|
+ - R (reveal)
|
|
|
|
+ - I (implicit)
|
|
|
|
+
|
|
|
|
+An attribute specification for a credential to be issued is one of:
|
|
|
|
+
|
|
|
|
+ - H (hide)
|
|
|
|
+ - R (reveal)
|
|
|
|
+ - I (implicit)
|
|
|
|
+ - S (set by issuer)
|
|
|
|
+ - J (joint creation)
|
|
|
|
+
|
|
|
|
+For the attributes:
|
|
|
|
+
|
|
|
|
+ - "hide" means that the attribute is not revealed to the issuer (but
|
|
|
|
+ the statements may still prove things about them).
|
|
|
|
+ - "reveal" means that the attribute is revealed to the issuer.
|
|
|
|
+ - "implicit" means that some other part of the overall system means
|
|
|
|
+ that both the client and the issuer already know what the value of
|
|
|
|
+ this attribute should be, and so it doesn't need to be sent in the
|
|
|
|
+ CMZ protocol (saving some space).
|
|
|
|
+ - "set by issuer", for an attribute in a credential to be issued,
|
|
|
|
+ means that the issuer will choose the value of this attribute, and
|
|
|
|
+ send it back to the client with the issued credential.
|
|
|
|
+ - "joint creation" means that both the client and the issuer will
|
|
|
|
+ contribute a random component to this attribute; the resulting
|
|
|
|
+ attribute will be the sum of those components. The issuer will have
|
|
|
|
+ no information about the resulting attribute value, and the client
|
|
|
|
+ will not be able to predict the resulting attribute value before
|
|
|
|
+ receiving the newly issued credential.
|
|
|
|
+
|
|
|
|
+So in the example, we are creating a protocol called `wallet_spend`,
|
|
|
|
+where the client needs to already have two credentials (their current
|
|
|
|
+Wallet W and the credential I for the item they wish to purchase). The
|
|
|
|
+client will receive back a new Wallet credential N. (Outside of this
|
|
|
|
+protocol, the issuer would likely send the item being purchased to the
|
|
|
|
+client, perhaps using Private Information Retrieval, or something like
|
|
|
|
+that, since the item's serial number and price are hidden from the
|
|
|
|
+issuer in this example protocol.)
|
|
|
|
+
|
|
|
|
+This macro invocation creates a _module_ called `wallet_spend` that
|
|
|
|
+contains definitions of three structs and two functions. The general
|
|
|
|
+flow is:
|
|
|
|
+
|
|
|
|
+ - The client calls the `prepare` function, passing it the two
|
|
|
|
+ credentials to be shown, as well as a partially constructed
|
|
|
|
+ credential to be issued
|
|
|
|
+ - The `prepare` function will output a `Request` struct, and a
|
|
|
|
+ `ClientState` struct.
|
|
|
|
+ - The client will send the `Request` struct to the issuer. (The
|
|
|
|
+ struct has serialization and deserialization methods.)
|
|
|
|
+ - The issuer will call the `handle` function, which, if everything
|
|
|
|
+ checks out, will output the two shown credentials and the newly
|
|
|
|
+ issued credential, with only the attributes visible to the issuer
|
|
|
|
+ filled in. It will also output a `Reply` struct.
|
|
|
|
+ - The issuer will send the `Reply` struct to the client. (Again it
|
|
|
|
+ has serialization and deserialization methods.)
|
|
|
|
+ - The client will pass the `Reply` struct to the `finalize` method of
|
|
|
|
+ the `ClientState` struct it held on to. If everything goes well,
|
|
|
|
+ the `finalize` method will output the completed newly issued
|
|
|
|
+ credential.
|
|
|
|
+
|
|
|
|
+### API
|
|
|
|
+
|
|
|
|
+The generated `wallet_spend::prepare` function (run by the client) has
|
|
|
|
+the following signature:
|
|
|
|
+
|
|
|
|
+```rust
|
|
|
|
+ pub fn prepare(
|
|
|
|
+ W: &Wallet,
|
|
|
|
+ I: &Item,
|
|
|
|
+ N: Wallet,
|
|
|
|
+ ) -> Result<(Request, ClientState), CMZError>
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+You should treat the `Request` and `ClientState` structs as opaque, but
|
|
|
|
+they are currently not, and have `Debug` implemented, so if you wanted,
|
|
|
|
+you could look inside with `println!("{:#?}", request)` or similar.
|
|
|
|
+
|
|
|
|
+You can serialize and deserialize a `Request` struct with
|
|
|
|
+`request.as_bytes()` and `wallet_spend::Request::try_from(bytes)`, or
|
|
|
|
+using `serde` (`Serialize` and `Deserialize` are implemented for
|
|
|
|
+`Request`.)
|
|
|
|
+
|
|
|
|
+The generated `wallet_spend::handle`` function (run by the issuer) has
|
|
|
|
+the following signature:
|
|
|
|
+
|
|
|
|
+```rust
|
|
|
|
+ pub fn handle(
|
|
|
|
+ request: Request,
|
|
|
|
+ fill_creds: F,
|
|
|
|
+ authorize: A,
|
|
|
|
+ ) -> Result<(Reply, (Wallet, Item, Wallet)), CMZError>
|
|
|
|
+ where
|
|
|
|
+ F: FnOnce(&mut Wallet, &mut Item, &mut Wallet) -> Result<(),CMZError>,
|
|
|
|
+ A: FnOnce(&Wallet, &Item, &Wallet) -> Result<(),CMZError>
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+Note that `handle` _consumes_ the `Request`.
|
|
|
|
+
|
|
|
|
+The `handle` function takes two callbacks: `fill_creds` and `authorize`.
|
|
|
|
+The `handle` function will read the request, and use it to fill in
|
|
|
|
+the revealed attributes from the shown and issued credentials (in this case,
|
|
|
|
+just `W.randid`). The hidden attributes from the credentials will be
|
|
|
|
+set to `None`, as will the implicit, set by issuer, and joint creation
|
|
|
|
+attributes. It is the job of the `fill_creds` callback to:
|
|
|
|
+
|
|
|
|
+ - Set the values of the implicit, set by issuer, and the issuer
|
|
|
|
+ contribution to joint creation attributes (if any) for each shown
|
|
|
|
+ and issued credential
|
|
|
|
+ - Set the private keys for each credential
|
|
|
|
+
|
|
|
|
+The `handle` function will then check that the credentials shown by the
|
|
|
|
+client are all valid, and that the statements given in the
|
|
|
|
+`CMZProtocol!` macro call are all true. If not, it will return with an
|
|
|
|
+`Err`. If so, `handle` will call the `authorize` callback, which can do
|
|
|
|
+any final application-specific checks on the credentials (and any other
|
|
|
|
+state it can access in its closure). If `authorize` returns `Err`, so
|
|
|
|
+will `handle`. If `authorize` returns `Ok`, then `handle` will issue
|
|
|
|
+the credentials to be issued (in this case, the new Wallet credential).
|
|
|
|
+It will return a `Reply` struct and copies of the shown and issued
|
|
|
|
+credentials (but the attributes not visible to the issuer will still be
|
|
|
|
+`None` of course).
|
|
|
|
+
|
|
|
|
+The `Reply` struct can be serialized and deserialized in the same way as
|
|
|
|
+the `Request` struct, so that it can be sent back to the client.
|
|
|
|
+
|
|
|
|
+The client will then pass that deserialized `Reply` struct into the
|
|
|
|
+`finalize` method of the `ClientState` struct that was output by
|
|
|
|
+`prepare`, above. The `finalize` method has the following signature:
|
|
|
|
+
|
|
|
|
+```rust
|
|
|
|
+ pub fn finalize(
|
|
|
|
+ self,
|
|
|
|
+ reply: Reply,
|
|
|
|
+ ) -> Result<Wallet, (CMZError, Self)>
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+Note that `finalize` _consumes_ both the `Reply` and also `self`. In
|
|
|
|
+the event of an error (such as a malicious reply impersonating the
|
|
|
|
+issuer?), `self` is returned so you can possibly try again. In the
|
|
|
|
+event of success, the newly issued credentials are returned as a tuple,
|
|
|
|
+or if as in this case, there's just one, as a single element.
|