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

Commit

Permalink
chore: Additional hash algorithms; check for _sd claim (#3503)
Browse files Browse the repository at this point in the history
Add SHA-384 and SHA-512 algorithms to SHA-256.
Also, check for _sd claim in claims before creating SD-JWT.

Closes #3502

Signed-off-by: Sandra Vrtikapa <[email protected]>
  • Loading branch information
sandrask authored Jan 30, 2023
1 parent 76c8467 commit f042364
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 3 deletions.
9 changes: 8 additions & 1 deletion pkg/doc/sdjwt/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,18 @@ func GetCryptoHash(sdAlg string) (crypto.Hash, error) {

var cryptoHash crypto.Hash

// From spec: the hash algorithms MD2, MD4, MD5, RIPEMD-160, and SHA-1 revealed fundamental weaknesses
// and they MUST NOT be used.

switch strings.ToUpper(sdAlg) {
case crypto.SHA256.String():
cryptoHash = crypto.SHA256
case crypto.SHA384.String():
cryptoHash = crypto.SHA384
case crypto.SHA512.String():
cryptoHash = crypto.SHA512
default:
err = fmt.Errorf("%s '%s 'not supported", SDAlgorithmKey, sdAlg)
err = fmt.Errorf("%s '%s' not supported", SDAlgorithmKey, sdAlg)
}

return cryptoHash, err
Expand Down
27 changes: 26 additions & 1 deletion pkg/doc/sdjwt/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ func TestVerifyDisclosuresInSDJWT(t *testing.T) {

err = VerifyDisclosuresInSDJWT(nil, signedJWT)
r.Error(err)
r.Contains(err.Error(), "_sd_alg 'SHA-XXX 'not supported")
r.Contains(err.Error(), "_sd_alg 'SHA-XXX' not supported")
})

t.Run("error - algorithm is not a string", func(t *testing.T) {
Expand Down Expand Up @@ -446,6 +446,31 @@ func TestGetDisclosedClaims(t *testing.T) {
})
}

func TestGetCryptoHash(t *testing.T) {
r := require.New(t)

t.Run("success", func(t *testing.T) {
hash, err := GetCryptoHash("sha-256")
r.NoError(err)
r.Equal(crypto.SHA256, hash)

hash, err = GetCryptoHash("sha-384")
r.NoError(err)
r.Equal(crypto.SHA384, hash)

hash, err = GetCryptoHash("sha-512")
r.NoError(err)
r.Equal(crypto.SHA512, hash)
})

t.Run("error - not supported", func(t *testing.T) {
hash, err := GetCryptoHash("invalid")
r.Error(err)
r.Equal(crypto.Hash(0), hash)
r.Contains(err.Error(), "_sd_alg 'invalid' not supported")
})
}

func TestGetSDAlg(t *testing.T) {
r := require.New(t)

Expand Down
2 changes: 2 additions & 0 deletions pkg/doc/sdjwt/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package sdjwt

import (
"bytes"
"crypto"
"crypto/ed25519"
"crypto/rand"
"encoding/json"
Expand Down Expand Up @@ -111,6 +112,7 @@ func TestSDJWTFlow(t *testing.T) {

// Issuer will issue SD-JWT for specified claims and holder public key.
token, err := issuer.New(testIssuer, claims, nil, signer,
issuer.WithHashAlgorithm(crypto.SHA512),
issuer.WithNotBefore(jwt.NewNumericDate(now)),
issuer.WithIssuedAt(jwt.NewNumericDate(now)),
issuer.WithExpiry(jwt.NewNumericDate(now.Add(year))),
Expand Down
23 changes: 23 additions & 0 deletions pkg/doc/sdjwt/issuer/issuer.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ func New(issuer string, claims interface{}, headers jose.Headers,
return nil, fmt.Errorf("convert payload to map: %w", err)
}

// check for the presence of the _sd claim in claims map
found := keyExistsInMap(common.SDKey, claimsMap)
if found {
return nil, fmt.Errorf("key '%s' cannot be present in the claims", common.SDKey)
}

disclosures, digests, err := createDisclosuresAndDigests("", claimsMap, nOpts)
if err != nil {
return nil, err
Expand Down Expand Up @@ -439,6 +445,23 @@ func generateSalt() (string, error) {
return base64.RawURLEncoding.EncodeToString(salt), nil
}

func keyExistsInMap(key string, claims map[string]interface{}) bool {
for k, v := range claims {
if k == key {
return true
}

if obj, ok := v.(map[string]interface{}); ok {
exists := keyExistsInMap(key, obj)
if exists {
return true
}
}
}

return false
}

// payload represents SD-JWT payload.
type payload struct {
Issuer string `json:"iss,omitempty"`
Expand Down
40 changes: 39 additions & 1 deletion pkg/doc/sdjwt/issuer/issuer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ func TestNew(t *testing.T) {
r.Contains(err.Error(), "unknown key id")
})

t.Run("Create Mixed (SD + non-SD) JWS with flat claims flag", func(t *testing.T) {
t.Run("Create Mixed (SD + non-SD) JWS with flat claims flag, SHA-512", func(t *testing.T) {
r := require.New(t)

_, privKey, err := ed25519.GenerateKey(rand.Reader)
Expand All @@ -306,6 +306,7 @@ func TestNew(t *testing.T) {

newOpts = append(newOpts,
WithNonSelectivelyDisclosableClaims([]string{"id", "degree.type"}),
WithHashAlgorithm(crypto.SHA512),
)

token, err := New(issuer, complexClaims, nil, afjwt.NewEd25519Signer(privKey), newOpts...)
Expand All @@ -315,6 +316,8 @@ func TestNew(t *testing.T) {
err = token.DecodeClaims(&tokenClaims)
r.NoError(err)

r.Equal("sha-512", tokenClaims[common.SDAlgorithmKey])

printObject(t, "Token Claims", tokenClaims)

combinedFormatForIssuance, err := token.Serialize(false)
Expand Down Expand Up @@ -403,6 +406,41 @@ func TestNew(t *testing.T) {
fmt.Println(prettyJSON)
})

t.Run("error - claims contain _sd key (top level object)", func(t *testing.T) {
r := require.New(t)

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

complexClaims := map[string]interface{}{
"_sd": "whatever",
}

token, err := New(issuer, complexClaims, nil, afjwt.NewEd25519Signer(privKey))
r.Error(err)
r.Nil(token)
r.Contains(err.Error(), "key '_sd' cannot be present in the claims")
})

t.Run("error - claims contain _sd key (inner object)", func(t *testing.T) {
r := require.New(t)

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

complexClaims := map[string]interface{}{
"degree": map[string]interface{}{
"_sd": "whatever",
"type": "BachelorDegree",
},
}

token, err := New(issuer, complexClaims, nil, afjwt.NewEd25519Signer(privKey))
r.Error(err)
r.Nil(token)
r.Contains(err.Error(), "key '_sd' cannot be present in the claims")
})

t.Run("error - invalid holder public key", func(t *testing.T) {
r := require.New(t)

Expand Down

0 comments on commit f042364

Please sign in to comment.