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

Commit

Permalink
fix: Release claims by claim disclosure not by claim name (#3485)
Browse files Browse the repository at this point in the history
Currently the holder releases disclosures based on claim name. If we have same name in different objects within claims this will not work.

This has been change to claim disclosure.

Closes #3484

Signed-off-by: Sandra Vrtikapa <[email protected]>

Signed-off-by: Sandra Vrtikapa <[email protected]>
  • Loading branch information
sandrask authored Jan 19, 2023
1 parent 8b9d225 commit 25c3bd6
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 63 deletions.
55 changes: 27 additions & 28 deletions pkg/doc/sdjwt/holder/holder.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ import (
"github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/common"
)

const notFound = -1

// Claim defines claim.
type Claim struct {
Name string
Value interface{}
Disclosure string
Name string
Value interface{}
}

// jwtParseOpts holds options for the SD-JWT parsing.
Expand Down Expand Up @@ -86,8 +85,9 @@ func getClaims(disclosures []string) ([]*Claim, error) {
for _, disclosure := range disclosureClaims {
claims = append(claims,
&Claim{
Name: disclosure.Name,
Value: disclosure.Value,
Disclosure: disclosure.Disclosure,
Name: disclosure.Name,
Value: disclosure.Value,
})
}

Expand Down Expand Up @@ -122,8 +122,10 @@ func WithHolderBinding(info *BindingInfo) Option {
}
}

// DiscloseClaims discloses claims with specified claim names.
func DiscloseClaims(combinedFormatForIssuance string, claimNames []string, opts ...Option) (string, error) {
// CreatePresentation is a convenience method to assemble combined format for presentation
// using selected disclosures (claimsToDisclose) and optional holder binding.
// This call assumes that combinedFormatForIssuance has already been parsed and verified using Parse() function.
func CreatePresentation(combinedFormatForIssuance string, claimsToDisclose []string, opts ...Option) (string, error) {
hOpts := &options{}

for _, opt := range opts {
Expand All @@ -136,39 +138,36 @@ func DiscloseClaims(combinedFormatForIssuance string, claimNames []string, opts
return "", fmt.Errorf("no disclosures found in SD-JWT")
}

disclosures, err := common.GetDisclosureClaims(cfi.Disclosures)
if err != nil {
return "", err
}

var selectedDisclosures []string
disclosuresMap := sliceToMap(cfi.Disclosures)

for _, claimName := range claimNames {
if index := getDisclosureByClaimName(claimName, disclosures); index != notFound {
selectedDisclosures = append(selectedDisclosures, cfi.Disclosures[index])
} else {
return "", fmt.Errorf("claim name '%s' not found", claimName)
for _, ctd := range claimsToDisclose {
if _, ok := disclosuresMap[ctd]; !ok {
return "", fmt.Errorf("disclosure '%s' not found in SD-JWT", ctd)
}
}

var err error

var hbJWT string

if hOpts.holderBindingInfo != nil {
hbJWT, err = createHolderBinding(hOpts.holderBindingInfo)
hbJWT, err = CreateHolderBinding(hOpts.holderBindingInfo)
if err != nil {
return "", fmt.Errorf("failed to create holder binding: %w", err)
}
}

cf := common.CombinedFormatForPresentation{
SDJWT: cfi.SDJWT,
Disclosures: selectedDisclosures,
Disclosures: claimsToDisclose,
HolderBinding: hbJWT,
}

return cf.Serialize(), nil
}

func createHolderBinding(info *BindingInfo) (string, error) {
// CreateHolderBinding will create holder binding from binding info.
func CreateHolderBinding(info *BindingInfo) (string, error) {
hbJWT, err := afgjwt.NewSigned(info.Payload, nil, info.Signer)
if err != nil {
return "", err
Expand All @@ -177,14 +176,14 @@ func createHolderBinding(info *BindingInfo) (string, error) {
return hbJWT.Serialize(false)
}

func getDisclosureByClaimName(name string, disclosures []*common.DisclosureClaim) int {
for index, disclosure := range disclosures {
if disclosure.Name == name {
return index
}
func sliceToMap(ids []string) map[string]bool {
// convert slice to map
values := make(map[string]bool)
for _, id := range ids {
values[id] = true
}

return notFound
return values
}

// NoopSignatureVerifier is no-op signature verifier (signature will not get checked).
Expand Down
29 changes: 12 additions & 17 deletions pkg/doc/sdjwt/holder/holder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,12 @@ func TestDiscloseClaims(t *testing.T) {
combinedFormatForIssuance, e := token.Serialize(false)
r.NoError(e)

cfi := common.ParseCombinedFormatForIssuance(combinedFormatForIssuance)

claimsToDisclose := []string{cfi.Disclosures[0]}

t.Run("success", func(t *testing.T) {
combinedFormatForPresentation, err := DiscloseClaims(combinedFormatForIssuance, []string{"given_name"})
combinedFormatForPresentation, err := CreatePresentation(combinedFormatForIssuance, claimsToDisclose)
r.NoError(err)
require.NotNil(t, combinedFormatForPresentation)
require.Equal(t, combinedFormatForIssuance+common.CombinedFormatSeparator, combinedFormatForPresentation)
Expand All @@ -153,7 +157,7 @@ func TestDiscloseClaims(t *testing.T) {

holderSigner := afjwt.NewEd25519Signer(holderPrivKey)

combinedFormatForPresentation, err := DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
WithHolderBinding(&BindingInfo{
Payload: BindingPayload{
Audience: "https://example.com/verifier",
Expand All @@ -168,7 +172,7 @@ func TestDiscloseClaims(t *testing.T) {
})

t.Run("error - failed to create holder binding due to signing error", func(t *testing.T) {
combinedFormatForPresentation, err := DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
WithHolderBinding(&BindingInfo{
Payload: BindingPayload{},
Signer: &mockSigner{Err: fmt.Errorf("signing error")},
Expand All @@ -184,27 +188,18 @@ func TestDiscloseClaims(t *testing.T) {
t.Run("error - no disclosure(s)", func(t *testing.T) {
cfi := common.ParseCombinedFormatForIssuance(combinedFormatForIssuance)

combinedFormatForPresentation, err := DiscloseClaims(cfi.SDJWT, []string{"given_name"})
combinedFormatForPresentation, err := CreatePresentation(cfi.SDJWT, claimsToDisclose)
r.Error(err)
r.Empty(combinedFormatForPresentation)
r.Contains(err.Error(), "no disclosures found in SD-JWT")
})

t.Run("error - add invalid disclosure", func(t *testing.T) {
cfiWithInvalidDisclosure := fmt.Sprintf("%s~%s", combinedFormatForIssuance, "abc")

combinedFormatForPresentation, err := DiscloseClaims(cfiWithInvalidDisclosure, []string{"given_name"})
r.Error(err)
r.Empty(combinedFormatForPresentation)
r.Contains(err.Error(), "failed to unmarshal disclosure array")
})

t.Run("error - claim name not found", func(t *testing.T) {
combinedFormatForPresentation, err := DiscloseClaims(combinedFormatForIssuance,
[]string{"given_name", "non_existent"})
t.Run("error - disclosure not found", func(t *testing.T) {
combinedFormatForPresentation, err := CreatePresentation(combinedFormatForIssuance,
[]string{"non_existent"})
r.Error(err)
r.Empty(combinedFormatForPresentation)
r.Contains(err.Error(), "claim name 'non_existent' not found")
r.Contains(err.Error(), "disclosure 'non_existent' not found")
})
}

Expand Down
6 changes: 4 additions & 2 deletions pkg/doc/sdjwt/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ func TestSDJWTFlow(t *testing.T) {
r.Equal(2, len(claims))

// Holder will disclose only sub-set of claims to verifier.
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"})
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance,
[]string{claims[0].Disclosure})
r.NoError(err)

fmt.Println(fmt.Sprintf("holder SD-JWT: %s", combinedFormatForPresentation))
Expand Down Expand Up @@ -110,7 +111,8 @@ func TestSDJWTFlow(t *testing.T) {
const testNonce = "nonce"

// Holder will disclose only sub-set of claims to verifier and add holder binding.
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance,
[]string{claims[0].Disclosure},
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand Down
38 changes: 22 additions & 16 deletions pkg/doc/sdjwt/verifier/verifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,12 @@ func TestHolderBinding(t *testing.T) {

holderSigner := afjwt.NewEd25519Signer(holderPrivKey)

cfi := common.ParseCombinedFormatForIssuance(combinedFormatForIssuance)

claimsToDisclose := []string{cfi.Disclosures[0]}

t.Run("success - with holder binding", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand All @@ -290,7 +294,7 @@ func TestHolderBinding(t *testing.T) {
})

t.Run("success - with holder binding; expected nonce and audience not specified", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand All @@ -311,7 +315,7 @@ func TestHolderBinding(t *testing.T) {
})

t.Run("success - with holder binding (required)", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand All @@ -336,7 +340,7 @@ func TestHolderBinding(t *testing.T) {

t.Run("error - holder binding required, however not provided by the holder", func(t *testing.T) {
// holder will not issue holder binding
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"})
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose)
r.NoError(err)

// Verifier will validate combined format for presentation and create verified claims.
Expand All @@ -352,7 +356,7 @@ func TestHolderBinding(t *testing.T) {
})

t.Run("error - holder signature is not matching holder public key in SD-JWT", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand All @@ -376,7 +380,7 @@ func TestHolderBinding(t *testing.T) {

t.Run("error - invalid holder binding JWT provided by the holder", func(t *testing.T) {
// holder will not issue holder binding
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"})
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose)
r.NoError(err)

// add fake holder binding
Expand All @@ -395,7 +399,7 @@ func TestHolderBinding(t *testing.T) {
})

t.Run("error - holder signature algorithm not supported", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand All @@ -421,7 +425,7 @@ func TestHolderBinding(t *testing.T) {
})

t.Run("error - invalid iat for holder binding", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: "different",
Expand All @@ -444,7 +448,7 @@ func TestHolderBinding(t *testing.T) {
})

t.Run("error - unexpected nonce for holder binding", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: "different",
Expand All @@ -467,7 +471,7 @@ func TestHolderBinding(t *testing.T) {
})

t.Run("error - unexpected audience for holder binding", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand Down Expand Up @@ -496,10 +500,12 @@ func TestHolderBinding(t *testing.T) {
cfiWithoutHolderPublicKey, err := tokenWithoutHolderPublicKey.Serialize(false)
r.NoError(err)

ctd := []string{common.ParseCombinedFormatForIssuance(cfiWithoutHolderPublicKey).Disclosures[0]}

_, err = holder.Parse(cfiWithoutHolderPublicKey, holder.WithSignatureVerifier(signatureVerifier))
r.NoError(err)

combinedFormatForPresentation, err := holder.DiscloseClaims(cfiWithoutHolderPublicKey, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(cfiWithoutHolderPublicKey, ctd,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand All @@ -522,7 +528,7 @@ func TestHolderBinding(t *testing.T) {
})

t.Run("error - holder binding provided, however cnf is not an object", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand Down Expand Up @@ -553,7 +559,7 @@ func TestHolderBinding(t *testing.T) {
})

t.Run("error - holder binding provided, cnf is missing jwk", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand Down Expand Up @@ -587,7 +593,7 @@ func TestHolderBinding(t *testing.T) {
})

t.Run("error - holder binding provided, invalid jwk in cnf", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand Down Expand Up @@ -621,7 +627,7 @@ func TestHolderBinding(t *testing.T) {
})

t.Run("error - holder binding provided, invalid jwk in cnf", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand Down Expand Up @@ -655,7 +661,7 @@ func TestHolderBinding(t *testing.T) {
})

t.Run("error - holder binding provided with EdDSA, jwk in cnf is RSA", func(t *testing.T) {
combinedFormatForPresentation, err := holder.DiscloseClaims(combinedFormatForIssuance, []string{"given_name"},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand Down

0 comments on commit 25c3bd6

Please sign in to comment.