Skip to content

Commit

Permalink
refactor: wallet binding check for attestation vp
Browse files Browse the repository at this point in the history
Signed-off-by: Andrii Holovko <[email protected]>
  • Loading branch information
aholovko committed Nov 27, 2023
1 parent 28cabe0 commit d6bdfc4
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 33 deletions.
30 changes: 24 additions & 6 deletions pkg/service/clientattestation/client_attestation_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ import (
"time"

"github.com/piprate/json-gold/ld"
"github.com/samber/lo"
"github.com/trustbloc/vc-go/jwt"
"github.com/trustbloc/vc-go/verifiable"

profileapi "github.com/trustbloc/vcs/pkg/profile"
)

const WalletAttestationVCType = "WalletAttestationCredential"

type httpClient interface {
Do(req *http.Request) (*http.Response, error)
}
Expand Down Expand Up @@ -60,18 +64,30 @@ func NewService(config *Config) *Service {
func (s *Service) ValidateAttestationJWTVP(ctx context.Context, profile *profileapi.Issuer, jwtVP string) error {
vp, err := verifiable.ParsePresentation(
[]byte(jwtVP),
verifiable.WithPresProofChecker(s.proofChecker),
// The verification of proof is conducted manually, along with an extra verification to ensure that signer of
// the VP matches the subject of the attestation VC.
verifiable.WithPresDisabledProofCheck(),
verifiable.WithPresJSONLDDocumentLoader(s.documentLoader),
)
if err != nil {
return fmt.Errorf("parse attestation vp: %w", err)
}

if len(vp.Credentials()) == 0 {
return fmt.Errorf("missing attestation vc")
var vc *verifiable.Credential

for _, credential := range vp.Credentials() {
content := credential.Contents()

if lo.Contains(content.Types, WalletAttestationVCType) {
vc = credential

break
}
}

vc := vp.Credentials()[0]
if vc == nil {
return fmt.Errorf("missing attestation vc")
}

// validate attestation VC
opts := []verifiable.CredentialOpt{
Expand All @@ -88,12 +104,14 @@ func (s *Service) ValidateAttestationJWTVP(ctx context.Context, profile *profile
}

vcc := vc.Contents()

if vcc.Expired != nil && time.Now().UTC().After(vcc.Expired.Time) {
return fmt.Errorf("attestation vc is expired")
}

if len(vcc.Subject) == 0 || vcc.Subject[0].ID != vp.Holder {
return fmt.Errorf("attestation vc subject does not match vp holder")
// validate vp proof with extra check for wallet binding
if err = jwt.CheckProof(jwtVP, s.proofChecker, &vcc.Subject[0].ID, nil); err != nil {
return fmt.Errorf("check attestation vp proof: %w", err)
}

// check attestation VC status
Expand Down
31 changes: 4 additions & 27 deletions pkg/service/clientattestation/client_attestation_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/require"
utiltime "github.com/trustbloc/did-go/doc/util/time"
"github.com/trustbloc/kms-go/doc/jose"
"github.com/trustbloc/kms-go/spi/kms"
"github.com/trustbloc/vc-go/jwt"
"github.com/trustbloc/vc-go/proof/checker"
Expand Down Expand Up @@ -86,15 +85,7 @@ func TestService_ValidateClientAttestationJWTVP(t *testing.T) {
{
name: "fail to parse attestation vp",
setup: func() {
attestationVC := createAttestationVC(t, attestationProofCreator, walletDID, false)

jwtVP = createAttestationVP(t, attestationVC,
&mockProofCreator{
SignJWTFunc: func(params jwt.SignParameters, data []byte) ([]byte, error) {
return []byte("invalid signature"), nil
},
},
)
jwtVP = "invalid-jwt-vp"

proofChecker = defaultProofChecker

Expand Down Expand Up @@ -133,7 +124,7 @@ func TestService_ValidateClientAttestationJWTVP(t *testing.T) {
},
},
{
name: "attestation vc subject does not match vp holder",
name: "attestation vc subject does not match vp signer",
setup: func() {
attestationVC := createAttestationVC(t, attestationProofCreator, "invalid-subject", false)

Expand All @@ -144,7 +135,7 @@ func TestService_ValidateClientAttestationJWTVP(t *testing.T) {
vcStatusVerifier.EXPECT().ValidateVCStatus(gomock.Any(), gomock.Any(), gomock.Any()).Times(0)
},
check: func(t *testing.T, err error) {
require.ErrorContains(t, err, "attestation vc subject does not match vp holder")
require.ErrorContains(t, err, "check attestation vp proof")
},
},
{
Expand Down Expand Up @@ -198,6 +189,7 @@ func createAttestationVC(
ID: uuid.New().String(),
Types: []string{
verifiable.VCType,
clientattestation.WalletAttestationVCType,
},
Subject: []verifiable.Subject{
{
Expand Down Expand Up @@ -247,7 +239,6 @@ func createAttestationVP(
}

vp.ID = uuid.New().String()
vp.Holder = walletDID

claims, err := vp.JWTClaims([]string{}, false)
require.NoError(t, err)
Expand All @@ -260,17 +251,3 @@ func createAttestationVP(

return jws
}

type mockProofCreator struct {
SignJWTFunc func(params jwt.SignParameters, data []byte) ([]byte, error)
}

func (m *mockProofCreator) SignJWT(params jwt.SignParameters, data []byte) ([]byte, error) {
return m.SignJWTFunc(params, data)
}

func (m *mockProofCreator) CreateJWTHeaders(_ jwt.SignParameters) (jose.Headers, error) {
return map[string]interface{}{
jose.HeaderAlgorithm: "ES256",
}, nil
}

0 comments on commit d6bdfc4

Please sign in to comment.