diff --git a/README.md b/README.md
index 4c9c80617f0a6e96dcf0b334bee7888db099527d..ed8047d9d9088c9db17c68b8c7c7bdd690f8684f 100644
--- a/README.md
+++ b/README.md
@@ -11,3 +11,6 @@ TODO - how key is selected if the request doesn't mention explicit key
 
 TODO - how signature suite is constructed based on key type
 
+### Counterfeiter 
+
+TODO - how to use it for generating fake implementations of interfaces
\ No newline at end of file
diff --git a/cmd/signer/main.go b/cmd/signer/main.go
index 9f2727ee17be5a3bd0e6826870e8d99f90e9d913..a11b6d61916e33776261c703e8bc5b41e1002e6a 100644
--- a/cmd/signer/main.go
+++ b/cmd/signer/main.go
@@ -105,6 +105,8 @@ func main() {
 		openapiServer = goaopenapisrv.New(openapiEndpoints, mux, dec, enc, nil, errFormatter, nil, nil)
 	}
 
+	// set custom request decoder, so that request body bytes are simply
+	// read and not decoded in some other way
 	signerServer.CredentialProof = goasignersrv.NewCredentialProofHandler(
 		signerEndpoints.CredentialProof,
 		mux,
@@ -114,6 +116,17 @@ func main() {
 		errFormatter,
 	)
 
+	// set custom request decoder, so that request body bytes are simply
+	// read and not decoded in some other way
+	signerServer.PresentationProof = goasignersrv.NewPresentationProofHandler(
+		signerEndpoints.PresentationProof,
+		mux,
+		decoder.RequestDecoder,
+		enc,
+		nil,
+		errFormatter,
+	)
+
 	// Configure the mux.
 	goasignersrv.Mount(mux, signerServer)
 	goahealthsrv.Mount(mux, healthServer)
diff --git a/design/design.go b/design/design.go
index b8901caa239339617dfce98522e5947a9e74a592..2698af26a264baa3e77647bbe7c2aa39bc85b1bb 100644
--- a/design/design.go
+++ b/design/design.go
@@ -30,6 +30,19 @@ var _ = Service("signer", func() {
 			Response(StatusOK)
 		})
 	})
