Skip to content

Commit

Permalink
chore: Additional hash algorithms; check for _sd claim
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 hyperledger-archives#3502

Signed-off-by: Sandra Vrtikapa <[email protected]>
  • Loading branch information
sandrask committed Jan 30, 2023
1 parent 001efb5 commit de6e1f2
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 2 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
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
35 changes: 35 additions & 0 deletions pkg/doc/sdjwt/issuer/issuer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,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 de6e1f2

Please sign in to comment.