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

Commit

Permalink
feat: SD-JWT Issuer: Add Holder Public Key Claim (JWK) (#3472)
Browse files Browse the repository at this point in the history
If the Issuer wants to enable Holder Binding, it MAY include a public key associated with the Holder, or a reference thereto.

We use confirmation "cnf" claim to include raw public key by value in SD-JWT.

In this first iteration of "cnf" claim we will use "jwk" confirmation method and include public key information in JWK format. Other confirmation methods such as "jwe", "kid" and "jku" may be implemented at later time (if required).

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

Signed-off-by: Sandra Vrtikapa <[email protected]>
  • Loading branch information
sandrask authored Jan 11, 2023
1 parent c8f690f commit 3e3fda9
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 7 deletions.
2 changes: 2 additions & 0 deletions pkg/doc/sdjwt/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ type Payload struct {
NotBefore *jwt.NumericDate `json:"nbf,omitempty"`
IssuedAt *jwt.NumericDate `json:"iat,omitempty"`

CNF map[string]interface{} `json:"cnf,omitempty"`

SD []string `json:"_sd,omitempty"`
SDAlg string `json:"_sd_alg,omitempty"`
}
Expand Down
35 changes: 29 additions & 6 deletions pkg/doc/sdjwt/issuer/issuer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/go-jose/go-jose/v3/jwt"

"github.com/hyperledger/aries-framework-go/pkg/doc/jose"
"github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk"
afgjwt "github.com/hyperledger/aries-framework-go/pkg/doc/jwt"
"github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/common"
)
Expand Down Expand Up @@ -48,6 +49,8 @@ type newOpts struct {
NotBefore *jwt.NumericDate
IssuedAt *jwt.NumericDate

HolderPublicKey *jwk.JWK

HashAlg crypto.Hash

jsonMarshal func(v interface{}) ([]byte, error)
Expand Down Expand Up @@ -108,6 +111,13 @@ func WithID(id string) NewOpt {
}
}

// WithHolderPublicKey is an option for SD-JWT payload.
func WithHolderPublicKey(jwk *jwk.JWK) NewOpt {
return func(opts *newOpts) {
opts.HolderPublicKey = jwk
}
}

// WithHashAlgorithm is an option for hashing disclosures.
func WithHashAlgorithm(alg crypto.Hash) NewOpt {
return func(opts *newOpts) {
Expand Down Expand Up @@ -162,23 +172,36 @@ func New(issuer string, claims interface{}, headers jose.Headers,
return nil, err
}

payload := createPayload(issuer, digests, nOpts)

signedJWT, err := afgjwt.NewSigned(payload, headers, signer)
if err != nil {
return nil, err
}

return &SelectiveDisclosureJWT{Disclosures: disclosures, SignedJWT: signedJWT}, nil
}

func createPayload(issuer string, digests []string, nOpts *newOpts) *common.Payload {
var cnf map[string]interface{}
if nOpts.HolderPublicKey != nil {
cnf = make(map[string]interface{})
cnf["jwk"] = nOpts.HolderPublicKey
}

payload := &common.Payload{
Issuer: issuer,
ID: nOpts.ID,
Subject: nOpts.Subject,
IssuedAt: nOpts.IssuedAt,
Expiry: nOpts.Expiry,
NotBefore: nOpts.NotBefore,
CNF: cnf,
SD: digests,
SDAlg: strings.ToLower(nOpts.HashAlg.String()),
}

signedJWT, err := afgjwt.NewSigned(payload, headers, signer)
if err != nil {
return nil, err
}

return &SelectiveDisclosureJWT{Disclosures: disclosures, SignedJWT: signedJWT}, nil
return payload
}

func createDigests(disclosures []string, nOpts *newOpts) ([]string, error) {
Expand Down
37 changes: 36 additions & 1 deletion pkg/doc/sdjwt/issuer/issuer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/hyperledger/aries-framework-go/pkg/doc/jose"
"github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk/jwksupport"
afjwt "github.com/hyperledger/aries-framework-go/pkg/doc/jwt"
"github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/common"
)
Expand Down Expand Up @@ -133,7 +134,6 @@ func TestNew(t *testing.T) {
WithNotBefore(jwt.NewNumericDate(notBefore)),
WithID("id"),
WithSubject("subject"),
// TODO: Audience ???
WithSaltFnc(generateSalt),
WithJSONMarshaller(json.Marshal),
WithHashAlgorithm(crypto.SHA256),
Expand Down Expand Up @@ -198,6 +198,41 @@ func TestNew(t *testing.T) {
}
})

t.Run("Create JWS with holder public key", func(t *testing.T) {
r := require.New(t)

pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
r.NoError(err)

_, holderPublicKey, err := ed25519.GenerateKey(rand.Reader)
require.NoError(t, err)

holderJWK, err := jwksupport.JWKFromKey(holderPublicKey)
require.NoError(t, err)

token, err := New(issuer, claims, nil, afjwt.NewEd25519Signer(privKey),
WithHolderPublicKey(holderJWK))
r.NoError(err)
combinedFormatForIssuance, err := token.Serialize(false)
require.NoError(t, err)

cfi := common.ParseCombinedFormatForIssuance(combinedFormatForIssuance)
require.Equal(t, 1, len(cfi.Disclosures))

var parsedClaims map[string]interface{}
err = verifyEd25519ViaGoJose(cfi.SDJWT, pubKey, &parsedClaims)
r.NoError(err)
require.NotEmpty(t, parsedClaims["cnf"])

parsedClaimsBytes, err := json.Marshal(parsedClaims)
require.NoError(t, err)

prettyJSON, err := prettyPrint(parsedClaimsBytes)
require.NoError(t, err)

fmt.Println(prettyJSON)
})

t.Run("error - create decoy disclosures failed", func(t *testing.T) {
r := require.New(t)

Expand Down

0 comments on commit 3e3fda9

Please sign in to comment.