Skip to content

Commit

Permalink
Documentation for Verifying Presentations (#636)
Browse files Browse the repository at this point in the history
* pres docs - bug in sdk

* fix tests

* update pres example

* Apply suggestions from code review

Co-authored-by: Andres Uribe <[email protected]>

---------

Co-authored-by: Andres Uribe <[email protected]>
  • Loading branch information
decentralgabe and andresuribe87 authored Aug 7, 2023
1 parent 55274ba commit 9c64560
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 39 deletions.
104 changes: 104 additions & 0 deletions doc/howto/presentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# How To: Verify a Presentation

## Background

By now you should be familiar with [DIDs](did.md) and [Verifiable Credentials](credential.md). A next logical question, once you've started using the technologies, is "how do I make use of it?" To use Verifiable Credentials anywhere you need to present them — that is to share them with another party. There are a few pieces of sharing: creating a package that you share and transmitting the data to another party. Transmission, simliar to credential issuance, can be accomplished with a number of different mechanisms like [Web5](https://github.com/TBD54566975/dwn-sdk-js#readme) and [OpenID Connect](https://openid.net/sg/openid4vc/). Packaging a credential, or set of credentials, into a presentation is something that has been standardized at the W3C in the same specification that defines Verifiable Credentials, called [Verifiable Presentations](https://www.w3.org/TR/vc-data-model/#presentations-0).

Verifiable Presentations are a standard data container for sharing credentials that provide a number of benefits, namely:

1. A common data format designed to work with Verifiable Credentials, reusable in any number of use cases.
2. Guidance on constructing presentations that ensures integrity protection and guards against replay attacks.

Presentations impose no constraints on who can construct a presentation, or what may be presented. This means if you have multiple credentials issued to different DIDs, you can still construct a Verifiable Presentation to present those credentials at the same time. This is why you will see that the [`proof` property](https://www.w3.org/TR/vc-data-model/#example-basic-structure-of-a-presentation) can be an array. Verifiers must take care to make sure that the credentials in a given presentation _can be_ presented by the presenter, and that the credentials themselves are valid.

Verifiable Presentations are currently accepted in two main SSI Service flows: when verifying [Presentation Submissoins](https://identity.foundation/presentation-exchange/spec/v2.0.0/#presentation-submission) while using [Presentation Exchange](https://identity.foundation/presentation-exchange/spec/v2.0.0), and verifying [Credential Applications](https://identity.foundation/credential-manifest/#credential-application) while using [Credential Manifest](https://identity.foundation/credential-manifest). As a utility, we've also exposed an endpoint to statelessly verify a presentation at `/v1/presentations/verification`.

## Constructing a Verifiable Presentation

Constructing a Verifiable Presentation is out of scope for the SSI Service, since the service acts as a utility for organizations managing their own credentials. However, the [SSI SDK](https://github.com/TBD54566975/ssi-sdk) provides a standards-based implementation of Verifiable Presentations using the [JWT representation](https://www.w3.org/TR/vc-data-model/#jwt-and-jws-considerations).

Library code for Verifiable Presentations can be found [here](https://github.com/TBD54566975/ssi-sdk/blob/d5c302a1d9b9d04c1636a0c8dfda015f61bb0f6b/credential/model.go#L110) with associated signing and verificatoin logic [here](https://github.com/TBD54566975/ssi-sdk/blob/d5c302a1d9b9d04c1636a0c8dfda015f61bb0f6b/credential/integrity/jwt.go#L208). An example of constructing a Verifiable Presentation using the SDK is provided below as a [runnable unit test here](presentation_test.go).

Upon running, we should see the test create a credential and a presentation for that credential as a JWT.

```plaintext
eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrI3o2TWttTjEyOTZ1YXBIbU02QTI4bkdaR2RBRW5pRDFhYTVSZEZDbjhKRXVucVY5ayIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTExNzU2MjQsImlzcyI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrIiwianRpIjoiYjQ0OTI0ZWEtMDIwMi00ZTllLWJiMmEtOTg5YmQwNWQ1N2FlIiwibmJmIjoxNjkxMTc1NjI0LCJub25jZSI6IjVjNmFhZDc2LWUyZWYtNGNiNy1iMWE4LTI3MjZiMjRhM2Y0ZSIsInZwIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6WyJleUpoYkdjaU9pSkZaRVJUUVNJc0ltdHBaQ0k2SW1ScFpEcHJaWGs2ZWpaTmEyMU9NVEk1Tm5WaGNFaHRUVFpCTWpodVIxcEhaRUZGYm1sRU1XRmhOVkprUmtOdU9FcEZkVzV4VmpsckkzbzJUV3R0VGpFeU9UWjFZWEJJYlUwMlFUSTRia2RhUjJSQlJXNXBSREZoWVRWU1pFWkRiamhLUlhWdWNWWTVheUlzSW5SNWNDSTZJa3BYVkNKOS5leUpwWVhRaU9qRTJPVEV4TnpVMk1qUXNJbWx6Y3lJNkltUnBaRHByWlhrNmVqWk5hMjFPTVRJNU5uVmhjRWh0VFRaQk1qaHVSMXBIWkVGRmJtbEVNV0ZoTlZKa1JrTnVPRXBGZFc1eFZqbHJJaXdpYW5ScElqb2lPRFUyTnpRellURXRZamxsWWkwME56Z3lMV0kzTkRjdE5UbGtOekkzWVRGaVlXWTFJaXdpYm1KbUlqb3hOamt4TVRjMU5qSTBMQ0p1YjI1alpTSTZJamsyTkRkalltTmtMV0k0WkRndE5HVXhNeTFpTURKa0xURXpZelUxTm1RNVlqRTFPQ0lzSW5OMVlpSTZJbVJwWkRwclpYazZlalpOYTIxT01USTVOblZoY0VodFRUWkJNamh1UjFwSFpFRkZibWxFTVdGaE5WSmtSa051T0VwRmRXNXhWamxySWl3aWRtTWlPbnNpUUdOdmJuUmxlSFFpT2xzaWFIUjBjSE02THk5M2QzY3Vkek11YjNKbkx6SXdNVGd2WTNKbFpHVnVkR2xoYkhNdmRqRWlYU3dpZEhsd1pTSTZXeUpXWlhKcFptbGhZbXhsUTNKbFpHVnVkR2xoYkNKZExDSmpjbVZrWlc1MGFXRnNVM1ZpYW1WamRDSTZleUpsYlhCc2IzbGxjaUk2SWxSQ1JDSXNJbXB2WWxScGRHeGxJam9pVkhWMGIzSnBZV3dnUVhWMGFHOXlJbjE5ZlEuWEJPbjBTd2RZZUMwN2dHM1VkT1ZLeHV2YXpfWVRpRkNmZ2tpZXJhZUZnVkEtT2tkWDM1SWl6T0NhdUtqdWlsQXJzZklvMkNYN1pYaDl3djRhUXZFRGciXX19.pJXQXSJcu4U752IE0IH21Yw26OsGMLrHE_-LpGLDHkfetQoJk56j9Fflg-P68xVgfNwZ4EBgGEJ88bXLRv1aDQ
```

Upon decoding, we can view the JWT as:

```json
{
"alg": "EdDSA",
"kid": "did:key:z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k#z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k",
"typ": "JWT"
}
```

```json
{
"iat": 1691175624,
"iss": "did:key:z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k",
"jti": "b44924ea-0202-4e9e-bb2a-989bd05d57ae",
"nbf": 1691175624,
"nonce": "5c6aad76-e2ef-4cb7-b1a8-2726b24a3f4e",
"vp": {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiablePresentation"],
"verifiableCredential": [
"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrI3o2TWttTjEyOTZ1YXBIbU02QTI4bkdaR2RBRW5pRDFhYTVSZEZDbjhKRXVucVY5ayIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTExNzU2MjQsImlzcyI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrIiwianRpIjoiODU2NzQzYTEtYjllYi00NzgyLWI3NDctNTlkNzI3YTFiYWY1IiwibmJmIjoxNjkxMTc1NjI0LCJub25jZSI6Ijk2NDdjYmNkLWI4ZDgtNGUxMy1iMDJkLTEzYzU1NmQ5YjE1OCIsInN1YiI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrIiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJlbXBsb3llciI6IlRCRCIsImpvYlRpdGxlIjoiVHV0b3JpYWwgQXV0aG9yIn19fQ.XBOn0SwdYeC07gG3UdOVKxuvaz_YTiFCfgkieraeFgVA-OkdX35IizOCauKjuilArsfIo2CX7ZXh9wv4aQvEDg"
]
}
}
```

We can also decode the JWT VC value within the `verifiableCredential` property (the one that ends with `v4aQvEDg`) as:

```json
{
"alg": "EdDSA",
"kid": "did:key:z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k#z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k",
"typ": "JWT"
}
```

```json
{
"iat": 1691175624,
"iss": "did:key:z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k",
"jti": "856743a1-b9eb-4782-b747-59d727a1baf5",
"nbf": 1691175624,
"nonce": "9647cbcd-b8d8-4e13-b02d-13c556d9b158",
"sub": "did:key:z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k",
"vc": {
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"type": [
"VerifiableCredential"
],
"credentialSubject": {
"employer": "TBD",
"jobTitle": "Tutorial Author"
}
}
}
```

## Verifying a Presentation

Once we have a Verifiable Presentation we can use the service to verify it by making a `PUT` request to `/v1/presentations/verification`. A sample request is as follows:

```bash
curl -X PUT localhost:3000/v1/presentations/verification -d '{
"presentationJwt": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrI3o2TWttTjEyOTZ1YXBIbU02QTI4bkdaR2RBRW5pRDFhYTVSZEZDbjhKRXVucVY5ayIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTExNzU2MjQsImlzcyI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrIiwianRpIjoiYjQ0OTI0ZWEtMDIwMi00ZTllLWJiMmEtOTg5YmQwNWQ1N2FlIiwibmJmIjoxNjkxMTc1NjI0LCJub25jZSI6IjVjNmFhZDc2LWUyZWYtNGNiNy1iMWE4LTI3MjZiMjRhM2Y0ZSIsInZwIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6WyJleUpoYkdjaU9pSkZaRVJUUVNJc0ltdHBaQ0k2SW1ScFpEcHJaWGs2ZWpaTmEyMU9NVEk1Tm5WaGNFaHRUVFpCTWpodVIxcEhaRUZGYm1sRU1XRmhOVkprUmtOdU9FcEZkVzV4VmpsckkzbzJUV3R0VGpFeU9UWjFZWEJJYlUwMlFUSTRia2RhUjJSQlJXNXBSREZoWVRWU1pFWkRiamhLUlhWdWNWWTVheUlzSW5SNWNDSTZJa3BYVkNKOS5leUpwWVhRaU9qRTJPVEV4TnpVMk1qUXNJbWx6Y3lJNkltUnBaRHByWlhrNmVqWk5hMjFPTVRJNU5uVmhjRWh0VFRaQk1qaHVSMXBIWkVGRmJtbEVNV0ZoTlZKa1JrTnVPRXBGZFc1eFZqbHJJaXdpYW5ScElqb2lPRFUyTnpRellURXRZamxsWWkwME56Z3lMV0kzTkRjdE5UbGtOekkzWVRGaVlXWTFJaXdpYm1KbUlqb3hOamt4TVRjMU5qSTBMQ0p1YjI1alpTSTZJamsyTkRkalltTmtMV0k0WkRndE5HVXhNeTFpTURKa0xURXpZelUxTm1RNVlqRTFPQ0lzSW5OMVlpSTZJbVJwWkRwclpYazZlalpOYTIxT01USTVOblZoY0VodFRUWkJNamh1UjFwSFpFRkZibWxFTVdGaE5WSmtSa051T0VwRmRXNXhWamxySWl3aWRtTWlPbnNpUUdOdmJuUmxlSFFpT2xzaWFIUjBjSE02THk5M2QzY3Vkek11YjNKbkx6SXdNVGd2WTNKbFpHVnVkR2xoYkhNdmRqRWlYU3dpZEhsd1pTSTZXeUpXWlhKcFptbGhZbXhsUTNKbFpHVnVkR2xoYkNKZExDSmpjbVZrWlc1MGFXRnNVM1ZpYW1WamRDSTZleUpsYlhCc2IzbGxjaUk2SWxSQ1JDSXNJbXB2WWxScGRHeGxJam9pVkhWMGIzSnBZV3dnUVhWMGFHOXlJbjE5ZlEuWEJPbjBTd2RZZUMwN2dHM1VkT1ZLeHV2YXpfWVRpRkNmZ2tpZXJhZUZnVkEtT2tkWDM1SWl6T0NhdUtqdWlsQXJzZklvMkNYN1pYaDl3djRhUXZFRGciXX19.pJXQXSJcu4U752IE0IH21Yw26OsGMLrHE_-LpGLDHkfetQoJk56j9Fflg-P68xVgfNwZ4EBgGEJ88bXLRv1aDQ"
}'
```

Upon success we see a response such as:

```json
{
"verified": true
}
```
78 changes: 78 additions & 0 deletions doc/howto/presentation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package howto

import (
"testing"
"time"

"github.com/TBD54566975/ssi-sdk/credential"
"github.com/TBD54566975/ssi-sdk/credential/integrity"
"github.com/TBD54566975/ssi-sdk/crypto"
"github.com/TBD54566975/ssi-sdk/crypto/jwx"
"github.com/TBD54566975/ssi-sdk/did/key"
"github.com/stretchr/testify/require"
)

func TestVerifiablePresentation(t *testing.T) {
// create a new DID to use to self-issue a credential and present it to a verifier
privateKey, didKey, err := key.GenerateDIDKey(crypto.Ed25519)
require.NoError(t, err)

// expand the DID Document to access the key ID
didKeyDocument, err := didKey.Expand()
require.NoError(t, err)
didKeyID := didKeyDocument.VerificationMethod[0].ID

// first create a credential we wish to present
credBuilder := credential.NewVerifiableCredentialBuilder()

// set our DID as the issuer of the credential, since it will be self-signed
err = credBuilder.SetIssuer(didKey.String())
require.NoError(t, err)

// set the issuance date as the current time
err = credBuilder.SetIssuanceDate(time.Now().Format(time.RFC3339))
require.NoError(t, err)

// add the data to the body of the credential
err = credBuilder.SetCredentialSubject(
map[string]any{
// set the ID property of the credential subject to our DID, since the credential is about us
credential.VerifiableCredentialIDProperty: didKey.String(),
"jobTitle": "Tutorial Author",
"employer": "TBD",
},
)
require.NoError(t, err)

// build the credential
unsignedCredential, err := credBuilder.Build()
require.NoError(t, err)

// sign the credential using our DID's private key
signer, err := jwx.NewJWXSigner(didKey.String(), didKeyID, privateKey)
require.NoError(t, err)

// sign as a JWT
signedCredentialBytes, err := integrity.SignVerifiableCredentialJWT(*signer, *unsignedCredential)
require.NoError(t, err)
signedCredentialJWTString := string(signedCredentialBytes)

// construct a Verifiable Presentation for the credential
presBuilder := credential.NewVerifiablePresentationBuilder()
err = presBuilder.SetHolder(didKey.String())
require.NoError(t, err)
err = presBuilder.AddVerifiableCredentials(signedCredentialJWTString)
require.NoError(t, err)

// build the presentation
unsignedPresentation, err := presBuilder.Build()
require.NoError(t, err)

// sign the presentation using our DID's private key, passing no additional parameters
signedPresentationBytes, err := integrity.SignVerifiablePresentationJWT(*signer, nil, *unsignedPresentation)
require.NoError(t, err)
signedPresentationJWTString := string(signedPresentationBytes)

// print the signed presentation JWT
println(signedPresentationJWTString)
}
2 changes: 1 addition & 1 deletion doc/howto/verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Upon success we see a response such as:

### Verifiable Presentations

The example we've gone through above verifies a credential from an _issuer_. But what about verifying the _presentation_ of a credential, or set of credentials, from a _holder_ to a _verifier_? To do this, a holder must construct what's called a [Verifiable Presentation](https://www.w3.org/TR/vc-data-model/#presentations-0), an object which is also defined by the VC Data Model, which allows a _holder_ of a verifiable credential to create an authenticated wrapper around a set of credentials it wishes to present to a _verifier_. The service's verification API does [not yet](https://github.com/TBD54566975/ssi-service/issues/615) support this functionality.
The example we've gone through above verifies a credential from an _issuer_. But what about verifying the _presentation_ of a credential, or set of credentials, from a _holder_ to a _verifier_? To do this, a holder must construct what's called a [Verifiable Presentation](https://www.w3.org/TR/vc-data-model/#presentations-0), an object which is also defined by the VC Data Model, which allows a _holder_ of a verifiable credential to create an authenticated wrapper around a set of credentials it wishes to present to a _verifier_. Learn more in [our guide on presentations here](presentation.md).

### Presentation Exchange

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.20

require (
github.com/BurntSushi/toml v1.3.2
github.com/TBD54566975/ssi-sdk v0.0.4-alpha.0.20230731175253-d5c302a1d9b9
github.com/TBD54566975/ssi-sdk v0.0.4-alpha.0.20230804184541-a62922c45264
github.com/alicebob/miniredis/v2 v2.30.4
github.com/ardanlabs/conf v1.5.0
github.com/benbjohnson/clock v1.3.5
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/TBD54566975/ssi-sdk v0.0.4-alpha.0.20230731175253-d5c302a1d9b9 h1:Ig2o+eOTFTaa9agWiz+Vz/7N4zoTJ2Na9PiRaYaAbXY=
github.com/TBD54566975/ssi-sdk v0.0.4-alpha.0.20230731175253-d5c302a1d9b9/go.mod h1:mVKRjfdpgmCxPwnfQluXGkgzsFyrPsjrCvHXCJ41avQ=
github.com/TBD54566975/ssi-sdk v0.0.4-alpha.0.20230804184541-a62922c45264 h1:5Sjk7Q8M9TNYWc3DV4SDORRxdNbFINz8JHkHCGeAnn4=
github.com/TBD54566975/ssi-sdk v0.0.4-alpha.0.20230804184541-a62922c45264/go.mod h1:mVKRjfdpgmCxPwnfQluXGkgzsFyrPsjrCvHXCJ41avQ=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
Expand Down
2 changes: 1 addition & 1 deletion internal/keyaccess/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (ka JWKKeyAccess) SignVerifiablePresentation(audience string, presentation
if err := presentation.IsValid(); err != nil {
return nil, errors.New("cannot sign invalid presentation")
}
tokenBytes, err := integrity.SignVerifiablePresentationJWT(*ka.Signer, integrity.JWTVVPParameters{Audience: []string{audience}}, presentation)
tokenBytes, err := integrity.SignVerifiablePresentationJWT(*ka.Signer, &integrity.JWTVVPParameters{Audience: []string{audience}}, presentation)
if err != nil {
return nil, errors.Wrap(err, "could not sign presentation")
}
Expand Down
2 changes: 1 addition & 1 deletion internal/keyaccess/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ func getJWTTestPresentation(ka JWKKeyAccess) credential.VerifiablePresentation {
knownContext := []string{"https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"}
knownID := uuid.NewString()
knownType := []string{"VerifiablePresentation", "HappyPresentation"}
knownHolder := "did:example:ebfeb1f712ebc6f1c276e12ec21"
knownHolder := ka.Signer.ID
testCredential := getTestCredential(ka.Signer.ID)
signedJWT, _ := ka.SignVerifiableCredential(testCredential)
return credential.VerifiablePresentation{
Expand Down
35 changes: 6 additions & 29 deletions internal/verification/verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/TBD54566975/ssi-sdk/crypto"
"github.com/TBD54566975/ssi-sdk/crypto/jwx"
"github.com/TBD54566975/ssi-sdk/cryptosuite/jws2020"
"github.com/TBD54566975/ssi-sdk/did"
"github.com/TBD54566975/ssi-sdk/did/resolution"
sdkutil "github.com/TBD54566975/ssi-sdk/util"
"github.com/goccy/go-json"
Expand Down Expand Up @@ -127,40 +126,18 @@ func (v Verifier) VerifyDataIntegrityCredential(ctx context.Context, credential
// VerifyJWTPresentation first parses and checks the signature on the given JWT presentation. Next, it runs
// a set of static verification checks on the presentation's credentials as per the service's configuration.
func (v Verifier) VerifyJWTPresentation(ctx context.Context, token keyaccess.JWT) error {
headers, jwt, vp, err := integrity.ParseVerifiablePresentationFromJWT(token.String())
_, err := integrity.VerifyJWTPresentation(ctx, token.String(), v.didResolver)
if err != nil {
return errors.Wrap(err, "parsing JWT presentation")
return errors.Wrap(err, "verifying JWT presentation")
}

// get key to verify the presentation with
issuerKID := headers.KeyID()
if issuerKID == "" {
return errors.Errorf("missing kid in header of presentation<%s>", jwt.JwtID())
}
issuerDID, err := v.didResolver.Resolve(ctx, jwt.Issuer())
if err != nil {
return errors.Wrapf(err, "getting issuer DID<%s> to verify presentation<%s>", jwt.Issuer(), jwt.JwtID())
}
issuerPublicKey, err := did.GetKeyFromVerificationMethod(issuerDID.Document, issuerKID)
_, _, pres, err := integrity.ParseVerifiablePresentationFromJWT(token.String())
if err != nil {
return errors.Wrapf(err, "getting key to verify presentation<%s>", jwt.JwtID())
}

// construct a verifier and verify the signature on the presentation
// note: this also verifies the signature of each credential in the presentation
verifier, err := jwx.NewJWXVerifier(issuerDID.ID, issuerKID, issuerPublicKey)
if err != nil {
return errors.Wrapf(err, "constructing verifier for presentation<%s>", jwt.JwtID())
}
_, _, _, err = integrity.VerifyVerifiablePresentationJWT(ctx, *verifier, v.didResolver, token.String())
if err != nil {
return errors.Wrapf(err, "verifying presentation<%s>", jwt.JwtID())
return errors.Wrap(err, "parsing vc from jwt")
}

// for each credential in the presentation, run a set of static verification checks
creds, err := credential.NewCredentialContainerFromArray(vp.VerifiableCredential)
creds, err := credential.NewCredentialContainerFromArray(pres.VerifiableCredential)
if err != nil {
return errors.Wrapf(err, "error parsing credentials in presentation<%s>", vp.ID)
return errors.Wrapf(err, "error parsing credentials in presentation<%s>", pres.ID)
}
for _, cred := range creds {
if err = v.staticValidationChecks(ctx, *cred.Credential); err != nil {
Expand Down
3 changes: 3 additions & 0 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ func PresentationAPI(rg *gin.RouterGroup, service svcframework.Service, webhookS
// make sure the presentation service is configured to use the correct path
config.SetServicePath(svcframework.Presentation, PresentationsPrefix)

presAPI := rg.Group(PresentationsPrefix)
presAPI.PUT(VerificationPath, presRouter.VerifyPresentation)

presDefAPI := rg.Group(PresentationsPrefix + DefinitionsPrefix)
presDefAPI.PUT("", presRouter.CreateDefinition)
presDefAPI.GET("/:id", presRouter.GetDefinition)
Expand Down
Loading

0 comments on commit 9c64560

Please sign in to comment.