diff --git a/doc/swagger.yaml b/doc/swagger.yaml index 228f301cb..205f14c4f 100644 --- a/doc/swagger.yaml +++ b/doc/swagger.yaml @@ -8,6 +8,8 @@ definitions: id: description: Credential ID type: string + revoked: + type: boolean type: object credential.CredentialSchema: properties: @@ -483,6 +485,8 @@ definitions: type: string issuer: type: string + revocable: + type: boolean schema: description: A schema is optional. If present, we'll attempt to look it up and validate the data against it. @@ -627,6 +631,20 @@ definitions: id: type: string type: object + github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialStatusListResponse: + properties: + credential: + $ref: '#/definitions/credential.VerifiableCredential' + credentialJwt: + type: string + id: + type: string + type: object + github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialStatusResponse: + properties: + revoked: + type: boolean + type: object github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialsResponse: properties: credentials: @@ -823,6 +841,18 @@ definitions: items: {} type: array type: object + github.com_tbd54566975_ssi-service_pkg_server_router.UpdateCredentialStatusRequest: + properties: + revoked: + type: boolean + required: + - revoked + type: object + github.com_tbd54566975_ssi-service_pkg_server_router.UpdateCredentialStatusResponse: + properties: + revoked: + type: boolean + type: object github.com_tbd54566975_ssi-service_pkg_server_router.VerifyCredentialRequest: properties: credential: @@ -979,6 +1009,8 @@ definitions: type: string issuer: type: string + revocable: + type: boolean schema: description: A schema is optional. If present, we'll attempt to look it up and validate the data against it. @@ -1123,6 +1155,20 @@ definitions: id: type: string type: object + pkg_server_router.GetCredentialStatusListResponse: + properties: + credential: + $ref: '#/definitions/credential.VerifiableCredential' + credentialJwt: + type: string + id: + type: string + type: object + pkg_server_router.GetCredentialStatusResponse: + properties: + revoked: + type: boolean + type: object pkg_server_router.GetCredentialsResponse: properties: credentials: @@ -1319,6 +1365,18 @@ definitions: items: {} type: array type: object + pkg_server_router.UpdateCredentialStatusRequest: + properties: + revoked: + type: boolean + required: + - revoked + type: object + pkg_server_router.UpdateCredentialStatusResponse: + properties: + revoked: + type: boolean + type: object pkg_server_router.VerifyCredentialRequest: properties: credential: @@ -1622,6 +1680,85 @@ paths: summary: Get Credential tags: - CredentialAPI + /v1/credentials/{id}/status: + get: + consumes: + - application/json + description: Get credential status by id + parameters: + - description: ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialStatusResponse' + "400": + description: Bad request + schema: + type: string + summary: Get Credential Status + tags: + - CredentialAPI + put: + consumes: + - application/json + description: Update a credential's status + parameters: + - description: request body + in: body + name: request + required: true + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.UpdateCredentialStatusRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.UpdateCredentialStatusResponse' + "400": + description: Bad request + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Update Credential Status + tags: + - CredentialAPI + /v1/credentials/status/{id}: + get: + consumes: + - application/json + description: Get credential status list by id + parameters: + - description: ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialStatusListResponse' + "400": + description: Bad request + schema: + type: string + summary: Get Credential Status List + tags: + - CredentialAPI /v1/credentials/verification: put: consumes: @@ -1794,7 +1931,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.StoreKeyRequest' + $ref: '#/definitions/pkg_server_router.StoreKeyRequest' produces: - application/json responses: @@ -1828,7 +1965,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetKeyDetailsResponse' + $ref: '#/definitions/pkg_server_router.GetKeyDetailsResponse' "400": description: Bad request schema: @@ -2156,7 +2293,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetOperationsResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetOperationsResponse' "400": description: Bad request schema: @@ -2185,7 +2322,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.Operation' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.Operation' "400": description: Bad request schema: @@ -2208,14 +2345,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.CreatePresentationDefinitionRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreatePresentationDefinitionRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/pkg_server_router.CreatePresentationDefinitionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreatePresentationDefinitionResponse' "400": description: Bad request schema: @@ -2272,7 +2409,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetPresentationDefinitionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetPresentationDefinitionResponse' "400": description: Bad request schema: @@ -2297,7 +2434,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetSubmissionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSubmissionResponse' "400": description: Bad request schema: @@ -2318,14 +2455,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.ReviewSubmissionRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.ReviewSubmissionRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.ReviewSubmissionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.ReviewSubmissionResponse' "400": description: Bad request schema: @@ -2347,14 +2484,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.CreateSubmissionRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSubmissionRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/pkg_server_router.Operation' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.Operation' "400": description: Bad request schema: @@ -2377,7 +2514,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemasResponse' + $ref: '#/definitions/pkg_server_router.GetSchemasResponse' "500": description: Internal server error schema: @@ -2395,14 +2532,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaRequest' + $ref: '#/definitions/pkg_server_router.CreateSchemaRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaResponse' + $ref: '#/definitions/pkg_server_router.CreateSchemaResponse' "400": description: Bad request schema: @@ -2459,7 +2596,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemaResponse' + $ref: '#/definitions/pkg_server_router.GetSchemaResponse' "400": description: Bad request schema: @@ -2478,14 +2615,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifySchemaRequest' + $ref: '#/definitions/pkg_server_router.VerifySchemaRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifySchemaResponse' + $ref: '#/definitions/pkg_server_router.VerifySchemaResponse' "400": description: Bad request schema: diff --git a/go.mod b/go.mod index 1867fb1b6..2df0190dd 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/BurntSushi/toml v1.2.1 - github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221110170444-a9e67907c8f9 + github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221129172807-92be548d54be github.com/ardanlabs/conf v1.5.0 github.com/dimfeld/httptreemux/v5 v5.5.0 github.com/go-playground/locales v0.14.0 @@ -31,7 +31,7 @@ require ( ) require ( - github.com/bits-and-blooms/bitset v1.3.3 // indirect + github.com/bits-and-blooms/bitset v1.4.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/go-logr/logr v1.2.3 // indirect @@ -46,11 +46,11 @@ require ( github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.1.0 // indirect github.com/multiformats/go-multicodec v0.7.0 // indirect - github.com/piprate/json-gold v0.4.2 // indirect + github.com/piprate/json-gold v0.5.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/cachecontrol v0.1.0 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect - github.com/santhosh-tekuri/jsonschema/v5 v5.0.2 // indirect + github.com/santhosh-tekuri/jsonschema/v5 v5.1.0 // indirect golang.org/x/sys v0.2.0 // indirect golang.org/x/term v0.2.0 // indirect golang.org/x/text v0.4.0 // indirect diff --git a/go.sum b/go.sum index b4a7c185d..fc909af4a 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,11 @@ github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221110170444-a9e67907c8f9 h1:46Xgt4DbZhaw4mOWYlABWuSMhZSGKsURlo2yzDjfVyA= -github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221110170444-a9e67907c8f9/go.mod h1:c2wq4GipGxjAPXgnxNBvnuB7OqDNKjriKuD60H0Q6Ho= +github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221129172807-92be548d54be h1:KK6Dbr1rFPQSAyeWQGRUm7ba1K3XoxAmlFicLeR/6Ek= +github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221129172807-92be548d54be/go.mod h1:RFja7smnRZpi5OPDPpW35TDK3SbHuAGCHs8wbNTSBxY= github.com/ardanlabs/conf v1.5.0 h1:5TwP6Wu9Xi07eLFEpiCUF3oQXh9UzHMDVnD3u/I5d5c= github.com/ardanlabs/conf v1.5.0/go.mod h1:ILsMo9dMqYzCxDjDXTiwMI0IgxOJd0MOiucbQY2wlJw= -github.com/bits-and-blooms/bitset v1.3.3 h1:R1XWiopGiXf66xygsiLpzLo67xEYvMkHw3w+rCOSAwg= -github.com/bits-and-blooms/bitset v1.3.3/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.4.0 h1:+YZ8ePm+He2pU3dZlIZiOeAKfrBkXi1lSrXJ/Xzgbu8= +github.com/bits-and-blooms/bitset v1.4.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -78,29 +78,27 @@ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/n github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 h1:Yl0tPBa8QPjGmesFh1D0rDy+q1Twx6FyU7VWHi8wZbI= github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod h1:eqOVx5Vwu4gd2mmMZvVZsgIqNSaW3xxRThUJ0k/TPk4= -github.com/piprate/json-gold v0.4.2 h1:Rq8V+637HOFcj20KdTqW/g/llCwX2qtau0g5d1pD79o= -github.com/piprate/json-gold v0.4.2/go.mod h1:OK1z7UgtBZk06n2cDE2OSq1kffmjFFp5/2yhLLCz9UM= +github.com/piprate/json-gold v0.5.0 h1:RmGh1PYboCFcchVFuh2pbSWAZy4XJaqTMU4KQYsApbM= +github.com/piprate/json-gold v0.5.0/go.mod h1:WZ501QQMbZZ+3pXFPhQKzNwS1+jls0oqov3uQ2WasLs= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/santhosh-tekuri/jsonschema/v5 v5.0.2 h1:zOYFITq/5SO7YOv39/Taw8s1skb0Py39K5V2XvCEP48= -github.com/santhosh-tekuri/jsonschema/v5 v5.0.2/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0= +github.com/santhosh-tekuri/jsonschema/v5 v5.1.0 h1:wSUNu/w/7OQ0Y3NVnfTU5uxzXY4uMpXW92VXEJKqBB0= +github.com/santhosh-tekuri/jsonschema/v5 v5.1.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/internal/did/access.go b/internal/did/access.go index 31e862a06..2efbcd0e0 100644 --- a/internal/did/access.go +++ b/internal/did/access.go @@ -3,7 +3,6 @@ package did import ( "crypto" "fmt" - "github.com/TBD54566975/ssi-sdk/cryptosuite" didsdk "github.com/TBD54566975/ssi-sdk/did" "github.com/goccy/go-json" @@ -66,11 +65,19 @@ func extractKeyFromVerificationMethod(method didsdk.VerificationMethod) (pubKey return case method.PublicKeyJWK != nil: jwkBytes, jwkErr := json.Marshal(method.PublicKeyJWK) - if err != nil { + if jwkErr != nil { err = jwkErr return } - pubKey, err = jwk.ParseKey(jwkBytes) + parsed, parseErr := jwk.ParseKey(jwkBytes) + if parseErr != nil { + err = parseErr + return + } + if err = parsed.Raw(&pubKey); err != nil { + return + } + return } err = errors.New("no public key found in verification method") diff --git a/pkg/jwt/verification.go b/pkg/jwt/verification.go new file mode 100644 index 000000000..4aa629916 --- /dev/null +++ b/pkg/jwt/verification.go @@ -0,0 +1,27 @@ +package jwt + +import ( + didsdk "github.com/TBD54566975/ssi-sdk/did" + "github.com/pkg/errors" + didint "github.com/tbd54566975/ssi-service/internal/did" + "github.com/tbd54566975/ssi-service/internal/keyaccess" + "github.com/tbd54566975/ssi-service/internal/util" +) + +// VerifyTokenFromDID verifies that the information in the token was digitally signed by the public key associated with +// the public key of the verification method of the did's document. The passed in resolver is used to map from the did +// to the did document. +func VerifyTokenFromDID(did string, token keyaccess.JWT, s *didsdk.Resolver) error { + kid, pubKey, err := didint.ResolveKeyForDID(s, did) + if err != nil { + return errors.Wrapf(err, "failed to resolve applicant's did: %s", did) + } + verifier, err := keyaccess.NewJWKKeyAccessVerifier(kid, pubKey) + if err != nil { + return util.LoggingErrorMsg(err, "could not create application verifier") + } + if err := verifier.Verify(token); err != nil { + return util.LoggingErrorMsg(err, "could not verify the application's signature") + } + return nil +} diff --git a/pkg/server/router/presentation.go b/pkg/server/router/presentation.go index 536309f22..0111c5a2f 100644 --- a/pkg/server/router/presentation.go +++ b/pkg/server/router/presentation.go @@ -4,9 +4,13 @@ import ( "context" "fmt" "github.com/TBD54566975/ssi-sdk/credential/exchange" + "github.com/TBD54566975/ssi-sdk/credential/signing" + "github.com/goccy/go-json" "github.com/pkg/errors" "github.com/sirupsen/logrus" + credint "github.com/tbd54566975/ssi-service/internal/credential" "github.com/tbd54566975/ssi-service/internal/keyaccess" + "github.com/tbd54566975/ssi-service/internal/util" "github.com/tbd54566975/ssi-service/pkg/server/framework" svcframework "github.com/tbd54566975/ssi-service/pkg/service/framework" "github.com/tbd54566975/ssi-service/pkg/service/presentation" @@ -179,6 +183,40 @@ type CreateSubmissionRequest struct { SubmissionJWT keyaccess.JWT `json:"submissionJwt" validate:"required"` } +func (r CreateSubmissionRequest) toServiceRequest() (*presentation.CreateSubmissionRequest, error) { + sdkVP, err := signing.ParseVerifiablePresentationFromJWT(r.SubmissionJWT.String()) + if err != nil { + return nil, errors.Wrap(err, "parsing presentation from jwt") + } + if err := sdkVP.IsValid(); err != nil { + return nil, errors.Wrap(err, "verifying vp validity") + } + + submissionData, err := json.Marshal(sdkVP.PresentationSubmission) + if err != nil { + return nil, errors.Wrap(err, "marshalling presentation_submission") + } + var s exchange.PresentationSubmission + if err := json.Unmarshal(submissionData, &s); err != nil { + return nil, errors.Wrap(err, "unmarshalling presentation submission") + } + if err := s.IsValid(); err != nil { + return nil, errors.Wrap(err, "verifying submission validity") + } + sdkVP.PresentationSubmission = s + + credContainers, err := credint.NewCredentialContainerFromArray(sdkVP.VerifiableCredential) + if err != nil { + return nil, errors.Wrap(err, "parsing verifiable credential array") + } + + return &presentation.CreateSubmissionRequest{ + Presentation: *sdkVP, + SubmissionJWT: r.SubmissionJWT, + Submission: s, + Credentials: credContainers}, nil +} + type Operation struct { ID string `json:"id"` Done bool `json:"done"` @@ -202,7 +240,27 @@ type OperationResult struct { // @Failure 500 {string} string "Internal server error" // @Router /v1/presentations/submissions [put] func (pr PresentationRouter) CreateSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - var resp Operation + var request CreateSubmissionRequest + if err := framework.Decode(r, &request); err != nil { + return framework.NewRequestError( + util.LoggingErrorMsg(err, "invalid create submission request"), http.StatusBadRequest) + } + + req, err := request.toServiceRequest() + if err != nil { + return framework.NewRequestError( + util.LoggingErrorMsg(err, "invalid create submission request"), http.StatusBadRequest) + } + + operation, err := pr.service.CreateSubmission(*req) + if err != nil { + return framework.NewRequestError( + util.LoggingErrorMsg(err, "cannot create submission"), http.StatusInternalServerError) + } + + resp := Operation{ + ID: operation.ID, + } return framework.Respond(ctx, w, resp, http.StatusCreated) } diff --git a/pkg/server/router/presentation_test.go b/pkg/server/router/presentation_test.go index dc9acb2b5..3f51be5ed 100644 --- a/pkg/server/router/presentation_test.go +++ b/pkg/server/router/presentation_test.go @@ -35,7 +35,11 @@ func TestPresentationDefinitionService(t *testing.T) { s, err := storage.NewStorage(storage.Bolt) assert.NoError(t, err) - service, err := presentation.NewPresentationService(config.PresentationServiceConfig{}, s) + keyStoreService := testKeyStoreService(t, s) + didService := testDIDService(t, s, keyStoreService) + schemaService := testSchemaService(t, s, keyStoreService, didService) + + service, err := presentation.NewPresentationService(config.PresentationServiceConfig{}, s, didService.GetResolver(), schemaService) assert.NoError(t, err) t.Run("Create returns the created definition", func(t *testing.T) { diff --git a/pkg/server/router/testutils_test.go b/pkg/server/router/testutils_test.go index 90bcd5430..6bee35610 100644 --- a/pkg/server/router/testutils_test.go +++ b/pkg/server/router/testutils_test.go @@ -14,7 +14,7 @@ import ( "github.com/tbd54566975/ssi-service/pkg/storage" ) -func testKeyStoreService(t *testing.T, db *storage.BoltDB) *keystore.Service { +func testKeyStoreService(t *testing.T, db storage.ServiceStorage) *keystore.Service { serviceConfig := config.KeyStoreServiceConfig{ServiceKeyPassword: "test-password"} // create a keystore service keystoreService, err := keystore.NewKeyStoreService(serviceConfig, db) @@ -23,7 +23,7 @@ func testKeyStoreService(t *testing.T, db *storage.BoltDB) *keystore.Service { return keystoreService } -func testDIDService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service) *did.Service { +func testDIDService(t *testing.T, db storage.ServiceStorage, keyStore *keystore.Service) *did.Service { serviceConfig := config.DIDServiceConfig{ BaseServiceConfig: &config.BaseServiceConfig{ Name: "did", @@ -38,7 +38,7 @@ func testDIDService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service return didService } -func testSchemaService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service, did *did.Service) *schema.Service { +func testSchemaService(t *testing.T, db storage.ServiceStorage, keyStore *keystore.Service, did *did.Service) *schema.Service { serviceConfig := config.SchemaServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "schema"}} // create a schema service schemaService, err := schema.NewSchemaService(serviceConfig, db, keyStore, did.GetResolver()) diff --git a/pkg/server/server_did_test.go b/pkg/server/server_did_test.go index 25a512040..4733cd22e 100644 --- a/pkg/server/server_did_test.go +++ b/pkg/server/server_did_test.go @@ -298,7 +298,7 @@ func TestDIDAPI(t *testing.T) { } err = didService.ResolveDID(newRequestContextWithParams(badParams), w, req) assert.Error(tt, err) - assert.Contains(tt, err.Error(), "did:key:abcd: selected encoding not supported") + assert.Contains(tt, err.Error(), "selected encoding not supported") w.Flush() diff --git a/pkg/server/server_presentation_test.go b/pkg/server/server_presentation_test.go index 6e3422fc0..442dc1ee6 100644 --- a/pkg/server/server_presentation_test.go +++ b/pkg/server/server_presentation_test.go @@ -2,13 +2,17 @@ package server import ( "fmt" + "github.com/TBD54566975/ssi-sdk/credential" "github.com/TBD54566975/ssi-sdk/credential/exchange" + "github.com/TBD54566975/ssi-sdk/credential/signing" "github.com/TBD54566975/ssi-sdk/crypto" "github.com/goccy/go-json" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/tbd54566975/ssi-service/config" + "github.com/tbd54566975/ssi-service/internal/keyaccess" "github.com/tbd54566975/ssi-service/pkg/server/router" "github.com/tbd54566975/ssi-service/pkg/service/presentation" "github.com/tbd54566975/ssi-service/pkg/storage" @@ -40,7 +44,11 @@ func TestPresentationAPI(t *testing.T) { s, err := storage.NewStorage(storage.Bolt) assert.NoError(t, err) - service, err := presentation.NewPresentationService(config.PresentationServiceConfig{}, s) + keyStoreService := testKeyStoreService(t, s) + didService := testDIDService(t, s, keyStoreService) + schemaService := testSchemaService(t, s, keyStoreService, didService) + + service, err := presentation.NewPresentationService(config.PresentationServiceConfig{}, s, didService.GetResolver(), schemaService) assert.NoError(t, err) pRouter, err := router.NewPresentationRouter(service) @@ -136,4 +144,144 @@ func TestPresentationAPI(t *testing.T) { assert.Error(t, pRouter.DeletePresentationDefinition(newRequestContext(), w, req)) w.Flush() }) + + t.Run("well formed submission returns operation", func(t *testing.T) { + + definition := createPresentationDefinition(t, pRouter) + vc := credential.VerifiableCredential{ + Context: []string{credential.VerifiableCredentialsLinkedDataContext}, + ID: "7035a7ec-66c8-4aec-9191-a34e8cf1e82b", + Type: []string{credential.VerifiablePresentationType}, + Issuer: "did:key:z4oJ8bFEFv7E3omhuK5LrAtL29Nmd8heBey9HtJCSvodSb7nrfaMrd6zb7fjYSRxrfSgBSDeM6Bs59KRKFgXSDWJcfcjs", + IssuanceDate: "2022-11-07T21:28:57Z", + ExpirationDate: "2051-10-05T14:48:00.000Z", + CredentialStatus: nil, + CredentialSubject: credential.CredentialSubject{ + "additionalName": "Mclovin", + "dateOfBirth": "1987-01-02", + "familyName": "Andres", + "givenName": "Uribe", + "id": "did:web:andresuribe.com", + }, + CredentialSchema: nil, + RefreshService: nil, + TermsOfUse: nil, + Evidence: nil, + Proof: nil, + } + + signer0 := getTestVectorKey0Signer(t) + vcData, err := signing.SignVerifiableCredentialJWT(signer0, vc) + assert.NoError(t, err) + ps := exchange.PresentationSubmission{ + ID: "a30e3b91-fb77-4d22-95fa-871689c322e2", + DefinitionID: definition.PresentationDefinition.ID, + DescriptorMap: []exchange.SubmissionDescriptor{ + { + ID: "wa_driver_license", + Format: string(exchange.JWTVPTarget), + Path: "$.verifiableCredential[0]", + PathNested: nil, + }, + }, + } + vp := credential.VerifiablePresentation{ + Context: []string{credential.VerifiableCredentialsLinkedDataContext}, + ID: "a9b575c7-bac2-47e7-a925-c432815ebb4c", + Holder: "did:key:z4oJ8eRi73fvkrXBgqTHZRTropESXLc7Vet8XpJrGUSBZAT2UHvQBYpBEPdAUiyKBi2XC2iFjgtn5Gw2Qd4WXHyj1LxjU", + Type: []string{credential.VerifiablePresentationType}, + PresentationSubmission: ps, + VerifiableCredential: []interface{}{keyaccess.JWT(vcData)}, + Proof: nil, + } + + signer1 := getTestVectorKey1Signer(t) + signed, err := signing.SignVerifiablePresentationJWT(signer1, vp) + assert.NoError(t, err) + + request := router.CreateSubmissionRequest{SubmissionJWT: keyaccess.JWT(signed)} + + value := newRequestValue(t, request) + req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/presentations/submissions", value) + w := httptest.NewRecorder() + + err = pRouter.CreateSubmission(newRequestContext(), w, req) + + require.NoError(t, err) + var resp router.Operation + assert.NoError(t, json.NewDecoder(w.Body).Decode(&resp)) + assert.Contains(t, resp.ID, "presentations/submissions/") + assert.False(t, resp.Done) + assert.Zero(t, resp.Result) + }) +} + +func createPresentationDefinition(t *testing.T, pRouter *router.PresentationRouter) router.CreatePresentationDefinitionResponse { + request := router.CreatePresentationDefinitionRequest{ + Name: "name", + Purpose: "purpose", + Format: nil, + SubmissionRequirements: nil, + InputDescriptors: []exchange.InputDescriptor{ + { + ID: "wa_driver_license", + Name: "washington state business license", + Purpose: "some testing stuff", + Format: nil, + Constraints: &exchange.Constraints{ + Fields: []exchange.Field{ + { + ID: "date_of_birth", + Path: []string{ + "$.credentialSubject.dateOfBirth", + "$.credentialSubject.dob", + "$.vc.credentialSubject.dateOfBirth", + "$.vc.credentialSubject.dob", + }, + }, + }, + }, + }, + }, + } + value := newRequestValue(t, request) + req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/presentations/definitions", value) + w := httptest.NewRecorder() + + assert.NoError(t, pRouter.CreatePresentationDefinition(newRequestContext(), w, req)) + var resp router.CreatePresentationDefinitionResponse + assert.NoError(t, json.NewDecoder(w.Body).Decode(&resp)) + return resp +} + +func getTestVectorKey0Signer(t *testing.T) crypto.JWTSigner { + // AKA the issuers key + // AKA did:key:z4oJ8bFEFv7E3omhuK5LrAtL29Nmd8heBey9HtJCSvodSb7nrfaMrd6zb7fjYSRxrfSgBSDeM6Bs59KRKFgXSDWJcfcjs + knownJWK := crypto.PrivateKeyJWK{ + KTY: "EC", + CRV: "P-256", + X: "SVqB4JcUD6lsfvqMr-OKUNUphdNn64Eay60978ZlL74", + Y: "lf0u0pMj4lGAzZix5u4Cm5CMQIgMNpkwy163wtKYVKI", + D: "0g5vAEKzugrXaRbgKG0Tj2qJ5lMP4Bezds1_sTybkfk", + } + + signer, err := crypto.NewJWTSignerFromJWK(knownJWK.KID, knownJWK) + assert.NoError(t, err) + return *signer +} + +func getTestVectorKey1Signer(t *testing.T) crypto.JWTSigner { + // AKA the submitter of the presentation + // AKA did:key:z4oJ8eRi73fvkrXBgqTHZRTropESXLc7Vet8XpJrGUSBZAT2UHvQBYpBEPdAUiyKBi2XC2iFjgtn5Gw2Qd4WXHyj1LxjU + knownJWK := crypto.PrivateKeyJWK{ + KTY: "EC", + CRV: "P-256", + X: "6HEz8SLP7NgHPGp0bElryiD7u3_cO1EmX-ngsV_yLsI", + Y: "QlIYaYyDLxLkybDan9LOSkfGvjzZsrdgAb_nQr_Li5M", + D: "7m6c2Axy9OWi7-d9hFVhmMe22vQTfQDL_pG-3WFsjzc", + } + + signer, err := crypto.NewJWTSignerFromJWK(knownJWK.KID, knownJWK) + assert.NoError(t, err) + return *signer } diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index 0aa39486a..ed449ee3f 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -195,7 +195,7 @@ func testKeyStore(t *testing.T, bolt *storage.BoltDB) (*router.KeyStoreRouter, * return keyStoreRouter, keyStoreService } -func testKeyStoreService(t *testing.T, db *storage.BoltDB) *keystore.Service { +func testKeyStoreService(t *testing.T, db storage.ServiceStorage) *keystore.Service { serviceConfig := config.KeyStoreServiceConfig{ BaseServiceConfig: &config.BaseServiceConfig{Name: "test-keystore"}, ServiceKeyPassword: "test-password", @@ -208,7 +208,7 @@ func testKeyStoreService(t *testing.T, db *storage.BoltDB) *keystore.Service { return keystoreService } -func testDIDService(t *testing.T, bolt *storage.BoltDB, keyStore *keystore.Service) *did.Service { +func testDIDService(t *testing.T, bolt storage.ServiceStorage, keyStore *keystore.Service) *did.Service { serviceConfig := config.DIDServiceConfig{ BaseServiceConfig: &config.BaseServiceConfig{Name: "test-did"}, Methods: []string{"key"}, @@ -232,7 +232,7 @@ func testDIDRouter(t *testing.T, bolt *storage.BoltDB, keyStore *keystore.Servic return didRouter } -func testSchemaService(t *testing.T, bolt *storage.BoltDB, keyStore *keystore.Service, did *did.Service) *schema.Service { +func testSchemaService(t *testing.T, bolt storage.ServiceStorage, keyStore *keystore.Service, did *did.Service) *schema.Service { schemaService, err := schema.NewSchemaService(config.SchemaServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "test-schema"}}, bolt, keyStore, did.GetResolver()) require.NoError(t, err) require.NotEmpty(t, schemaService) @@ -249,7 +249,7 @@ func testSchemaRouter(t *testing.T, bolt *storage.BoltDB, keyStore *keystore.Ser return schemaRouter } -func testCredentialService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service, did *did.Service, schema *schema.Service) *credential.Service { +func testCredentialService(t *testing.T, db storage.ServiceStorage, keyStore *keystore.Service, did *did.Service, schema *schema.Service) *credential.Service { serviceConfig := config.CredentialServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "credential"}} // create a credential service diff --git a/pkg/service/manifest/application.go b/pkg/service/manifest/application.go index edf9ecf55..04b6cf9c7 100644 --- a/pkg/service/manifest/application.go +++ b/pkg/service/manifest/application.go @@ -2,31 +2,18 @@ package manifest import ( "fmt" + "github.com/tbd54566975/ssi-service/pkg/jwt" "strings" "github.com/TBD54566975/ssi-sdk/credential/manifest" errresp "github.com/TBD54566975/ssi-sdk/error" - "github.com/pkg/errors" - - didint "github.com/tbd54566975/ssi-service/internal/did" "github.com/tbd54566975/ssi-service/internal/keyaccess" "github.com/tbd54566975/ssi-service/internal/util" "github.com/tbd54566975/ssi-service/pkg/service/credential" ) func (s Service) verifyApplicationJWT(did string, token keyaccess.JWT) error { - kid, pubKey, err := didint.ResolveKeyForDID(s.didResolver, did) - if err != nil { - return errors.Wrapf(err, "failed to resolve applicant's did: %s", did) - } - verifier, err := keyaccess.NewJWKKeyAccessVerifier(kid, pubKey) - if err != nil { - return util.LoggingErrorMsg(err, "could not create application verifier") - } - if err := verifier.Verify(token); err != nil { - return util.LoggingErrorMsg(err, "could not verify the application's signature") - } - return nil + return jwt.VerifyTokenFromDID(did, token, s.didResolver) } // validateCredentialApplication validates the credential application's signature(s) in addition to making sure it diff --git a/pkg/service/operation/model.go b/pkg/service/operation/model.go new file mode 100644 index 000000000..4dc1f35cb --- /dev/null +++ b/pkg/service/operation/model.go @@ -0,0 +1,12 @@ +package operation + +type Result struct { + Error string `json:"error,omitempty"` + Response interface{} `json:"response,omitempty"` +} + +type Operation struct { + ID string `json:"json"` + Done bool `json:"done"` + Result Result `json:"result,omitempty"` +} diff --git a/pkg/service/operation/storage/bolt.go b/pkg/service/operation/storage/bolt.go index 0198cfaee..b74d0caa0 100644 --- a/pkg/service/operation/storage/bolt.go +++ b/pkg/service/operation/storage/bolt.go @@ -1,32 +1,69 @@ package storage import ( + "github.com/goccy/go-json" "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/tbd54566975/ssi-service/internal/util" "github.com/tbd54566975/ssi-service/pkg/storage" ) +const ( + namespace = "operation" +) + type BoltOperationStorage struct { db *storage.BoltDB } func (b BoltOperationStorage) StoreOperation(op StoredOperation) error { - // TODO(andres) implement me - panic("implement me") + id := op.ID + if id == "" { + return util.LoggingNewError("ID is required for storing operations") + } + jsonBytes, err := json.Marshal(op) + if err != nil { + return util.LoggingErrorMsgf(err, "marshalling operation with id: %s", id) + } + return b.db.Write(namespace, id, jsonBytes) } func (b BoltOperationStorage) GetOperation(id string) (*StoredOperation, error) { - // TODO(andres) implement me - panic("implement me") + jsonBytes, err := b.db.Read(namespace, id) + if err != nil { + return nil, util.LoggingErrorMsgf(err, "reading operation with id: %s", id) + } + if len(jsonBytes) == 0 { + return nil, util.LoggingNewErrorf("operation not found with id: %s", id) + } + var stored StoredOperation + if err := json.Unmarshal(jsonBytes, &stored); err != nil { + return nil, util.LoggingErrorMsgf(err, "unmarshalling stored operation: %s", id) + } + return &stored, nil } func (b BoltOperationStorage) GetOperations() ([]StoredOperation, error) { - // TODO(andres) implement me - panic("implement me") + operations, err := b.db.ReadAll(namespace) + if err != nil { + return nil, util.LoggingErrorMsgf(err, "could not get all operations") + } + stored := make([]StoredOperation, 0, len(operations)) + for i, manifestBytes := range operations { + var nextOp StoredOperation + if err = json.Unmarshal(manifestBytes, &nextOp); err != nil { + logrus.WithError(err).WithField("idx", i).Warnf("Skipping operation") + } + stored = append(stored, nextOp) + } + return stored, nil } func (b BoltOperationStorage) DeleteOperation(id string) error { - // TODO(andres) implement me - panic("implement me") + if err := b.db.Delete(namespace, id); err != nil { + return util.LoggingErrorMsgf(err, "deleting operation: %s", id) + } + return nil } func NewBoltOperationStorage(db *storage.BoltDB) (*BoltOperationStorage, error) { diff --git a/pkg/service/presentation/model.go b/pkg/service/presentation/model.go index 784effa94..9d540bc98 100644 --- a/pkg/service/presentation/model.go +++ b/pkg/service/presentation/model.go @@ -1,8 +1,11 @@ package presentation import ( + credsdk "github.com/TBD54566975/ssi-sdk/credential" "github.com/TBD54566975/ssi-sdk/credential/exchange" "github.com/TBD54566975/ssi-sdk/util" + "github.com/tbd54566975/ssi-service/internal/credential" + "github.com/tbd54566975/ssi-service/internal/keyaccess" ) type CreatePresentationDefinitionRequest struct { @@ -31,7 +34,10 @@ type DeletePresentationDefinitionRequest struct { } type CreateSubmissionRequest struct { - Submission exchange.PresentationSubmission `json:"submission" validate:"required"` + Presentation credsdk.VerifiablePresentation `json:"presentation" validate:"required"` + SubmissionJWT keyaccess.JWT `json:"submissionJwt,omitempty" validate:"required"` + Submission exchange.PresentationSubmission `json:"submission" validate:"required"` + Credentials []credential.Container `json:"credentials,omitempty"` } func (csr CreateSubmissionRequest) IsValid() bool { diff --git a/pkg/service/presentation/service.go b/pkg/service/presentation/service.go index ce6875fd3..118103104 100644 --- a/pkg/service/presentation/service.go +++ b/pkg/service/presentation/service.go @@ -3,19 +3,30 @@ package presentation import ( "fmt" "github.com/TBD54566975/ssi-sdk/credential/exchange" + "github.com/TBD54566975/ssi-sdk/credential/signing" + didsdk "github.com/TBD54566975/ssi-sdk/did" sdkutil "github.com/TBD54566975/ssi-sdk/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/tbd54566975/ssi-service/config" + "github.com/tbd54566975/ssi-service/internal/credential" "github.com/tbd54566975/ssi-service/internal/util" + "github.com/tbd54566975/ssi-service/pkg/jwt" "github.com/tbd54566975/ssi-service/pkg/service/framework" + "github.com/tbd54566975/ssi-service/pkg/service/operation" + opstorage "github.com/tbd54566975/ssi-service/pkg/service/operation/storage" presentationstorage "github.com/tbd54566975/ssi-service/pkg/service/presentation/storage" + "github.com/tbd54566975/ssi-service/pkg/service/schema" "github.com/tbd54566975/ssi-service/pkg/storage" ) type Service struct { - storage presentationstorage.Storage - config config.PresentationServiceConfig + storage presentationstorage.Storage + opsStorage opstorage.Storage + config config.PresentationServiceConfig + resolver *didsdk.Resolver + schema *schema.Service + verifier *credential.Verifier } func (s Service) Type() framework.Type { @@ -40,14 +51,26 @@ func (s Service) Config() config.PresentationServiceConfig { return s.config } -func NewPresentationService(config config.PresentationServiceConfig, s storage.ServiceStorage) (*Service, error) { +func NewPresentationService(config config.PresentationServiceConfig, s storage.ServiceStorage, resolver *didsdk.Resolver, schema *schema.Service) (*Service, error) { presentationStorage, err := presentationstorage.NewPresentationStorage(s) if err != nil { return nil, util.LoggingErrorMsg(err, "could not instantiate definition storage for the presentation service") } + opsStorage, err := opstorage.NewOperationStorage(s) + if err != nil { + return nil, util.LoggingErrorMsg(err, "could not instantiate storage for the operations") + } + verifier, err := credential.NewCredentialVerifier(resolver, schema) + if err != nil { + return nil, util.LoggingErrorMsg(err, "could not instantiate verifier") + } service := Service{ - storage: presentationStorage, - config: config, + storage: presentationStorage, + opsStorage: opsStorage, + config: config, + resolver: resolver, + schema: schema, + verifier: verifier, } if !service.Status().IsReady() { return nil, errors.New(service.Status().Message) @@ -104,25 +127,72 @@ func (s Service) DeletePresentationDefinition(request DeletePresentationDefiniti // CreateSubmission houses the main service logic for presentation submission creation. It validates the input, and // produces a presentation submission value that conforms with the Submission specification. -func (s Service) CreateSubmission(request CreateSubmissionRequest) (*CreateSubmissionResponse, error) { - logrus.Debugf("creating presentation submission: %+v", request) - +func (s Service) CreateSubmission(request CreateSubmissionRequest) (*operation.Operation, error) { if !request.IsValid() { - return nil, util.LoggingNewErrorf("invalid create presentation submission request: %+v", request) + return nil, errors.Errorf("invalid create presentation submission request: %+v", request) } if err := exchange.IsValidPresentationSubmission(request.Submission); err != nil { - return nil, util.LoggingErrorMsg(err, "provided value is not a valid presentation submission") + return nil, errors.Wrap(err, "provided value is not a valid presentation submission") + } + + sdkVP, err := signing.ParseVerifiablePresentationFromJWT(request.SubmissionJWT.String()) + if err != nil { + return nil, errors.Wrap(err, "parsing vp from jwt") + } + if err := jwt.VerifyTokenFromDID(sdkVP.Holder, request.SubmissionJWT, s.resolver); err != nil { + return nil, errors.Wrap(err, "verifying token from did") + } + + if _, err := s.storage.GetSubmission(request.Submission.ID); !errors.Is(err, presentationstorage.ErrSubmissionNotFound) { + return nil, errors.Errorf("submission with id %s already present", request.Submission.ID) + } + + definition, err := s.storage.GetPresentation(request.Submission.DefinitionID) + if err != nil { + return nil, errors.Wrap(err, "getting presentation definition") + } + + for _, cred := range request.Credentials { + if !cred.IsValid() { + return nil, errors.Errorf("invalid credential %+v", cred) + } + if cred.CredentialJWT != nil { + if err := s.verifier.VerifyJWTCredential(*cred.CredentialJWT); err != nil { + return nil, errors.Wrapf(err, "verifying jwt credential %s", cred.CredentialJWT) + } + } else { + if cred.Credential != nil && cred.Credential.Proof != nil { + if err := s.verifier.VerifyDataIntegrityCredential(*cred.Credential); err != nil { + return nil, errors.Wrapf(err, "verifying data integrity credential %+v", cred.Credential) + } + } + } + } + + if err := exchange.VerifyPresentationSubmissionVP(definition.PresentationDefinition, request.Presentation); err != nil { + return nil, errors.Wrap(err, "verifying presentation submission vp") } storedSubmission := presentationstorage.StoredSubmission{Submission: request.Submission} + // TODO(andres): IO requests should be done in parallel, once we have context wired up. if err := s.storage.StoreSubmission(storedSubmission); err != nil { - return nil, util.LoggingErrorMsg(err, "could not store presentation") + return nil, errors.Wrap(err, "could not store presentation") + } + + opID := fmt.Sprintf("presentations/submissions/%s", storedSubmission.Submission.ID) + storedOp := opstorage.StoredOperation{ + ID: opID, + Done: false, + } + if err := s.opsStorage.StoreOperation(storedOp); err != nil { + return nil, errors.Wrap(err, "could not store operation") } - return &CreateSubmissionResponse{ - Submission: storedSubmission.Submission, + return &operation.Operation{ + ID: storedOp.ID, + Done: false, }, nil } diff --git a/pkg/service/presentation/storage/bolt.go b/pkg/service/presentation/storage/bolt.go index 2c1f88371..2c6b7c7e1 100644 --- a/pkg/service/presentation/storage/bolt.go +++ b/pkg/service/presentation/storage/bolt.go @@ -85,7 +85,7 @@ func (b BoltPresentationStorage) GetSubmission(id string) (*StoredSubmission, er return nil, util.LoggingNewErrorf("could not get submission definition: %s", id) } if len(jsonBytes) == 0 { - return nil, util.LoggingNewErrorf("submission definition not found with id: %s", id) + return nil, util.LoggingErrorMsgf(ErrSubmissionNotFound, "submission not found with id: %s", id) } var stored StoredSubmission if err := json.Unmarshal(jsonBytes, &stored); err != nil { diff --git a/pkg/service/presentation/storage/storage.go b/pkg/service/presentation/storage/storage.go index 28f02053b..fb49eef8a 100644 --- a/pkg/service/presentation/storage/storage.go +++ b/pkg/service/presentation/storage/storage.go @@ -2,6 +2,7 @@ package storage import ( "github.com/TBD54566975/ssi-sdk/credential/exchange" + "github.com/pkg/errors" "github.com/tbd54566975/ssi-service/internal/util" "github.com/tbd54566975/ssi-service/pkg/storage" ) @@ -48,3 +49,5 @@ type SubmissionStorage interface { StoreSubmission(schema StoredSubmission) error GetSubmission(id string) (*StoredSubmission, error) } + +var ErrSubmissionNotFound = errors.New("submission not found") diff --git a/pkg/service/service.go b/pkg/service/service.go index 4b2e25d72..15247a675 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -97,7 +97,7 @@ func instantiateServices(config config.ServicesConfig) ([]framework.Service, err return nil, util.LoggingErrorMsg(err, "could not instantiate the manifest service") } - presentationService, err := presentation.NewPresentationService(config.PresentationConfig, storageProvider) + presentationService, err := presentation.NewPresentationService(config.PresentationConfig, storageProvider, didResolver, schemaService) if err != nil { return nil, util.LoggingErrorMsg(err, "could not instantiate the presentation service") } diff --git a/sip/sips/sip6/README.md b/sip/sips/sip6/README.md index b2429617b..7432f50c5 100644 --- a/sip/sips/sip6/README.md +++ b/sip/sips/sip6/README.md @@ -184,7 +184,7 @@ A `Submission` object: { "iss": "did:web:andresuribe.com", "vp": { - "presentation_submission": { + "presentation_submission": { "id": "a30e3b91-fb77-4d22-95fa-871689c322e2", "definition_id": "32f54163-7166-48f1-93d8-ff217bdb0653", "descriptor_map": [