From d7323ebc2f126109f39e5fc1bb7ac05bd3c8ee85 Mon Sep 17 00:00:00 2001 From: David Venhoek Date: Fri, 27 Nov 2020 15:08:40 +0100 Subject: [PATCH] Implemented new keyshare protocol --- builder.go | 32 ++- credential.go | 6 +- gabi_test.go | 357 +++++++++++++++++++++++-- internal/common/hashtool.go | 20 +- internal/common/hashtool_test.go | 13 +- issuer.go | 2 +- keyproof/quasisafeprimeproduct_test.go | 4 +- keyproof/validkeyproof.go | 4 +- keys.go | 1 + keyshare.go | 72 ++++- prooflist.go | 81 ++++-- proofs.go | 67 ++++- revocation/proof.go | 2 +- revocation/revocation_test.go | 2 +- 14 files changed, 572 insertions(+), 91 deletions(-) diff --git a/builder.go b/builder.go index a4814ab..11dd3e5 100644 --- a/builder.go +++ b/builder.go @@ -18,11 +18,13 @@ import ( // IssueCommitmentMessage encapsulates the messages sent by the receiver to the // issuer in the second step of the issuance protocol. type IssueCommitmentMessage struct { - U *big.Int `json:"U,omitempty"` - Nonce2 *big.Int `json:"n_2"` - Proofs ProofList `json:"combinedProofs"` - ProofPjwt string `json:"proofPJwt,omitempty"` - ProofPjwts map[string]string `json:"proofPJwts,omitempty"` + U *big.Int `json:"U,omitempty"` + Nonce2 *big.Int `json:"n_2"` + Proofs ProofList `json:"combinedProofs"` + ProofPjwt string `json:"proofPJwt,omitempty"` + ProofPjwts map[string]string `json:"proofPJwts,omitempty"` + KeyshareWs map[string]*big.Int `json:"keyshareWs,omitempty"` + KeyshareContribution string `json:"keyshareContribution,omitempty"` } // UnmarshalJSON implements json.Unmarshaler (json's default unmarshaler @@ -188,7 +190,7 @@ func (b *CredentialBuilder) ConstructCredential(msg *IssueSignatureMessage, attr func (b *CredentialBuilder) proveCommitment(nonce1 *big.Int) Proof { sCommit, _ := common.RandomBigInt(b.pk.Params.LsCommit) contrib := b.Commit(map[string]*big.Int{"secretkey": sCommit}) - c := createChallenge(b.context, nonce1, contrib, false) + c := createChallenge(b.context, nonce1, contrib, nil, false) return b.CreateProof(c) } @@ -210,6 +212,8 @@ type CredentialBuilder struct { mUser map[int]*big.Int // Map of users shares of random blind attributes mUserCommit map[int]*big.Int + + newStyleKeyshareSession bool } func (b *CredentialBuilder) MergeProofPCommitment(commitment *ProofPCommitment) { @@ -220,6 +224,11 @@ func (b *CredentialBuilder) MergeProofPCommitment(commitment *ProofPCommitment) ) } +func (b *CredentialBuilder) MergeKeyshareP(keyshareP *big.Int) { + b.proofPcomm = &ProofPCommitment{P: new(big.Int).Set(keyshareP)} + b.newStyleKeyshareSession = true +} + // PublicKey returns the Idemix public key against which the credential will verify. func (b *CredentialBuilder) PublicKey() *PublicKey { return b.pk @@ -265,8 +274,17 @@ func (b *CredentialBuilder) CreateProof(challenge *big.Int) Proof { mUserResponses[i] = new(big.Int).Add(b.mUserCommit[i], new(big.Int).Mul(challenge, miUser)) } + var U *big.Int + if b.newStyleKeyshareSession { + U = new(big.Int).Mul(b.u, b.proofPcomm.P) + U.Mod(U, b.pk.N) + } else { + // U can be modified in the proof, so make sure this is a copy + U = new(big.Int).Set(b.u) + } + return &ProofU{ - U: b.u, + U: U, C: challenge, VPrimeResponse: vPrimeResponse, SResponse: sResponse, diff --git a/credential.go b/credential.go index 93af7b4..b668b0a 100644 --- a/credential.go +++ b/credential.go @@ -99,7 +99,7 @@ func (ic *Credential) CreateDisclosureProof(disclosedAttributes []int, nonrev bo if err != nil { return nil, err } - challenge := ProofBuilderList{builder}.Challenge(context, nonce1, false) + challenge := ProofBuilderList{builder}.Challenge(context, nonce1, nil, false) return builder.CreateProof(challenge).(*ProofD), nil } @@ -224,6 +224,10 @@ func (d *DisclosureProofBuilder) MergeProofPCommitment(commitment *ProofPCommitm ) } +func (d *DisclosureProofBuilder) MergeKeyshareP(keyshareP *big.Int) { + //nop +} + // PublicKey returns the Idemix public key against which this disclosure proof will verify. func (d *DisclosureProofBuilder) PublicKey() *PublicKey { return d.pk diff --git a/gabi_test.go b/gabi_test.go index 99649cc..9430e16 100644 --- a/gabi_test.go +++ b/gabi_test.go @@ -132,6 +132,7 @@ func setupParameters() error { testPrivK = NewPrivateKey(p, q, "", 0, time.Now().AddDate(1, 0, 0)) testPubK = NewPublicKey(n, Z, S, nil, nil, R, "", 0, time.Now().AddDate(1, 0, 0)) + testPubK.KeyID = "testPubK" var err error testPrivK1, err = NewPrivateKeyFromXML(xmlPrivKey1, false) @@ -142,6 +143,7 @@ func setupParameters() error { if err != nil { return err } + testPubK1.KeyID = "testPubK1" testPrivK2, err = NewPrivateKeyFromXML(xmlPrivKey2, false) if err != nil { return err @@ -150,6 +152,7 @@ func setupParameters() error { if err != nil { return err } + testPubK2.KeyID = "testPubK2" return nil } @@ -234,11 +237,11 @@ func TestProofU(t *testing.T) { secret, _ := common.RandomBigInt(DefaultSystemParameters[keylength].Lm) b := NewCredentialBuilder(testPubK, context, secret, nonce2, nil) - proofU := b.CreateProof(createChallenge(context, nonce1, b.Commit(map[string]*big.Int{"secretkey": secret}), false)) + proofU := b.CreateProof(createChallenge(context, nonce1, b.Commit(map[string]*big.Int{"secretkey": secret}), nil, false)) - contrib, err := proofU.ChallengeContribution(testPubK) + contrib, err := proofU.ChallengeContribution(testPubK, nil) require.NoError(t, err) - assert.True(t, proofU.VerifyWithChallenge(testPubK, createChallenge(context, nonce1, contrib, false)), "ProofU does not verify, whereas it should.") + assert.True(t, proofU.VerifyWithChallenge(testPubK, createChallenge(context, nonce1, contrib, nil, false)), "ProofU does not verify, whereas it should.") } func TestProofULogged(t *testing.T) { @@ -265,7 +268,7 @@ func TestCommitmentMessage(t *testing.T) { b := NewCredentialBuilder(testPubK, context, secret, nonce2, nil) msg := b.CommitToSecretAndProve(nonce1) - assert.True(t, msg.Proofs.Verify([]*PublicKey{testPubK}, context, nonce1, false, nil), "Commitment message proof does not verify, whereas it should.") + assert.True(t, msg.Proofs.Verify([]*PublicKey{testPubK}, context, nonce1, false, nil, nil, nil), "Commitment message proof does not verify, whereas it should.") } func TestProofS(t *testing.T) { @@ -373,9 +376,9 @@ func TestCombinedShowingProof(t *testing.T) { b2, err := cred2.CreateDisclosureProofBuilder([]int{1, 3}, false) require.NoError(t, err) builders := ProofBuilderList([]ProofBuilder{b1, b2}) - prooflist := builders.BuildProofList(context, nonce1, false) + prooflist := builders.BuildProofList(context, nonce1, nil, false) - assert.True(t, prooflist.Verify([]*PublicKey{issuer1.Pk, issuer2.Pk}, context, nonce1, false, nil), "Prooflist does not verify whereas it should!") + assert.True(t, prooflist.Verify([]*PublicKey{issuer1.Pk, issuer2.Pk}, context, nonce1, false, nil, nil, nil), "Prooflist does not verify whereas it should!") } // A convenience function for initializing big integers from known correct (10 @@ -463,11 +466,11 @@ func TestFullBoundIssuanceAndShowing(t *testing.T) { db, err := cred1.CreateDisclosureProofBuilder([]int{1, 2}, false) require.NoError(t, err) builders := ProofBuilderList([]ProofBuilder{db, cb2}) - prooflist := builders.BuildProofList(context, nonce1, false) + prooflist := builders.BuildProofList(context, nonce1, nil, false) commitMsg2 := cb2.CreateIssueCommitmentMessage(prooflist) - assert.True(t, commitMsg2.Proofs.Verify([]*PublicKey{testPubK, testPubK}, context, nonce1, false, nil), "Proofs in commit message do not verify!") + assert.True(t, commitMsg2.Proofs.Verify([]*PublicKey{testPubK, testPubK}, context, nonce1, false, nil, nil, nil), "Proofs in commit message do not verify!") msg, err := issuer2.IssueSignature(commitMsg2.U, testAttributes1, nil, nonce2, nil) assert.NoError(t, err, "error creating Issue Signature") @@ -573,11 +576,11 @@ func TestFullBoundIssuanceAndShowingRandomIssuers(t *testing.T) { db, err := cred1.CreateDisclosureProofBuilder([]int{1, 2}, false) require.NoError(t, err) builders := ProofBuilderList([]ProofBuilder{db, cb2}) - prooflist := builders.BuildProofList(context, nonce1, false) + prooflist := builders.BuildProofList(context, nonce1, nil, false) commitMsg := cb2.CreateIssueCommitmentMessage(prooflist) - assert.True(t, commitMsg.Proofs.Verify([]*PublicKey{issuer1.Pk, issuer2.Pk}, context, nonce1, false, nil), "Proofs in commit message do not verify!") + assert.True(t, commitMsg.Proofs.Verify([]*PublicKey{issuer1.Pk, issuer2.Pk}, context, nonce1, false, nil, nil, nil), "Proofs in commit message do not verify!") msg, err := issuer2.IssueSignature(commitMsg.U, testAttributes2, nil, nonce2, nil) assert.NoError(t, err, "error creating Issue Signature") @@ -613,11 +616,11 @@ func TestWronglyBoundIssuanceAndShowingWithDifferentIssuers(t *testing.T) { db, err := cred1.CreateDisclosureProofBuilder([]int{1, 2}, false) require.NoError(t, err) builders := ProofBuilderList([]ProofBuilder{db, cb2}) - prooflist := builders.BuildProofList(context, nonce1, false) + prooflist := builders.BuildProofList(context, nonce1, nil, false) commitMsg := cb2.CreateIssueCommitmentMessage(prooflist) - assert.False(t, commitMsg.Proofs.Verify([]*PublicKey{issuer1.Pk, issuer2.Pk}, context, nonce1, false, nil), "Proofs in commit message verify, whereas they should not!") + assert.False(t, commitMsg.Proofs.Verify([]*PublicKey{issuer1.Pk, issuer2.Pk}, context, nonce1, false, nil, nil, nil), "Proofs in commit message verify, whereas they should not!") } func TestBigAttribute(t *testing.T) { @@ -694,7 +697,7 @@ func TestNotRevoked(t *testing.T) { proofd, err := cred.CreateDisclosureProof([]int{1, 2}, true, context, nonce) require.NoError(t, err) require.NotNil(t, proofd.NonRevocationProof) - require.True(t, ProofList{proofd}.Verify([]*PublicKey{testPubK}, context, nonce, false, nil)) + require.True(t, ProofList{proofd}.Verify([]*PublicKey{testPubK}, context, nonce, false, nil, nil, nil)) } func TestRevoked(t *testing.T) { @@ -771,10 +774,10 @@ func TestKeyshare(t *testing.T) { secret, err := NewKeyshareSecret() require.NoError(t, err) - commit, W, err := NewKeyshareCommitments(secret, []*PublicKey{testPubK}) + commit, W, err := NewProofPCommitments(secret, []*PublicKey{testPubK}) require.NoError(t, err) - response := KeyshareResponse(secret, commit, big.NewInt(123), testPubK) + response := KeyshareProofP(secret, commit, big.NewInt(123), testPubK) assert.Equal(t, new(big.Int).Exp(testPubK.R[0], response.SResponse, testPubK.N), new(big.Int).Mod( new(big.Int).Mul( @@ -803,9 +806,9 @@ func TestRandomBlindProofU(t *testing.T) { assert.Len(t, proofU.MUserResponses, 1) assert.Contains(t, proofU.MUserResponses, 2+1) - c, err := proofU.ChallengeContribution(testPubK) + c, err := proofU.ChallengeContribution(testPubK, nil) assert.NoError(t, err) - assert.True(t, proofU.VerifyWithChallenge(testPubK, createChallenge(context, nonce1, c, false))) + assert.True(t, proofU.VerifyWithChallenge(testPubK, createChallenge(context, nonce1, c, nil, false))) } // Tests CreateProof() and Commit() @@ -817,10 +820,10 @@ func TestRandomBlindCreateProofUandCommit(t *testing.T) { secret, _ := common.RandomBigInt(DefaultSystemParameters[keylength].Lm) b := NewCredentialBuilder(testPubK, context, secret, nonce2, []int{2}) - proofU := b.CreateProof(createChallenge(context, nonce1, b.Commit(map[string]*big.Int{"secretkey": secret}), false)) - c, err := proofU.ChallengeContribution(testPubK) + proofU := b.CreateProof(createChallenge(context, nonce1, b.Commit(map[string]*big.Int{"secretkey": secret}), nil, false)) + c, err := proofU.ChallengeContribution(testPubK, nil) assert.NoError(t, err) - assert.True(t, proofU.VerifyWithChallenge(testPubK, createChallenge(context, nonce1, c, false)), "ProofU does not verify, whereas it should.") + assert.True(t, proofU.VerifyWithChallenge(testPubK, createChallenge(context, nonce1, c, nil, false)), "ProofU does not verify, whereas it should.") } func TestRandomBlindIssuance(t *testing.T) { @@ -940,6 +943,320 @@ func TestConstructCredentialNonZeroRandomBlindAttributes(t *testing.T) { assert.EqualError(t, err, "attribute at random blind index should be nil before issuance") } +func TestCreateChallengeKeyshareCompatibility(t *testing.T) { + context, err := common.RandomBigInt(testPubK.Params.Lh) + require.NoError(t, err) + nonce, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + a, err := common.RandomBigInt(256) + require.NoError(t, err) + b, err := common.RandomBigInt(256) + require.NoError(t, err) + + c1 := createChallenge(context, nonce, []*big.Int{a}, []*big.Int{b}, false) + k := createChallenge(context, nonce, []*big.Int{a}, nil, false) + c2 := KeyshareChallenge(k, map[string]*big.Int{"b": b}) + assert.Equal(t, 0, c1.Cmp(c2), "Keyshare and discloser dont agree on challenge") +} + +func TestFullIssuanceAndShowingWithOldKeyshare(t *testing.T) { + context, err := common.RandomBigInt(testPubK.Params.Lh) + require.NoError(t, err) + nonce1, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + nonce2, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + secret, err := common.RandomBigInt(testPubK.Params.Lm) + require.NoError(t, err) + kssecret, err := NewKeyshareSecret() + require.NoError(t, err) + + // Issuance + builder := NewCredentialBuilder(testPubK, context, secret, nonce2, nil) + ksCommit, ProofPCommitment, err := NewProofPCommitments(kssecret, []*PublicKey{testPubK}) + require.NoError(t, err) + builder.MergeProofPCommitment(ProofPCommitment[0]) + challenge := ProofBuilderList{builder}.Challenge(context, nonce1, nil, false) + userProof := builder.CreateProof(challenge) + proofP := KeyshareProofP(kssecret, ksCommit, challenge, testPubK) + userProof.MergeProofP(proofP, testPubK) + commitMsg := builder.CreateIssueCommitmentMessage(ProofList{userProof}) + + assert.True(t, commitMsg.Proofs.Verify([]*PublicKey{testPubK}, context, nonce1, false, []bool{true}, nil, nil), "Issuance proof not valid") + issuer := NewIssuer(testPrivK, testPubK, context) + sigMsg, err := issuer.IssueSignature(commitMsg.Proofs[0].(*ProofU).U, testAttributes1, nil, nonce2, nil) + assert.NoError(t, err, "Error in IssueSignature") + + cred, err := builder.ConstructCredential(sigMsg, testAttributes1) + require.NoError(t, err, "Error in credential construction") + + // Showing + n1, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + disclosed := []int{1, 2} + + discloseBuilder, err := cred.CreateDisclosureProofBuilder(disclosed, false) + require.NoError(t, err) + discloseKsCommit, discloseProofPCommitment, err := NewProofPCommitments(kssecret, []*PublicKey{testPubK}) + discloseBuilder.MergeProofPCommitment(discloseProofPCommitment[0]) + discloseChallenge := ProofBuilderList{discloseBuilder}.Challenge(context, n1, nil, false) + discloseUserProof := discloseBuilder.CreateProof(discloseChallenge) + discloseProofP := KeyshareProofP(kssecret, discloseKsCommit, discloseChallenge, testPubK) + discloseUserProof.MergeProofP(discloseProofP, testPubK) + assert.True(t, discloseUserProof.(*ProofD).Verify(testPubK, context, n1, false), "proof of disclosure does not verify, whereas it should.") +} + +func TestMixedSessionOldKeyshare(t *testing.T) { + context, err := common.RandomBigInt(testPubK.Params.Lh) + require.NoError(t, err) + nonce1, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + nonce2, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + secret, err := common.RandomBigInt(testPubK.Params.Lm) + require.NoError(t, err) + kssecret, err := NewKeyshareSecret() + require.NoError(t, err) + + // Issuance + builder := NewCredentialBuilder(testPubK, context, secret, nonce2, nil) + ksCommit, ProofPCommitment, err := NewProofPCommitments(kssecret, []*PublicKey{testPubK}) + require.NoError(t, err) + builder.MergeProofPCommitment(ProofPCommitment[0]) + challenge := ProofBuilderList{builder}.Challenge(context, nonce1, nil, false) + userProof := builder.CreateProof(challenge) + proofP := KeyshareProofP(kssecret, ksCommit, challenge, testPubK) + userProof.MergeProofP(proofP, testPubK) + commitMsg := builder.CreateIssueCommitmentMessage(ProofList{userProof}) + + assert.True(t, commitMsg.Proofs.Verify([]*PublicKey{testPubK}, context, nonce1, false, []bool{true}, nil, nil), "Issuance proof not valid") + issuer := NewIssuer(testPrivK, testPubK, context) + sigMsg, err := issuer.IssueSignature(commitMsg.Proofs[0].(*ProofU).U, testAttributes1, nil, nonce2, nil) + assert.NoError(t, err, "Error in IssueSignature") + + cred1, err := builder.ConstructCredential(sigMsg, testAttributes1) + require.NoError(t, err, "Error in credential construction") + + // prepare second (non-Keyshare) cred + issuer2 := NewIssuer(testPrivK2, testPubK2, context) + cred2 := createCredential(t, context, secret, issuer2) + + // Disclosure + n1, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + b1, err := cred1.CreateDisclosureProofBuilder([]int{1, 2}, false) + require.NoError(t, err) + b2, err := cred2.CreateDisclosureProofBuilder([]int{1, 3}, false) + require.NoError(t, err) + builders := ProofBuilderList([]ProofBuilder{b1, b2}) + discloseCommit, discloseProofPCommits, err := NewProofPCommitments(kssecret, []*PublicKey{testPubK}) + require.NoError(t, err) + b1.MergeProofPCommitment(discloseProofPCommits[0]) + discloseChallenge := builders.Challenge(context, n1, nil, false) + discloseProofP := KeyshareProofP(kssecret, discloseCommit, discloseChallenge, testPubK) + proofs, err := builders.BuildDistributedProofList(discloseChallenge, []*ProofP{discloseProofP, nil}) + require.NoError(t, err) + assert.True(t, proofs.Verify([]*PublicKey{testPubK, testPubK2}, context, n1, false, []bool{true, false}, nil, nil)) +} + +func TestFullIssuanceAndShowingWithNewKeyshare(t *testing.T) { + context, err := common.RandomBigInt(testPubK.Params.Lh) + require.NoError(t, err) + nonce1, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + nonce2, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + secret, err := common.RandomBigInt(testPubK.Params.Lm) + require.NoError(t, err) + kssecret, err := NewKeyshareSecret() + require.NoError(t, err) + + builder := NewCredentialBuilder(testPubK, context, secret, nonce2, nil) + Ps := KeysharePs(kssecret, []*PublicKey{testPubK}) + builder.MergeKeyshareP(Ps["testPubK"]) + k := ProofBuilderList{builder}.Challenge(context, nonce1, nil, false) + ksCommit, Ws, err := NewKeyshareCommitments([]*PublicKey{testPubK}) + require.NoError(t, err) + challenge := KeyshareChallenge(k, Ws) + userProof := builder.CreateProof(challenge) + keyshareContribution := KeyshareResponse(userProof.SecretKeyResponse(), kssecret, ksCommit, challenge) + userProof.MergeKeyshareContribution(keyshareContribution) + commitMsg := builder.CreateIssueCommitmentMessage(ProofList{userProof}) + + assert.True(t, commitMsg.Proofs.Verify([]*PublicKey{testPubK}, context, nonce1, false, []bool{true}, Ws, keyshareContribution), "Issuance proof not valid") + issuer := NewIssuer(testPrivK, testPubK, context) + sigMsg, err := issuer.IssueSignature(commitMsg.Proofs[0].(*ProofU).U, testAttributes1, nil, nonce2, nil) + assert.NoError(t, err, "Error in IssueSignature") + + cred, err := builder.ConstructCredential(sigMsg, testAttributes1) + require.NoError(t, err, "error in credential construction") + + // Showing + n1, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + disclosed := []int{1, 2} + + discloseBuilder, err := cred.CreateDisclosureProofBuilder(disclosed, false) + discloseK := ProofBuilderList{discloseBuilder}.Challenge(context, n1, nil, false) + discloseKsCommit, Ws, err := NewKeyshareCommitments([]*PublicKey{testPubK}) + require.NoError(t, err) + discloseChallenge := KeyshareChallenge(discloseK, Ws) + discloseUserProof := discloseBuilder.CreateProof(discloseChallenge) + discloseKeyshareResponse := KeyshareResponse(discloseUserProof.SecretKeyResponse(), kssecret, discloseKsCommit, discloseChallenge) + discloseUserProof.MergeKeyshareContribution(discloseKeyshareResponse) + assert.True(t, ProofList{discloseUserProof}.Verify([]*PublicKey{testPubK}, context, n1, false, []bool{true}, Ws, discloseKeyshareResponse), "Disclosure not valid") +} + +func TestMixedSessionNewKeyshare(t *testing.T) { + context, err := common.RandomBigInt(testPubK.Params.Lh) + require.NoError(t, err) + nonce1, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + nonce2, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + secret, err := common.RandomBigInt(testPubK.Params.Lm) + require.NoError(t, err) + kssecret, err := NewKeyshareSecret() + require.NoError(t, err) + + builder := NewCredentialBuilder(testPubK, context, secret, nonce2, nil) + Ps := KeysharePs(kssecret, []*PublicKey{testPubK}) + builder.MergeKeyshareP(Ps["testPubK"]) + k := ProofBuilderList{builder}.Challenge(context, nonce1, nil, false) + ksCommit, Ws, err := NewKeyshareCommitments([]*PublicKey{testPubK}) + require.NoError(t, err) + challenge := KeyshareChallenge(k, Ws) + userProof := builder.CreateProof(challenge) + keyshareContribution := KeyshareResponse(userProof.SecretKeyResponse(), kssecret, ksCommit, challenge) + userProof.MergeKeyshareContribution(keyshareContribution) + commitMsg := builder.CreateIssueCommitmentMessage(ProofList{userProof}) + + assert.True(t, commitMsg.Proofs.Verify([]*PublicKey{testPubK}, context, nonce1, false, []bool{true}, Ws, keyshareContribution), "Issuance proof not valid") + issuer := NewIssuer(testPrivK, testPubK, context) + sigMsg, err := issuer.IssueSignature(commitMsg.Proofs[0].(*ProofU).U, testAttributes1, nil, nonce2, nil) + assert.NoError(t, err, "Error in IssueSignature") + + cred1, err := builder.ConstructCredential(sigMsg, testAttributes1) + require.NoError(t, err, "error in credential construction") + + // prepare second (non-Keyshare) cred + issuer2 := NewIssuer(testPrivK2, testPubK2, context) + cred2 := createCredential(t, context, secret, issuer2) + + // Disclosure + n1, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + b1, err := cred1.CreateDisclosureProofBuilder([]int{1, 2}, false) + require.NoError(t, err) + b2, err := cred2.CreateDisclosureProofBuilder([]int{1, 3}, false) + require.NoError(t, err) + builders := ProofBuilderList([]ProofBuilder{b1, b2}) + discloseK := builders.Challenge(context, n1, nil, false) + discloseCommit, discloseWs, err := NewKeyshareCommitments([]*PublicKey{testPubK}) + require.NoError(t, err) + discloseChallenge := KeyshareChallenge(discloseK, discloseWs) + proofs, err := builders.BuildDistributedProofList(discloseChallenge, nil) + require.NoError(t, err) + discloseKeyshareContribution := KeyshareResponse(proofs[0].SecretKeyResponse(), kssecret, discloseCommit, discloseChallenge) + proofs[0].MergeKeyshareContribution(discloseKeyshareContribution) + assert.True(t, proofs.Verify([]*PublicKey{testPubK, testPubK2}, context, n1, false, []bool{true, false}, discloseWs, discloseKeyshareContribution)) +} + +func TestFullIssuanceAndShowingOldToNew(t *testing.T) { + context, err := common.RandomBigInt(testPubK.Params.Lh) + require.NoError(t, err) + nonce1, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + nonce2, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + secret, err := common.RandomBigInt(testPubK.Params.Lm) + require.NoError(t, err) + kssecret, err := NewKeyshareSecret() + require.NoError(t, err) + + // Issuance + builder := NewCredentialBuilder(testPubK, context, secret, nonce2, nil) + ksCommit, ProofPCommitment, err := NewProofPCommitments(kssecret, []*PublicKey{testPubK}) + require.NoError(t, err) + builder.MergeProofPCommitment(ProofPCommitment[0]) + challenge := ProofBuilderList{builder}.Challenge(context, nonce1, nil, false) + userProof := builder.CreateProof(challenge) + proofP := KeyshareProofP(kssecret, ksCommit, challenge, testPubK) + userProof.MergeProofP(proofP, testPubK) + commitMsg := builder.CreateIssueCommitmentMessage(ProofList{userProof}) + + assert.True(t, commitMsg.Proofs.Verify([]*PublicKey{testPubK}, context, nonce1, false, []bool{true}, nil, nil), "Issuance proof not valid") + issuer := NewIssuer(testPrivK, testPubK, context) + sigMsg, err := issuer.IssueSignature(commitMsg.Proofs[0].(*ProofU).U, testAttributes1, nil, nonce2, nil) + assert.NoError(t, err, "Error in IssueSignature") + + cred, err := builder.ConstructCredential(sigMsg, testAttributes1) + require.NoError(t, err, "Error in credential construction") + + // Showing + n1, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + disclosed := []int{1, 2} + + discloseBuilder, err := cred.CreateDisclosureProofBuilder(disclosed, false) + k := ProofBuilderList{discloseBuilder}.Challenge(context, n1, nil, false) + discloseKsCommit, Ws, err := NewKeyshareCommitments([]*PublicKey{testPubK}) + require.NoError(t, err) + discloseChallenge := KeyshareChallenge(k, Ws) + discloseUserProof := discloseBuilder.CreateProof(discloseChallenge) + discloseKeyshareResponse := KeyshareResponse(discloseUserProof.SecretKeyResponse(), kssecret, discloseKsCommit, discloseChallenge) + discloseUserProof.MergeKeyshareContribution(discloseKeyshareResponse) + assert.True(t, ProofList{discloseUserProof}.Verify([]*PublicKey{testPubK}, context, n1, false, []bool{true}, Ws, discloseKeyshareResponse), "Disclosure not valid") +} + +func TestFullIssuanceAndShowingNewToOld(t *testing.T) { + context, err := common.RandomBigInt(testPubK.Params.Lh) + require.NoError(t, err) + nonce1, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + nonce2, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + secret, err := common.RandomBigInt(testPubK.Params.Lm) + require.NoError(t, err) + kssecret, err := NewKeyshareSecret() + require.NoError(t, err) + + builder := NewCredentialBuilder(testPubK, context, secret, nonce2, nil) + Ps := KeysharePs(kssecret, []*PublicKey{testPubK}) + builder.MergeKeyshareP(Ps["testPubK"]) + k := ProofBuilderList{builder}.Challenge(context, nonce1, nil, false) + ksCommit, Ws, err := NewKeyshareCommitments([]*PublicKey{testPubK}) + require.NoError(t, err) + challenge := KeyshareChallenge(k, Ws) + userProof := builder.CreateProof(challenge) + keyshareContribution := KeyshareResponse(userProof.SecretKeyResponse(), kssecret, ksCommit, challenge) + userProof.MergeKeyshareContribution(keyshareContribution) + commitMsg := builder.CreateIssueCommitmentMessage(ProofList{userProof}) + + assert.True(t, commitMsg.Proofs.Verify([]*PublicKey{testPubK}, context, nonce1, false, []bool{true}, Ws, keyshareContribution), "Issuance proof not valid") + issuer := NewIssuer(testPrivK, testPubK, context) + sigMsg, err := issuer.IssueSignature(commitMsg.Proofs[0].(*ProofU).U, testAttributes1, nil, nonce2, nil) + assert.NoError(t, err, "Error in IssueSignature") + + cred, err := builder.ConstructCredential(sigMsg, testAttributes1) + require.NoError(t, err, "error in credential construction") + + // Showing + n1, err := common.RandomBigInt(testPubK.Params.Lstatzk) + require.NoError(t, err) + disclosed := []int{1, 2} + + discloseBuilder, err := cred.CreateDisclosureProofBuilder(disclosed, false) + require.NoError(t, err) + discloseKsCommit, discloseProofPCommitment, err := NewProofPCommitments(kssecret, []*PublicKey{testPubK}) + discloseBuilder.MergeProofPCommitment(discloseProofPCommitment[0]) + discloseChallenge := ProofBuilderList{discloseBuilder}.Challenge(context, n1, nil, false) + discloseUserProof := discloseBuilder.CreateProof(discloseChallenge) + discloseProofP := KeyshareProofP(kssecret, discloseKsCommit, discloseChallenge, testPubK) + discloseUserProof.MergeProofP(discloseProofP, testPubK) + assert.True(t, discloseUserProof.(*ProofD).Verify(testPubK, context, n1, false), "proof of disclosure does not verify, whereas it should.") +} + func TestMain(m *testing.M) { err := setupParameters() if err != nil { diff --git a/internal/common/hashtool.go b/internal/common/hashtool.go index 9d09a77..64ba3d4 100644 --- a/internal/common/hashtool.go +++ b/internal/common/hashtool.go @@ -1,14 +1,18 @@ package common -import "crypto/sha256" -import "encoding/asn1" -import "github.com/privacybydesign/gabi/big" -import gobig "math/big" +import ( + "crypto/sha256" + "encoding/asn1" + + "github.com/privacybydesign/gabi/big" + + gobig "math/big" +) // hashCommit computes the sha256 hash over the asn1 representation of a slice // of big integers and returns a positive big integer that can be represented // with that hash. -func HashCommit(values []*big.Int, issig bool) *big.Int { +func HashCommit(values []*big.Int, issig bool, iskeyshare bool) *big.Int { // The first element is the number of elements var tmp []interface{} offset := 0 @@ -16,6 +20,10 @@ func HashCommit(values []*big.Int, issig bool) *big.Int { tmp = make([]interface{}, len(values)+2) tmp[0] = true offset++ + } else if iskeyshare { + tmp = make([]interface{}, len(values)+2) + tmp[0] = false + offset++ } else { tmp = make([]interface{}, len(values)+1) } @@ -47,7 +55,7 @@ func GetHashNumber(a *big.Int, b *big.Int, index int, bitlen uint) *big.Int { k := uint(0) res := big.NewInt(0) for k < bitlen { - cur := HashCommit(tmp, false) + cur := HashCommit(tmp, false, false) cur.Lsh(cur, uint(k)) res.Add(res, cur) k += 256 diff --git a/internal/common/hashtool_test.go b/internal/common/hashtool_test.go index 6491b96..185e620 100644 --- a/internal/common/hashtool_test.go +++ b/internal/common/hashtool_test.go @@ -1,7 +1,10 @@ package common -import "testing" -import "github.com/privacybydesign/gabi/big" +import ( + "testing" + + "github.com/privacybydesign/gabi/big" +) func TestHashCommit(t *testing.T) { listA := []*big.Int{ @@ -9,7 +12,7 @@ func TestHashCommit(t *testing.T) { big.NewInt(2), big.NewInt(3), } - hashA := HashCommit(listA, false) + hashA := HashCommit(listA, false, false) if hashA == nil { t.Error("Failed to generate hash for A") return @@ -20,7 +23,7 @@ func TestHashCommit(t *testing.T) { nil, big.NewInt(3), } - hashB := HashCommit(listB, false) + hashB := HashCommit(listB, false, false) if hashB == nil { t.Error("Failed to generate hash for B") return @@ -30,7 +33,7 @@ func TestHashCommit(t *testing.T) { big.NewInt(1), big.NewInt(2), } - hashC := HashCommit(listC, false) + hashC := HashCommit(listC, false, false) if hashC == nil { t.Error("Failed to generate hash for C") return diff --git a/issuer.go b/issuer.go index 8cb477e..554ee0b 100644 --- a/issuer.go +++ b/issuer.go @@ -90,7 +90,7 @@ func (i *Issuer) proveSignature(signature *CLSignature, nonce2 *big.Int) *ProofS eCommit := randomElementMultiplicativeGroup(groupModulus) ACommit := new(big.Int).Exp(Q, eCommit, i.Pk.N) - c := common.HashCommit([]*big.Int{i.Context, Q, signature.A, nonce2, ACommit}, false) + c := common.HashCommit([]*big.Int{i.Context, Q, signature.A, nonce2, ACommit}, false, false) eResponse := new(big.Int).Mul(c, d) eResponse.Sub(eCommit, eResponse).Mod(eResponse, groupModulus) diff --git a/keyproof/quasisafeprimeproduct_test.go b/keyproof/quasisafeprimeproduct_test.go index abfaeb4..3c24640 100644 --- a/keyproof/quasisafeprimeproduct_test.go +++ b/keyproof/quasisafeprimeproduct_test.go @@ -28,7 +28,7 @@ func TestQuasiSafePrimeProductFullCycle(t *testing.T) { const p = 13451 const q = 13901 listBefore, commit := quasiSafePrimeProductBuildCommitments([]*big.Int{}, big.NewInt(p), big.NewInt(q)) - challengeBefore := common.HashCommit(listBefore, false) + challengeBefore := common.HashCommit(listBefore, false, false) proofBefore := quasiSafePrimeProductBuildProof(big.NewInt(p), big.NewInt(q), challengeBefore, commit) proofJSON, err := json.Marshal(proofBefore) require.NoError(t, err, "error during json marshal") @@ -38,7 +38,7 @@ func TestQuasiSafePrimeProductFullCycle(t *testing.T) { err = json.Unmarshal(proofJSON, &proofAfter) require.NoError(t, err, "error during json unmarshal") listAfter := quasiSafePrimeProductExtractCommitments([]*big.Int{}, proofAfter) - challengeAfter := common.HashCommit(listAfter, false) + challengeAfter := common.HashCommit(listAfter, false, false) ok := quasiSafePrimeProductVerifyProof(big.NewInt((2*p+1)*(2*q+1)), challengeAfter, proofAfter) assert.True(t, ok, "JSON proof rejected") } diff --git a/keyproof/validkeyproof.go b/keyproof/validkeyproof.go index 85b82cc..0f99a2e 100644 --- a/keyproof/validkeyproof.go +++ b/keyproof/validkeyproof.go @@ -140,7 +140,7 @@ func (s *ValidKeyProofStructure) BuildProof(Pprime *big.Int, Qprime *big.Int) Va Follower.StepStart("Generating proof", 0) // Calculate challenge - challenge := common.HashCommit(list, false) + challenge := common.HashCommit(list, false, false) // Calculate proofs proof := ValidKeyProof{ @@ -232,7 +232,7 @@ func (s *ValidKeyProofStructure) VerifyProof(proof ValidKeyProof) bool { Follower.StepStart("Verifying proof", 0) // Check challenge - if proof.Challenge.Cmp(common.HashCommit(list, false)) != 0 { + if proof.Challenge.Cmp(common.HashCommit(list, false, false)) != 0 { return false } diff --git a/keys.go b/keys.go index e42462c..9fd9f87 100644 --- a/keys.go +++ b/keys.go @@ -319,6 +319,7 @@ type PublicKey struct { EpochLength EpochLength `xml:"Features"` Params *SystemParameters `xml:"-"` Issuer string `xml:"-"` + KeyID string `xml:"-"` // Used during new keyshare protocol ECDSA string `xml:",omitempty"` revocationKey *revocation.PublicKey diff --git a/keyshare.go b/keyshare.go index 3ce90ec..b69f728 100644 --- a/keyshare.go +++ b/keyshare.go @@ -5,6 +5,7 @@ import ( "errors" "github.com/privacybydesign/gabi/big" + "github.com/privacybydesign/gabi/internal/common" ) var ( @@ -18,8 +19,16 @@ func NewKeyshareSecret() (*big.Int, error) { return big.RandInt(rand.Reader, new(big.Int).Lsh(big.NewInt(1), DefaultSystemParameters[1024].Lm-1)) } -// Generate commitments for the keyshare server for given set of keys -func NewKeyshareCommitments(secret *big.Int, keys []*PublicKey) (*big.Int, []*ProofPCommitment, error) { +// Generate keyshare parts of P for given set of keys +func KeysharePs(secret *big.Int, keys []*PublicKey) map[string]*big.Int { + Ps := make(map[string]*big.Int) + for _, key := range keys { + Ps[key.KeyID] = new(big.Int).Exp(key.R[0], secret, key.N) + } + return Ps +} + +func NewKeyshareCommitments(keys []*PublicKey) (*big.Int, map[string]*big.Int, error) { // Determine required randomizer length var lRand uint = 0 for _, key := range keys { @@ -37,23 +46,58 @@ func NewKeyshareCommitments(secret *big.Int, keys []*PublicKey) (*big.Int, []*Pr } // And exponentiate it with all keys - var exponentiatedCommitments []*ProofPCommitment + Ws := make(map[string]*big.Int) for _, key := range keys { - exponentiatedCommitments = append(exponentiatedCommitments, - &ProofPCommitment{ - P: new(big.Int).Exp(key.R[0], secret, key.N), - Pcommit: new(big.Int).Exp(key.R[0], commit, key.N), - }) + Ws[key.KeyID] = new(big.Int).Exp(key.R[0], commit, key.N) } - return commit, exponentiatedCommitments, nil + return commit, Ws, nil } -// Generate keyshare response for a given challenge and commit, given a secret -func KeyshareResponse(secret, commit, challenge *big.Int, key *PublicKey) *ProofP { +// Generate commitments for the keyshare server for given set of keys +func NewProofPCommitments(secret *big.Int, keys []*PublicKey) (*big.Int, []*ProofPCommitment, error) { + Ps := KeysharePs(secret, keys) + commit, Ws, err := NewKeyshareCommitments(keys) + + if err != nil { + return nil, nil, err + } + + // Merge Ps and Ws + ppCommitments := make([]*ProofPCommitment, len(keys)) + for i, key := range keys { + ppCommitments[i] = &ProofPCommitment{ + P: Ps[key.KeyID], + Pcommit: Ws[key.KeyID], + } + } + + return commit, ppCommitments, nil +} + +// Generate keyshare response for a givven challenge and commit, given a secret +func KeyshareResponse(userS, secret, commit, challenge *big.Int) *KeyshareContribution { + return &KeyshareContribution{ + S: new(big.Int).Add(userS, new(big.Int).Add(commit, new(big.Int).Mul(challenge, secret))), + C: new(big.Int).Set(challenge), + } +} + +// Generate keyshare ProofP for a given challenge and commit, given a secret +func KeyshareProofP(secret, commit, challenge *big.Int, key *PublicKey) *ProofP { + P := KeysharePs(secret, []*PublicKey{key}) + response := KeyshareResponse(big.NewInt(0), secret, commit, challenge) return &ProofP{ - P: new(big.Int).Exp(key.R[0], secret, key.N), - C: new(big.Int).Set(challenge), - SResponse: new(big.Int).Add(commit, new(big.Int).Mul(challenge, secret)), + P: P[key.KeyID], + C: response.C, + SResponse: response.S, } } + +func KeyshareChallenge(userK *big.Int, Ws map[string]*big.Int) *big.Int { + Wcontrib := prepareKeyshareContributions(Ws) + hashList := make([]*big.Int, 1+len(Wcontrib)) + hashList[0] = userK + copy(hashList[1:], Wcontrib) + return common.HashCommit(hashList, false, true) +} diff --git a/prooflist.go b/prooflist.go index 0f883f5..c932903 100644 --- a/prooflist.go +++ b/prooflist.go @@ -5,6 +5,8 @@ package gabi import ( + "sort" + "github.com/go-errors/errors" "github.com/privacybydesign/gabi/big" "github.com/privacybydesign/gabi/internal/common" @@ -15,6 +17,7 @@ import ( type ProofBuilder interface { Commit(randomizers map[string]*big.Int) []*big.Int CreateProof(challenge *big.Int) Proof + MergeKeyshareP(keyshareP *big.Int) PublicKey() *PublicKey MergeProofPCommitment(commitment *ProofPCommitment) } @@ -51,18 +54,46 @@ func (pl ProofList) GetFirstProofU() (*ProofU, error) { return pl.GetProofU(0) } +// Compute the keyshare contribution array from the map of keyshare W values +func prepareKeyshareContributions(keyshareWs map[string]*big.Int) []*big.Int { + // Keyshare contributions are the Ws, ordered alphabetically on their issuer ids + // note that it is allowed to have unused keyshare contributions, so we dont + // need to check for that + keyshareIssuerIds := make([]string, 0, len(keyshareWs)) + for k := range keyshareWs { + keyshareIssuerIds = append(keyshareIssuerIds, k) + } + sort.Strings(keyshareIssuerIds) + keyshareContributions := make([]*big.Int, len(keyshareWs)) + for i, k := range keyshareIssuerIds { + keyshareContributions[i] = keyshareWs[k] + } + return keyshareContributions +} + // challengeContributions collects and returns all the challenge contributions -// of the proofs contained in the proof list. -func (pl ProofList) challengeContributions(publicKeys []*PublicKey, context, nonce *big.Int) ([]*big.Int, error) { +// of the proofs contained in the proof list, including those of the keyshare server +func (pl ProofList) challengeContributions( + publicKeys []*PublicKey, + context *big.Int, + nonce *big.Int, + hasKeyshareContribution []bool, + keyshareWs map[string]*big.Int) ([]*big.Int, []*big.Int, error) { + // Calculate proof contributions contributions := make([]*big.Int, 0, len(pl)*2) for i, proof := range pl { - contrib, err := proof.ChallengeContribution(publicKeys[i]) + var keyshareW *big.Int = nil + if len(keyshareWs) != 0 && hasKeyshareContribution[i] { + keyshareW = keyshareWs[publicKeys[i].KeyID] + } + contrib, err := proof.ChallengeContribution(publicKeys[i], keyshareW) if err != nil { - return nil, err + return nil, nil, err } contributions = append(contributions, contrib...) } - return contributions, nil + + return contributions, prepareKeyshareContributions(keyshareWs), nil } // Verify returns true when all the proofs inside verify. @@ -73,10 +104,21 @@ func (pl ProofList) challengeContributions(publicKeys []*PublicKey, context, non // the same secret key (i.e. it should be verified that all proofs use either none, // or one and the same keyshare server). // An empty ProofList is not considered valid. -func (pl ProofList) Verify(publicKeys []*PublicKey, context, nonce *big.Int, issig bool, keyshareServers []string) bool { +func (pl ProofList) Verify( + publicKeys []*PublicKey, + context *big.Int, + nonce *big.Int, + issig bool, + hasKeyshareContribution []bool, + keyshareWs map[string]*big.Int, + keyshareContribution *KeyshareContribution) bool { + // Note that, because of the legacy protocol, it is perfectly valid to have hasKeyshareContribution, but not have keyshareWs or a keyshareContribution if len(pl) == 0 || len(pl) != len(publicKeys) || - len(keyshareServers) > 0 && len(pl) != len(keyshareServers) { + (len(hasKeyshareContribution) > 0 && len(pl) != len(hasKeyshareContribution)) || + (len(keyshareWs) != 0 && len(hasKeyshareContribution) == 0) || + (len(keyshareWs) != 0 && keyshareContribution == nil) || + (len(keyshareWs) == 0 && keyshareContribution != nil) { return false } @@ -84,25 +126,30 @@ func (pl ProofList) Verify(publicKeys []*PublicKey, context, nonce *big.Int, iss // then the secretkey = userpart + keysharepart. // So, we can only expect two secret key responses to be equal if their credentials // are both associated to either no keyshare server, or the same keyshare server. + // Since we only support a single keyshare server during a session, we need to keep two + // responses in mind, one with keysharepart, one without. // During verification of the proofs we keep track of their secret key responses in this map. - secretkeyResponses := make(map[string]*big.Int) + secretkeyResponses := make(map[bool]*big.Int) - contributions, err := pl.challengeContributions(publicKeys, context, nonce) + contributions, keyshareContributions, err := pl.challengeContributions(publicKeys, context, nonce, hasKeyshareContribution, keyshareWs) if err != nil { return false } - expectedChallenge := createChallenge(context, nonce, contributions, issig) + expectedChallenge := createChallenge(context, nonce, contributions, keyshareContributions, issig) // If keyshareServers == nil then we never update this variable, // so the check below verifies that all creds share the same secret key. - kss := "" + kss := false for i, proof := range pl { if !proof.VerifyWithChallenge(publicKeys[i], expectedChallenge) { return false } - if len(keyshareServers) > 0 { - kss = keyshareServers[i] + if len(hasKeyshareContribution) > 0 { + kss = hasKeyshareContribution[i] + } + if kss && keyshareContribution != nil && !proof.VerifyKeyshareContribution(keyshareContribution) { + return false } if response, contains := secretkeyResponses[kss]; !contains { // First time we see this keyshare server @@ -118,7 +165,7 @@ func (pl ProofList) Verify(publicKeys []*PublicKey, context, nonce *big.Int, iss return true } -func (builders ProofBuilderList) Challenge(context, nonce *big.Int, issig bool) *big.Int { +func (builders ProofBuilderList) Challenge(context, nonce *big.Int, keyshareWs map[string]*big.Int, issig bool) *big.Int { // The secret key may be used across credentials supporting different attribute sizes. // So we should take it, and hence also its commitment, to fit within the smallest size - // otherwise it will be too big so that we cannot perform the range proof showing @@ -131,7 +178,7 @@ func (builders ProofBuilderList) Challenge(context, nonce *big.Int, issig bool) } // Create a shared challenge - return createChallenge(context, nonce, commitmentValues, issig) + return createChallenge(context, nonce, commitmentValues, prepareKeyshareContributions(keyshareWs), issig) } func (builders ProofBuilderList) BuildDistributedProofList( @@ -155,8 +202,8 @@ func (builders ProofBuilderList) BuildDistributedProofList( // BuildProofList builds a list of bounded proofs. For this it is given a list // of ProofBuilders. Examples of proof builders are CredentialBuilder and // DisclosureProofBuilder. -func (builders ProofBuilderList) BuildProofList(context, nonce *big.Int, issig bool) ProofList { - challenge := builders.Challenge(context, nonce, issig) +func (builders ProofBuilderList) BuildProofList(context, nonce *big.Int, keyshareWs map[string]*big.Int, issig bool) ProofList { + challenge := builders.Challenge(context, nonce, keyshareWs, issig) list, _ := builders.BuildDistributedProofList(challenge, nil) return list } diff --git a/proofs.go b/proofs.go index a1a7bf1..f5fd18f 100644 --- a/proofs.go +++ b/proofs.go @@ -16,19 +16,35 @@ import ( type Proof interface { VerifyWithChallenge(pk *PublicKey, reconstructedChallenge *big.Int) bool SecretKeyResponse() *big.Int - ChallengeContribution(pk *PublicKey) ([]*big.Int, error) + ChallengeContribution(pk *PublicKey, keyshareW *big.Int) ([]*big.Int, error) MergeProofP(proofP *ProofP, pk *PublicKey) + VerifyKeyshareContribution(contrib *KeyshareContribution) bool + MergeKeyshareContribution(contrib *KeyshareContribution) +} + +// KeyshareContribution represents the (signed) portion of a keyshare servers contribution to a proof +type KeyshareContribution struct { + S *big.Int + C *big.Int } // createChallenge creates a challenge based on context, nonce and the // contributions. -func createChallenge(context, nonce *big.Int, contributions []*big.Int, issig bool) *big.Int { +func createChallenge(context, nonce *big.Int, contributions []*big.Int, keyshareContributions []*big.Int, issig bool) *big.Int { // Basically, sandwich the contributions between context and nonce input := make([]*big.Int, 2+len(contributions)) input[0] = context copy(input[1:1+len(contributions)], contributions) input[len(input)-1] = nonce - return common.HashCommit(input, issig) + inner := common.HashCommit(input, issig, false) + if len(keyshareContributions) != 0 { + outerinput := make([]*big.Int, 1+len(keyshareContributions)) + outerinput[0] = inner + copy(outerinput[1:], keyshareContributions) + return common.HashCommit(outerinput, false, true) + } else { + return inner + } } // ProofU represents a proof of correctness of the commitment in the first phase @@ -49,13 +65,21 @@ func (p *ProofU) MergeProofP(proofP *ProofP, pk *PublicKey) { p.SResponse.Add(p.SResponse, proofP.SResponse) } +func (p *ProofU) MergeKeyshareContribution(contribution *KeyshareContribution) { + p.SResponse.Set(contribution.S) +} + // Verify verifies whether the proof is correct. func (p *ProofU) Verify(pk *PublicKey, context, nonce *big.Int) bool { - contrib, err := p.ChallengeContribution(pk) + contrib, err := p.ChallengeContribution(pk, nil) if err != nil { return false } - return p.VerifyWithChallenge(pk, createChallenge(context, nonce, contrib, false)) + return p.VerifyWithChallenge(pk, createChallenge(context, nonce, contrib, nil, false)) +} + +func (p *ProofU) VerifyKeyshareContribution(contribution *KeyshareContribution) bool { + return p.C.Cmp(contribution.C) == 0 && p.SResponse.Cmp(contribution.S) == 0 } // correctResponseSizes checks the sizes of the elements in the ProofU proof. @@ -74,7 +98,7 @@ func (p *ProofU) VerifyWithChallenge(pk *PublicKey, reconstructedChallenge *big. // reconstructUcommit reconstructs U from the information in the proof and the // provided public key. -func (p *ProofU) reconstructUcommit(pk *PublicKey) *big.Int { +func (p *ProofU) reconstructUcommit(pk *PublicKey, keyshareW *big.Int) *big.Int { // Reconstruct Ucommit // U_commit = U^{-C} * S^{VPrimeResponse} * R_0^{SResponse} Uc := common.ModPow(p.U, new(big.Int).Neg(p.C), pk.N) @@ -87,6 +111,9 @@ func (p *ProofU) reconstructUcommit(pk *PublicKey) *big.Int { Rimi := common.ModPow(pk.R[i], miUserResponse, pk.N) Ucommit.Mul(Ucommit, Rimi).Mod(Ucommit, pk.N) } + if keyshareW != nil { + Ucommit.Mul(Ucommit, new(big.Int).ModInverse(keyshareW, pk.N)).Mod(Ucommit, pk.N) + } return Ucommit } @@ -104,8 +131,8 @@ func (p *ProofU) Challenge() *big.Int { // ChallengeContribution returns the contribution of this proof to the // challenge. -func (p *ProofU) ChallengeContribution(pk *PublicKey) ([]*big.Int, error) { - return []*big.Int{p.U, p.reconstructUcommit(pk)}, nil +func (p *ProofU) ChallengeContribution(pk *PublicKey, keyshareW *big.Int) ([]*big.Int, error) { + return []*big.Int{p.U, p.reconstructUcommit(pk, keyshareW)}, nil } // ProofS represents a proof. @@ -127,7 +154,7 @@ func (p *ProofS) Verify(pk *PublicKey, signature *CLSignature, context, nonce *b Q := new(big.Int).Exp(signature.A, signature.E, pk.N) // Recalculate hash - cPrime := common.HashCommit([]*big.Int{context, Q, signature.A, nonce, ACommit}, false) + cPrime := common.HashCommit([]*big.Int{context, Q, signature.A, nonce, ACommit}, false, false) return p.C.Cmp(cPrime) == 0 } @@ -147,6 +174,10 @@ func (p *ProofD) MergeProofP(proofP *ProofP, pk *PublicKey) { p.SecretKeyResponse().Add(p.SecretKeyResponse(), proofP.SResponse) } +func (p *ProofD) MergeKeyshareContribution(contribution *KeyshareContribution) { + p.AResponses[0].Set(contribution.S) +} + // correctResponseSizes checks the sizes of the elements in the ProofD proof. func (p *ProofD) correctResponseSizes(pk *PublicKey) bool { // Check range on the AResponses @@ -173,7 +204,7 @@ func (p *ProofD) correctResponseSizes(pk *PublicKey) bool { // reconstructZ reconstructs Z from the information in the proof and the // provided public key. -func (p *ProofD) reconstructZ(pk *PublicKey) *big.Int { +func (p *ProofD) reconstructZ(pk *PublicKey, keyshareW *big.Int) *big.Int { // known = Z / ( prod_{disclosed} R_i^{a_i} * A^{2^{l_e - 1}} ) numerator := new(big.Int).Lsh(big.NewInt(1), pk.Params.Le-1) numerator.Exp(p.A, numerator, pk.N) @@ -198,16 +229,24 @@ func (p *ProofD) reconstructZ(pk *PublicKey) *big.Int { Z := new(big.Int).Mul(knownC, Ae) Z.Mul(Z, Rs).Mul(Z, Sv).Mod(Z, pk.N) + if keyshareW != nil { + Z.Mul(Z, new(big.Int).ModInverse(keyshareW, pk.N)).Mod(Z, pk.N) + } + return Z } // Verify verifies the proof against the given public key, context, and nonce. func (p *ProofD) Verify(pk *PublicKey, context, nonce1 *big.Int, issig bool) bool { - contrib, err := p.ChallengeContribution(pk) + contrib, err := p.ChallengeContribution(pk, nil) if err != nil { return false } - return p.VerifyWithChallenge(pk, createChallenge(context, nonce1, contrib, issig)) + return p.VerifyWithChallenge(pk, createChallenge(context, nonce1, contrib, nil, issig)) +} + +func (p *ProofD) VerifyKeyshareContribution(contribution *KeyshareContribution) bool { + return p.C.Cmp(contribution.C) == 0 && p.AResponses[0].Cmp(contribution.S) == 0 } func (p *ProofD) HasNonRevocationProof() bool { @@ -239,8 +278,8 @@ func (p *ProofD) VerifyWithChallenge(pk *PublicKey, reconstructedChallenge *big. // ChallengeContribution returns the contribution of this proof to the // challenge. -func (p *ProofD) ChallengeContribution(pk *PublicKey) ([]*big.Int, error) { - l := []*big.Int{p.A, p.reconstructZ(pk)} +func (p *ProofD) ChallengeContribution(pk *PublicKey, keyshareW *big.Int) ([]*big.Int, error) { + l := []*big.Int{p.A, p.reconstructZ(pk, keyshareW)} if p.NonRevocationProof != nil { revPk, err := pk.RevocationKey() if err != nil { diff --git a/revocation/proof.go b/revocation/proof.go index 73f41d3..dab668d 100644 --- a/revocation/proof.go +++ b/revocation/proof.go @@ -373,7 +373,7 @@ func (p *proof) ProofResult(name string) *big.Int { func (p *proof) verify(pk *PublicKey) bool { grp := (*qrGroup)(pk.Group) commitments := proofstructure.commitmentsFromProof(grp, []*big.Int{}, p.Challenge, grp, p, p) - return (*Proof)(p).VerifyWithChallenge(pk, common.HashCommit(commitments, false)) + return (*Proof)(p).VerifyWithChallenge(pk, common.HashCommit(commitments, false, false)) } func (s *proofStructure) commitmentsFromSecrets(g *qrGroup, list []*big.Int, bases keyproof.BaseLookup, secretdata keyproof.SecretLookup) ([]*big.Int, proofCommit) { diff --git a/revocation/revocation_test.go b/revocation/revocation_test.go index f886371..e0a06a7 100644 --- a/revocation/revocation_test.go +++ b/revocation/revocation_test.go @@ -106,7 +106,7 @@ func testProof(t *testing.T, grp qrGroup, p, q *big.Int, valid bool) bool { require.Equal(t, valid, proofstructure.isTrue((*witness)(witn), acc.Nu, grp.N), "statement to prove ") list, commit := proofstructure.commitmentsFromSecrets(&grp, []*big.Int{}, &bases, (*witness)(witn)) - challenge := common.HashCommit(list, false) + challenge := common.HashCommit(list, false, false) sacc, err := acc.Sign(&privKey) require.NoError(t, err) prf := (*ProofCommit)(&commit).BuildProof(challenge)