Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Commit

Permalink
CL Anoncreds Crypto services
Browse files Browse the repository at this point in the history
* 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 16, 2022
1 parent aab94d2 commit a56c6a7
Show file tree
Hide file tree
Showing 7 changed files with 1,047 additions and 0 deletions.
77 changes: 77 additions & 0 deletions pkg/doc/cl/api.go
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
}
64 changes: 64 additions & 0 deletions pkg/doc/cl/model.go
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
}
228 changes: 228 additions & 0 deletions pkg/doc/cl/ursa/crypto_test.go
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,
}
}

0 comments on commit a56c6a7

Please sign in to comment.