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

update to latest vc-jwt spec; differentiate between id and key id #341

Merged
merged 4 commits into from
Apr 7, 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: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.20.2
go-version: 1.20.3

- name: Install Mage
run: go install github.com/magefile/mage
Expand All @@ -38,7 +38,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.20.2
go-version: 1.20.3

- name: Install Mage
run: go install github.com/magefile/mage
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.20.2
go-version: 1.20.3
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This guide is for you.

| Requirement | Tested Version | Installation Instructions |
|-------------|----------------|--------------------------------------------------------|
| Go | 1.20.2 | [go.dev](https://go.dev/doc/tutorial/compile-install) |
| Go | 1.20.3 | [go.dev](https://go.dev/doc/tutorial/compile-install) |
| Mage | 1.13.0-6 | [magefile.org](https://magefile.org/) |

### Go
Expand All @@ -25,7 +25,7 @@ You may verify your `go` installation via the terminal:

```
$> go version
go version go1.20.2 darwin/amd64
go version go1.20.3 darwin/amd64
```

If you do not have go, we recommend installing it by:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[![godoc ssi-sdk](https://img.shields.io/badge/godoc-ssi--sdk-blue)](https://pkg.go.dev/github.com/TBD54566975/ssi-sdk)
[![go version 1.20.2](https://img.shields.io/badge/go_version-1.20.2-brightgreen)](https://golang.org/)
[![go version 1.20.3](https://img.shields.io/badge/go_version-1.20.3-brightgreen)](https://golang.org/)
[![Go Report Card A+](https://goreportcard.com/badge/github.com/TBD54566975/ssi-sdk)](https://goreportcard.com/report/github.com/TBD54566975/ssi-sdk)
[![license Apache 2](https://img.shields.io/badge/license-Apache%202-black)](https://github.com/TBD54566975/ssi-sdk/blob/main/LICENSE)
[![issues](https://img.shields.io/github/issues/TBD54566975/ssi-sdk)](https://github.com/TBD54566975/ssi-sdk/issues)
Expand Down
2 changes: 1 addition & 1 deletion credential/exchange/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func BuildPresentationRequest(signer any, pt PresentationRequestType, def Presen
func BuildJWTPresentationRequest(signer crypto.JWTSigner, def PresentationDefinition, target string) ([]byte, error) {
jwtValues := map[string]any{
jwt.JwtIDKey: uuid.NewString(),
jwt.IssuerKey: signer.KeyID(),
jwt.IssuerKey: signer.ID,
jwt.AudienceKey: target,
PresentationDefinitionKey: def,
}
Expand Down
6 changes: 3 additions & 3 deletions credential/exchange/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestBuildPresentationRequest(t *testing.T) {
_, privKey, err := crypto.GenerateEd25519Key()
assert.NoError(t, err)

signer, err := crypto.NewJWTSigner("test-id", privKey)
signer, err := crypto.NewJWTSigner("test-id", "test-kid", privKey)
assert.NoError(t, err)

testDef := getDummyPresentationDefinition()
Expand All @@ -36,7 +36,7 @@ func TestBuildPresentationRequest(t *testing.T) {
_, privKey, err := crypto.GenerateEd25519Key()
assert.NoError(t, err)

signer, err := crypto.NewJWTSigner("test-id", privKey)
signer, err := crypto.NewJWTSigner("test-id", "test-kid", privKey)
assert.NoError(t, err)

testDef := getDummyPresentationDefinition()
Expand All @@ -59,7 +59,7 @@ func TestBuildPresentationRequest(t *testing.T) {
_, privKey, err := crypto.GenerateEd25519Key()
assert.NoError(t, err)

signer, err := crypto.NewJWTSigner("test-id", privKey)
signer, err := crypto.NewJWTSigner("test-id", "test-kid", privKey)
assert.NoError(t, err)

testDef := getDummyPresentationDefinition()
Expand Down
11 changes: 7 additions & 4 deletions credential/exchange/submission.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func (pc *PresentationClaim) GetClaimJSON() (map[string]any, error) {
// https://identity.foundation/presentation-exchange/#presentation-submission
// Note: this method does not support LD cryptosuites, and prefers JWT representations. Future refactors
// may include an analog method for LD suites.
func BuildPresentationSubmission(signer crypto.JWTSigner, def PresentationDefinition, claims []PresentationClaim, et EmbedTarget) ([]byte, error) {
func BuildPresentationSubmission(signer crypto.JWTSigner, requester string, def PresentationDefinition, claims []PresentationClaim, et EmbedTarget) ([]byte, error) {
if !IsSupportedEmbedTarget(et) {
return nil, fmt.Errorf("unsupported presentation submission embed target type: %s", et)
}
Expand All @@ -137,11 +137,11 @@ func BuildPresentationSubmission(signer crypto.JWTSigner, def PresentationDefini
}
switch et {
case JWTVPTarget:
vpSubmission, err := BuildPresentationSubmissionVP(def, normalizedClaims)
vpSubmission, err := BuildPresentationSubmissionVP(signer.ID, def, normalizedClaims)
if err != nil {
return nil, errors.Wrap(err, "unable to fulfill presentation definition with given credentials")
}
return signing.SignVerifiablePresentationJWT(signer, *vpSubmission)
return signing.SignVerifiablePresentationJWT(signer, signing.JWTVVPParameters{Audience: requester}, *vpSubmission)
default:
return nil, fmt.Errorf("presentation submission embed target <%s> is not implemented", et)
}
Expand Down Expand Up @@ -203,7 +203,7 @@ type processedClaim struct {
// BuildPresentationSubmissionVP takes a presentation definition and a set of claims. According to the presentation
// definition, and the algorithm defined - https://identity.foundation/presentation-exchange/#input-evaluation - in
// the specification, a presentation submission is constructed as a Verifiable Presentation.
func BuildPresentationSubmissionVP(def PresentationDefinition, claims []NormalizedClaim) (*credential.VerifiablePresentation, error) {
func BuildPresentationSubmissionVP(submitter string, def PresentationDefinition, claims []NormalizedClaim) (*credential.VerifiablePresentation, error) {
if err := canProcessDefinition(def); err != nil {
return nil, errors.Wrap(err, "feature not supported in processing given presentation definition")
}
Expand All @@ -214,6 +214,9 @@ func BuildPresentationSubmissionVP(def PresentationDefinition, claims []Normaliz
if err := builder.AddType(PresentationSubmissionType); err != nil {
return nil, err
}
if err := builder.SetHolder(submitter); err != nil {
return nil, err
}

submission := PresentationSubmission{
ID: uuid.NewString(),
Expand Down
17 changes: 9 additions & 8 deletions credential/exchange/submission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (

func TestBuildPresentationSubmission(t *testing.T) {
t.Run("Unsupported embed target", func(tt *testing.T) {
_, err := BuildPresentationSubmission(crypto.JWTSigner{}, PresentationDefinition{}, nil, "badEmbedTarget")
_, err := BuildPresentationSubmission(crypto.JWTSigner{}, "requester", PresentationDefinition{}, nil, "badEmbedTarget")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if "requester" and "submitter" are things we will use mostly in production may be good to make a const

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not used in production as these strings. I would imagine they're each the DIDs of the requester/submitter

assert.Error(tt, err)
assert.Contains(tt, err.Error(), "unsupported presentation submission embed target type")
})
Expand Down Expand Up @@ -50,11 +50,11 @@ func TestBuildPresentationSubmission(t *testing.T) {
LDPFormat: LDPVC.Ptr(),
SignatureAlgorithmOrProofType: string(cryptosuite.JSONWebSignature2020),
}
submissionBytes, err := BuildPresentationSubmission(*signer, def, []PresentationClaim{presentationClaim}, JWTVPTarget)
submissionBytes, err := BuildPresentationSubmission(*signer, "requester", def, []PresentationClaim{presentationClaim}, JWTVPTarget)
assert.NoError(tt, err)
assert.NotEmpty(tt, submissionBytes)

vp, err := signing.VerifyVerifiablePresentationJWT(*verifier, string(submissionBytes))
_, vp, err := signing.VerifyVerifiablePresentationJWT(*verifier, string(submissionBytes))
assert.NoError(tt, err)

assert.NoError(tt, vp.IsValid())
Expand Down Expand Up @@ -91,7 +91,7 @@ func TestBuildPresentationSubmissionVP(t *testing.T) {
}
normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim})
assert.NoError(tt, err)
vp, err := BuildPresentationSubmissionVP(def, normalized)
vp, err := BuildPresentationSubmissionVP("submitter", def, normalized)
assert.NoError(tt, err)
assert.NotEmpty(tt, vp)

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

assert.NoError(tt, def.IsValid())
vp, err := BuildPresentationSubmissionVP(def, nil)
vp, err := BuildPresentationSubmissionVP("submitter", def, nil)
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "no claims match the required format, and signing alg/proof type requirements for input descriptor")
assert.Empty(tt, vp)
Expand Down Expand Up @@ -185,7 +185,7 @@ func TestBuildPresentationSubmissionVP(t *testing.T) {
}
normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim})
assert.NoError(tt, err)
vp, err := BuildPresentationSubmissionVP(def, normalized)
vp, err := BuildPresentationSubmissionVP("submitter", def, normalized)
assert.NoError(tt, err)
assert.NotEmpty(tt, vp)

Expand Down Expand Up @@ -259,7 +259,7 @@ func TestBuildPresentationSubmissionVP(t *testing.T) {

normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim, presentationClaimJWT})
assert.NoError(tt, err)
vp, err := BuildPresentationSubmissionVP(def, normalized)
vp, err := BuildPresentationSubmissionVP("submitter", def, normalized)
assert.NoError(tt, err)
assert.NotEmpty(tt, vp)

Expand Down Expand Up @@ -801,8 +801,9 @@ func getJWKSignerVerifier(t *testing.T) (*crypto.JWTSigner, *crypto.JWTVerifier)
key, err := jwk.FromRaw(privKey)
require.NoError(t, err)

id := "test-id"
kid := "test-key"
signer, err := crypto.NewJWTSignerFromKey(kid, key)
signer, err := crypto.NewJWTSignerFromKey(id, kid, key)
require.NoError(t, err)

verifier, err := signer.ToVerifier()
Expand Down
2 changes: 1 addition & 1 deletion credential/exchange/verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func VerifyPresentationSubmission(verifier crypto.JWTVerifier, et EmbedTarget, d
}
switch et {
case JWTVPTarget:
vp, err := signing.VerifyVerifiablePresentationJWT(verifier, string(submission))
_, vp, err := signing.VerifyVerifiablePresentationJWT(verifier, string(submission))
if err != nil {
return errors.Wrap(err, "verification of the presentation submission failed")
}
Expand Down
6 changes: 3 additions & 3 deletions credential/exchange/verification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func TestVerifyPresentationSubmission(t *testing.T) {
LDPFormat: LDPVC.Ptr(),
SignatureAlgorithmOrProofType: string(cryptosuite.JSONWebSignature2020),
}
submissionBytes, err := BuildPresentationSubmission(*signer, def, []PresentationClaim{presentationClaim}, JWTVPTarget)
submissionBytes, err := BuildPresentationSubmission(*signer, "requester", def, []PresentationClaim{presentationClaim}, JWTVPTarget)
assert.NoError(tt, err)
assert.NotEmpty(tt, submissionBytes)

Expand Down Expand Up @@ -107,11 +107,11 @@ func TestVerifyPresentationSubmissionVP(t *testing.T) {
LDPFormat: LDPVC.Ptr(),
SignatureAlgorithmOrProofType: string(cryptosuite.JSONWebSignature2020),
}
submissionBytes, err := BuildPresentationSubmission(*signer, def, []PresentationClaim{presentationClaim}, JWTVPTarget)
submissionBytes, err := BuildPresentationSubmission(*signer, "requester", def, []PresentationClaim{presentationClaim}, JWTVPTarget)
assert.NoError(tt, err)
assert.NotEmpty(tt, submissionBytes)

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

err = VerifyPresentationSubmissionVP(def, *verifiablePresentation)
Expand Down
2 changes: 1 addition & 1 deletion credential/manifest/testdata/full-credential.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"EUDriversLicense"
],
"issuer": "did:example:123",
"issuanceDate": "2010-01-01T19:73:24Z",
"issuanceDate": "2010-01-01T19:23:24Z",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"givenName": "ricky bobby",
Expand Down
2 changes: 1 addition & 1 deletion credential/manifest/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ func getValidTestCredManifestCredApplicationJWTCred(t *testing.T) (CredentialMan
// turn into a jwt
_, privKey, err := crypto.GenerateEd25519Key()
require.NoError(t, err)
signer, err := crypto.NewJWTSigner("test-kid", privKey)
signer, err := crypto.NewJWTSigner("test-id", "test-kid", privKey)
require.NoError(t, err)
jwt, err := signing.SignVerifiableCredentialJWT(*signer, vc)
require.NoError(t, err)
Expand Down
15 changes: 8 additions & 7 deletions credential/signing/jws.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ func SignVerifiableCredentialJWS(signer crypto.JWTSigner, cred credential.Verifi
// 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) (*credential.VerifiableCredential, error) {
func ParseVerifiableCredentialFromJWS(token string) (*jws.Message, *credential.VerifiableCredential, error) {
parsed, err := jws.Parse([]byte(token))
if err != nil {
return nil, errors.Wrap(err, "parsing token")
return nil, nil, errors.Wrap(err, "parsing JWS")
}

var signature *jws.Signature
Expand All @@ -50,23 +50,24 @@ func ParseVerifiableCredentialFromJWS(token string) (*credential.VerifiableCrede
}
}
if signature == nil {
return ParseVerifiableCredentialFromJWT(token)
_, cred, err := ParseVerifiableCredentialFromJWT(token)
return parsed, cred, err
}

var cred credential.VerifiableCredential
if err = json.Unmarshal(parsed.Payload(), &cred); err != nil {
return nil, errors.Wrap(err, "reconstructing Verifiable Credential")
return nil, nil, errors.Wrap(err, "reconstructing Verifiable Credential")
}

return &cred, nil
return parsed, &cred, nil
}

// 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 crypto.JWTVerifier, token string) (*credential.VerifiableCredential, error) {
func VerifyVerifiableCredentialJWS(verifier crypto.JWTVerifier, token string) (*jws.Message, *credential.VerifiableCredential, error) {
if err := verifier.VerifyJWS(token); err != nil {
return nil, errors.Wrap(err, "verifying JWT")
return nil, nil, errors.Wrap(err, "verifying JWS")
}
return ParseVerifiableCredentialFromJWS(token)
}
9 changes: 6 additions & 3 deletions credential/signing/jws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ func TestVerifiableCredentialJWS(t *testing.T) {
assert.NoError(t, err)

token := string(signedJWT)
parsedCred, err := ParseVerifiableCredentialFromJWS(token)
jws, parsedCred, err := ParseVerifiableCredentialFromJWS(token)
assert.NoError(t, err)
assert.NotEmpty(t, jws)
assert.Equal(t, &testCredential, parsedCred)
})

Expand Down Expand Up @@ -58,8 +59,9 @@ func TestVerifiableCredentialJWS(t *testing.T) {
assert.NoError(t, err)

token := string(signed)
cred, err := VerifyVerifiableCredentialJWS(*verifier, token)
jws, cred, err := VerifyVerifiableCredentialJWS(*verifier, token)
assert.NoError(t, err)
assert.NotEmpty(t, jws)
assert.Equal(t, &testCredential, cred)
})

Expand All @@ -68,8 +70,9 @@ func TestVerifiableCredentialJWS(t *testing.T) {
assert.NoError(t, err)

token := string(signedJWT)
parsedCred, err := ParseVerifiableCredentialFromJWS(token)
jws, parsedCred, err := ParseVerifiableCredentialFromJWS(token)
assert.NoError(t, err)
assert.NotEmpty(t, jws)
assert.Equal(t, &testCredential, parsedCred)
})
}
Loading