This repository has been archived by the owner on Mar 27, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 158
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* added high-level CL crypto Issuer API * added high-level CL crypto Prover API * added high-level CL crypto Verifier API * added ursa-implementations for the services * added unit tests for issue credential flow with CL crypto * added unit tests for present proof flow with CL crypto Signed-off-by: konstantin.goncharov <konstantin.goncharov@avast.com>
- Loading branch information
konstantin.goncharov
committed
Aug 19, 2022
1 parent
5e75e60
commit d0a3384
Showing
7 changed files
with
1,047 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
Copyright Avast Software. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package cl | ||
|
||
import ( | ||
"github.com/hyperledger/aries-framework-go/pkg/crypto" | ||
"github.com/hyperledger/aries-framework-go/pkg/kms" | ||
) | ||
|
||
// Issuer contains all high-level methods to process CL Anoncreds on the issuer's side. | ||
type Issuer interface { | ||
// GetCredentialDefinition returns a public CredDef data - public key, correctness proof and attributes | ||
// returns: | ||
// credDef as *CredentialDefinition | ||
// error in case of errors | ||
GetCredentialDefinition() (*CredentialDefinition, error) | ||
// OfferCredential generates CredOffer containing valid nonce | ||
// returns: | ||
// offer as *CredentialOffer | ||
// error in case of errors | ||
OfferCredential() (*CredentialOffer, error) | ||
// IssueCredential issues and signs Credential for values and CredRequest | ||
// provided by prover and CredOffer from the previous step | ||
// Resulting Credential will contain signature and signature's correctness proof, along with issued attributes | ||
// returns: | ||
// credential as *Credential | ||
// error in case of errors | ||
IssueCredential(values map[string]interface{}, | ||
credentialRequest *CredentialRequest, credOffer *CredentialOffer) (*Credential, error) | ||
} | ||
|
||
// Prover contains all high-level methods to process CL Anoncreds on the prover's side. | ||
type Prover interface { | ||
// RequestCredential generates CredRequest which contains blinded secrets with MS, using issuer's CredDef public data | ||
// and CredOffer from the previous step | ||
// returns: | ||
// request as *CredentialRequest | ||
// error in case of errors | ||
RequestCredential(credOffer *CredentialOffer, | ||
credDef *CredentialDefinition, proverID string) (*CredentialRequest, error) | ||
// ProcessCredential updates issued Credential signature for CredDef, using blinding factor from a CredRequest | ||
// returns: | ||
// credential as *Credential | ||
// error in case of errors | ||
ProcessCredential(credential *Credential, credRequest *CredentialRequest, | ||
credDef *CredentialDefinition) (*Credential, error) | ||
// CreateProof composes Proof for the provided Credentials for CredDefs | ||
// matching revealead attrs and predicates specified in PresentationRequest | ||
// returns: | ||
// proof as *Proof | ||
// error in case of errors | ||
CreateProof(presentationRequest *PresentationRequest, credentials []*Credential, | ||
credDefs []*CredentialDefinition) (*Proof, error) | ||
} | ||
|
||
// Verifier contains all high-level methods to process CL Anoncreds on the verifier's side. | ||
type Verifier interface { | ||
// RequestPresentation generates PresentationRequest with unique nonce and provided list of attrs and predicates | ||
// returns: | ||
// request as *PresentationRequest | ||
// error in case of errors | ||
RequestPresentation(items []*PresentationRequestItem) (*PresentationRequest, error) | ||
// VerifyProof verifies given Proof according to PresentationRequest and CredDefs | ||
// returns: | ||
// error in case of errors or nil if proof verification was successful | ||
VerifyProof(proof *Proof, presentationRequest *PresentationRequest, credDefs []*CredentialDefinition) error | ||
} | ||
|
||
// Provider for CL services constructors. | ||
type Provider interface { | ||
KMS() kms.KeyManager | ||
Crypto() crypto.Crypto | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/* | ||
Copyright Avast Software. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package cl | ||
|
||
// CredentialDefinition contains public data of CL CredDef. | ||
type CredentialDefinition struct { | ||
CredPubKey []byte | ||
CredDefCorrectnessProof []byte | ||
Attrs []string | ||
} | ||
|
||
// CredentialOffer contains nonce of CL CredOffer. | ||
type CredentialOffer struct { | ||
Nonce []byte | ||
} | ||
|
||
// CredentialRequest contains nonce, proverID and blinded secrets of CL CredRequest. | ||
type CredentialRequest struct { | ||
BlindedCredentialSecrets *BlindedCredentialSecrets | ||
Nonce []byte | ||
ProverID string | ||
} | ||
|
||
// BlindedCredentialSecrets contains handle, blinding factor and correctness proof of CL BlindedSecrets. | ||
type BlindedCredentialSecrets struct { | ||
Handle []byte | ||
BlindingFactor []byte | ||
CorrectnessProof []byte | ||
} | ||
|
||
// Credential contains CL Credential's signature, correctness proof for it and related credential's values. | ||
type Credential struct { | ||
Signature []byte | ||
Values map[string]interface{} | ||
SigProof []byte | ||
} | ||
|
||
// PresentationRequest contains items used for CL Proof generation. | ||
type PresentationRequest struct { | ||
Items []*PresentationRequestItem | ||
Nonce []byte | ||
} | ||
|
||
// PresentationRequestItem consists of revealed attributes and predicates upon which CL Proof is generated. | ||
type PresentationRequestItem struct { | ||
RevealedAttrs []string | ||
Predicates []*Predicate | ||
} | ||
|
||
// Predicate defines predicate for CL Proof. | ||
type Predicate struct { | ||
Attr string | ||
PType string | ||
Value int32 | ||
} | ||
|
||
// Proof wraps CL Proof in raw bytes. | ||
type Proof struct { | ||
Proof []byte | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
//go:build ursa | ||
// +build ursa | ||
|
||
/* | ||
Copyright SecureKey Technologies Inc. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package ursa | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/google/tink/go/keyset" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/hyperledger/aries-framework-go/pkg/crypto" | ||
"github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto" | ||
bld "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/cl/blinder" | ||
sgn "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/cl/signer" | ||
"github.com/hyperledger/aries-framework-go/pkg/doc/cl" | ||
"github.com/hyperledger/aries-framework-go/pkg/kms" | ||
mockkms "github.com/hyperledger/aries-framework-go/pkg/mock/kms" | ||
) | ||
|
||
func TestCL(t *testing.T) { | ||
km := &mockkms.KeyManager{} | ||
c := &tinkcrypto.Crypto{} | ||
provider := newProvider(km, c) | ||
|
||
values := map[string]interface{}{"attr1": 5, "attr2": "aaa"} | ||
values2 := map[string]interface{}{"attr3": 5, "attr4": "aaa"} | ||
|
||
presentaionItems := []*cl.PresentationRequestItem{{ | ||
RevealedAttrs: []string{"attr2"}, | ||
Predicates: []*cl.Predicate{ | ||
{ | ||
PType: "GE", | ||
Attr: "attr1", | ||
Value: 4, | ||
}, | ||
}, | ||
}} | ||
|
||
var ( | ||
issuer *Issuer | ||
prover *Prover | ||
verifier *Verifier | ||
|
||
credDef *cl.CredentialDefinition | ||
offer *cl.CredentialOffer | ||
request *cl.CredentialRequest | ||
credential *cl.Credential | ||
|
||
originalSignature []byte | ||
|
||
presentation *cl.PresentationRequest | ||
proof *cl.Proof | ||
) | ||
|
||
t.Run("test CL services creation", func(t *testing.T) { | ||
var err error | ||
|
||
issKh, issPubKey := createAndExportSignerKey(t, []string{"attr1", "attr2"}) | ||
km.GetKeyValue = issKh | ||
km.ExportPubKeyBytesValue = issPubKey | ||
km.ExportPubKeyTypeValue = kms.CLCredDefType | ||
|
||
issuer, err = NewIssuer(provider, "credDefKID", []string{"attr1", "attr2"}) | ||
require.NoError(t, err) | ||
|
||
prvKh := createBlinderKey(t) | ||
km.GetKeyValue = prvKh | ||
|
||
prover, err = NewProver(provider, "msKID") | ||
require.NoError(t, err) | ||
|
||
verifier, err = NewVerifier() | ||
require.NoError(t, err) | ||
}) | ||
|
||
t.Run("test CL issue credential flow", func(t *testing.T) { | ||
var err error | ||
|
||
// 0. Issuer expose CredDef | ||
credDef, err = issuer.GetCredentialDefinition() | ||
require.NoError(t, err) | ||
require.NotEmpty(t, credDef.CredPubKey) | ||
require.NotEmpty(t, credDef.CredDefCorrectnessProof) | ||
|
||
// 1. Issuer offers credential | ||
offer, err = issuer.OfferCredential() | ||
require.NoError(t, err) | ||
require.NotEmpty(t, offer.Nonce) | ||
|
||
// 2. Prover requests credential | ||
request, err = prover.RequestCredential(offer, credDef, "proverDID") | ||
require.NoError(t, err) | ||
require.NotEmpty(t, request.Nonce) | ||
require.NotEmpty(t, request.ProverID) | ||
require.NotEmpty(t, request.BlindedCredentialSecrets) | ||
|
||
// 3. Issuer issues credential | ||
credential, err = issuer.IssueCredential(values, request, offer) | ||
require.NoError(t, err) | ||
require.NotEmpty(t, credential.Signature) | ||
require.NotEmpty(t, credential.SigProof) | ||
require.NotEmpty(t, credential.Values) | ||
|
||
// 4. Prover verifies credential | ||
originalSignature = credential.Signature | ||
|
||
err = prover.ProcessCredential(credential, request, credDef) | ||
require.NoError(t, err) | ||
require.NotEmpty(t, credential.Signature) | ||
require.NotEmpty(t, credential.SigProof) | ||
require.NotEmpty(t, credential.Values) | ||
|
||
require.NotEqual(t, originalSignature, credential.Signature) | ||
}) | ||
|
||
t.Run("test CL present proof flow", func(t *testing.T) { | ||
var err error | ||
|
||
// 1. Verifier makes presentation request | ||
presentation, err = verifier.RequestPresentation(presentaionItems) | ||
require.NoError(t, err) | ||
require.NotEmpty(t, presentation.Items) | ||
require.NotEmpty(t, presentation.Nonce) | ||
|
||
// 2. Prover creates proof accordingly | ||
proof, err = prover.CreateProof(presentation, []*cl.Credential{credential}, []*cl.CredentialDefinition{credDef}) | ||
require.NoError(t, err) | ||
require.NotEmpty(t, proof.Proof) | ||
|
||
// 3. Verifier verifies resulting proof | ||
err = verifier.VerifyProof(proof, presentation, []*cl.CredentialDefinition{credDef}) | ||
require.NoError(t, err) | ||
}) | ||
|
||
t.Run("test CL failures", func(t *testing.T) { | ||
var err error | ||
|
||
issKh2, issPubKey2 := createAndExportSignerKey(t, []string{"attr3", "attr4"}) | ||
km.GetKeyValue = issKh2 | ||
km.ExportPubKeyBytesValue = issPubKey2 | ||
km.ExportPubKeyTypeValue = kms.CLCredDefType | ||
|
||
issuer2, err := NewIssuer(provider, "credDefKID2", []string{"attr3", "attr4"}) | ||
require.NoError(t, err) | ||
credDef2, err := issuer2.GetCredentialDefinition() | ||
require.NoError(t, err) | ||
|
||
// Issuer fails to issue credential for unknown credDef | ||
_, err = issuer2.IssueCredential(values, request, offer) | ||
require.Error(t, err) | ||
|
||
// Issuer fails to issue credential for invalid values | ||
_, err = issuer.IssueCredential(values2, request, offer) | ||
require.Error(t, err) | ||
|
||
// Prover fails to process credential with unmatched credDef | ||
err = prover.ProcessCredential(credential, request, credDef2) | ||
require.Error(t, err) | ||
|
||
// Prover fails to create proof with unmatched credDefs | ||
_, err = prover.CreateProof(presentation, []*cl.Credential{credential}, []*cl.CredentialDefinition{credDef2}) | ||
require.Error(t, err) | ||
|
||
// Verifier fails to verify proof for unprocessed credential | ||
unprocessed := &cl.Credential{ | ||
Signature: originalSignature, | ||
SigProof: credential.SigProof, | ||
Values: credential.Values, | ||
} | ||
|
||
invalid, err := prover.CreateProof(presentation, []*cl.Credential{unprocessed}, []*cl.CredentialDefinition{credDef}) | ||
require.NoError(t, err) | ||
|
||
err = verifier.VerifyProof(invalid, presentation, []*cl.CredentialDefinition{credDef}) | ||
require.Error(t, err) | ||
|
||
// Verifier fails to verify proof for other credDef | ||
err = verifier.VerifyProof(proof, presentation, []*cl.CredentialDefinition{credDef2}) | ||
require.Error(t, err) | ||
}) | ||
} | ||
|
||
func createAndExportSignerKey(t *testing.T, attrs []string) (*keyset.Handle, []byte) { | ||
kh, err := keyset.NewHandle(sgn.CredDefKeyTemplate(attrs)) | ||
require.NoError(t, err) | ||
|
||
pKh, err := kh.Public() | ||
require.NoError(t, err) | ||
|
||
pubKey, err := sgn.ExportCredDefPubKey(pKh) | ||
require.NoError(t, err) | ||
|
||
return kh, pubKey | ||
} | ||
|
||
func createBlinderKey(t *testing.T) *keyset.Handle { | ||
kh, err := keyset.NewHandle(bld.MasterSecretKeyTemplate()) | ||
require.NoError(t, err) | ||
|
||
return kh | ||
} | ||
|
||
type mockProvider struct { | ||
km kms.KeyManager | ||
cr crypto.Crypto | ||
} | ||
|
||
func (p *mockProvider) KMS() kms.KeyManager { | ||
return p.km | ||
} | ||
|
||
func (p *mockProvider) Crypto() crypto.Crypto { | ||
return p.cr | ||
} | ||
|
||
func newProvider(km kms.KeyManager, cr crypto.Crypto) *mockProvider { | ||
return &mockProvider{ | ||
km: km, | ||
cr: cr, | ||
} | ||
} |
Oops, something went wrong.