Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor verification, integrity, parsing #404

Merged
merged 11 commits into from
Jun 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions credential/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,6 @@ func (vcb *VerifiableCredentialBuilder) SetCredentialSubject(subject CredentialS
return errors.New(BuilderEmptyError)
}

if subject.GetID() == "" {
return errors.New("credential subject must have an ID property")
}

vcb.CredentialSubject = subject
return nil
}
Expand Down
9 changes: 4 additions & 5 deletions credential/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,12 @@ func TestCredentialBuilder(t *testing.T) {
err = builder.SetCredentialStatus(status)
assert.NoError(t, err)

// bad cred subject - no id
badSubject := CredentialSubject{
// cred subject - no id
subjectWithMissingID := CredentialSubject{
"name": "Satoshi",
}
err = builder.SetCredentialSubject(badSubject)
assert.Error(t, err)
assert.Contains(t, err.Error(), "credential subject must have an ID property")
err = builder.SetCredentialSubject(subjectWithMissingID)
assert.NoError(t, err)

// good subject
subject := CredentialSubject{
Expand Down
3 changes: 2 additions & 1 deletion credential/exchange/submission.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"

"github.com/TBD54566975/ssi-sdk/credential"
"github.com/TBD54566975/ssi-sdk/credential/integrity"
"github.com/TBD54566975/ssi-sdk/crypto/jwx"
"github.com/TBD54566975/ssi-sdk/util"
"github.com/goccy/go-json"
Expand Down Expand Up @@ -165,7 +166,7 @@ func BuildPresentationSubmission(signer any, requester string, def PresentationD
if err != nil {
return nil, errors.Wrap(err, "unable to fulfill presentation definition with given credentials")
}
return credential.SignVerifiablePresentationJWT(jwtSigner, credential.JWTVVPParameters{Audience: []string{requester}}, *vpSubmission)
return integrity.SignVerifiablePresentationJWT(jwtSigner, integrity.JWTVVPParameters{Audience: []string{requester}}, *vpSubmission)
default:
return nil, fmt.Errorf("presentation submission embed target <%s> is not implemented", et)
}
Expand Down
9 changes: 5 additions & 4 deletions credential/exchange/submission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"testing"

"github.com/TBD54566975/ssi-sdk/credential/integrity"
"github.com/TBD54566975/ssi-sdk/cryptosuite/jws2020"
"github.com/goccy/go-json"
"github.com/oliveagle/jsonpath"
Expand Down Expand Up @@ -64,7 +65,7 @@ func TestBuildPresentationSubmission(t *testing.T) {

resolver, err := resolution.NewResolver([]resolution.Resolver{key.Resolver{}}...)
assert.NoError(tt, err)
_, _, _, err = credential.VerifyVerifiablePresentationJWT(context.Background(), *verifier, resolver, string(submissionBytes))
_, _, _, err = integrity.VerifyVerifiablePresentationJWT(context.Background(), *verifier, resolver, string(submissionBytes))
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "credential must have a proof")
})
Expand Down Expand Up @@ -92,7 +93,7 @@ func TestBuildPresentationSubmission(t *testing.T) {
signer, verifier := getJWKSignerVerifier(tt)
testVC := getTestVerifiableCredential(signer.ID, signer.ID)

credJWT, err := credential.SignVerifiableCredentialJWT(*signer, testVC)
credJWT, err := integrity.SignVerifiableCredentialJWT(*signer, testVC)
assert.NoError(tt, err)
assert.NotEmpty(tt, credJWT)

Expand All @@ -107,7 +108,7 @@ func TestBuildPresentationSubmission(t *testing.T) {

resolver, err := resolution.NewResolver([]resolution.Resolver{key.Resolver{}}...)
assert.NoError(tt, err)
_, _, vp, err := credential.VerifyVerifiablePresentationJWT(context.Background(), *verifier, resolver, string(submissionBytes))
_, _, vp, err := integrity.VerifyVerifiablePresentationJWT(context.Background(), *verifier, resolver, string(submissionBytes))
assert.NoError(tt, err)

assert.NoError(tt, vp.IsValid())
Expand Down Expand Up @@ -358,7 +359,7 @@ func TestBuildPresentationSubmissionVP(t *testing.T) {
assert.Equal(tt, "test-verifiable-credential", asVC.ID)
assert.Equal(tt, "Block", asVC.CredentialSubject["company"])

_, vcJWTToken, asVCJWT, err := credential.ParseVerifiableCredentialFromJWT(*(vp.VerifiableCredential[1].(*string)))
_, vcJWTToken, asVCJWT, err := integrity.ParseVerifiableCredentialFromJWT(*(vp.VerifiableCredential[1].(*string)))
assert.NoError(tt, err)
assert.NotEmpty(tt, vcJWTToken)
assert.NotEmpty(tt, asVCJWT)
Expand Down
10 changes: 6 additions & 4 deletions credential/exchange/verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"strings"

"github.com/TBD54566975/ssi-sdk/credential/integrity"
"github.com/TBD54566975/ssi-sdk/credential/parsing"
"github.com/TBD54566975/ssi-sdk/crypto/jwx"
"github.com/TBD54566975/ssi-sdk/did/resolution"
"github.com/TBD54566975/ssi-sdk/schema"
Expand Down Expand Up @@ -56,7 +58,7 @@ func VerifyPresentationSubmission(ctx context.Context, verifier any, resolver re
return nil, fmt.Errorf("verifier<%T> is not a JWT verifier", verifier)
}
// verify the VP, which in turn verifies all credentials in it
_, _, vp, err := credential.VerifyVerifiablePresentationJWT(ctx, jwtVerifier, resolver, string(submission))
_, _, vp, err := integrity.VerifyVerifiablePresentationJWT(ctx, jwtVerifier, resolver, string(submission))
if err != nil {
return nil, errors.Wrap(err, "verification of the presentation submission failed")
}
Expand Down Expand Up @@ -134,8 +136,8 @@ func VerifyPresentationSubmissionVP(def PresentationDefinition, vp credential.Ve
submissionDescriptor.ID, submissionDescriptor.Path)
}

// TODO(gabe) add in signature verification of claims here https://github.com/TBD54566975/ssi-sdk/issues/71
_, _, cred, err := credential.ToCredential(claim)
// get the credential from the claim
_, _, cred, err := parsing.ToCredential(claim)
if err != nil {
return nil, errors.Wrapf(err, "getting claim as json: <%s>", claim)
}
Expand All @@ -150,7 +152,7 @@ func VerifyPresentationSubmissionVP(def PresentationDefinition, vp credential.Ve

// TODO(gabe) consider enforcing limited disclosure if present
// for each field we need to verify at least one path matches
credJSON, err := credential.ToCredentialJSONMap(claim)
credJSON, err := parsing.ToCredentialJSONMap(claim)
if err != nil {
return nil, errors.Wrapf(err, "getting credential as json: %v", cred)
}
Expand Down
7 changes: 4 additions & 3 deletions credential/exchange/verification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"testing"

"github.com/TBD54566975/ssi-sdk/credential/integrity"
"github.com/TBD54566975/ssi-sdk/cryptosuite/jws2020"
"github.com/stretchr/testify/assert"

Expand Down Expand Up @@ -138,7 +139,7 @@ func TestVerifyPresentationSubmission(t *testing.T) {

signer, verifier := getJWKSignerVerifier(tt)
testVC := getTestVerifiableCredential(signer.ID, signer.ID)
credJWT, err := credential.SignVerifiableCredentialJWT(*signer, testVC)
credJWT, err := integrity.SignVerifiableCredentialJWT(*signer, testVC)
assert.NoError(tt, err)
presentationClaim := PresentationClaim{
Token: util.StringPtr(string(credJWT)),
Expand Down Expand Up @@ -186,7 +187,7 @@ func TestVerifyPresentationSubmissionVP(t *testing.T) {
assert.NoError(tt, err)
assert.NotEmpty(tt, submissionBytes)

_, _, verifiablePresentation, err := credential.ParseVerifiablePresentationFromJWT(string(submissionBytes))
_, _, verifiablePresentation, err := integrity.ParseVerifiablePresentationFromJWT(string(submissionBytes))
assert.NoError(tt, err)

_, err = VerifyPresentationSubmissionVP(def, *verifiablePresentation)
Expand Down Expand Up @@ -523,7 +524,7 @@ func TestVerifyPresentationSubmissionVP(t *testing.T) {
}
signer, _ := getJWKSignerVerifier(tt)
testVC := getTestVerifiableCredential("test-issuer", "test-subject")
vcData, err := credential.SignVerifiableCredentialJWT(*signer, testVC)
vcData, err := integrity.SignVerifiableCredentialJWT(*signer, testVC)
assert.NoError(tt, err)
b := NewPresentationSubmissionBuilder(def.ID)
assert.NoError(tt, b.SetDescriptorMap([]SubmissionDescriptor{
Expand Down
11 changes: 6 additions & 5 deletions credential/jws.go → credential/integrity/jws.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package credential
package integrity

import (
"github.com/TBD54566975/ssi-sdk/credential"
"github.com/TBD54566975/ssi-sdk/crypto/jwx"
"github.com/goccy/go-json"
"github.com/lestrrat-go/jwx/v2/jwa"
Expand All @@ -14,7 +15,7 @@ const (

// SignVerifiableCredentialJWS is prepared according to https://transmute-industries.github.io/vc-jws/.
// This is currently an experimental. It's unstable and subject to change. Use at your own peril.
func SignVerifiableCredentialJWS(signer jwx.Signer, cred VerifiableCredential) ([]byte, error) {
func SignVerifiableCredentialJWS(signer jwx.Signer, cred credential.VerifiableCredential) ([]byte, error) {
payload, err := json.Marshal(cred)
if err != nil {
return nil, errors.Wrap(err, "marshalling credential")
Expand All @@ -38,7 +39,7 @@ func SignVerifiableCredentialJWS(signer jwx.Signer, cred VerifiableCredential) (
// ParseVerifiableCredentialFromJWS parses a JWS. Depending on the `cty` header value, it parses as a JWT or simply
// decodes the payload.
// This is currently an experimental. It's unstable and subject to change. Use at your own peril.
func ParseVerifiableCredentialFromJWS(token string) (*jws.Message, *VerifiableCredential, error) {
func ParseVerifiableCredentialFromJWS(token string) (*jws.Message, *credential.VerifiableCredential, error) {
parsed, err := jws.Parse([]byte(token))
if err != nil {
return nil, nil, errors.Wrap(err, "parsing JWS")
Expand All @@ -55,7 +56,7 @@ func ParseVerifiableCredentialFromJWS(token string) (*jws.Message, *VerifiableCr
return parsed, cred, err
}

var cred VerifiableCredential
var cred credential.VerifiableCredential
if err = json.Unmarshal(parsed.Payload(), &cred); err != nil {
return nil, nil, errors.Wrap(err, "reconstructing Verifiable Credential")
}
Expand All @@ -66,7 +67,7 @@ func ParseVerifiableCredentialFromJWS(token string) (*jws.Message, *VerifiableCr
// VerifyVerifiableCredentialJWS verifies the signature validity on the token and parses
// the token in a verifiable credential.
// This is currently an experimental. It's unstable and subject to change. Use at your own peril.
func VerifyVerifiableCredentialJWS(verifier jwx.Verifier, token string) (*jws.Message, *VerifiableCredential, error) {
func VerifyVerifiableCredentialJWS(verifier jwx.Verifier, token string) (*jws.Message, *credential.VerifiableCredential, error) {
if err := verifier.VerifyJWS(token); err != nil {
return nil, nil, errors.Wrap(err, "verifying JWS")
}
Expand Down
5 changes: 3 additions & 2 deletions credential/jws_test.go → credential/integrity/jws_test.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package credential
package integrity

import (
"context"
"testing"

"github.com/TBD54566975/ssi-sdk/credential"
"github.com/lestrrat-go/jwx/v2/jws"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/stretchr/testify/assert"
)

func TestVerifiableCredentialJWS(t *testing.T) {
testCredential := VerifiableCredential{
testCredential := credential.VerifiableCredential{
Context: []any{"https://www.w3.org/2018/credentials/v1", "https://w3id.org/security/suites/jws-2020/v1"},
Type: []any{"VerifiableCredential"},
Issuer: "did:example:123",
Expand Down
27 changes: 14 additions & 13 deletions credential/jwt.go → credential/integrity/jwt.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package credential
package integrity

import (
"context"
"fmt"
"time"

"github.com/TBD54566975/ssi-sdk/credential"
"github.com/TBD54566975/ssi-sdk/crypto/jwx"
"github.com/TBD54566975/ssi-sdk/did/resolution"

Expand All @@ -24,7 +25,7 @@ const (

// SignVerifiableCredentialJWT is prepared according to https://w3c.github.io/vc-jwt/#version-1.1
// which will soon be deprecated by https://w3c.github.io/vc-jwt/ see: https://github.com/TBD54566975/ssi-sdk/issues/191
func SignVerifiableCredentialJWT(signer jwx.Signer, cred VerifiableCredential) ([]byte, error) {
func SignVerifiableCredentialJWT(signer jwx.Signer, cred credential.VerifiableCredential) ([]byte, error) {
if cred.IsEmpty() {
return nil, errors.New("credential cannot be empty")
}
Expand Down Expand Up @@ -98,9 +99,9 @@ func SignVerifiableCredentialJWT(signer jwx.Signer, cred VerifiableCredential) (

// VerifyVerifiableCredentialJWT verifies the signature validity on the token and parses
// the token in a verifiable credential.
// TODO(gabe) modify this to add additional verification steps such as credential status, expiration, etc.
// TODO(gabe) modify this to add additional validation steps such as credential status, expiration, etc.
// related to https://github.com/TBD54566975/ssi-service/issues/122
func VerifyVerifiableCredentialJWT(verifier jwx.Verifier, token string) (jws.Headers, jwt.Token, *VerifiableCredential, error) {
func VerifyVerifiableCredentialJWT(verifier jwx.Verifier, token string) (jws.Headers, jwt.Token, *credential.VerifiableCredential, error) {
if err := verifier.Verify(token); err != nil {
return nil, nil, nil, errors.Wrap(err, "verifying JWT")
}
Expand All @@ -111,7 +112,7 @@ func VerifyVerifiableCredentialJWT(verifier jwx.Verifier, token string) (jws.Hea
// https://www.w3.org/TR/vc-data-model/#jwt-decoding
// If there are any issues during decoding, an error is returned. As a result, a successfully
// decoded VerifiableCredential object is returned.
func ParseVerifiableCredentialFromJWT(token string) (jws.Headers, jwt.Token, *VerifiableCredential, error) {
func ParseVerifiableCredentialFromJWT(token string) (jws.Headers, jwt.Token, *credential.VerifiableCredential, error) {
parsed, err := jwt.Parse([]byte(token), jwt.WithValidate(false), jwt.WithVerify(false))
if err != nil {
return nil, nil, nil, errors.Wrap(err, "parsing credential token")
Expand All @@ -133,7 +134,7 @@ func ParseVerifiableCredentialFromJWT(token string) (jws.Headers, jwt.Token, *Ve
}

// ParseVerifiableCredentialFromToken takes a JWT object and parses it into a VerifiableCredential
func ParseVerifiableCredentialFromToken(token jwt.Token) (*VerifiableCredential, error) {
func ParseVerifiableCredentialFromToken(token jwt.Token) (*credential.VerifiableCredential, error) {
// parse remaining JWT properties and set in the credential
vcClaim, ok := token.Get(VCJWTProperty)
if !ok {
Expand All @@ -143,7 +144,7 @@ func ParseVerifiableCredentialFromToken(token jwt.Token) (*VerifiableCredential,
if err != nil {
return nil, errors.Wrap(err, "marshalling credential claim")
}
var cred VerifiableCredential
var cred credential.VerifiableCredential
if err = json.Unmarshal(vcBytes, &cred); err != nil {
return nil, errors.Wrap(err, "reconstructing Verifiable Credential")
}
Expand Down Expand Up @@ -179,7 +180,7 @@ func ParseVerifiableCredentialFromToken(token jwt.Token) (*VerifiableCredential,
if cred.CredentialSubject == nil {
cred.CredentialSubject = make(map[string]any)
}
cred.CredentialSubject[VerifiableCredentialIDProperty] = subStr
cred.CredentialSubject[credential.VerifiableCredentialIDProperty] = subStr
}

return &cred, nil
Expand All @@ -195,7 +196,7 @@ type JWTVVPParameters struct {

// SignVerifiablePresentationJWT transforms a VP into a VP JWT and signs it
// According to https://w3c.github.io/vc-jwt/#version-1.1
func SignVerifiablePresentationJWT(signer jwx.Signer, parameters JWTVVPParameters, presentation VerifiablePresentation) ([]byte, error) {
func SignVerifiablePresentationJWT(signer jwx.Signer, parameters JWTVVPParameters, presentation credential.VerifiablePresentation) ([]byte, error) {
if presentation.IsEmpty() {
return nil, errors.New("presentation cannot be empty")
}
Expand Down Expand Up @@ -270,7 +271,7 @@ func SignVerifiablePresentationJWT(signer jwx.Signer, parameters JWTVVPParameter
// After decoding the signature of each credential in the presentation is verified. If there are any issues during
// decoding or signature validation, an error is returned. As a result, a successfully decoded VerifiablePresentation
// object is returned.
func VerifyVerifiablePresentationJWT(ctx context.Context, verifier jwx.Verifier, r resolution.Resolver, token string) (jws.Headers, jwt.Token, *VerifiablePresentation, error) {
func VerifyVerifiablePresentationJWT(ctx context.Context, verifier jwx.Verifier, r resolution.Resolver, token string) (jws.Headers, jwt.Token, *credential.VerifiablePresentation, error) {
if r == nil {
return nil, nil, nil, errors.New("r cannot be empty")
}
Expand Down Expand Up @@ -306,7 +307,7 @@ func VerifyVerifiablePresentationJWT(ctx context.Context, verifier jwx.Verifier,
return nil, nil, nil, errors.Wrapf(err, "verifying credential %d", i)
}
if !verified {
return nil, nil, nil, errors.Errorf("credential %d failed signature verification", i)
return nil, nil, nil, errors.Errorf("credential %d failed signature validation", i)
}
}

Expand All @@ -318,7 +319,7 @@ func VerifyVerifiablePresentationJWT(ctx context.Context, verifier jwx.Verifier,
// https://www.w3.org/TR/vc-data-model/#jwt-decoding
// If there are any issues during decoding, an error is returned. As a result, a successfully
// decoded VerifiablePresentation object is returned.
func ParseVerifiablePresentationFromJWT(token string) (jws.Headers, jwt.Token, *VerifiablePresentation, error) {
func ParseVerifiablePresentationFromJWT(token string) (jws.Headers, jwt.Token, *credential.VerifiablePresentation, error) {
parsed, err := jwt.Parse([]byte(token), jwt.WithValidate(false), jwt.WithVerify(false))
if err != nil {
return nil, nil, nil, errors.Wrap(err, "parsing vp token")
Expand All @@ -331,7 +332,7 @@ func ParseVerifiablePresentationFromJWT(token string) (jws.Headers, jwt.Token, *
if err != nil {
return nil, nil, nil, errors.Wrap(err, "could not marshalling vp claim")
}
var pres VerifiablePresentation
var pres credential.VerifiablePresentation
if err = json.Unmarshal(vpBytes, &pres); err != nil {
return nil, nil, nil, errors.Wrap(err, "reconstructing Verifiable Presentation")
}
Expand Down
Loading