+
+	Method("PresentationProof", func() {
+		Description("PresentationProof adds a proof to a given Verifiable Presentation.")
+		Payload(PresentationProofRequest)
+		Result(Any)
+		HTTP(func() {
+			POST("/v1/presentation/proof")
+			Param("key")
+			Body("presentation")
+
+			Response(StatusOK)
+		})
+	})
 })
 
 var _ = Service("health", func() {
diff --git a/design/types.go b/design/types.go
index af9095942aeb78527c59fb31e9da0d0604116a3b..bfa43f588f926a4a1caaa26db7b19ff957cd6033 100644
--- a/design/types.go
+++ b/design/types.go
@@ -10,3 +10,11 @@ var CredentialProofRequest = Type("CredentialProofRequest", func() {
 	Field(2, "credential", Bytes, "Verifiable Credential in JSON format.")
 	Required("credential")
 })
+
+var PresentationProofRequest = Type("PresentationProofRequest", func() {
+	Field(1, "key", String, "Key to use for the proof signature (optional).", func() {
+		Example("key1")
+	})
+	Field(2, "presentation", Bytes, "Verifiable Presentation in JSON format.")
+	Required("presentation")
+})
diff --git a/gen/http/cli/signer/cli.go b/gen/http/cli/signer/cli.go
index 3a001251c39473c6f2b6302d265e3c7b4ab7324c..e79126c525d60bc8f83ace826365b1998cc62fb6 100644
--- a/gen/http/cli/signer/cli.go
+++ b/gen/http/cli/signer/cli.go
@@ -25,14 +25,14 @@ import (
 //
 func UsageCommands() string {
 	return `health (liveness|readiness)
-signer credential-proof
+signer (credential-proof|presentation-proof)
 `
 }
 
 // UsageExamples produces an example of a valid invocation of the CLI tool.
 func UsageExamples() string {
 	return os.Args[0] + ` health liveness` + "\n" +
-		os.Args[0] + ` signer credential-proof --body "QXV0IGZ1Z2lhdCBoYXJ1bS4=" --key "key1"` + "\n" +
+		os.Args[0] + ` signer credential-proof --body "SXRhcXVlIG9mZmljaWEgY29tbW9kaSBhdXRlbSBhdCBiZWF0YWUu" --key "key1"` + "\n" +
 		""
 }
 
@@ -57,6 +57,10 @@ func ParseEndpoint(
 		signerCredentialProofFlags    = flag.NewFlagSet("credential-proof", flag.ExitOnError)
 		signerCredentialProofBodyFlag = signerCredentialProofFlags.String("body", "REQUIRED", "")
 		signerCredentialProofKeyFlag  = signerCredentialProofFlags.String("key", "", "")
+
+		signerPresentationProofFlags    = flag.NewFlagSet("presentation-proof", flag.ExitOnError)
+		signerPresentationProofBodyFlag = signerPresentationProofFlags.String("body", "REQUIRED", "")
+		signerPresentationProofKeyFlag  = signerPresentationProofFlags.String("key", "", "")
 	)
 	healthFlags.Usage = healthUsage
 	healthLivenessFlags.Usage = healthLivenessUsage
@@ -64,6 +68,7 @@ func ParseEndpoint(
 
 	signerFlags.Usage = signerUsage
 	signerCredentialProofFlags.Usage = signerCredentialProofUsage
+	signerPresentationProofFlags.Usage = signerPresentationProofUsage
 
 	if err := flag.CommandLine.Parse(os.Args[1:]); err != nil {
 		return nil, nil, err
@@ -114,6 +119,9 @@ func ParseEndpoint(
 			case "credential-proof":
 				epf = signerCredentialProofFlags
 
+			case "presentation-proof":
+				epf = signerPresentationProofFlags
+
 			}
 
 		}
@@ -152,6 +160,9 @@ func ParseEndpoint(
 			case "credential-proof":
 				endpoint = c.CredentialProof()
 				data, err = signerc.BuildCredentialProofPayload(*signerCredentialProofBodyFlag, *signerCredentialProofKeyFlag)
+			case "presentation-proof":
+				endpoint = c.PresentationProof()
+				data, err = signerc.BuildPresentationProofPayload(*signerPresentationProofBodyFlag, *signerPresentationProofKeyFlag)
 			}
 		}
 	}
@@ -204,6 +215,7 @@ Usage:
 
 COMMAND:
     credential-proof: CredentialProof adds a proof to a given Verifiable Credential.
+    presentation-proof: PresentationProof adds a proof to a given Verifiable Presentation.
 
 Additional help:
     %[1]s signer COMMAND --help
@@ -217,6 +229,18 @@ CredentialProof adds a proof to a given Verifiable Credential.
     -key STRING: 
 
 Example:
-    %[1]s signer credential-proof --body "QXV0IGZ1Z2lhdCBoYXJ1bS4=" --key "key1"
+    %[1]s signer credential-proof --body "SXRhcXVlIG9mZmljaWEgY29tbW9kaSBhdXRlbSBhdCBiZWF0YWUu" --key "key1"
+`, os.Args[0])
+}
+
+func signerPresentationProofUsage() {
+	fmt.Fprintf(os.Stderr, `%[1]s [flags] signer presentation-proof -body STRING -key STRING
+
+PresentationProof adds a proof to a given Verifiable Presentation.
+    -body STRING: 
+    -key STRING: 
+
+Example:
+    %[1]s signer presentation-proof --body "QXBlcmlhbSBxdWlhIGNvbnNlcXVhdHVyIGRvbG9yZW0u" --key "key1"
 `, os.Args[0])
 }
diff --git a/gen/http/openapi.json b/gen/http/openapi.json
index 57e248fc2ea0f20cdc1ee0c75b257f137c392287..fa70d6c891e58cc66f5c4667cb9180bdbaad02d5 100644
--- a/gen/http/openapi.json
+++ b/gen/http/openapi.json
@@ -1 +1 @@
-{"swagger":"2.0","info":{"title":"Signer Service","description":"The signer service exposes HTTP API for creating and verifying digital signatures.","version":""},"host":"localhost:8085","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/liveness":{"get":{"tags":["health"],"summary":"Liveness health","operationId":"health#Liveness","responses":{"200":{"description":"OK response."}},"schemes":["http"]}},"/readiness":{"get":{"tags":["health"],"summary":"Readiness health","operationId":"health#Readiness","responses":{"200":{"description":"OK response."}},"schemes":["http"]}},"/v1/credential/proof":{"post":{"tags":["signer"],"summary":"CredentialProof signer","description":"CredentialProof adds a proof to a given Verifiable Credential.","operationId":"signer#CredentialProof","parameters":[{"name":"key","in":"query","description":"Key to use for the proof signature (optional).","required":false,"type":"string"},{"name":"bytes","in":"body","description":"Verifiable Credential in JSON format.","required":true,"schema":{"type":"string","format":"byte"}}],"responses":{"200":{"description":"OK response.","schema":{"type":"string","format":"binary"}}},"schemes":["http"]}}}}
\ No newline at end of file
+{"swagger":"2.0","info":{"title":"Signer Service","description":"The signer service exposes HTTP API for creating and verifying digital signatures.","version":""},"host":"localhost:8085","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/liveness":{"get":{"tags":["health"],"summary":"Liveness health","operationId":"health#Liveness","responses":{"200":{"description":"OK response."}},"schemes":["http"]}},"/readiness":{"get":{"tags":["health"],"summary":"Readiness health","operationId":"health#Readiness","responses":{"200":{"description":"OK response."}},"schemes":["http"]}},"/v1/credential/proof":{"post":{"tags":["signer"],"summary":"CredentialProof signer","description":"CredentialProof adds a proof to a given Verifiable Credential.","operationId":"signer#CredentialProof","parameters":[{"name":"key","in":"query","description":"Key to use for the proof signature (optional).","required":false,"type":"string"},{"name":"bytes","in":"body","description":"Verifiable Credential in JSON format.","required":true,"schema":{"type":"string","format":"byte"}}],"responses":{"200":{"description":"OK response.","schema":{"type":"string","format":"binary"}}},"schemes":["http"]}},"/v1/presentation/proof":{"post":{"tags":["signer"],"summary":"PresentationProof signer","description":"PresentationProof adds a proof to a given Verifiable Presentation.","operationId":"signer#PresentationProof","parameters":[{"name":"key","in":"query","description":"Key to use for the proof signature (optional).","required":false,"type":"string"},{"name":"bytes","in":"body","description":"Verifiable Presentation in JSON format.","required":true,"schema":{"type":"string","format":"byte"}}],"responses":{"200":{"description":"OK response.","schema":{"type":"string","format":"binary"}}},"schemes":["http"]}}}}
\ No newline at end of file
diff --git a/gen/http/openapi.yaml b/gen/http/openapi.yaml
index 07ccef65ab32cc3a75e540e390d0837f2c1873b2..9de69bf02efee965db05769aafbdf9f488b426ce 100644
--- a/gen/http/openapi.yaml
+++ b/gen/http/openapi.yaml
@@ -63,3 +63,31 @@ paths:
                         format: binary
             schemes:
                 - http
+    /v1/presentation/proof:
+        post:
+            tags:
+                - signer
+            summary: PresentationProof signer
+            description: PresentationProof adds a proof to a given Verifiable Presentation.
+            operationId: signer#PresentationProof
+            parameters:
+                - name: key
+                  in: query
+                  description: Key to use for the proof signature (optional).
+                  required: false
+                  type: string
+                - name: bytes
+                  in: body
+                  description: Verifiable Presentation in JSON format.
+                  required: true
+                  schema:
+                    type: string
+                    format: byte
+            responses:
+                "200":
+                    description: OK response.
+                    schema:
+                        type: string
+                        format: binary
+            schemes:
+                - http
diff --git a/gen/http/openapi3.json b/gen/http/openapi3.json
index 7aef9891518bbd1b3525c9dbb0126ee7ab9abca3..44444084b6b678b58c927432dea8a4b8fdee84d0 100644
--- a/gen/http/openapi3.json
+++ b/gen/http/openapi3.json
@@ -1 +1 @@
-{"openapi":"3.0.3","info":{"title":"Signer Service","description":"The signer service exposes HTTP API for creating and verifying digital signatures.","version":"1.0"},"servers":[{"url":"http://localhost:8085","description":"Signer Server"}],"paths":{"/liveness":{"get":{"tags":["health"],"summary":"Liveness health","operationId":"health#Liveness","responses":{"200":{"description":"OK response."}}}},"/readiness":{"get":{"tags":["health"],"summary":"Readiness health","operationId":"health#Readiness","responses":{"200":{"description":"OK response."}}}},"/v1/credential/proof":{"post":{"tags":["signer"],"summary":"CredentialProof signer","description":"CredentialProof adds a proof to a given Verifiable Credential.","operationId":"signer#CredentialProof","parameters":[{"name":"key","in":"query","description":"Key to use for the proof signature (optional).","allowEmptyValue":true,"schema":{"type":"string","description":"Key to use for the proof signature (optional).","example":"key1"},"example":"key1"}],"requestBody":{"description":"Verifiable Credential in JSON format.","required":true,"content":{"application/json":{"schema":{"type":"string","description":"Verifiable Credential in JSON format.","example":"RXVtIGl1cmUgbmVtby4=","format":"binary"},"example":"VmVuaWFtIHF1aWEu"}}},"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"type":"string","example":"Ut voluptas sed.","format":"binary"},"example":"Ad magni."}}}}}}},"components":{},"tags":[{"name":"health","description":"Health service provides health check endpoints."},{"name":"signer","description":"Sign service provides endpoints for making digital signatures and proofs for verifiable credentials and presentations."}]}
\ No newline at end of file
+{"openapi":"3.0.3","info":{"title":"Signer Service","description":"The signer service exposes HTTP API for creating and verifying digital signatures.","version":"1.0"},"servers":[{"url":"http://localhost:8085","description":"Signer Server"}],"paths":{"/liveness":{"get":{"tags":["health"],"summary":"Liveness health","operationId":"health#Liveness","responses":{"200":{"description":"OK response."}}}},"/readiness":{"get":{"tags":["health"],"summary":"Readiness health","operationId":"health#Readiness","responses":{"200":{"description":"OK response."}}}},"/v1/credential/proof":{"post":{"tags":["signer"],"summary":"CredentialProof signer","description":"CredentialProof adds a proof to a given Verifiable Credential.","operationId":"signer#CredentialProof","parameters":[{"name":"key","in":"query","description":"Key to use for the proof signature (optional).","allowEmptyValue":true,"schema":{"type":"string","description":"Key to use for the proof signature (optional).","example":"key1"},"example":"key1"}],"requestBody":{"description":"Verifiable Credential in JSON format.","required":true,"content":{"application/json":{"schema":{"type":"string","description":"Verifiable Credential in JSON format.","example":"Q3VscGEgb2NjYWVjYXRpIGRvbG9ydW0gcXVpIGVuaW0gaW5jaWR1bnQu","format":"binary"},"example":"SXRhcXVlIGFkaXBpc2NpIHZvbHVwdGFzLg=="}}},"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"type":"string","example":"Ipsam facere.","format":"binary"},"example":"Asperiores molestias qui."}}}}}},"/v1/presentation/proof":{"post":{"tags":["signer"],"summary":"PresentationProof signer","description":"PresentationProof adds a proof to a given Verifiable Presentation.","operationId":"signer#PresentationProof","parameters":[{"name":"key","in":"query","description":"Key to use for the proof signature (optional).","allowEmptyValue":true,"schema":{"type":"string","description":"Key to use for the proof signature (optional).","example":"key1"},"example":"key1"}],"requestBody":{"description":"Verifiable Presentation in JSON format.","required":true,"content":{"application/json":{"schema":{"type":"string","description":"Verifiable Presentation in JSON format.","example":"RXN0IHF1aWRlbSBoYXJ1bSB2ZXJvLg==","format":"binary"},"example":"SXBzYSB2ZWwgaW4gcmVwdWRpYW5kYWUgcmVwZWxsYXQu"}}},"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"type":"string","example":"Deleniti ipsa.","format":"binary"},"example":"Laudantium exercitationem quis sunt eos."}}}}}}},"components":{},"tags":[{"name":"health","description":"Health service provides health check endpoints."},{"name":"signer","description":"Sign service provides endpoints for making digital signatures and proofs for verifiable credentials and presentations."}]}
\ No newline at end of file
diff --git a/gen/http/openapi3.yaml b/gen/http/openapi3.yaml
index 49ea7ab38506b3ccb2e51145e8e1f5b7cbc9017c..502993a6ed52590afccf536b71251a91d0810839 100644
--- a/gen/http/openapi3.yaml
+++ b/gen/http/openapi3.yaml
@@ -51,33 +51,167 @@ paths:
                             type: string
                             description: Verifiable Credential in JSON format.
                             example:
-                                - 69
+                                - 67
                                 - 117
-                                - 109
+                                - 108
+                                - 112
+                                - 97
                                 - 32
+                                - 111
+                                - 99
+                                - 99
+                                - 97
+                                - 101
+                                - 99
+                                - 97
+                                - 116
                                 - 105
-                                - 117
+                                - 32
+                                - 100
+                                - 111
+                                - 108
+                                - 111
                                 - 114
+                                - 117
+                                - 109
+                                - 32
+                                - 113
+                                - 117
+                                - 105
+                                - 32
                                 - 101
+                                - 110
+                                - 105
+                                - 109
                                 - 32
+                                - 105
                                 - 110
+                                - 99
+                                - 105
+                                - 100
+                                - 117
+                                - 110
+                                - 116
+                                - 46
+                            format: binary
+                        example:
+                            - 73
+                            - 116
+                            - 97
+                            - 113
+                            - 117
+                            - 101
+                            - 32
+                            - 97
+                            - 100
+                            - 105
+                            - 112
+                            - 105
+                            - 115
+                            - 99
+                            - 105
+                            - 32
+                            - 118
+                            - 111
+                            - 108
+                            - 117
+                            - 112
+                            - 116
+                            - 97
+                            - 115
+                            - 46
+            responses:
+                "200":
+                    description: OK response.
+                    content:
+                        application/json:
+                            schema:
+                                type: string
+                                example: Ipsam facere.
+                                format: binary
+                            example: Asperiores molestias qui.
+    /v1/presentation/proof:
+        post:
+            tags:
+                - signer
+            summary: PresentationProof signer
+            description: PresentationProof adds a proof to a given Verifiable Presentation.
+            operationId: signer#PresentationProof
+            parameters:
+                - name: key
+                  in: query
+                  description: Key to use for the proof signature (optional).
+                  allowEmptyValue: true
+                  schema:
+                    type: string
+                    description: Key to use for the proof signature (optional).
+                    example: key1
+                  example: key1
+            requestBody:
+                description: Verifiable Presentation in JSON format.
+                required: true
+                content:
+                    application/json:
+                        schema:
+                            type: string
+                            description: Verifiable Presentation in JSON format.
+                            example:
+                                - 69
+                                - 115
+                                - 116
+                                - 32
+                                - 113
+                                - 117
+                                - 105
+                                - 100
                                 - 101
                                 - 109
+                                - 32
+                                - 104
+                                - 97
+                                - 114
+                                - 117
+                                - 109
+                                - 32
+                                - 118
+                                - 101
+                                - 114
                                 - 111
                                 - 46
                             format: binary
                         example:
-                            - 86
+                            - 73
+                            - 112
+                            - 115
+                            - 97
+                            - 32
+                            - 118
                             - 101
-                            - 110
+                            - 108
+                            - 32
                             - 105
-                            - 97
-                            - 109
+                            - 110
                             - 32
-                            - 113
+                            - 114
+                            - 101
+                            - 112
                             - 117
+                            - 100
                             - 105
                             - 97
+                            - 110
+                            - 100
+                            - 97
+                            - 101
+                            - 32
+                            - 114
+                            - 101
+                            - 112
+                            - 101
+                            - 108
+                            - 108
+                            - 97
+                            - 116
                             - 46
             responses:
                 "200":
@@ -86,9 +220,9 @@ paths:
                         application/json:
                             schema:
                                 type: string
-                                example: Ut voluptas sed.
+                                example: Deleniti ipsa.
                                 format: binary
-                            example: Ad magni.
+                            example: Laudantium exercitationem quis sunt eos.
 components: {}
 tags:
     - name: health
diff --git a/gen/http/signer/client/cli.go b/gen/http/signer/client/cli.go
index 97328cbcd385969b78fee31b9abb9ef68d8a67c3..007efc105b2105801f3f2f75c4cb3d48e32679f3 100644
--- a/gen/http/signer/client/cli.go
+++ b/gen/http/signer/client/cli.go
@@ -32,3 +32,25 @@ func BuildCredentialProofPayload(signerCredentialProofBody string, signerCredent
 
 	return res, nil
 }
+
+// BuildPresentationProofPayload builds the payload for the signer
+// PresentationProof endpoint from CLI flags.
+func BuildPresentationProofPayload(signerPresentationProofBody string, signerPresentationProofKey string) (*signer.PresentationProofRequest, error) {
+	var body []byte
+	{
+		body = []byte(signerPresentationProofBody)
+	}
+	var key *string
+	{
+		if signerPresentationProofKey != "" {
+			key = &signerPresentationProofKey
+		}
+	}
+	v := body
+	res := &signer.PresentationProofRequest{
+		Presentation: v,
+	}
+	res.Key = key
+
+	return res, nil
+}
diff --git a/gen/http/signer/client/client.go b/gen/http/signer/client/client.go
index 60ea86188d3a49a09d7d6f37d18c538c9e8ffae8..8088ebd580ccc0e5d098a0ce900c6ef0e72da7c2 100644
--- a/gen/http/signer/client/client.go
+++ b/gen/http/signer/client/client.go
@@ -21,6 +21,10 @@ type Client struct {
 	// CredentialProof endpoint.
 	CredentialProofDoer goahttp.Doer
 
+	// PresentationProof Doer is the HTTP client used to make requests to the
+	// PresentationProof endpoint.
+	PresentationProofDoer goahttp.Doer
+
 	// RestoreResponseBody controls whether the response bodies are reset after
 	// decoding so they can be read again.
 	RestoreResponseBody bool
@@ -41,12 +45,13 @@ func NewClient(
 	restoreBody bool,
 ) *Client {
 	return &Client{
-		CredentialProofDoer: doer,
-		RestoreResponseBody: restoreBody,
-		scheme:              scheme,
-		host:                host,
-		decoder:             dec,
-		encoder:             enc,
+		CredentialProofDoer:   doer,
+		PresentationProofDoer: doer,
+		RestoreResponseBody:   restoreBody,
+		scheme:                scheme,
+		host:                  host,
+		decoder:               dec,
+		encoder:               enc,
 	}
 }
 
@@ -73,3 +78,27 @@ func (c *Client) CredentialProof() goa.Endpoint {
 		return decodeResponse(resp)
 	}
 }
+
+// PresentationProof returns an endpoint that makes HTTP requests to the signer
+// service PresentationProof server.
+func (c *Client) PresentationProof() goa.Endpoint {
+	var (
+		encodeRequest  = EncodePresentationProofRequest(c.encoder)
+		decodeResponse = DecodePresentationProofResponse(c.decoder, c.RestoreResponseBody)
+	)
+	return func(ctx context.Context, v interface{}) (interface{}, error) {
+		req, err := c.BuildPresentationProofRequest(ctx, v)
+		if err != nil {
+			return nil, err
+		}
+		err = encodeRequest(req, v)
+		if err != nil {
+			return nil, err
+		}
+		resp, err := c.PresentationProofDoer.Do(req)
+		if err != nil {
+			return nil, goahttp.ErrRequestError("signer", "PresentationProof", err)
+		}
+		return decodeResponse(resp)
+	}
+}
diff --git a/gen/http/signer/client/encode_decode.go b/gen/http/signer/client/encode_decode.go
index 1c7b6dc7315717d711f68de3e7330309dff2f745..004dc905c53bbbdec5abc8fa6e3a5cd81ab04e02 100644
--- a/gen/http/signer/client/encode_decode.go
+++ b/gen/http/signer/client/encode_decode.go
@@ -88,3 +88,74 @@ func DecodeCredentialProofResponse(decoder func(*http.Response) goahttp.Decoder,
 		}
 	}
 }
+
+// BuildPresentationProofRequest instantiates a HTTP request object with method
+// and path set to call the "signer" service "PresentationProof" endpoint
+func (c *Client) BuildPresentationProofRequest(ctx context.Context, v interface{}) (*http.Request, error) {
+	u := &url.URL{Scheme: c.scheme, Host: c.host, Path: PresentationProofSignerPath()}
+	req, err := http.NewRequest("POST", u.String(), nil)
+	if err != nil {
+		return nil, goahttp.ErrInvalidURL("signer", "PresentationProof", u.String(), err)
+	}
+	if ctx != nil {
+		req = req.WithContext(ctx)
+	}
+
+	return req, nil
+}
+
+// EncodePresentationProofRequest returns an encoder for requests sent to the
+// signer PresentationProof server.
+func EncodePresentationProofRequest(encoder func(*http.Request) goahttp.Encoder) func(*http.Request, interface{}) error {
+	return func(req *http.Request, v interface{}) error {
+		p, ok := v.(*signer.PresentationProofRequest)
+		if !ok {
+			return goahttp.ErrInvalidType("signer", "PresentationProof", "*signer.PresentationProofRequest", v)
+		}
+		values := req.URL.Query()
+		if p.Key != nil {
+			values.Add("key", *p.Key)
+		}
+		req.URL.RawQuery = values.Encode()
+		body := p.Presentation
+		if err := encoder(req).Encode(&body); err != nil {
+			return goahttp.ErrEncodingError("signer", "PresentationProof", err)
+		}
+		return nil
+	}
+}
+
+// DecodePresentationProofResponse returns a decoder for responses returned by
+// the signer PresentationProof endpoint. restoreBody controls whether the
+// response body should be restored after having been read.
+func DecodePresentationProofResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) {
+	return func(resp *http.Response) (interface{}, error) {
+		if restoreBody {
+			b, err := ioutil.ReadAll(resp.Body)
+			if err != nil {
+				return nil, err
+			}
+			resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
+			defer func() {
+				resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
+			}()
+		} else {
+			defer resp.Body.Close()
+		}
+		switch resp.StatusCode {
+		case http.StatusOK:
+			var (
+				body interface{}
+				err  error
+			)
+			err = decoder(resp).Decode(&body)
+			if err != nil {
+				return nil, goahttp.ErrDecodingError("signer", "PresentationProof", err)
+			}
+			return body, nil
+		default:
+			body, _ := ioutil.ReadAll(resp.Body)
+			return nil, goahttp.ErrInvalidResponse("signer", "PresentationProof", resp.StatusCode, string(body))
+		}
+	}
+}
diff --git a/gen/http/signer/client/paths.go b/gen/http/signer/client/paths.go
index 0463734d1169ebe2de2866026f8d6b41416b2e23..308da7a1f7119f5a91820ab7c227fd5ee0256886 100644
--- a/gen/http/signer/client/paths.go
+++ b/gen/http/signer/client/paths.go
@@ -11,3 +11,8 @@ package client
 func CredentialProofSignerPath() string {
 	return "/v1/credential/proof"
 }
+
+// PresentationProofSignerPath returns the URL path to the signer service PresentationProof HTTP endpoint.
+func PresentationProofSignerPath() string {
+	return "/v1/presentation/proof"
+}
diff --git a/gen/http/signer/server/encode_decode.go b/gen/http/signer/server/encode_decode.go
index 884453d5d0c71cae1239fb66713587752ce90b32..558cd47c13782e5c835412d2fb7997d02cf88765 100644
--- a/gen/http/signer/server/encode_decode.go
+++ b/gen/http/signer/server/encode_decode.go
@@ -56,3 +56,44 @@ func DecodeCredentialProofRequest(mux goahttp.Muxer, decoder func(*http.Request)
 		return payload, nil
 	}
 }
+
+// EncodePresentationProofResponse returns an encoder for responses returned by
+// the signer PresentationProof endpoint.
+func EncodePresentationProofResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
+	return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
+		res, _ := v.(interface{})
+		enc := encoder(ctx, w)
+		body := res
+		w.WriteHeader(http.StatusOK)
+		return enc.Encode(body)
+	}
+}
+
+// DecodePresentationProofRequest returns a decoder for requests sent to the
+// signer PresentationProof endpoint.
+func DecodePresentationProofRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.Decoder) func(*http.Request) (interface{}, error) {
+	return func(r *http.Request) (interface{}, error) {
+		var (
+			body []byte
+			err  error
+		)
+		err = decoder(r).Decode(&body)
+		if err != nil {
+			if err == io.EOF {
+				return nil, goa.MissingPayloadError()
+			}
+			return nil, goa.DecodePayloadError(err.Error())
+		}
+
+		var (
+			key *string
+		)
+		keyRaw := r.URL.Query().Get("key")
+		if keyRaw != "" {
+			key = &keyRaw
+		}
+		payload := NewPresentationProofRequest(body, key)
+
+		return payload, nil
+	}
+}
diff --git a/gen/http/signer/server/paths.go b/gen/http/signer/server/paths.go
index 1135f5a1767e41fe5c61016576b93a0f5e9442b4..7bc55146b94c7fe8a6283513609f4cf569538492 100644
--- a/gen/http/signer/server/paths.go
+++ b/gen/http/signer/server/paths.go
@@ -11,3 +11,8 @@ package server
 func CredentialProofSignerPath() string {
 	return "/v1/credential/proof"
 }
+
+// PresentationProofSignerPath returns the URL path to the signer service PresentationProof HTTP endpoint.
+func PresentationProofSignerPath() string {
+	return "/v1/presentation/proof"
+}
diff --git a/gen/http/signer/server/server.go b/gen/http/signer/server/server.go
index 35cb6d56490b195c66a323f91a3c4a8cc4518c66..350a87ef40e9c4260e0ded1c602bbd35a81e5b8a 100644
--- a/gen/http/signer/server/server.go
+++ b/gen/http/signer/server/server.go
@@ -18,8 +18,9 @@ import (
 
 // Server lists the signer service endpoint HTTP handlers.
 type Server struct {
-	Mounts          []*MountPoint
-	CredentialProof http.Handler
+	Mounts            []*MountPoint
+	CredentialProof   http.Handler
+	PresentationProof http.Handler
 }
 
 // ErrorNamer is an interface implemented by generated error structs that
@@ -56,8 +57,10 @@ func New(
 	return &Server{
 		Mounts: []*MountPoint{
 			{"CredentialProof", "POST", "/v1/credential/proof"},
+			{"PresentationProof", "POST", "/v1/presentation/proof"},
 		},
-		CredentialProof: NewCredentialProofHandler(e.CredentialProof, mux, decoder, encoder, errhandler, formatter),
+		CredentialProof:   NewCredentialProofHandler(e.CredentialProof, mux, decoder, encoder, errhandler, formatter),
+		PresentationProof: NewPresentationProofHandler(e.PresentationProof, mux, decoder, encoder, errhandler, formatter),
 	}
 }
 
@@ -67,11 +70,13 @@ func (s *Server) Service() string { return "signer" }
 // Use wraps the server handlers with the given middleware.
 func (s *Server) Use(m func(http.Handler) http.Handler) {
 	s.CredentialProof = m(s.CredentialProof)
+	s.PresentationProof = m(s.PresentationProof)
 }
 
 // Mount configures the mux to serve the signer endpoints.
 func Mount(mux goahttp.Muxer, h *Server) {
 	MountCredentialProofHandler(mux, h.CredentialProof)
+	MountPresentationProofHandler(mux, h.PresentationProof)
 }
 
 // Mount configures the mux to serve the signer endpoints.
@@ -129,3 +134,54 @@ func NewCredentialProofHandler(
 		}
 	})
 }
+
+// MountPresentationProofHandler configures the mux to serve the "signer"
+// service "PresentationProof" endpoint.
+func MountPresentationProofHandler(mux goahttp.Muxer, h http.Handler) {
+	f, ok := h.(http.HandlerFunc)
+	if !ok {
+		f = func(w http.ResponseWriter, r *http.Request) {
+			h.ServeHTTP(w, r)
+		}
+	}
+	mux.Handle("POST", "/v1/presentation/proof", f)
+}
+
+// NewPresentationProofHandler creates a HTTP handler which loads the HTTP
+// request and calls the "signer" service "PresentationProof" endpoint.
+func NewPresentationProofHandler(
+	endpoint goa.Endpoint,
+	mux goahttp.Muxer,
+	decoder func(*http.Request) goahttp.Decoder,
+	encoder func(context.Context, http.ResponseWriter) goahttp.Encoder,
+	errhandler func(context.Context, http.ResponseWriter, error),
+	formatter func(err error) goahttp.Statuser,
+) http.Handler {
+	var (
+		decodeRequest  = DecodePresentationProofRequest(mux, decoder)
+		encodeResponse = EncodePresentationProofResponse(encoder)
+		encodeError    = goahttp.ErrorEncoder(encoder, formatter)
+	)
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		ctx := context.WithValue(r.Context(), goahttp.AcceptTypeKey, r.Header.Get("Accept"))
+		ctx = context.WithValue(ctx, goa.MethodKey, "PresentationProof")
+		ctx = context.WithValue(ctx, goa.ServiceKey, "signer")
+		payload, err := decodeRequest(r)
+		if err != nil {
+			if err := encodeError(ctx, w, err); err != nil {
+				errhandler(ctx, w, err)
+			}
+			return
+		}
+		res, err := endpoint(ctx, payload)
+		if err != nil {
+			if err := encodeError(ctx, w, err); err != nil {
+				errhandler(ctx, w, err)
+			}
+			return
+		}
+		if err := encodeResponse(ctx, w, res); err != nil {
+			errhandler(ctx, w, err)
+		}
+	})
+}
diff --git a/gen/http/signer/server/types.go b/gen/http/signer/server/types.go
index 7643f58d008578a4943a5c1e22cbdb743d681793..c86ba033b724cc98e99410669edf69c1ff8c44e6 100644
--- a/gen/http/signer/server/types.go
+++ b/gen/http/signer/server/types.go
@@ -22,3 +22,15 @@ func NewCredentialProofRequest(body []byte, key *string) *signer.CredentialProof
 
 	return res
 }
+
+// NewPresentationProofRequest builds a signer service PresentationProof
+// endpoint payload.
+func NewPresentationProofRequest(body []byte, key *string) *signer.PresentationProofRequest {
+	v := body
+	res := &signer.PresentationProofRequest{
+		Presentation: v,
+	}
+	res.Key = key
+
+	return res
+}
diff --git a/gen/signer/client.go b/gen/signer/client.go
index b8f9199fd437235d7a492790c91543ad8297fd66..033d69f43c586f8c1450c8eed9afa39088a8f98a 100644
--- a/gen/signer/client.go
+++ b/gen/signer/client.go
@@ -15,13 +15,15 @@ import (
 
 // Client is the "signer" service client.
 type Client struct {
-	CredentialProofEndpoint goa.Endpoint
+	CredentialProofEndpoint   goa.Endpoint
+	PresentationProofEndpoint goa.Endpoint
 }
 
 // NewClient initializes a "signer" service client given the endpoints.
-func NewClient(credentialProof goa.Endpoint) *Client {
+func NewClient(credentialProof, presentationProof goa.Endpoint) *Client {
 	return &Client{
-		CredentialProofEndpoint: credentialProof,
+		CredentialProofEndpoint:   credentialProof,
+		PresentationProofEndpoint: presentationProof,
 	}
 }
 
@@ -34,3 +36,14 @@ func (c *Client) CredentialProof(ctx context.Context, p *CredentialProofRequest)
 	}
 	return ires.(interface{}), nil
 }
+
+// PresentationProof calls the "PresentationProof" endpoint of the "signer"
+// service.
+func (c *Client) PresentationProof(ctx context.Context, p *PresentationProofRequest) (res interface{}, err error) {
+	var ires interface{}
+	ires, err = c.PresentationProofEndpoint(ctx, p)
+	if err != nil {
+		return
+	}
+	return ires.(interface{}), nil
+}
diff --git a/gen/signer/endpoints.go b/gen/signer/endpoints.go
index 9b81418a13992d8dad28b0a52af5db9c20fd907e..a5ebf6c81a72589682f2394ffc8489988a662534 100644
--- a/gen/signer/endpoints.go
+++ b/gen/signer/endpoints.go
@@ -15,19 +15,22 @@ import (
 
 // Endpoints wraps the "signer" service endpoints.
 type Endpoints struct {
-	CredentialProof goa.Endpoint
+	CredentialProof   goa.Endpoint
+	PresentationProof goa.Endpoint
 }
 
 // NewEndpoints wraps the methods of the "signer" service with endpoints.
 func NewEndpoints(s Service) *Endpoints {
 	return &Endpoints{
-		CredentialProof: NewCredentialProofEndpoint(s),
+		CredentialProof:   NewCredentialProofEndpoint(s),
+		PresentationProof: NewPresentationProofEndpoint(s),
 	}
 }
 
 // Use applies the given middleware to all the "signer" service endpoints.
 func (e *Endpoints) Use(m func(goa.Endpoint) goa.Endpoint) {
 	e.CredentialProof = m(e.CredentialProof)
+	e.PresentationProof = m(e.PresentationProof)
 }
 
 // NewCredentialProofEndpoint returns an endpoint function that calls the
@@ -38,3 +41,12 @@ func NewCredentialProofEndpoint(s Service) goa.Endpoint {
 		return s.CredentialProof(ctx, p)
 	}
 }
+
+// NewPresentationProofEndpoint returns an endpoint function that calls the
+// method "PresentationProof" of service "signer".
+func NewPresentationProofEndpoint(s Service) goa.Endpoint {
+	return func(ctx context.Context, req interface{}) (interface{}, error) {
+		p := req.(*PresentationProofRequest)
+		return s.PresentationProof(ctx, p)
+	}
+}
diff --git a/gen/signer/service.go b/gen/signer/service.go
index c64308776d773296f054b546b547c51bf6844db1..a1c7d2c184985bf5cc7f5eadb09848e84e4a9684 100644
--- a/gen/signer/service.go
+++ b/gen/signer/service.go
@@ -16,6 +16,8 @@ import (
 type Service interface {
 	// CredentialProof adds a proof to a given Verifiable Credential.
 	CredentialProof(context.Context, *CredentialProofRequest) (res interface{}, err error)
+	// PresentationProof adds a proof to a given Verifiable Presentation.
+	PresentationProof(context.Context, *PresentationProofRequest) (res interface{}, err error)
 }
 
 // ServiceName is the name of the service as defined in the design. This is the
@@ -26,7 +28,7 @@ const ServiceName = "signer"
 // MethodNames lists the service method names as defined in the design. These
 // are the same values that are set in the endpoint request contexts under the
 // MethodKey key.
-var MethodNames = [1]string{"CredentialProof"}
+var MethodNames = [2]string{"CredentialProof", "PresentationProof"}
 
 // CredentialProofRequest is the payload type of the signer service
 // CredentialProof method.
@@ -36,3 +38,12 @@ type CredentialProofRequest struct {
 	// Verifiable Credential in JSON format.
 	Credential []byte
 }
+
+// PresentationProofRequest is the payload type of the signer service
+// PresentationProof method.
+type PresentationProofRequest struct {
+	// Key to use for the proof signature (optional).
+	Key *string
+	// Verifiable Presentation in JSON format.
+	Presentation []byte
+}
diff --git a/internal/service/signer/service.go b/internal/service/signer/service.go
index f216f1a8f39627221e3c8598ef02bfb7a9b8eb46..06755754577a21cf4db4c77c181f216d651783c6 100644
--- a/internal/service/signer/service.go
+++ b/internal/service/signer/service.go
@@ -50,9 +50,11 @@ func New(signer Signer, defaultKey string, httpClient *http.Client, logger *zap.
 
 // CredentialProof adds a proof to a given Verifiable Credential.
 func (s *Service) CredentialProof(ctx context.Context, req *signer.CredentialProofRequest) (interface{}, error) {
+	logger := s.logger.With(zap.String("operation", "credentialProof"))
+
 	vc, err := verifiable.ParseCredential(req.Credential, verifiable.WithJSONLDDocumentLoader(s.docLoader))
 	if err != nil {
-		s.logger.Error("error parsing credential", zap.Error(err))
+		logger.Error("error parsing verifiable credential", zap.Error(err))
 		return nil, err
 	}
 
@@ -63,24 +65,60 @@ func (s *Service) CredentialProof(ctx context.Context, req *signer.CredentialPro
 
 	key, err := s.signer.Key(keyname)
 	if err != nil {
-		s.logger.Error("error getting signing key", zap.String("key", keyname), zap.Error(err))
+		logger.Error("error getting signing key", zap.String("key", keyname), zap.Error(err))
 		return nil, errors.New("error getting signing key", err)
 	}
 
 	proofContext, err := s.proofContext(key.Name, key.Type)
 	if err != nil {
-		s.logger.Error("error building proof context", zap.Error(err))
+		logger.Error("error building proof context", zap.Error(err))
 		return nil, err
 	}
 
 	if err := vc.AddLinkedDataProof(proofContext, jsonld.WithDocumentLoader(s.docLoader)); err != nil {
-		s.logger.Error("error adding linked data proof", zap.Error(err))
+		logger.Error("error adding linked data proof", zap.Error(err))
 		return nil, err
 	}
 
 	return vc, nil
 }
 
+// PresentationProof adds a proof to a given Verifiable Presentation.
+func (s *Service) PresentationProof(ctx context.Context, req *signer.PresentationProofRequest) (interface{}, error) {
+	logger := s.logger.With(zap.String("operation", "presentationProof"))
+
+	vp, err := verifiable.ParsePresentation(req.Presentation, verifiable.WithPresJSONLDDocumentLoader(s.docLoader))
+	if err != nil {
+		logger.Error("error parsing verifiable presentation", zap.Error(err))
+		return nil, err
+	}
+
+	keyname := s.defaultKey
+	if req.Key != nil && *req.Key != "" {
+		keyname = *req.Key
+	}
+
+	key, err := s.signer.Key(keyname)
+	if err != nil {
+		logger.Error("error getting signing key", zap.String("key", keyname), zap.Error(err))
+		return nil, errors.New("error getting signing key", err)
+	}
+
+	proofContext, err := s.proofContext(key.Name, key.Type)
+	if err != nil {
+		logger.Error("error building proof context", zap.Error(err))
+		return nil, err
+	}
+
+	if err := vp.AddLinkedDataProof(proofContext, jsonld.WithDocumentLoader(s.docLoader)); err != nil {
+		logger.Error("error adding linked data proof", zap.Error(err))
+		return nil, err
+	}
+
+	return vp, nil
+}
+
+// proofContext is used to create proofs.
 func (s *Service) proofContext(key string, keyType string) (*verifiable.LinkedDataProofContext, error) {
 	sigSuite, sigType, err := s.signatureSuite(key, keyType)
 	if err != nil {
@@ -97,6 +135,7 @@ func (s *Service) proofContext(key string, keyType string) (*verifiable.LinkedDa
 	return proofContext, nil
 }
 
+// signatureSuite is used to create digital signatures on proofs.
 func (s *Service) signatureSuite(key string, keyType string) (sigSuite ariesigner.SignatureSuite, sigType string, err error) {
 	signer := s.signer.WithKey(key)
 
diff --git a/internal/service/signer/service_test.go b/internal/service/signer/service_test.go
index 19b47e2e3dab68ea8693c2ed45de86ae2a6972ef..9a44fb3b27cf0dc41b44aafb7340c34a9693d682 100644
--- a/internal/service/signer/service_test.go
+++ b/internal/service/signer/service_test.go
@@ -17,47 +17,6 @@ import (
 	"code.vereign.com/gaiax/tsa/signer/internal/service/signer/signerfakes"
 )
 
-//nolint:gosec
-var validCredential = `{
-  "@context": [
-    "https://www.w3.org/2018/credentials/v1"
-  ],
-  "credentialSubject": {
-    "hello": "world"
-  },
-  "issuanceDate": "2022-06-02T17:24:05.032533+03:00",
-  "issuer": "https://example.com",
-  "type": "VerifiableCredential"
-}`
-
-//nolint:gosec
-var invalidCredential = `{"invalid":"credential"}`
-
-//nolint:gosec
-var invalidCredentialContexts = `{
-  "@context": ["https://www.w3.org/2018/credentials/v123"],
-  "credentialSubject": {
-    "hello": "world"
-  },
-  "issuanceDate": "2022-06-02T17:24:05.032533+03:00",
-  "issuer": "https://example.com",
-  "type": "VerifiableCredential"
-}`
-
-//nolint:gosec
-var nonExistingCredentialContexts = `{
-  "@context": [
-    "https://www.w3.org/2018/credentials/v1",
-    "https://no-schema-here.com/credentials/context"
-  ],
-  "credentialSubject": {
-    "hello": "world"
-  },
-  "issuanceDate": "2022-06-02T17:24:05.032533+03:00",
-  "issuer": "https://example.com",
-  "type": "VerifiableCredential"
-}`
-
 func TestService_CredentialProof(t *testing.T) {
 	tests := []struct {
 		name       string
@@ -191,6 +150,7 @@ func TestService_CredentialProof(t *testing.T) {
 			contexts:                []string{"https://www.w3.org/2018/credentials/v1"},
 			subject:                 []verifiable.Subject{{CustomFields: verifiable.CustomFields{"hello": "world"}}},
 			issuer:                  verifiable.Issuer{ID: "https://example.com"},
+			types:                   []string{verifiable.VCType},
 			proofPurpose:            "assertionMethod",
 			proofType:               "Ed25519Signature2018",
 			proofValue:              base64.RawURLEncoding.EncodeToString([]byte("test signature")),
@@ -223,6 +183,7 @@ func TestService_CredentialProof(t *testing.T) {
 			contexts:                []string{"https://www.w3.org/2018/credentials/v1"},
 			subject:                 []verifiable.Subject{{CustomFields: verifiable.CustomFields{"hello": "world"}}},
 			issuer:                  verifiable.Issuer{ID: "https://example.com"},
+			types:                   []string{verifiable.VCType},
 			proofPurpose:            "assertionMethod",
 			proofType:               "EcdsaSecp256k1Signature2019",
 			proofValue:              base64.RawURLEncoding.EncodeToString([]byte("test signature")),
@@ -253,6 +214,7 @@ func TestService_CredentialProof(t *testing.T) {
 				assert.Equal(t, test.contexts, vc.Context)
 				assert.Equal(t, test.subject, vc.Subject)
 				assert.Equal(t, test.issuer, vc.Issuer)
+				assert.Equal(t, test.types, vc.Types)
 				assert.Equal(t, test.proofPurpose, vc.Proofs[0]["proofPurpose"])
 				assert.Equal(t, test.proofType, vc.Proofs[0]["type"])
 				assert.Equal(t, test.proofValue, vc.Proofs[0]["proofValue"])
@@ -261,3 +223,368 @@ func TestService_CredentialProof(t *testing.T) {
 		})
 	}
 }
+
+func TestService_PresentationProof(t *testing.T) {
+	tests := []struct {
+		name       string
+		signer     *signerfakes.FakeSigner
+		defaultKey string
+		req        *goasigner.PresentationProofRequest
+
+		errkind errors.Kind
+		errtext string
+
+		contexts                []string
+		types                   []string
+		proofPurpose            string
+		proofValue              string
+		proofType               string
+		proofVerificationMethod string
+	}{
+		{
+			name:       "invalid verifiable presentation",
+			defaultKey: "key1",
+			req: &goasigner.PresentationProofRequest{
+				Presentation: []byte(invalidPresentation),
+			},
+			errtext: "verifiable presentation is not valid",
+		},
+		{
+			name:       "invalid presentation contexts",
+			defaultKey: "key1",
+			req: &goasigner.PresentationProofRequest{
+				Presentation: []byte(invalidPresentationContexts),
+			},
+			errtext: "verifiable presentation is not valid",
+		},
+		{
+			name:       "non-existing presentation contexts",
+			defaultKey: "key1",
+			req: &goasigner.PresentationProofRequest{
+				Presentation: []byte(nonExistingPresentationContexts),
+			},
+			errtext: "Dereferencing a URL did not result in a valid JSON-LD context",
+		},
+		{
+			name:       "valid presentation but signer cannot find key",
+			defaultKey: "key1",
+			req: &goasigner.PresentationProofRequest{
+				Key:          ptr.String("key2"),
+				Presentation: []byte(validPresentation),
+			},
+			signer: &signerfakes.FakeSigner{
+				KeyStub: func(key string) (*signer.SignKey, error) {
+					return nil, errors.New(errors.NotFound)
+				},
+			},
+			errkind: errors.NotFound,
+			errtext: "error getting signing key",
+		},
+		{
+			name:       "valid presentation but signer returns internal error",
+			defaultKey: "key1",
+			req: &goasigner.PresentationProofRequest{
+				Key:          ptr.String("key2"),
+				Presentation: []byte(validPresentation),
+			},
+			signer: &signerfakes.FakeSigner{
+				KeyStub: func(key string) (*signer.SignKey, error) {
+					return nil, errors.New(errors.Internal)
+				},
+			},
+			errkind: errors.Internal,
+			errtext: "error getting signing key",
+		},
+		{
+			name:       "valid presentation but signer returns internal error",
+			defaultKey: "key1",
+			req: &goasigner.PresentationProofRequest{
+				Key:          ptr.String("key2"),
+				Presentation: []byte(validPresentation),
+			},
+			signer: &signerfakes.FakeSigner{
+				KeyStub: func(key string) (*signer.SignKey, error) {
+					return nil, errors.New(errors.Internal)
+				},
+			},
+			errkind: errors.Internal,
+			errtext: "error getting signing key",
+		},
+		{
+			name:       "valid presentation but signer returns unsupported key type",
+			defaultKey: "key1",
+			req: &goasigner.PresentationProofRequest{
+				Key:          ptr.String("key2"),
+				Presentation: []byte(validPresentation),
+			},
+			signer: &signerfakes.FakeSigner{
+				KeyStub: func(key string) (*signer.SignKey, error) {
+					return &signer.SignKey{
+						Name: "key23",
+						Type: "rsa4096",
+					}, nil
+				},
+			},
+			errkind: errors.Unknown,
+			errtext: "unsupported key type",
+		},
+		{
+			name:       "valid presentation and signer key type ed25519",
+			defaultKey: "key1",
+			req: &goasigner.PresentationProofRequest{
+				Key:          ptr.String("key2"),
+				Presentation: []byte(validPresentation),
+			},
+			signer: &signerfakes.FakeSigner{
+				KeyStub: func(key string) (*signer.SignKey, error) {
+					return &signer.SignKey{
+						Name: "key123",
+						Type: "ed25519",
+					}, nil
+				},
+				WithKeyStub: func(key string) signer.Signer {
+					return &signerfakes.FakeSigner{
+						SignStub: func(data []byte) ([]byte, error) {
+							return []byte("test signature"), nil
+						},
+					}
+				},
+			},
+
+			// expected attributes the VC must have
+			contexts:                []string{"https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"},
+			types:                   []string{verifiable.VPType},
+			proofPurpose:            "assertionMethod",
+			proofType:               "Ed25519Signature2018",
+			proofValue:              base64.RawURLEncoding.EncodeToString([]byte("test signature")),
+			proofVerificationMethod: "key123",
+		},
+		{
+			name:       "valid presentation and signer key type ecdsa-p256",
+			defaultKey: "key1",
+			req: &goasigner.PresentationProofRequest{
+				Key:          ptr.String("key2"),
+				Presentation: []byte(validPresentation),
+			},
+			signer: &signerfakes.FakeSigner{
+				KeyStub: func(key string) (*signer.SignKey, error) {
+					return &signer.SignKey{
+						Name: "key123",
+						Type: "ecdsa-p256",
+					}, nil
+				},
+				WithKeyStub: func(key string) signer.Signer {
+					return &signerfakes.FakeSigner{
+						SignStub: func(data []byte) ([]byte, error) {
+							return []byte("test signature"), nil
+						},
+					}
+				},
+			},
+
+			// expected attributes the VC must have
+			contexts:                []string{"https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"},
+			types:                   []string{verifiable.VPType},
+			proofPurpose:            "assertionMethod",
+			proofType:               "EcdsaSecp256k1Signature2019",
+			proofValue:              base64.RawURLEncoding.EncodeToString([]byte("test signature")),
+			proofVerificationMethod: "key123",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			svc := signer.New(test.signer, test.defaultKey, http.DefaultClient, zap.NewNop())
+			res, err := svc.PresentationProof(context.Background(), test.req)
+			if err != nil {
+				assert.Nil(t, res)
+				assert.NotEmpty(t, test.errtext)
+				if e, ok := err.(*errors.Error); ok {
+					assert.Equal(t, test.errkind, e.Kind)
+					assert.Contains(t, e.Message, test.errtext)
+				} else {
+					assert.Contains(t, err.Error(), test.errtext)
+				}
+			} else {
+				assert.Empty(t, test.errtext)
+				assert.NotNil(t, res)
+
+				vp, ok := res.(*verifiable.Presentation)
+				assert.True(t, ok)
+
+				assert.Equal(t, test.contexts, vp.Context)
+				assert.Equal(t, test.types, vp.Type)
+				assert.Equal(t, test.proofPurpose, vp.Proofs[0]["proofPurpose"])
+				assert.Equal(t, test.proofType, vp.Proofs[0]["type"])
+				assert.Equal(t, test.proofValue, vp.Proofs[0]["proofValue"])
+				assert.Equal(t, test.proofVerificationMethod, vp.Proofs[0]["verificationMethod"])
+			}
+		})
+	}
+}
+
+// ---------- Verifiable Credentials ---------- //
+
+//nolint:gosec
+var validCredential = `{
+  "@context": [
+    "https://www.w3.org/2018/credentials/v1"
+  ],
+  "credentialSubject": {
+    "hello": "world"
+  },
+  "issuanceDate": "2022-06-02T17:24:05.032533+03:00",
+  "issuer": "https://example.com",
+  "type": "VerifiableCredential"
+}`
+
+//nolint:gosec
+var invalidCredential = `{"invalid":"credential"}`
+
+//nolint:gosec
+var invalidCredentialContexts = `{
+  "@context": ["https://www.w3.org/2018/credentials/v123"],
+  "credentialSubject": {
+    "hello": "world"
+  },
+  "issuanceDate": "2022-06-02T17:24:05.032533+03:00",
+  "issuer": "https://example.com",
+  "type": "VerifiableCredential"
+}`
+
+//nolint:gosec
+var nonExistingCredentialContexts = `{
+  "@context": [
+    "https://www.w3.org/2018/credentials/v1",
+    "https://no-schema-here.com/credentials/context"
+  ],
+  "credentialSubject": {
+    "hello": "world"
+  },
+  "issuanceDate": "2022-06-02T17:24:05.032533+03:00",
+  "issuer": "https://example.com",
+  "type": "VerifiableCredential"
+}`
+
+// ---------- Verifiable Presentations ---------- //
+
+var validPresentation = `{
+  "@context": [
+    "https://www.w3.org/2018/credentials/v1",
+    "https://www.w3.org/2018/credentials/examples/v1"
+  ],
+  "id": "did:123",
+  "type": "VerifiablePresentation",
+  "verifiableCredential": [
+    {
+      "@context": [
+        "https://www.w3.org/2018/credentials/v1",
+        "https://www.w3.org/2018/credentials/examples/v1"
+      ],
+      "credentialSubject": {
+        "allow": true,
+        "id": "example/example/1.0",
+        "taskID": "0123456789abcdef"
+      },
+      "issuanceDate": "2022-06-14T08:43:22.78309334Z",
+      "issuer": "https://example.com",
+      "type": "VerifiableCredential"
+    },
+    {
+      "@context": [
+        "https://www.w3.org/2018/credentials/v1",
+        "https://www.w3.org/2018/credentials/examples/v1"
+      ],
+      "credentialSubject": {
+        "id": "example/example/2.0",
+        "result": {
+          "hello": "world"
+        }
+      },
+      "issuanceDate": "2022-06-14T08:43:22.783102173Z",
+      "issuer": "https://example.com",
+      "type": "VerifiableCredential"
+    }
+  ]
+}`
+
+var invalidPresentation = `{"invalid":"presentation"}`
+
+var invalidPresentationContexts = `{
+  "@context": [
+    "https://www.w3.org/2018/credentials/v123"
+  ],
+  "id": "did:123",
+  "type": "VerifiablePresentation",
+  "verifiableCredential": [
+    {
+      "@context": [
+        "https://www.w3.org/2018/credentials/v1",
+        "https://www.w3.org/2018/credentials/examples/v1"
+      ],
+      "credentialSubject": {
+        "allow": true,
+        "id": "example/example/1.0",
+        "taskID": "0123456789abcdef"
+      },
+      "issuanceDate": "2022-06-14T08:43:22.78309334Z",
+      "issuer": "https://example.com",
+      "type": "VerifiableCredential"
+    },
+    {
+      "@context": [
+        "https://www.w3.org/2018/credentials/v1",
+        "https://www.w3.org/2018/credentials/examples/v1"
+      ],
+      "credentialSubject": {
+        "id": "example/example/2.0",
+        "result": {
+          "hello": "world"
+        }
+      },
+      "issuanceDate": "2022-06-14T08:43:22.783102173Z",
+      "issuer": "https://example.com",
+      "type": "VerifiableCredential"
+    }
+  ]
+}`
+
+var nonExistingPresentationContexts = `{
+  "@context": [
+    "https://www.w3.org/2018/credentials/v1",
+    "https://www.nonexistingschema.org"
+  ],
+  "id": "did:123",
+  "type": "VerifiablePresentation",
+  "verifiableCredential": [
+    {
+      "@context": [
+        "https://www.w3.org/2018/credentials/v1",
+        "https://www.w3.org/2018/credentials/examples/v1"
+      ],
+      "credentialSubject": {
+        "allow": true,
+        "id": "example/example/1.0",
+        "taskID": "0123456789abcdef"
+      },
+      "issuanceDate": "2022-06-14T08:43:22.78309334Z",
+      "issuer": "https://example.com",
+      "type": "VerifiableCredential"
+    },
+    {
+      "@context": [
+        "https://www.w3.org/2018/credentials/v1",
+        "https://www.w3.org/2018/credentials/examples/v1"
+      ],
+      "credentialSubject": {
+        "id": "example/example/2.0",
+        "result": {
+          "hello": "world"
+        }
+      },
+      "issuanceDate": "2022-06-14T08:43:22.783102173Z",
+      "issuer": "https://example.com",
+      "type": "VerifiableCredential"
+    }
+  ]
+}`