Coconut: Threshold Issuance Selective Disclosure Credentials with Applications to Distributed Ledgers

Based on the Coconut paper, arxiv link, NDSS submission. Can use Shamir secret sharing or Pedersen verifiable secret sharing (centalized and decentralized versions available) for key generation. Centralized keygen is done by a trusted third party. This trusted party's role ends at key generation. The decentralized version of Pedersen's secret sharing can be used to avoid the trusted third party.

API

  1. Generate system parameters. rust let threshold = 3; // threshold number of participants let total = 5; // total number of participants let msg_count = 6; // total attributes in credential let count_hidden = 2; // number of attributes hidden from issuers. let params = Params::new(msg_count, "test-label".as_bytes());

  2. Keygen for issuer. This could be a distributed keygen procedure or something done by a trusted third party. For prototyping, the code used the latter. ``rust // Below is an example of keys generated with Shamir secret sharing // signers is a vector of structSigner` which corresponds to a single signer and contains a signer id, a signing and a // verification key pub struct Signer { pub id: usize, pub sigkey: Sigkey, pub verkey: Verkey, } let (, _, signers: Vec) = trustedpartySSSkeygen(threshold, total, &params);

    // Below is an example of Pedersen verifiable secret sharing let (g, h) = PedersenVSS::gens("testPVSS-label".asbytes()); // secret_x and secret_y are the combined secrets and should never be given away. Only use for testing. // x_shares an y_shares are shares of the secrets x and y (vector) // comm_coeff_x and comm_coeff_y are commitments to coefficients of polynomial used to share secret_x and secret_y // signers is a vector of struct Signer like above let ( secretx, secrety, signers, _, commcoeffx, xshares, xtshares, , commcoeffy, yshares, ytshares, ) = trustedpartyPVSS_keygen(threshold, total, &params, &g, &h);

    // Each participant can verify its share as PedersenVSS::verifyshare( threshold, participantid, (&xshares[&i], &xtshares[&i]), &commcoeff_x, &g, &h )

    PedersenVSS::verifyshare( threshold, participantid, (&yshares[j][&i], &ytshares[j][&i]), &commcoeffy[j], &g, &h ) `` Also provided a decentralized (no trusted third party) verifiable secret sharing which can be used for keygen. UsePedersenDVSSParticipant. Look at teststestPedersenDVSS,testkeygenreconstructiondecentralizedverifiablesecretsharing, testsignverifydecentralizedverifiablesecretsharingkeygen` for examples of secret sharing, key generation and signing respectively

  3. User takes his attributes, generates an Elgamal keypair and creates a signature request to be sent to Signers (Issuers) rust let msgs = FieldElementVector::random(msg_count); let (elg_sk, elg_pk) = elgamal_keygen!(&params.g1); // sig_req is the signature request. randomness will be used to create proof of knowledge of // various elements in the signature request let (sig_req, randomness) = SignatureRequest::new(&msgs, count_hidden, &elg_pk, &params);

  4. Create a proof of knowledge of hidden messages, elgamal secret key and others. ```rust // Initiate proof of knowledge of various items of Signature request let sigreqpok = SignatureRequestPoK::init(&sigreq, &elgpk, &params);

    // The challenge can include other things also (if proving other predicates) let challenge = FieldElement::frommsghash(&sigreqpok.to_bytes());

    // Create proof once the challenge is finalized let hiddenmsgs: FieldElementVector = msgs .iter() .take(counthidden) .map(|m| m.clone()) .collect::>() .into(); let sigreqproof = sigreqpok .genproof(&hiddenmsgs, randomness, &elg_sk, &challenge) .unwrap(); ```

  5. Each signer will verify the proof and create a blind signature which is sent back to user. rust assert!(sig_req_proof.verify(&sig_req, &elg_pk, &challenge, &params)); let blinded_sig = BlindSignature::new(&sig_req, &sig_key);

  6. User unblinds the signature and verifies correctness of signature rust let unblinded_sig = blinded_sig.unblind(&elg_sk); unblinded_sig.verify(&msgs, &verkey, &params);

  7. User aggregates the unblinded signatures and verifies correctness of the aggregated signature rust let aggr_sig = Signature::aggregate(threshold, unblinded_sigs); // keys is a vector of tuples containing signer id and verification key (usize, Verkey) let aggr_vk = Verkey::aggregate( threshold, keys ); assert!(aggr_sig.verify(&msgs, &aggr_vk, &params));

  8. To prove knowledge of signature, transform the verkey and signature to an appropriate structure and then use the Schnorr protocol similar to above ```rust let psverkey = transformtoPSverkey(&aggrvk, &params); let pssig = transformtoPSsig(&aggrsig);

    // Empty HashSet indicates that no message is being revealed, only knowledge of signature is proved let pok = PoKOfSignature::init(&pssig, &psverkey, msgs.as_slice(), HashSet::new()).unwrap();

    let chal = FieldElement::frommsghash(&pok.to_bytes());

    let proof = pok.gen_proof(&chal).unwrap();

    // The verifier verifies the proof. Empty HashMap indicates the no message was revealed assert!(proof.verify(&ps_verkey, HashMap::new(), &chal).unwrap()); ```

  9. To prove knowledge of signature and reveal some of the messages, the prover specifies the indices of the messages being revealed to PoKOfSignature. ```rust let psverkey = transformtoPSverkey(&aggrvk, &params); let pssig = transformtoPSsig(&aggrsig);

    // Reveal the following messages to the verifier let mut revealedmsgindices = HashSet::new(); revealedmsgindices.insert(3); revealedmsgindices.insert(5);

    let pok = PoKOfSignature::init(&pssig, &psverkey, msgs.asslice(), revealedmsg_indices.clone()).unwrap();

    let chal = FieldElement::frommsghash(&pok.to_bytes());

    let proof = pok.gen_proof(&chal).unwrap();

    // The prover reveals these messages let mut revealedmsgs = HashMap::new(); for i in &revealedmsgindices { revealedmsgs.insert(i.clone(), msgs[*i].clone()); }

    // The verifier verifies the proof, passing the revealed messages assert!(proof.verify(&psverkey, revealedmsgs.clone(), &chal).unwrap()); ```

Pending

  1. Error handling. Start with asserts in non-test code.
  2. Documentation
  3. Benchmarking