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.
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());
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 struct
Signer` 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
// 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, ¶ms, &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. Use
PedersenDVSSParticipant. Look at tests
testPedersenDVSS,
testkeygenreconstructiondecentralizedverifiablesecretsharing,
testsignverifydecentralizedverifiablesecretsharingkeygen` for examples of secret sharing, key generation and signing respectively
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!(¶ms.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, ¶ms);
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, ¶ms);
// 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::
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, ¶ms));
let blinded_sig = BlindSignature::new(&sig_req, &sig_key);
User unblinds the signature and verifies correctness of signature
rust
let unblinded_sig = blinded_sig.unblind(&elg_sk);
unblinded_sig.verify(&msgs, &verkey, ¶ms);
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, ¶ms));
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, ¶ms); 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()); ```
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, ¶ms);
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()); ```