From 1d1e93a20246b8c566d45a9a28a6184673d55637 Mon Sep 17 00:00:00 2001 From: asraa <asraa@google.com> Date: Mon, 24 May 2021 09:03:11 -0400 Subject: [PATCH] Implement RFC 3161 timestamp responses (#293) * Add timestamps Signed-off-by: Asra Ali <asraa@google.com> * change Signed-off-by: Asra Ali <asraa@google.com> * address comments Signed-off-by: Asra Ali <asraa@google.com> * address comments Signed-off-by: Asra Ali <asraa@google.com> * fix binary writer Signed-off-by: Asra Ali <asraa@google.com> * add tsa Signed-off-by: Asra Ali <asraa@google.com> * distangle cert chain creation from new signer Signed-off-by: Asra Ali <asraa@google.com> * revert some now unncessary changes Signed-off-by: Asra Ali <asraa@google.com> * cert chain 404 Signed-off-by: Asra Ali <asraa@google.com> * fix Signed-off-by: Asra Ali <asraa@google.com> --- cmd/rekor-cli/app/root.go | 3 + cmd/rekor-cli/app/timestamp.go | 252 ++++++++++++++++++ cmd/rekor-cli/app/timestamp_test.go | 130 +++++++++ cmd/rekor-server/app/root.go | 1 + go.mod | 5 +- go.sum | 20 +- openapi.yaml | 52 ++++ pkg/api/api.go | 54 +++- pkg/api/error.go | 42 ++- pkg/api/timestamp.go | 81 ++++++ .../get_timestamp_cert_chain_responses.go | 129 +++++++++ pkg/generated/client/rekor_client.go | 5 + .../get_timestamp_cert_chain_parameters.go | 142 ++++++++++ .../get_timestamp_cert_chain_responses.go | 156 +++++++++++ .../get_timestamp_response_parameters.go | 165 ++++++++++++ .../get_timestamp_response_responses.go | 198 ++++++++++++++ .../client/timestamp/timestamp_client.go | 135 ++++++++++ .../timestamp_response_parameters.go | 164 ++++++++++++ .../timestamp/timestamp_response_responses.go | 169 ++++++++++++ pkg/generated/models/timestamp_request.go | 67 +++++ pkg/generated/models/timestamp_response.go | 67 +++++ .../restapi/configure_rekor_server.go | 9 + pkg/generated/restapi/doc.go | 3 + pkg/generated/restapi/embedded_spec.go | 157 +++++++++++ .../pubkey/get_timestamp_cert_chain.go | 74 +++++ .../get_timestamp_cert_chain_parameters.go | 62 +++++ .../get_timestamp_cert_chain_responses.go | 130 +++++++++ .../get_timestamp_cert_chain_urlbuilder.go | 100 +++++++ .../restapi/operations/rekor_server_api.go | 58 ++++ .../timestamp/get_timestamp_cert_chain.go | 74 +++++ .../get_timestamp_cert_chain_parameters.go | 62 +++++ .../get_timestamp_cert_chain_responses.go | 154 +++++++++++ .../get_timestamp_cert_chain_urlbuilder.go | 100 +++++++ .../timestamp/get_timestamp_response.go | 72 +++++ .../get_timestamp_response_parameters.go | 75 ++++++ .../get_timestamp_response_responses.go | 199 ++++++++++++++ .../get_timestamp_response_urlbuilder.go | 100 +++++++ .../timestamp/timestamp_response.go | 72 +++++ .../timestamp_response_parameters.go | 101 +++++++ .../timestamp/timestamp_response_responses.go | 176 ++++++++++++ .../timestamp_response_urlbuilder.go | 100 +++++++ pkg/pki/x509/x509.go | 45 ++++ pkg/pki/x509/x509_test.go | 44 +++ pkg/signer/memory.go | 71 +++++ pkg/signer/memory_test.go | 26 ++ pkg/util/rfc3161.go | 242 +++++++++++++++++ pkg/util/rfc3161_test.go | 164 ++++++++++++ tests/e2e_test.go | 95 +++++++ tests/test_request.tsq | Bin 0 -> 59 bytes 49 files changed, 4573 insertions(+), 29 deletions(-) create mode 100644 cmd/rekor-cli/app/timestamp.go create mode 100644 cmd/rekor-cli/app/timestamp_test.go create mode 100644 pkg/api/timestamp.go create mode 100644 pkg/generated/client/pubkey/get_timestamp_cert_chain_responses.go create mode 100644 pkg/generated/client/timestamp/get_timestamp_cert_chain_parameters.go create mode 100644 pkg/generated/client/timestamp/get_timestamp_cert_chain_responses.go create mode 100644 pkg/generated/client/timestamp/get_timestamp_response_parameters.go create mode 100644 pkg/generated/client/timestamp/get_timestamp_response_responses.go create mode 100644 pkg/generated/client/timestamp/timestamp_client.go create mode 100644 pkg/generated/client/timestamp/timestamp_response_parameters.go create mode 100644 pkg/generated/client/timestamp/timestamp_response_responses.go create mode 100644 pkg/generated/models/timestamp_request.go create mode 100644 pkg/generated/models/timestamp_response.go create mode 100644 pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain.go create mode 100644 pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain_parameters.go create mode 100644 pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain_responses.go create mode 100644 pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain_urlbuilder.go create mode 100644 pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain.go create mode 100644 pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain_parameters.go create mode 100644 pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain_responses.go create mode 100644 pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain_urlbuilder.go create mode 100644 pkg/generated/restapi/operations/timestamp/get_timestamp_response.go create mode 100644 pkg/generated/restapi/operations/timestamp/get_timestamp_response_parameters.go create mode 100644 pkg/generated/restapi/operations/timestamp/get_timestamp_response_responses.go create mode 100644 pkg/generated/restapi/operations/timestamp/get_timestamp_response_urlbuilder.go create mode 100644 pkg/generated/restapi/operations/timestamp/timestamp_response.go create mode 100644 pkg/generated/restapi/operations/timestamp/timestamp_response_parameters.go create mode 100644 pkg/generated/restapi/operations/timestamp/timestamp_response_responses.go create mode 100644 pkg/generated/restapi/operations/timestamp/timestamp_response_urlbuilder.go create mode 100644 pkg/util/rfc3161.go create mode 100644 pkg/util/rfc3161_test.go create mode 100644 tests/test_request.tsq diff --git a/cmd/rekor-cli/app/root.go b/cmd/rekor-cli/app/root.go index e6fa4eb..c9fae09 100644 --- a/cmd/rekor-cli/app/root.go +++ b/cmd/rekor-cli/app/root.go @@ -123,7 +123,10 @@ func GetRekorClient(rekorServerURL string) (*client.Rekor, error) { rt := httptransport.New(url.Host, client.DefaultBasePath, []string{url.Scheme}) rt.Consumers["application/yaml"] = util.YamlConsumer() rt.Consumers["application/x-pem-file"] = runtime.TextConsumer() + rt.Consumers["application/pem-certificate-chain"] = runtime.TextConsumer() rt.Producers["application/yaml"] = util.YamlProducer() + rt.Producers["application/timestamp-query"] = runtime.ByteStreamProducer() + rt.Consumers["application/timestamp-reply"] = runtime.ByteStreamConsumer() if viper.GetString("api-key") != "" { rt.DefaultAuthentication = httptransport.APIKeyAuth("apiKey", "query", viper.GetString("api-key")) diff --git a/cmd/rekor-cli/app/timestamp.go b/cmd/rekor-cli/app/timestamp.go new file mode 100644 index 0000000..138ba12 --- /dev/null +++ b/cmd/rekor-cli/app/timestamp.go @@ -0,0 +1,252 @@ +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "bytes" + "crypto" + "encoding/asn1" + "encoding/hex" + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/sassoftware/relic/lib/pkcs9" + "github.com/sassoftware/relic/lib/x509tools" + "github.com/sigstore/rekor/cmd/rekor-cli/app/format" + "github.com/sigstore/rekor/pkg/generated/client/timestamp" + "github.com/sigstore/rekor/pkg/log" + "github.com/sigstore/rekor/pkg/util" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +type fileFlag struct { + value string +} + +func (f *fileFlag) String() string { + return f.value +} + +func (f *fileFlag) Set(s string) error { + if s == "" { + return errors.New("flag must be specified") + } + if _, err := os.Stat(filepath.Clean(s)); os.IsNotExist(err) { + return err + } + f.value = s + return nil +} + +func (f *fileFlag) Type() string { + return "fileFlag" +} + +type oidFlag struct { + value asn1.ObjectIdentifier +} + +func (f *oidFlag) String() string { + return f.value.String() +} + +func (f *oidFlag) Set(s string) error { + parts := strings.Split(s, ".") + f.value = make(asn1.ObjectIdentifier, len(parts)) + for i, part := range parts { + num, err := strconv.Atoi(part) + if err != nil { + return errors.New("error parsing OID") + } + f.value[i] = num + } + return nil +} + +func (f *oidFlag) Type() string { + return "oidFlag" +} + +func addTimestampFlags(cmd *cobra.Command) error { + cmd.Flags().Var(&fileFlag{}, "artifact", "path to an artifact to timestamp") + cmd.Flags().Var(&uuidFlag{}, "artifact-hash", "hex encoded SHA256 hash of the the artifact to timestamp") + cmd.Flags().Bool("nonce", true, "specify a pseudo-random nonce in the request") + cmd.Flags().Var(&oidFlag{}, "tsa-policy", "optional dotted OID notation for the policy that the TSA should use to create the response") + + cmd.Flags().String("out", "response.tsr", "path to a file to write response.") + + // TODO: Add a flag to upload a pre-formed timestamp response to the log. + // TODO: Add a flag to indicate a JSON formatted timestamp request/response. + return nil +} + +func validateTimestampFlags() error { + artifactStr := viper.GetString("artifact") + digestStr := viper.GetString("artifact-hash") + + if artifactStr == "" && digestStr == "" { + return errors.New("artifact or hash to timestamp must be specified") + } + + if digestStr != "" { + digest := uuidFlag{} + if err := digest.Set(digestStr); err != nil { + return err + } + } + + policyStr := viper.GetString("tsa-policy") + if policyStr != "" { + oid := oidFlag{} + if err := oid.Set(policyStr); err != nil { + return err + } + } + + return nil +} + +func createRequestFromFlags() (*pkcs9.TimeStampReq, error) { + var timestampReq *pkcs9.TimeStampReq + digestStr := viper.GetString("artifact-hash") + policyStr := viper.GetString("tsa-policy") + + opts := util.TimestampRequestOptions{ + // Always use a SHA256 right now. + Hash: crypto.SHA256, + } + if policyStr != "" { + oid := oidFlag{} + if err := oid.Set(policyStr); err != nil { + return nil, err + } + opts.TSAPolicyOid = oid.value + } + if viper.GetBool("nonce") { + opts.Nonce = x509tools.MakeSerial() + } + + var digest []byte + if digestStr != "" { + decoded, err := hex.DecodeString(digestStr) + if err != nil { + return nil, err + } + digest = decoded + } + if digestStr == "" { + artifactStr := viper.GetString("artifact") + artifactBytes, err := ioutil.ReadFile(filepath.Clean(artifactStr)) + if err != nil { + return nil, fmt.Errorf("error reading request from file: %w", err) + } + h := opts.Hash.New() + if _, err := h.Write(artifactBytes); err != nil { + return nil, err + } + digest = h.Sum(nil) + } + + timestampReq, err := util.TimestampRequestFromDigest(digest, opts) + if err != nil { + return nil, fmt.Errorf("error creating timestamp request: %w", err) + } + + return timestampReq, nil +} + +type timestampCmdOutput struct { + Location string +} + +func (t *timestampCmdOutput) String() string { + return fmt.Sprintf(` +Wrote response to: %v +`, t.Location) +} + +var timestampCmd = &cobra.Command{ + Use: "timestamp", + Short: "Rekor timestamp command", + Long: "Generates and uploads (WIP) an RFC 3161 timestamp response to the log. The timestamp response can be verified locally using Rekor's timestamping cert chain.", + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := viper.BindPFlags(cmd.Flags()); err != nil { + log.Logger.Fatal("Error initializing cmd line args: ", err) + } + if err := validateTimestampFlags(); err != nil { + log.Logger.Error(err) + _ = cmd.Help() + return err + } + return nil + }, + Run: format.WrapCmd(func(args []string) (interface{}, error) { + rekorClient, err := GetRekorClient(viper.GetString("rekor_server")) + if err != nil { + return nil, err + } + + timestampReq, err := createRequestFromFlags() + if err != nil { + return nil, err + } + requestBytes, err := asn1.Marshal(*timestampReq) + if err != nil { + return nil, err + } + + params := timestamp.NewGetTimestampResponseParams() + params.Request = ioutil.NopCloser(bytes.NewReader(requestBytes)) + + var respBytes bytes.Buffer + _, err = rekorClient.Timestamp.GetTimestampResponse(params, &respBytes) + if err != nil { + return nil, err + } + // Sanity check response and check if the TimeStampToken was successfully created + if _, err = timestampReq.ParseResponse(respBytes.Bytes()); err != nil { + return nil, err + } + + // Write response to file + outStr := viper.GetString("out") + if outStr == "" { + outStr = "response.tsr" + } + if err := ioutil.WriteFile(outStr, respBytes.Bytes(), 0600); err != nil { + return nil, err + } + + // TODO: Add log index after support for uploading to transparency log is added. + return ×tampCmdOutput{ + Location: outStr, + }, nil + }), +} + +func init() { + if err := addTimestampFlags(timestampCmd); err != nil { + log.Logger.Fatal("Error parsing cmd line args: ", err) + } + + rootCmd.AddCommand(timestampCmd) +} diff --git a/cmd/rekor-cli/app/timestamp_test.go b/cmd/rekor-cli/app/timestamp_test.go new file mode 100644 index 0000000..957809f --- /dev/null +++ b/cmd/rekor-cli/app/timestamp_test.go @@ -0,0 +1,130 @@ +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "testing" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func TestTimestampFlags(t *testing.T) { + type test struct { + caseDesc string + artifact string + artifactHash string + oid string + expectParseSuccess bool + expectValidateSuccess bool + expectRequestSuccess bool + } + + tests := []test{ + { + caseDesc: "valid local artifact", + artifact: "../../../tests/test_file.txt", + expectParseSuccess: true, + expectValidateSuccess: true, + expectRequestSuccess: true, + }, + { + caseDesc: "nonexistant local artifact", + artifact: "../../../tests/not_a_file", + expectParseSuccess: false, + expectValidateSuccess: false, + expectRequestSuccess: false, + }, + { + caseDesc: "valid artifact hash", + artifactHash: "45c7b11fcbf07dec1694adecd8c5b85770a12a6c8dfdcf2580a2db0c47c31779", + expectParseSuccess: true, + expectValidateSuccess: true, + expectRequestSuccess: true, + }, + { + caseDesc: "invalid artifact hash", + artifactHash: "aaa", + expectParseSuccess: false, + expectValidateSuccess: false, + expectRequestSuccess: false, + }, + { + caseDesc: "nonexistant request artifact", + artifact: "../../../tests/not_a_request", + expectParseSuccess: false, + expectValidateSuccess: false, + expectRequestSuccess: false, + }, + { + caseDesc: "valid oid", + artifact: "../../../tests/test_file.txt", + oid: "1.2.3.4", + expectParseSuccess: true, + expectValidateSuccess: true, + expectRequestSuccess: true, + }, + { + caseDesc: "invalid oid", + artifact: "../../../tests/test_file.txt", + oid: "1.a.3.4", + expectParseSuccess: false, + expectValidateSuccess: true, + expectRequestSuccess: true, + }, + { + caseDesc: "no request or artifact specified", + expectParseSuccess: true, + expectValidateSuccess: false, + expectRequestSuccess: false, + }, + } + + for _, tc := range tests { + var blankCmd = &cobra.Command{} + if err := addTimestampFlags(blankCmd); err != nil { + t.Fatalf("unexpected error adding flags in '%v': %v", tc.caseDesc, err) + } + + args := []string{} + + if tc.artifact != "" { + args = append(args, "--artifact", tc.artifact) + } + if tc.artifactHash != "" { + args = append(args, "--artifact-hash", tc.artifactHash) + } + if tc.oid != "" { + args = append(args, "--tsa-policy", tc.oid) + } + if err := blankCmd.ParseFlags(args); (err == nil) != tc.expectParseSuccess { + t.Errorf("unexpected result parsing '%v': %v", tc.caseDesc, err) + continue + } + + if err := viper.BindPFlags(blankCmd.Flags()); err != nil { + t.Fatalf("unexpected result initializing viper in '%v': %v", tc.caseDesc, err) + } + if err := validateTimestampFlags(); (err == nil) != tc.expectValidateSuccess { + t.Errorf("unexpected result validating '%v': %v", tc.caseDesc, err) + continue + } + if _, err := createRequestFromFlags(); (err == nil) != tc.expectRequestSuccess { + t.Errorf("unexpected result creating timestamp request '%v': %v", tc.caseDesc, err) + continue + } + } +} diff --git a/cmd/rekor-server/app/root.go b/cmd/rekor-server/app/root.go index 531bf11..c8f01ac 100644 --- a/cmd/rekor-server/app/root.go +++ b/cmd/rekor-server/app/root.go @@ -61,6 +61,7 @@ func init() { rootCmd.PersistentFlags().Uint("trillian_log_server.tlog_id", 0, "Trillian tree id") rootCmd.PersistentFlags().String("rekor_server.address", "127.0.0.1", "Address to bind to") rootCmd.PersistentFlags().String("rekor_server.signer", "memory", "Rekor signer to use. Current valid options include: [gcpkms, memory]") + rootCmd.PersistentFlags().String("rekor_server.timestamp_chain", "", "PEM encoded cert chain to use for timestamping") rootCmd.PersistentFlags().Uint16("rekor_server.port", 3000, "Port to bind to") diff --git a/go.mod b/go.mod index 78c4f1c..a3f6580 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,6 @@ require ( github.com/go-playground/validator v9.31.0+incompatible github.com/google/rpmpack v0.0.0-20210107155803-d6befbf05148 github.com/google/trillian v1.3.14-0.20210413093047-5e12fb368c8f - github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c // indirect github.com/jedisct1/go-minisign v0.0.0-20210106175330-e54e81d562c7 github.com/mediocregopher/radix/v4 v4.0.0-beta.1 github.com/mitchellh/go-homedir v1.1.0 @@ -27,7 +26,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.10.0 github.com/rs/cors v1.7.0 - github.com/sassoftware/relic v7.2.1+incompatible + github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74 github.com/sigstore/sigstore v0.0.0-20210415112811-cb2061113e4a github.com/spf13/cobra v1.1.3 github.com/spf13/pflag v1.0.5 @@ -48,3 +47,5 @@ require ( gopkg.in/go-playground/assert.v1 v1.2.1 // indirect honnef.co/go/tools v0.0.1-2020.1.6 // indirect ) + +// replace github.com/sassoftware/relic => ../relic-7.2.6 diff --git a/go.sum b/go.sum index 117fba2..c767873 100644 --- a/go.sum +++ b/go.sum @@ -173,6 +173,7 @@ github.com/aws/aws-sdk-go v1.38.35 h1:7AlAO0FC+8nFjxiGKEmq0QLpiA8/XFr6eIxgRTwkdT github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= +github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -182,6 +183,7 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -237,6 +239,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberphone/json-canonicalization v0.0.0-20210303052042-6bc126869bf4 h1:7AjYfmq7AmviXsuZjV5DcE7PuhJ4dWMi8gLllpLVDQY= github.com/cyberphone/json-canonicalization v0.0.0-20210303052042-6bc126869bf4/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= +github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= github.com/danieljoos/wincred v1.1.0 h1:3RNcEpBg4IhIChZdFRSdlQt1QjCp1sMAPIrOnm7Yf8g= github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -477,6 +480,8 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4= +github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -527,6 +532,7 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= @@ -751,6 +757,7 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx github.com/letsencrypt/pkcs11key v2.0.1-0.20170608213348-396559074696+incompatible/go.mod h1:iGYXKqDXt0cpBthCHdr9ZdsQwyGlYFh/+8xa4WzIQ34= github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -955,6 +962,7 @@ github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in github.com/pseudomuto/protoc-gen-doc v1.4.1/go.mod h1:exDTOVwqpp30eV/EDPFLZy3Pwr2sn6hBC1WIYH/UbIg= github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= +github.com/qur/ar v0.0.0-20130629153254-282534b91770/go.mod h1:SjlYv2m9lpV0UW6K7lDqVJwEIIvSjaHbGk7nIfY8Hxw= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -971,8 +979,10 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I= -github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= -github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= +github.com/sassoftware/go-rpmutils v0.1.1/go.mod h1:euhXULoBpvAxqrBHEyJS4Tsu3hHxUmQWNymxoJbzgUY= +github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74 h1:sUNzanSKA9z/h8xXl+ZJoxIYZL0Qx306MmxqRrvUgr0= +github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74/go.mod h1:YlB8wFIZmFLZ1JllNBfSURzz52fBxbliNgYALk1UDmk= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/ksuid v1.0.3/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= @@ -1038,6 +1048,7 @@ github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1101,6 +1112,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zalando/go-keyring v0.1.0/go.mod h1:RaxNwUITJaHVdQ0VC7pELPZ3tOWn13nr0gZMZEhpVU0= github.com/zalando/go-keyring v0.1.1 h1:w2V9lcx/Uj4l+dzAf1m9s+DJ1O8ROkEHnynonHjTcYE= github.com/zalando/go-keyring v0.1.1/go.mod h1:OIC+OZ28XbmwFxU/Rp9V7eKzZjamBJwRzC8UFJH9+L8= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1183,8 +1195,10 @@ golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200930160638-afb6bcd081ae/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o= @@ -1275,6 +1289,7 @@ golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -1315,6 +1330,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200930132711-30421366ff76/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= diff --git a/openapi.yaml b/openapi.yaml index e88ca63..5953bbe 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -224,6 +224,56 @@ paths: $ref: '#/responses/BadContent' default: $ref: '#/responses/InternalServerError' + + /api/v1/timestamp: + post: + # TODO: Add uploads to transparency log. + summary: Returns a timestamp response generated by Rekor + operationId: getTimestampResponse + tags: + - timestamp + consumes: + - application/timestamp-query + produces: + - application/timestamp-reply + parameters: + - in: body + name: request + required: true + schema: + type: string + format: binary + responses: + 200: + description: Returns a timestamp response + schema: + type: string + format: binary + 400: + $ref: '#/responses/BadContent' + 501: + $ref: '#/responses/NotImplemented' + default: + $ref: '#/responses/InternalServerError' + + /api/v1/timestamp/certchain: + get: + summary: Retrieve the certfiicate chain for timestamping that can be used to validate trusted timestamps + description: Returns the certfiicate chain for timestamping that can be used to validate trusted timestamps + operationId: getTimestampCertChain + tags: + - timestamp + produces: + - application/pem-certificate-chain + responses: + 200: + description: The PEM encoded cert chain + schema: + type: string + 404: + $ref: '#/responses/NotFound' + default: + $ref: '#/responses/InternalServerError' definitions: ProposedEntry: @@ -471,6 +521,8 @@ responses: format: uri NotFound: description: The content requested could not be found + NotImplemented: + description: The content requested is not implemented InternalServerError: description: There was an internal error in the server while processing the request schema: diff --git a/pkg/api/api.go b/pkg/api/api.go index d1b8fbd..2b2fc32 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -32,6 +32,7 @@ import ( "google.golang.org/grpc" "github.com/sigstore/rekor/pkg/log" + pki "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/signer" "github.com/sigstore/sigstore/pkg/signature" ) @@ -49,12 +50,14 @@ func dial(ctx context.Context, rpcServer string) (*grpc.ClientConn, error) { } type API struct { - logClient trillian.TrillianLogClient - logID int64 - pubkey string // PEM encoded public key - pubkeyHash string // SHA256 hash of DER-encoded public key - signer signature.Signer - verifier *client.LogVerifier + logClient trillian.TrillianLogClient + logID int64 + pubkey string // PEM encoded public key + pubkeyHash string // SHA256 hash of DER-encoded public key + signer signature.Signer + certChain []*x509.Certificate // timestamping cert chain + certChainPem string // PEM encoded timestamping cert chain + verifier *client.LogVerifier } func NewAPI() (*API, error) { @@ -85,11 +88,11 @@ func NewAPI() (*API, error) { return nil, errors.Wrap(err, "get tree") } - signer, err := signer.New(ctx, viper.GetString("rekor_server.signer")) + rekorSigner, err := signer.New(ctx, viper.GetString("rekor_server.signer")) if err != nil { return nil, errors.Wrap(err, "getting new signer") } - pk, err := signer.PublicKey(ctx) + pk, err := rekorSigner.PublicKey(ctx) if err != nil { return nil, errors.Wrap(err, "getting public key") } @@ -113,13 +116,36 @@ func NewAPI() (*API, error) { return nil, errors.Wrap(err, "new verifier") } + var certChain []*x509.Certificate + certChainStr := viper.GetString("rekor_server.timestamp_chain") + if certChainStr != "" { + var err error + if certChain, err = pki.ParseTimestampCertChain([]byte(certChainStr)); err != nil { + return nil, errors.Wrap(err, "parsing timestamp cert chain") + } + } else if viper.GetString("rekor_server.signer") == signer.MemoryScheme { + // Generate a timestaming cert with a self signed CA if we are configured with an in-memory signer. + var err error + certChain, err = signer.NewTimestampingCertWithSelfSignedCA(pk) + if err != nil { + return nil, errors.Wrap(err, "generating timestaping cert chain") + } + } + + certChainPem, err := pki.CertChainToPEM(certChain) + if err != nil { + return nil, errors.Wrap(err, "timestamping cert chain") + } + return &API{ - logClient: logClient, - logID: tLogID, - pubkey: string(pubkey), - pubkeyHash: hex.EncodeToString(pubkeyHashBytes), - signer: signer, - verifier: verifier, + logClient: logClient, + logID: tLogID, + pubkey: string(pubkey), + pubkeyHash: hex.EncodeToString(pubkeyHashBytes), + signer: rekorSigner, + certChain: certChain, + certChainPem: string(certChainPem), + verifier: verifier, }, nil } diff --git a/pkg/api/error.go b/pkg/api/error.go index 1b63af6..c614499 100644 --- a/pkg/api/error.go +++ b/pkg/api/error.go @@ -27,22 +27,24 @@ import ( "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/generated/restapi/operations/entries" "github.com/sigstore/rekor/pkg/generated/restapi/operations/index" + "github.com/sigstore/rekor/pkg/generated/restapi/operations/timestamp" "github.com/sigstore/rekor/pkg/generated/restapi/operations/tlog" "github.com/sigstore/rekor/pkg/log" ) const ( - trillianCommunicationError = "Unexpected error communicating with transparency log" - trillianUnexpectedResult = "Unexpected result from transparency log" - failedToGenerateCanonicalEntry = "Error generating canonicalized entry" - entryAlreadyExists = "An equivalent entry already exists in the transparency log with UUID %v" - firstSizeLessThanLastSize = "firstSize(%d) must be less than lastSize(%d)" - malformedUUID = "UUID must be a 64-character hexadecimal string" - malformedPublicKey = "Public key provided could not be parsed" - failedToGenerateCanonicalKey = "Error generating canonicalized public key" - redisUnexpectedResult = "Unexpected result from searching index" - lastSizeGreaterThanKnown = "The tree size requested(%d) was greater than what is currently observable(%d)" - signingError = "Error signing" + trillianCommunicationError = "Unexpected error communicating with transparency log" + trillianUnexpectedResult = "Unexpected result from transparency log" + failedToGenerateCanonicalEntry = "Error generating canonicalized entry" + entryAlreadyExists = "An equivalent entry already exists in the transparency log with UUID %v" + firstSizeLessThanLastSize = "firstSize(%d) must be less than lastSize(%d)" + malformedUUID = "UUID must be a 64-character hexadecimal string" + malformedPublicKey = "Public key provided could not be parsed" + failedToGenerateCanonicalKey = "Error generating canonicalized public key" + redisUnexpectedResult = "Unexpected result from searching index" + lastSizeGreaterThanKnown = "The tree size requested(%d) was greater than what is currently observable(%d)" + signingError = "Error signing" + failedToGenerateTimestampResponse = "Error generating timestamp response" ) func errorMsg(message string, code int) *models.Error { @@ -138,6 +140,24 @@ func handleRekorAPIError(params interface{}, code int, err error, message string default: return index.NewSearchIndexDefault(code).WithPayload(errorMsg(message, code)) } + case timestamp.GetTimestampResponseParams: + logMsg(params.HTTPRequest) + switch code { + case http.StatusBadRequest: + return timestamp.NewGetTimestampResponseBadRequest().WithPayload(errorMsg(message, code)) + case http.StatusNotImplemented: + return timestamp.NewGetTimestampResponseNotImplemented() + default: + return timestamp.NewGetTimestampResponseDefault(code).WithPayload(errorMsg(message, code)) + } + case timestamp.GetTimestampCertChainParams: + logMsg(params.HTTPRequest) + switch code { + case http.StatusNotFound: + return timestamp.NewGetTimestampCertChainNotFound() + default: + return timestamp.NewGetTimestampCertChainDefault(code).WithPayload(errorMsg(message, code)) + } default: log.Logger.Errorf("unable to find method for type %T; error: %v", params, err) return middleware.Error(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) diff --git a/pkg/api/timestamp.go b/pkg/api/timestamp.go new file mode 100644 index 0000000..b6fa46d --- /dev/null +++ b/pkg/api/timestamp.go @@ -0,0 +1,81 @@ +/* +Copyright The Rekor Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package api + +import ( + "bytes" + "context" + "encoding/asn1" + "io/ioutil" + "net/http" + + "github.com/go-openapi/runtime/middleware" + "github.com/pkg/errors" + "github.com/sassoftware/relic/lib/pkcs9" + "github.com/sigstore/rekor/pkg/generated/restapi/operations/timestamp" + "github.com/sigstore/rekor/pkg/log" + "github.com/sigstore/rekor/pkg/util" +) + +func RequestFromRekor(ctx context.Context, req pkcs9.TimeStampReq) ([]byte, error) { + resp, err := util.CreateRfc3161Response(ctx, req, api.certChain, api.signer) + if err != nil { + return nil, err + } + + body, err := asn1.Marshal(*resp) + if err != nil { + return nil, err + } + + return body, nil +} + +func TimestampResponseHandler(params timestamp.GetTimestampResponseParams) middleware.Responder { + // Fail early if we don't haven't configured rekor with a certificate for timestamping. + if len(api.certChain) == 0 { + return handleRekorAPIError(params, http.StatusNotImplemented, errors.New("rekor is not configured to serve timestamps"), "") + } + + // TODO: Add support for in-house JSON based timestamp response. + requestBytes, err := ioutil.ReadAll(params.Request) + if err != nil { + return handleRekorAPIError(params, http.StatusBadRequest, err, failedToGenerateTimestampResponse) + } + req, err := util.ParseTimestampRequest(requestBytes) + if err != nil { + return handleRekorAPIError(params, http.StatusBadRequest, err, failedToGenerateTimestampResponse) + } + + // Create response + ctx := params.HTTPRequest.Context() + resp, err := RequestFromRekor(ctx, *req) + if err != nil { + return handleRekorAPIError(params, http.StatusInternalServerError, err, failedToGenerateTimestampResponse) + } + + // TODO: Upload to transparency log and add entry UUID to location header. + log.Logger.Errorf("generated OK") + return timestamp.NewGetTimestampResponseOK().WithPayload(ioutil.NopCloser(bytes.NewReader(resp))) +} + +func GetTimestampCertChainHandler(params timestamp.GetTimestampCertChainParams) middleware.Responder { + if len(api.certChain) == 0 { + return handleRekorAPIError(params, http.StatusNotFound, errors.New("rekor is not configured with a timestamping certificate"), "") + } + return timestamp.NewGetTimestampCertChainOK().WithPayload(api.certChainPem) +} diff --git a/pkg/generated/client/pubkey/get_timestamp_cert_chain_responses.go b/pkg/generated/client/pubkey/get_timestamp_cert_chain_responses.go new file mode 100644 index 0000000..b93b7ef --- /dev/null +++ b/pkg/generated/client/pubkey/get_timestamp_cert_chain_responses.go @@ -0,0 +1,129 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package pubkey + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/sigstore/rekor/pkg/generated/models" +) + +// GetTimestampCertChainReader is a Reader for the GetTimestampCertChain structure. +type GetTimestampCertChainReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetTimestampCertChainReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetTimestampCertChainOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewGetTimestampCertChainDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewGetTimestampCertChainOK creates a GetTimestampCertChainOK with default headers values +func NewGetTimestampCertChainOK() *GetTimestampCertChainOK { + return &GetTimestampCertChainOK{} +} + +/* GetTimestampCertChainOK describes a response with status code 200, with default header values. + +The PEM encoded cert chain +*/ +type GetTimestampCertChainOK struct { + Payload string +} + +func (o *GetTimestampCertChainOK) Error() string { + return fmt.Sprintf("[GET /api/v1/log/timestampCertChain][%d] getTimestampCertChainOK %+v", 200, o.Payload) +} +func (o *GetTimestampCertChainOK) GetPayload() string { + return o.Payload +} + +func (o *GetTimestampCertChainOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetTimestampCertChainDefault creates a GetTimestampCertChainDefault with default headers values +func NewGetTimestampCertChainDefault(code int) *GetTimestampCertChainDefault { + return &GetTimestampCertChainDefault{ + _statusCode: code, + } +} + +/* GetTimestampCertChainDefault describes a response with status code -1, with default header values. + +There was an internal error in the server while processing the request +*/ +type GetTimestampCertChainDefault struct { + _statusCode int + + Payload *models.Error +} + +// Code gets the status code for the get timestamp cert chain default response +func (o *GetTimestampCertChainDefault) Code() int { + return o._statusCode +} + +func (o *GetTimestampCertChainDefault) Error() string { + return fmt.Sprintf("[GET /api/v1/log/timestampCertChain][%d] getTimestampCertChain default %+v", o._statusCode, o.Payload) +} +func (o *GetTimestampCertChainDefault) GetPayload() *models.Error { + return o.Payload +} + +func (o *GetTimestampCertChainDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/pkg/generated/client/rekor_client.go b/pkg/generated/client/rekor_client.go index bee3811..b25cb57 100644 --- a/pkg/generated/client/rekor_client.go +++ b/pkg/generated/client/rekor_client.go @@ -29,6 +29,7 @@ import ( "github.com/sigstore/rekor/pkg/generated/client/entries" "github.com/sigstore/rekor/pkg/generated/client/index" "github.com/sigstore/rekor/pkg/generated/client/pubkey" + "github.com/sigstore/rekor/pkg/generated/client/timestamp" "github.com/sigstore/rekor/pkg/generated/client/tlog" ) @@ -77,6 +78,7 @@ func New(transport runtime.ClientTransport, formats strfmt.Registry) *Rekor { cli.Entries = entries.New(transport, formats) cli.Index = index.New(transport, formats) cli.Pubkey = pubkey.New(transport, formats) + cli.Timestamp = timestamp.New(transport, formats) cli.Tlog = tlog.New(transport, formats) return cli } @@ -128,6 +130,8 @@ type Rekor struct { Pubkey pubkey.ClientService + Timestamp timestamp.ClientService + Tlog tlog.ClientService Transport runtime.ClientTransport @@ -139,5 +143,6 @@ func (c *Rekor) SetTransport(transport runtime.ClientTransport) { c.Entries.SetTransport(transport) c.Index.SetTransport(transport) c.Pubkey.SetTransport(transport) + c.Timestamp.SetTransport(transport) c.Tlog.SetTransport(transport) } diff --git a/pkg/generated/client/timestamp/get_timestamp_cert_chain_parameters.go b/pkg/generated/client/timestamp/get_timestamp_cert_chain_parameters.go new file mode 100644 index 0000000..5fee865 --- /dev/null +++ b/pkg/generated/client/timestamp/get_timestamp_cert_chain_parameters.go @@ -0,0 +1,142 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetTimestampCertChainParams creates a new GetTimestampCertChainParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetTimestampCertChainParams() *GetTimestampCertChainParams { + return &GetTimestampCertChainParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetTimestampCertChainParamsWithTimeout creates a new GetTimestampCertChainParams object +// with the ability to set a timeout on a request. +func NewGetTimestampCertChainParamsWithTimeout(timeout time.Duration) *GetTimestampCertChainParams { + return &GetTimestampCertChainParams{ + timeout: timeout, + } +} + +// NewGetTimestampCertChainParamsWithContext creates a new GetTimestampCertChainParams object +// with the ability to set a context for a request. +func NewGetTimestampCertChainParamsWithContext(ctx context.Context) *GetTimestampCertChainParams { + return &GetTimestampCertChainParams{ + Context: ctx, + } +} + +// NewGetTimestampCertChainParamsWithHTTPClient creates a new GetTimestampCertChainParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetTimestampCertChainParamsWithHTTPClient(client *http.Client) *GetTimestampCertChainParams { + return &GetTimestampCertChainParams{ + HTTPClient: client, + } +} + +/* GetTimestampCertChainParams contains all the parameters to send to the API endpoint + for the get timestamp cert chain operation. + + Typically these are written to a http.Request. +*/ +type GetTimestampCertChainParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get timestamp cert chain params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetTimestampCertChainParams) WithDefaults() *GetTimestampCertChainParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get timestamp cert chain params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetTimestampCertChainParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get timestamp cert chain params +func (o *GetTimestampCertChainParams) WithTimeout(timeout time.Duration) *GetTimestampCertChainParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get timestamp cert chain params +func (o *GetTimestampCertChainParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get timestamp cert chain params +func (o *GetTimestampCertChainParams) WithContext(ctx context.Context) *GetTimestampCertChainParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get timestamp cert chain params +func (o *GetTimestampCertChainParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get timestamp cert chain params +func (o *GetTimestampCertChainParams) WithHTTPClient(client *http.Client) *GetTimestampCertChainParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get timestamp cert chain params +func (o *GetTimestampCertChainParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *GetTimestampCertChainParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/generated/client/timestamp/get_timestamp_cert_chain_responses.go b/pkg/generated/client/timestamp/get_timestamp_cert_chain_responses.go new file mode 100644 index 0000000..34bbf52 --- /dev/null +++ b/pkg/generated/client/timestamp/get_timestamp_cert_chain_responses.go @@ -0,0 +1,156 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/sigstore/rekor/pkg/generated/models" +) + +// GetTimestampCertChainReader is a Reader for the GetTimestampCertChain structure. +type GetTimestampCertChainReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetTimestampCertChainReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetTimestampCertChainOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 404: + result := NewGetTimestampCertChainNotFound() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + result := NewGetTimestampCertChainDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewGetTimestampCertChainOK creates a GetTimestampCertChainOK with default headers values +func NewGetTimestampCertChainOK() *GetTimestampCertChainOK { + return &GetTimestampCertChainOK{} +} + +/* GetTimestampCertChainOK describes a response with status code 200, with default header values. + +The PEM encoded cert chain +*/ +type GetTimestampCertChainOK struct { + Payload string +} + +func (o *GetTimestampCertChainOK) Error() string { + return fmt.Sprintf("[GET /api/v1/timestamp/certchain][%d] getTimestampCertChainOK %+v", 200, o.Payload) +} +func (o *GetTimestampCertChainOK) GetPayload() string { + return o.Payload +} + +func (o *GetTimestampCertChainOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetTimestampCertChainNotFound creates a GetTimestampCertChainNotFound with default headers values +func NewGetTimestampCertChainNotFound() *GetTimestampCertChainNotFound { + return &GetTimestampCertChainNotFound{} +} + +/* GetTimestampCertChainNotFound describes a response with status code 404, with default header values. + +The content requested could not be found +*/ +type GetTimestampCertChainNotFound struct { +} + +func (o *GetTimestampCertChainNotFound) Error() string { + return fmt.Sprintf("[GET /api/v1/timestamp/certchain][%d] getTimestampCertChainNotFound ", 404) +} + +func (o *GetTimestampCertChainNotFound) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + +// NewGetTimestampCertChainDefault creates a GetTimestampCertChainDefault with default headers values +func NewGetTimestampCertChainDefault(code int) *GetTimestampCertChainDefault { + return &GetTimestampCertChainDefault{ + _statusCode: code, + } +} + +/* GetTimestampCertChainDefault describes a response with status code -1, with default header values. + +There was an internal error in the server while processing the request +*/ +type GetTimestampCertChainDefault struct { + _statusCode int + + Payload *models.Error +} + +// Code gets the status code for the get timestamp cert chain default response +func (o *GetTimestampCertChainDefault) Code() int { + return o._statusCode +} + +func (o *GetTimestampCertChainDefault) Error() string { + return fmt.Sprintf("[GET /api/v1/timestamp/certchain][%d] getTimestampCertChain default %+v", o._statusCode, o.Payload) +} +func (o *GetTimestampCertChainDefault) GetPayload() *models.Error { + return o.Payload +} + +func (o *GetTimestampCertChainDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/pkg/generated/client/timestamp/get_timestamp_response_parameters.go b/pkg/generated/client/timestamp/get_timestamp_response_parameters.go new file mode 100644 index 0000000..8965647 --- /dev/null +++ b/pkg/generated/client/timestamp/get_timestamp_response_parameters.go @@ -0,0 +1,165 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "io" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetTimestampResponseParams creates a new GetTimestampResponseParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetTimestampResponseParams() *GetTimestampResponseParams { + return &GetTimestampResponseParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetTimestampResponseParamsWithTimeout creates a new GetTimestampResponseParams object +// with the ability to set a timeout on a request. +func NewGetTimestampResponseParamsWithTimeout(timeout time.Duration) *GetTimestampResponseParams { + return &GetTimestampResponseParams{ + timeout: timeout, + } +} + +// NewGetTimestampResponseParamsWithContext creates a new GetTimestampResponseParams object +// with the ability to set a context for a request. +func NewGetTimestampResponseParamsWithContext(ctx context.Context) *GetTimestampResponseParams { + return &GetTimestampResponseParams{ + Context: ctx, + } +} + +// NewGetTimestampResponseParamsWithHTTPClient creates a new GetTimestampResponseParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetTimestampResponseParamsWithHTTPClient(client *http.Client) *GetTimestampResponseParams { + return &GetTimestampResponseParams{ + HTTPClient: client, + } +} + +/* GetTimestampResponseParams contains all the parameters to send to the API endpoint + for the get timestamp response operation. + + Typically these are written to a http.Request. +*/ +type GetTimestampResponseParams struct { + + // Request. + // + // Format: binary + Request io.ReadCloser + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get timestamp response params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetTimestampResponseParams) WithDefaults() *GetTimestampResponseParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get timestamp response params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetTimestampResponseParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get timestamp response params +func (o *GetTimestampResponseParams) WithTimeout(timeout time.Duration) *GetTimestampResponseParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get timestamp response params +func (o *GetTimestampResponseParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get timestamp response params +func (o *GetTimestampResponseParams) WithContext(ctx context.Context) *GetTimestampResponseParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get timestamp response params +func (o *GetTimestampResponseParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get timestamp response params +func (o *GetTimestampResponseParams) WithHTTPClient(client *http.Client) *GetTimestampResponseParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get timestamp response params +func (o *GetTimestampResponseParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithRequest adds the request to the get timestamp response params +func (o *GetTimestampResponseParams) WithRequest(request io.ReadCloser) *GetTimestampResponseParams { + o.SetRequest(request) + return o +} + +// SetRequest adds the request to the get timestamp response params +func (o *GetTimestampResponseParams) SetRequest(request io.ReadCloser) { + o.Request = request +} + +// WriteToRequest writes these params to a swagger request +func (o *GetTimestampResponseParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if o.Request != nil { + if err := r.SetBodyParam(o.Request); err != nil { + return err + } + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/generated/client/timestamp/get_timestamp_response_responses.go b/pkg/generated/client/timestamp/get_timestamp_response_responses.go new file mode 100644 index 0000000..7d37e3b --- /dev/null +++ b/pkg/generated/client/timestamp/get_timestamp_response_responses.go @@ -0,0 +1,198 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/sigstore/rekor/pkg/generated/models" +) + +// GetTimestampResponseReader is a Reader for the GetTimestampResponse structure. +type GetTimestampResponseReader struct { + formats strfmt.Registry + writer io.Writer +} + +// ReadResponse reads a server response into the received o. +func (o *GetTimestampResponseReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetTimestampResponseOK(o.writer) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewGetTimestampResponseBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 501: + result := NewGetTimestampResponseNotImplemented() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + result := NewGetTimestampResponseDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewGetTimestampResponseOK creates a GetTimestampResponseOK with default headers values +func NewGetTimestampResponseOK(writer io.Writer) *GetTimestampResponseOK { + return &GetTimestampResponseOK{ + + Payload: writer, + } +} + +/* GetTimestampResponseOK describes a response with status code 200, with default header values. + +Returns a timestamp response +*/ +type GetTimestampResponseOK struct { + Payload io.Writer +} + +func (o *GetTimestampResponseOK) Error() string { + return fmt.Sprintf("[POST /api/v1/timestamp][%d] getTimestampResponseOK %+v", 200, o.Payload) +} +func (o *GetTimestampResponseOK) GetPayload() io.Writer { + return o.Payload +} + +func (o *GetTimestampResponseOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetTimestampResponseBadRequest creates a GetTimestampResponseBadRequest with default headers values +func NewGetTimestampResponseBadRequest() *GetTimestampResponseBadRequest { + return &GetTimestampResponseBadRequest{} +} + +/* GetTimestampResponseBadRequest describes a response with status code 400, with default header values. + +The content supplied to the server was invalid +*/ +type GetTimestampResponseBadRequest struct { + Payload *models.Error +} + +func (o *GetTimestampResponseBadRequest) Error() string { + return fmt.Sprintf("[POST /api/v1/timestamp][%d] getTimestampResponseBadRequest %+v", 400, o.Payload) +} +func (o *GetTimestampResponseBadRequest) GetPayload() *models.Error { + return o.Payload +} + +func (o *GetTimestampResponseBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetTimestampResponseNotImplemented creates a GetTimestampResponseNotImplemented with default headers values +func NewGetTimestampResponseNotImplemented() *GetTimestampResponseNotImplemented { + return &GetTimestampResponseNotImplemented{} +} + +/* GetTimestampResponseNotImplemented describes a response with status code 501, with default header values. + +The content requested is not implemented +*/ +type GetTimestampResponseNotImplemented struct { +} + +func (o *GetTimestampResponseNotImplemented) Error() string { + return fmt.Sprintf("[POST /api/v1/timestamp][%d] getTimestampResponseNotImplemented ", 501) +} + +func (o *GetTimestampResponseNotImplemented) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + +// NewGetTimestampResponseDefault creates a GetTimestampResponseDefault with default headers values +func NewGetTimestampResponseDefault(code int) *GetTimestampResponseDefault { + return &GetTimestampResponseDefault{ + _statusCode: code, + } +} + +/* GetTimestampResponseDefault describes a response with status code -1, with default header values. + +There was an internal error in the server while processing the request +*/ +type GetTimestampResponseDefault struct { + _statusCode int + + Payload *models.Error +} + +// Code gets the status code for the get timestamp response default response +func (o *GetTimestampResponseDefault) Code() int { + return o._statusCode +} + +func (o *GetTimestampResponseDefault) Error() string { + return fmt.Sprintf("[POST /api/v1/timestamp][%d] getTimestampResponse default %+v", o._statusCode, o.Payload) +} +func (o *GetTimestampResponseDefault) GetPayload() *models.Error { + return o.Payload +} + +func (o *GetTimestampResponseDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/pkg/generated/client/timestamp/timestamp_client.go b/pkg/generated/client/timestamp/timestamp_client.go new file mode 100644 index 0000000..b82d707 --- /dev/null +++ b/pkg/generated/client/timestamp/timestamp_client.go @@ -0,0 +1,135 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" +) + +// New creates a new timestamp API client. +func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService { + return &Client{transport: transport, formats: formats} +} + +/* +Client for timestamp API +*/ +type Client struct { + transport runtime.ClientTransport + formats strfmt.Registry +} + +// ClientOption is the option for Client methods +type ClientOption func(*runtime.ClientOperation) + +// ClientService is the interface for Client methods +type ClientService interface { + GetTimestampCertChain(params *GetTimestampCertChainParams, opts ...ClientOption) (*GetTimestampCertChainOK, error) + + GetTimestampResponse(params *GetTimestampResponseParams, writer io.Writer, opts ...ClientOption) (*GetTimestampResponseOK, error) + + SetTransport(transport runtime.ClientTransport) +} + +/* + GetTimestampCertChain retrieves the certfiicate chain for timestamping that can be used to validate trusted timestamps + + Returns the certfiicate chain for timestamping that can be used to validate trusted timestamps +*/ +func (a *Client) GetTimestampCertChain(params *GetTimestampCertChainParams, opts ...ClientOption) (*GetTimestampCertChainOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetTimestampCertChainParams() + } + op := &runtime.ClientOperation{ + ID: "getTimestampCertChain", + Method: "GET", + PathPattern: "/api/v1/timestamp/certchain", + ProducesMediaTypes: []string{"application/pem-certificate-chain"}, + ConsumesMediaTypes: []string{"application/json", "application/yaml"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetTimestampCertChainReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetTimestampCertChainOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*GetTimestampCertChainDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +/* + GetTimestampResponse returns a timestamp response generated by rekor +*/ +func (a *Client) GetTimestampResponse(params *GetTimestampResponseParams, writer io.Writer, opts ...ClientOption) (*GetTimestampResponseOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetTimestampResponseParams() + } + op := &runtime.ClientOperation{ + ID: "getTimestampResponse", + Method: "POST", + PathPattern: "/api/v1/timestamp", + ProducesMediaTypes: []string{"application/timestamp-reply"}, + ConsumesMediaTypes: []string{"application/timestamp-query"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetTimestampResponseReader{formats: a.formats, writer: writer}, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetTimestampResponseOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*GetTimestampResponseDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +// SetTransport changes the transport on the client +func (a *Client) SetTransport(transport runtime.ClientTransport) { + a.transport = transport +} diff --git a/pkg/generated/client/timestamp/timestamp_response_parameters.go b/pkg/generated/client/timestamp/timestamp_response_parameters.go new file mode 100644 index 0000000..422a51c --- /dev/null +++ b/pkg/generated/client/timestamp/timestamp_response_parameters.go @@ -0,0 +1,164 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + + "github.com/sigstore/rekor/pkg/generated/models" +) + +// NewTimestampResponseParams creates a new TimestampResponseParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewTimestampResponseParams() *TimestampResponseParams { + return &TimestampResponseParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewTimestampResponseParamsWithTimeout creates a new TimestampResponseParams object +// with the ability to set a timeout on a request. +func NewTimestampResponseParamsWithTimeout(timeout time.Duration) *TimestampResponseParams { + return &TimestampResponseParams{ + timeout: timeout, + } +} + +// NewTimestampResponseParamsWithContext creates a new TimestampResponseParams object +// with the ability to set a context for a request. +func NewTimestampResponseParamsWithContext(ctx context.Context) *TimestampResponseParams { + return &TimestampResponseParams{ + Context: ctx, + } +} + +// NewTimestampResponseParamsWithHTTPClient creates a new TimestampResponseParams object +// with the ability to set a custom HTTPClient for a request. +func NewTimestampResponseParamsWithHTTPClient(client *http.Client) *TimestampResponseParams { + return &TimestampResponseParams{ + HTTPClient: client, + } +} + +/* TimestampResponseParams contains all the parameters to send to the API endpoint + for the timestamp response operation. + + Typically these are written to a http.Request. +*/ +type TimestampResponseParams struct { + + // Query. + Query *models.TimestampRequest + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the timestamp response params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *TimestampResponseParams) WithDefaults() *TimestampResponseParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the timestamp response params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *TimestampResponseParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the timestamp response params +func (o *TimestampResponseParams) WithTimeout(timeout time.Duration) *TimestampResponseParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the timestamp response params +func (o *TimestampResponseParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the timestamp response params +func (o *TimestampResponseParams) WithContext(ctx context.Context) *TimestampResponseParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the timestamp response params +func (o *TimestampResponseParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the timestamp response params +func (o *TimestampResponseParams) WithHTTPClient(client *http.Client) *TimestampResponseParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the timestamp response params +func (o *TimestampResponseParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithQuery adds the query to the timestamp response params +func (o *TimestampResponseParams) WithQuery(query *models.TimestampRequest) *TimestampResponseParams { + o.SetQuery(query) + return o +} + +// SetQuery adds the query to the timestamp response params +func (o *TimestampResponseParams) SetQuery(query *models.TimestampRequest) { + o.Query = query +} + +// WriteToRequest writes these params to a swagger request +func (o *TimestampResponseParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if o.Query != nil { + if err := r.SetBodyParam(o.Query); err != nil { + return err + } + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/generated/client/timestamp/timestamp_response_responses.go b/pkg/generated/client/timestamp/timestamp_response_responses.go new file mode 100644 index 0000000..8805d19 --- /dev/null +++ b/pkg/generated/client/timestamp/timestamp_response_responses.go @@ -0,0 +1,169 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/sigstore/rekor/pkg/generated/models" +) + +// TimestampResponseReader is a Reader for the TimestampResponse structure. +type TimestampResponseReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *TimestampResponseReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewTimestampResponseOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewTimestampResponseBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + result := NewTimestampResponseDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewTimestampResponseOK creates a TimestampResponseOK with default headers values +func NewTimestampResponseOK() *TimestampResponseOK { + return &TimestampResponseOK{} +} + +/* TimestampResponseOK describes a response with status code 200, with default header values. + +Returns a timestamp response +*/ +type TimestampResponseOK struct { + Payload *models.TimestampResponse +} + +func (o *TimestampResponseOK) Error() string { + return fmt.Sprintf("[POST /api/v1/tsr][%d] timestampResponseOK %+v", 200, o.Payload) +} +func (o *TimestampResponseOK) GetPayload() *models.TimestampResponse { + return o.Payload +} + +func (o *TimestampResponseOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.TimestampResponse) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewTimestampResponseBadRequest creates a TimestampResponseBadRequest with default headers values +func NewTimestampResponseBadRequest() *TimestampResponseBadRequest { + return &TimestampResponseBadRequest{} +} + +/* TimestampResponseBadRequest describes a response with status code 400, with default header values. + +The content supplied to the server was invalid +*/ +type TimestampResponseBadRequest struct { + Payload *models.Error +} + +func (o *TimestampResponseBadRequest) Error() string { + return fmt.Sprintf("[POST /api/v1/tsr][%d] timestampResponseBadRequest %+v", 400, o.Payload) +} +func (o *TimestampResponseBadRequest) GetPayload() *models.Error { + return o.Payload +} + +func (o *TimestampResponseBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewTimestampResponseDefault creates a TimestampResponseDefault with default headers values +func NewTimestampResponseDefault(code int) *TimestampResponseDefault { + return &TimestampResponseDefault{ + _statusCode: code, + } +} + +/* TimestampResponseDefault describes a response with status code -1, with default header values. + +There was an internal error in the server while processing the request +*/ +type TimestampResponseDefault struct { + _statusCode int + + Payload *models.Error +} + +// Code gets the status code for the timestamp response default response +func (o *TimestampResponseDefault) Code() int { + return o._statusCode +} + +func (o *TimestampResponseDefault) Error() string { + return fmt.Sprintf("[POST /api/v1/tsr][%d] timestampResponse default %+v", o._statusCode, o.Payload) +} +func (o *TimestampResponseDefault) GetPayload() *models.Error { + return o.Payload +} + +func (o *TimestampResponseDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/pkg/generated/models/timestamp_request.go b/pkg/generated/models/timestamp_request.go new file mode 100644 index 0000000..f5014d5 --- /dev/null +++ b/pkg/generated/models/timestamp_request.go @@ -0,0 +1,67 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// TimestampRequest timestamp request +// +// swagger:model TimestampRequest +type TimestampRequest struct { + + // RFC 3161 formatted timestamp request + // Format: byte + RfcRequest strfmt.Base64 `json:"rfcRequest,omitempty"` +} + +// Validate validates this timestamp request +func (m *TimestampRequest) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this timestamp request based on context it is used +func (m *TimestampRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *TimestampRequest) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *TimestampRequest) UnmarshalBinary(b []byte) error { + var res TimestampRequest + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/generated/models/timestamp_response.go b/pkg/generated/models/timestamp_response.go new file mode 100644 index 0000000..0f770a4 --- /dev/null +++ b/pkg/generated/models/timestamp_response.go @@ -0,0 +1,67 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// TimestampResponse timestamp response +// +// swagger:model TimestampResponse +type TimestampResponse struct { + + // RFC 3161 formatted timestamp response + // Format: byte + RfcResponse strfmt.Base64 `json:"rfcResponse,omitempty"` +} + +// Validate validates this timestamp response +func (m *TimestampResponse) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this timestamp response based on context it is used +func (m *TimestampResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *TimestampResponse) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *TimestampResponse) UnmarshalBinary(b []byte) error { + var res TimestampResponse + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/generated/restapi/configure_rekor_server.go b/pkg/generated/restapi/configure_rekor_server.go index ef69b08..0d22cfe 100644 --- a/pkg/generated/restapi/configure_rekor_server.go +++ b/pkg/generated/restapi/configure_rekor_server.go @@ -35,6 +35,7 @@ import ( "github.com/sigstore/rekor/pkg/generated/restapi/operations/entries" "github.com/sigstore/rekor/pkg/generated/restapi/operations/index" "github.com/sigstore/rekor/pkg/generated/restapi/operations/pubkey" + "github.com/sigstore/rekor/pkg/generated/restapi/operations/timestamp" "github.com/sigstore/rekor/pkg/generated/restapi/operations/tlog" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/util" @@ -70,6 +71,9 @@ func configureAPI(api *operations.RekorServerAPI) http.Handler { api.YamlProducer = util.YamlProducer() api.ApplicationXPemFileProducer = runtime.TextProducer() + api.ApplicationPemCertificateChainProducer = runtime.TextProducer() + api.ApplicationTimestampQueryConsumer = runtime.ByteStreamConsumer() + api.ApplicationTimestampReplyProducer = runtime.ByteStreamProducer() api.EntriesCreateLogEntryHandler = entries.CreateLogEntryHandlerFunc(pkgapi.CreateLogEntryHandler) api.EntriesGetLogEntryByIndexHandler = entries.GetLogEntryByIndexHandlerFunc(pkgapi.GetLogEntryByIndexHandler) @@ -87,6 +91,9 @@ func configureAPI(api *operations.RekorServerAPI) http.Handler { api.IndexSearchIndexHandler = index.SearchIndexHandlerFunc(pkgapi.SearchIndexNotImplementedHandler) } + api.TimestampGetTimestampResponseHandler = timestamp.GetTimestampResponseHandlerFunc(pkgapi.TimestampResponseHandler) + api.TimestampGetTimestampCertChainHandler = timestamp.GetTimestampCertChainHandlerFunc(pkgapi.GetTimestampCertChainHandler) + api.PreServerShutdown = func() {} api.ServerShutdown = func() {} @@ -96,9 +103,11 @@ func configureAPI(api *operations.RekorServerAPI) http.Handler { api.AddMiddlewareFor("GET", "/api/v1/log/proof", middleware.NoCache) api.AddMiddlewareFor("GET", "/api/v1/log/entries", middleware.NoCache) api.AddMiddlewareFor("GET", "/api/v1/log/entries/{entryUUID}", middleware.NoCache) + api.AddMiddlewareFor("GET", "/api/v1/timestamp", middleware.NoCache) // cache forever api.AddMiddlewareFor("GET", "/api/v1/log/publicKey", cacheForever) + api.AddMiddlewareFor("GET", "/api/v1/log/timestamp/certchain", cacheForever) return setupGlobalMiddleware(api.Serve(setupMiddlewares)) } diff --git a/pkg/generated/restapi/doc.go b/pkg/generated/restapi/doc.go index b2462c6..8c81a2a 100644 --- a/pkg/generated/restapi/doc.go +++ b/pkg/generated/restapi/doc.go @@ -25,10 +25,13 @@ // Version: 0.0.1 // // Consumes: +// - application/timestamp-query // - application/json // - application/yaml // // Produces: +// - application/pem-certificate-chain +// - application/timestamp-reply // - application/x-pem-file // - application/json // - application/yaml diff --git a/pkg/generated/restapi/embedded_spec.go b/pkg/generated/restapi/embedded_spec.go index 98c5fd5..d5ce2d9 100644 --- a/pkg/generated/restapi/embedded_spec.go +++ b/pkg/generated/restapi/embedded_spec.go @@ -325,6 +325,77 @@ func init() { } } } + }, + "/api/v1/timestamp": { + "post": { + "consumes": [ + "application/timestamp-query" + ], + "produces": [ + "application/timestamp-reply" + ], + "tags": [ + "timestamp" + ], + "summary": "Returns a timestamp response generated by Rekor", + "operationId": "getTimestampResponse", + "parameters": [ + { + "name": "request", + "in": "body", + "required": true, + "schema": { + "type": "string", + "format": "binary" + } + } + ], + "responses": { + "200": { + "description": "Returns a timestamp response", + "schema": { + "type": "string", + "format": "binary" + } + }, + "400": { + "$ref": "#/responses/BadContent" + }, + "501": { + "$ref": "#/responses/NotImplemented" + }, + "default": { + "$ref": "#/responses/InternalServerError" + } + } + } + }, + "/api/v1/timestamp/certchain": { + "get": { + "description": "Returns the certfiicate chain for timestamping that can be used to validate trusted timestamps", + "produces": [ + "application/pem-certificate-chain" + ], + "tags": [ + "timestamp" + ], + "summary": "Retrieve the certfiicate chain for timestamping that can be used to validate trusted timestamps", + "operationId": "getTimestampCertChain", + "responses": { + "200": { + "description": "The PEM encoded cert chain", + "schema": { + "type": "string" + } + }, + "404": { + "$ref": "#/responses/NotFound" + }, + "default": { + "$ref": "#/responses/InternalServerError" + } + } + } } }, "definitions": { @@ -666,6 +737,9 @@ func init() { }, "NotFound": { "description": "The content requested could not be found" + }, + "NotImplemented": { + "description": "The content requested is not implemented" } } }`)) @@ -1007,6 +1081,86 @@ func init() { } } } + }, + "/api/v1/timestamp": { + "post": { + "consumes": [ + "application/timestamp-query" + ], + "produces": [ + "application/timestamp-reply" + ], + "tags": [ + "timestamp" + ], + "summary": "Returns a timestamp response generated by Rekor", + "operationId": "getTimestampResponse", + "parameters": [ + { + "name": "request", + "in": "body", + "required": true, + "schema": { + "type": "string", + "format": "binary" + } + } + ], + "responses": { + "200": { + "description": "Returns a timestamp response", + "schema": { + "type": "string", + "format": "binary" + } + }, + "400": { + "description": "The content supplied to the server was invalid", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "501": { + "description": "The content requested is not implemented" + }, + "default": { + "description": "There was an internal error in the server while processing the request", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/api/v1/timestamp/certchain": { + "get": { + "description": "Returns the certfiicate chain for timestamping that can be used to validate trusted timestamps", + "produces": [ + "application/pem-certificate-chain" + ], + "tags": [ + "timestamp" + ], + "summary": "Retrieve the certfiicate chain for timestamping that can be used to validate trusted timestamps", + "operationId": "getTimestampCertChain", + "responses": { + "200": { + "description": "The PEM encoded cert chain", + "schema": { + "type": "string" + } + }, + "404": { + "description": "The content requested could not be found" + }, + "default": { + "description": "There was an internal error in the server while processing the request", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } } }, "definitions": { @@ -2169,6 +2323,9 @@ func init() { }, "NotFound": { "description": "The content requested could not be found" + }, + "NotImplemented": { + "description": "The content requested is not implemented" } } }`)) diff --git a/pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain.go b/pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain.go new file mode 100644 index 0000000..6d57996 --- /dev/null +++ b/pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain.go @@ -0,0 +1,74 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package pubkey + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// GetTimestampCertChainHandlerFunc turns a function with the right signature into a get timestamp cert chain handler +type GetTimestampCertChainHandlerFunc func(GetTimestampCertChainParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetTimestampCertChainHandlerFunc) Handle(params GetTimestampCertChainParams) middleware.Responder { + return fn(params) +} + +// GetTimestampCertChainHandler interface for that can handle valid get timestamp cert chain params +type GetTimestampCertChainHandler interface { + Handle(GetTimestampCertChainParams) middleware.Responder +} + +// NewGetTimestampCertChain creates a new http.Handler for the get timestamp cert chain operation +func NewGetTimestampCertChain(ctx *middleware.Context, handler GetTimestampCertChainHandler) *GetTimestampCertChain { + return &GetTimestampCertChain{Context: ctx, Handler: handler} +} + +/* GetTimestampCertChain swagger:route GET /api/v1/log/timestampCertChain pubkey getTimestampCertChain + +Retrieve the certfiicate chain for timestamping that can be used to validate trusted timestamps + +Returns the certfiicate chain for timestamping that can be used to validate trusted timestamps + +*/ +type GetTimestampCertChain struct { + Context *middleware.Context + Handler GetTimestampCertChainHandler +} + +func (o *GetTimestampCertChain) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetTimestampCertChainParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain_parameters.go b/pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain_parameters.go new file mode 100644 index 0000000..3d4baa3 --- /dev/null +++ b/pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain_parameters.go @@ -0,0 +1,62 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package pubkey + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" +) + +// NewGetTimestampCertChainParams creates a new GetTimestampCertChainParams object +// +// There are no default values defined in the spec. +func NewGetTimestampCertChainParams() GetTimestampCertChainParams { + + return GetTimestampCertChainParams{} +} + +// GetTimestampCertChainParams contains all the bound params for the get timestamp cert chain operation +// typically these are obtained from a http.Request +// +// swagger:parameters getTimestampCertChain +type GetTimestampCertChainParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetTimestampCertChainParams() beforehand. +func (o *GetTimestampCertChainParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain_responses.go b/pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain_responses.go new file mode 100644 index 0000000..e439fe9 --- /dev/null +++ b/pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain_responses.go @@ -0,0 +1,130 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package pubkey + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/sigstore/rekor/pkg/generated/models" +) + +// GetTimestampCertChainOKCode is the HTTP code returned for type GetTimestampCertChainOK +const GetTimestampCertChainOKCode int = 200 + +/*GetTimestampCertChainOK The PEM encoded cert chain + +swagger:response getTimestampCertChainOK +*/ +type GetTimestampCertChainOK struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewGetTimestampCertChainOK creates GetTimestampCertChainOK with default headers values +func NewGetTimestampCertChainOK() *GetTimestampCertChainOK { + + return &GetTimestampCertChainOK{} +} + +// WithPayload adds the payload to the get timestamp cert chain o k response +func (o *GetTimestampCertChainOK) WithPayload(payload string) *GetTimestampCertChainOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get timestamp cert chain o k response +func (o *GetTimestampCertChainOK) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetTimestampCertChainOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +/*GetTimestampCertChainDefault There was an internal error in the server while processing the request + +swagger:response getTimestampCertChainDefault +*/ +type GetTimestampCertChainDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewGetTimestampCertChainDefault creates GetTimestampCertChainDefault with default headers values +func NewGetTimestampCertChainDefault(code int) *GetTimestampCertChainDefault { + if code <= 0 { + code = 500 + } + + return &GetTimestampCertChainDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the get timestamp cert chain default response +func (o *GetTimestampCertChainDefault) WithStatusCode(code int) *GetTimestampCertChainDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the get timestamp cert chain default response +func (o *GetTimestampCertChainDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the get timestamp cert chain default response +func (o *GetTimestampCertChainDefault) WithPayload(payload *models.Error) *GetTimestampCertChainDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get timestamp cert chain default response +func (o *GetTimestampCertChainDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetTimestampCertChainDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain_urlbuilder.go b/pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain_urlbuilder.go new file mode 100644 index 0000000..7f66be8 --- /dev/null +++ b/pkg/generated/restapi/operations/pubkey/get_timestamp_cert_chain_urlbuilder.go @@ -0,0 +1,100 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package pubkey + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// GetTimestampCertChainURL generates an URL for the get timestamp cert chain operation +type GetTimestampCertChainURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetTimestampCertChainURL) WithBasePath(bp string) *GetTimestampCertChainURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetTimestampCertChainURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetTimestampCertChainURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/api/v1/log/timestampCertChain" + + _basePath := o._basePath + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetTimestampCertChainURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetTimestampCertChainURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetTimestampCertChainURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetTimestampCertChainURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetTimestampCertChainURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetTimestampCertChainURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/pkg/generated/restapi/operations/rekor_server_api.go b/pkg/generated/restapi/operations/rekor_server_api.go index bbe6dd4..1e69e40 100644 --- a/pkg/generated/restapi/operations/rekor_server_api.go +++ b/pkg/generated/restapi/operations/rekor_server_api.go @@ -40,6 +40,7 @@ import ( "github.com/sigstore/rekor/pkg/generated/restapi/operations/entries" "github.com/sigstore/rekor/pkg/generated/restapi/operations/index" "github.com/sigstore/rekor/pkg/generated/restapi/operations/pubkey" + "github.com/sigstore/rekor/pkg/generated/restapi/operations/timestamp" "github.com/sigstore/rekor/pkg/generated/restapi/operations/tlog" ) @@ -61,9 +62,18 @@ func NewRekorServerAPI(spec *loads.Document) *RekorServerAPI { APIKeyAuthenticator: security.APIKeyAuth, BearerAuthenticator: security.BearerAuth, + ApplicationTimestampQueryConsumer: runtime.ConsumerFunc(func(r io.Reader, target interface{}) error { + return errors.NotImplemented("applicationTimestampQuery consumer has not yet been implemented") + }), JSONConsumer: runtime.JSONConsumer(), YamlConsumer: yamlpc.YAMLConsumer(), + ApplicationPemCertificateChainProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error { + return errors.NotImplemented("applicationPemCertificateChain producer has not yet been implemented") + }), + ApplicationTimestampReplyProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error { + return errors.NotImplemented("applicationTimestampReply producer has not yet been implemented") + }), ApplicationXPemFileProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error { return errors.NotImplemented("applicationXPemFile producer has not yet been implemented") }), @@ -88,6 +98,12 @@ func NewRekorServerAPI(spec *loads.Document) *RekorServerAPI { PubkeyGetPublicKeyHandler: pubkey.GetPublicKeyHandlerFunc(func(params pubkey.GetPublicKeyParams) middleware.Responder { return middleware.NotImplemented("operation pubkey.GetPublicKey has not yet been implemented") }), + TimestampGetTimestampCertChainHandler: timestamp.GetTimestampCertChainHandlerFunc(func(params timestamp.GetTimestampCertChainParams) middleware.Responder { + return middleware.NotImplemented("operation timestamp.GetTimestampCertChain has not yet been implemented") + }), + TimestampGetTimestampResponseHandler: timestamp.GetTimestampResponseHandlerFunc(func(params timestamp.GetTimestampResponseParams) middleware.Responder { + return middleware.NotImplemented("operation timestamp.GetTimestampResponse has not yet been implemented") + }), IndexSearchIndexHandler: index.SearchIndexHandlerFunc(func(params index.SearchIndexParams) middleware.Responder { return middleware.NotImplemented("operation index.SearchIndex has not yet been implemented") }), @@ -122,6 +138,9 @@ type RekorServerAPI struct { // It has a default implementation in the security package, however you can replace it for your particular usage. BearerAuthenticator func(string, security.ScopedTokenAuthentication) runtime.Authenticator + // ApplicationTimestampQueryConsumer registers a consumer for the following mime types: + // - application/timestamp-query + ApplicationTimestampQueryConsumer runtime.Consumer // JSONConsumer registers a consumer for the following mime types: // - application/json JSONConsumer runtime.Consumer @@ -129,6 +148,12 @@ type RekorServerAPI struct { // - application/yaml YamlConsumer runtime.Consumer + // ApplicationPemCertificateChainProducer registers a producer for the following mime types: + // - application/pem-certificate-chain + ApplicationPemCertificateChainProducer runtime.Producer + // ApplicationTimestampReplyProducer registers a producer for the following mime types: + // - application/timestamp-reply + ApplicationTimestampReplyProducer runtime.Producer // ApplicationXPemFileProducer registers a producer for the following mime types: // - application/x-pem-file ApplicationXPemFileProducer runtime.Producer @@ -151,6 +176,10 @@ type RekorServerAPI struct { TlogGetLogProofHandler tlog.GetLogProofHandler // PubkeyGetPublicKeyHandler sets the operation handler for the get public key operation PubkeyGetPublicKeyHandler pubkey.GetPublicKeyHandler + // TimestampGetTimestampCertChainHandler sets the operation handler for the get timestamp cert chain operation + TimestampGetTimestampCertChainHandler timestamp.GetTimestampCertChainHandler + // TimestampGetTimestampResponseHandler sets the operation handler for the get timestamp response operation + TimestampGetTimestampResponseHandler timestamp.GetTimestampResponseHandler // IndexSearchIndexHandler sets the operation handler for the search index operation IndexSearchIndexHandler index.SearchIndexHandler // EntriesSearchLogQueryHandler sets the operation handler for the search log query operation @@ -224,6 +253,9 @@ func (o *RekorServerAPI) RegisterFormat(name string, format strfmt.Format, valid func (o *RekorServerAPI) Validate() error { var unregistered []string + if o.ApplicationTimestampQueryConsumer == nil { + unregistered = append(unregistered, "ApplicationTimestampQueryConsumer") + } if o.JSONConsumer == nil { unregistered = append(unregistered, "JSONConsumer") } @@ -231,6 +263,12 @@ func (o *RekorServerAPI) Validate() error { unregistered = append(unregistered, "YamlConsumer") } + if o.ApplicationPemCertificateChainProducer == nil { + unregistered = append(unregistered, "ApplicationPemCertificateChainProducer") + } + if o.ApplicationTimestampReplyProducer == nil { + unregistered = append(unregistered, "ApplicationTimestampReplyProducer") + } if o.ApplicationXPemFileProducer == nil { unregistered = append(unregistered, "ApplicationXPemFileProducer") } @@ -259,6 +297,12 @@ func (o *RekorServerAPI) Validate() error { if o.PubkeyGetPublicKeyHandler == nil { unregistered = append(unregistered, "pubkey.GetPublicKeyHandler") } + if o.TimestampGetTimestampCertChainHandler == nil { + unregistered = append(unregistered, "timestamp.GetTimestampCertChainHandler") + } + if o.TimestampGetTimestampResponseHandler == nil { + unregistered = append(unregistered, "timestamp.GetTimestampResponseHandler") + } if o.IndexSearchIndexHandler == nil { unregistered = append(unregistered, "index.SearchIndexHandler") } @@ -294,6 +338,8 @@ func (o *RekorServerAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Co result := make(map[string]runtime.Consumer, len(mediaTypes)) for _, mt := range mediaTypes { switch mt { + case "application/timestamp-query": + result["application/timestamp-query"] = o.ApplicationTimestampQueryConsumer case "application/json": result["application/json"] = o.JSONConsumer case "application/yaml": @@ -313,6 +359,10 @@ func (o *RekorServerAPI) ProducersFor(mediaTypes []string) map[string]runtime.Pr result := make(map[string]runtime.Producer, len(mediaTypes)) for _, mt := range mediaTypes { switch mt { + case "application/pem-certificate-chain": + result["application/pem-certificate-chain"] = o.ApplicationPemCertificateChainProducer + case "application/timestamp-reply": + result["application/timestamp-reply"] = o.ApplicationTimestampReplyProducer case "application/x-pem-file": result["application/x-pem-file"] = o.ApplicationXPemFileProducer case "application/json": @@ -383,6 +433,14 @@ func (o *RekorServerAPI) initHandlerCache() { o.handlers["GET"] = make(map[string]http.Handler) } o.handlers["GET"]["/api/v1/log/publicKey"] = pubkey.NewGetPublicKey(o.context, o.PubkeyGetPublicKeyHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } + o.handlers["GET"]["/api/v1/timestamp/certchain"] = timestamp.NewGetTimestampCertChain(o.context, o.TimestampGetTimestampCertChainHandler) + if o.handlers["POST"] == nil { + o.handlers["POST"] = make(map[string]http.Handler) + } + o.handlers["POST"]["/api/v1/timestamp"] = timestamp.NewGetTimestampResponse(o.context, o.TimestampGetTimestampResponseHandler) if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) } diff --git a/pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain.go b/pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain.go new file mode 100644 index 0000000..de58dd3 --- /dev/null +++ b/pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain.go @@ -0,0 +1,74 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// GetTimestampCertChainHandlerFunc turns a function with the right signature into a get timestamp cert chain handler +type GetTimestampCertChainHandlerFunc func(GetTimestampCertChainParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetTimestampCertChainHandlerFunc) Handle(params GetTimestampCertChainParams) middleware.Responder { + return fn(params) +} + +// GetTimestampCertChainHandler interface for that can handle valid get timestamp cert chain params +type GetTimestampCertChainHandler interface { + Handle(GetTimestampCertChainParams) middleware.Responder +} + +// NewGetTimestampCertChain creates a new http.Handler for the get timestamp cert chain operation +func NewGetTimestampCertChain(ctx *middleware.Context, handler GetTimestampCertChainHandler) *GetTimestampCertChain { + return &GetTimestampCertChain{Context: ctx, Handler: handler} +} + +/* GetTimestampCertChain swagger:route GET /api/v1/timestamp/certchain timestamp getTimestampCertChain + +Retrieve the certfiicate chain for timestamping that can be used to validate trusted timestamps + +Returns the certfiicate chain for timestamping that can be used to validate trusted timestamps + +*/ +type GetTimestampCertChain struct { + Context *middleware.Context + Handler GetTimestampCertChainHandler +} + +func (o *GetTimestampCertChain) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetTimestampCertChainParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain_parameters.go b/pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain_parameters.go new file mode 100644 index 0000000..e177de4 --- /dev/null +++ b/pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain_parameters.go @@ -0,0 +1,62 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" +) + +// NewGetTimestampCertChainParams creates a new GetTimestampCertChainParams object +// +// There are no default values defined in the spec. +func NewGetTimestampCertChainParams() GetTimestampCertChainParams { + + return GetTimestampCertChainParams{} +} + +// GetTimestampCertChainParams contains all the bound params for the get timestamp cert chain operation +// typically these are obtained from a http.Request +// +// swagger:parameters getTimestampCertChain +type GetTimestampCertChainParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetTimestampCertChainParams() beforehand. +func (o *GetTimestampCertChainParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain_responses.go b/pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain_responses.go new file mode 100644 index 0000000..6611894 --- /dev/null +++ b/pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain_responses.go @@ -0,0 +1,154 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/sigstore/rekor/pkg/generated/models" +) + +// GetTimestampCertChainOKCode is the HTTP code returned for type GetTimestampCertChainOK +const GetTimestampCertChainOKCode int = 200 + +/*GetTimestampCertChainOK The PEM encoded cert chain + +swagger:response getTimestampCertChainOK +*/ +type GetTimestampCertChainOK struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewGetTimestampCertChainOK creates GetTimestampCertChainOK with default headers values +func NewGetTimestampCertChainOK() *GetTimestampCertChainOK { + + return &GetTimestampCertChainOK{} +} + +// WithPayload adds the payload to the get timestamp cert chain o k response +func (o *GetTimestampCertChainOK) WithPayload(payload string) *GetTimestampCertChainOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get timestamp cert chain o k response +func (o *GetTimestampCertChainOK) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetTimestampCertChainOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// GetTimestampCertChainNotFoundCode is the HTTP code returned for type GetTimestampCertChainNotFound +const GetTimestampCertChainNotFoundCode int = 404 + +/*GetTimestampCertChainNotFound The content requested could not be found + +swagger:response getTimestampCertChainNotFound +*/ +type GetTimestampCertChainNotFound struct { +} + +// NewGetTimestampCertChainNotFound creates GetTimestampCertChainNotFound with default headers values +func NewGetTimestampCertChainNotFound() *GetTimestampCertChainNotFound { + + return &GetTimestampCertChainNotFound{} +} + +// WriteResponse to the client +func (o *GetTimestampCertChainNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(404) +} + +/*GetTimestampCertChainDefault There was an internal error in the server while processing the request + +swagger:response getTimestampCertChainDefault +*/ +type GetTimestampCertChainDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewGetTimestampCertChainDefault creates GetTimestampCertChainDefault with default headers values +func NewGetTimestampCertChainDefault(code int) *GetTimestampCertChainDefault { + if code <= 0 { + code = 500 + } + + return &GetTimestampCertChainDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the get timestamp cert chain default response +func (o *GetTimestampCertChainDefault) WithStatusCode(code int) *GetTimestampCertChainDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the get timestamp cert chain default response +func (o *GetTimestampCertChainDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the get timestamp cert chain default response +func (o *GetTimestampCertChainDefault) WithPayload(payload *models.Error) *GetTimestampCertChainDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get timestamp cert chain default response +func (o *GetTimestampCertChainDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetTimestampCertChainDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain_urlbuilder.go b/pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain_urlbuilder.go new file mode 100644 index 0000000..25cb613 --- /dev/null +++ b/pkg/generated/restapi/operations/timestamp/get_timestamp_cert_chain_urlbuilder.go @@ -0,0 +1,100 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// GetTimestampCertChainURL generates an URL for the get timestamp cert chain operation +type GetTimestampCertChainURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetTimestampCertChainURL) WithBasePath(bp string) *GetTimestampCertChainURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetTimestampCertChainURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetTimestampCertChainURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/api/v1/timestamp/certchain" + + _basePath := o._basePath + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetTimestampCertChainURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetTimestampCertChainURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetTimestampCertChainURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetTimestampCertChainURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetTimestampCertChainURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetTimestampCertChainURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/pkg/generated/restapi/operations/timestamp/get_timestamp_response.go b/pkg/generated/restapi/operations/timestamp/get_timestamp_response.go new file mode 100644 index 0000000..56fcc6b --- /dev/null +++ b/pkg/generated/restapi/operations/timestamp/get_timestamp_response.go @@ -0,0 +1,72 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// GetTimestampResponseHandlerFunc turns a function with the right signature into a get timestamp response handler +type GetTimestampResponseHandlerFunc func(GetTimestampResponseParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetTimestampResponseHandlerFunc) Handle(params GetTimestampResponseParams) middleware.Responder { + return fn(params) +} + +// GetTimestampResponseHandler interface for that can handle valid get timestamp response params +type GetTimestampResponseHandler interface { + Handle(GetTimestampResponseParams) middleware.Responder +} + +// NewGetTimestampResponse creates a new http.Handler for the get timestamp response operation +func NewGetTimestampResponse(ctx *middleware.Context, handler GetTimestampResponseHandler) *GetTimestampResponse { + return &GetTimestampResponse{Context: ctx, Handler: handler} +} + +/* GetTimestampResponse swagger:route POST /api/v1/timestamp timestamp getTimestampResponse + +Returns a timestamp response generated by Rekor + +*/ +type GetTimestampResponse struct { + Context *middleware.Context + Handler GetTimestampResponseHandler +} + +func (o *GetTimestampResponse) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetTimestampResponseParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/pkg/generated/restapi/operations/timestamp/get_timestamp_response_parameters.go b/pkg/generated/restapi/operations/timestamp/get_timestamp_response_parameters.go new file mode 100644 index 0000000..231cfe0 --- /dev/null +++ b/pkg/generated/restapi/operations/timestamp/get_timestamp_response_parameters.go @@ -0,0 +1,75 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "io" + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" +) + +// NewGetTimestampResponseParams creates a new GetTimestampResponseParams object +// +// There are no default values defined in the spec. +func NewGetTimestampResponseParams() GetTimestampResponseParams { + + return GetTimestampResponseParams{} +} + +// GetTimestampResponseParams contains all the bound params for the get timestamp response operation +// typically these are obtained from a http.Request +// +// swagger:parameters getTimestampResponse +type GetTimestampResponseParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: body + */ + Request io.ReadCloser +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetTimestampResponseParams() beforehand. +func (o *GetTimestampResponseParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + o.Request = r.Body + } else { + res = append(res, errors.Required("request", "body", "")) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/generated/restapi/operations/timestamp/get_timestamp_response_responses.go b/pkg/generated/restapi/operations/timestamp/get_timestamp_response_responses.go new file mode 100644 index 0000000..be7911a --- /dev/null +++ b/pkg/generated/restapi/operations/timestamp/get_timestamp_response_responses.go @@ -0,0 +1,199 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "io" + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/sigstore/rekor/pkg/generated/models" +) + +// GetTimestampResponseOKCode is the HTTP code returned for type GetTimestampResponseOK +const GetTimestampResponseOKCode int = 200 + +/*GetTimestampResponseOK Returns a timestamp response + +swagger:response getTimestampResponseOK +*/ +type GetTimestampResponseOK struct { + + /* + In: Body + */ + Payload io.ReadCloser `json:"body,omitempty"` +} + +// NewGetTimestampResponseOK creates GetTimestampResponseOK with default headers values +func NewGetTimestampResponseOK() *GetTimestampResponseOK { + + return &GetTimestampResponseOK{} +} + +// WithPayload adds the payload to the get timestamp response o k response +func (o *GetTimestampResponseOK) WithPayload(payload io.ReadCloser) *GetTimestampResponseOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get timestamp response o k response +func (o *GetTimestampResponseOK) SetPayload(payload io.ReadCloser) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetTimestampResponseOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// GetTimestampResponseBadRequestCode is the HTTP code returned for type GetTimestampResponseBadRequest +const GetTimestampResponseBadRequestCode int = 400 + +/*GetTimestampResponseBadRequest The content supplied to the server was invalid + +swagger:response getTimestampResponseBadRequest +*/ +type GetTimestampResponseBadRequest struct { + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewGetTimestampResponseBadRequest creates GetTimestampResponseBadRequest with default headers values +func NewGetTimestampResponseBadRequest() *GetTimestampResponseBadRequest { + + return &GetTimestampResponseBadRequest{} +} + +// WithPayload adds the payload to the get timestamp response bad request response +func (o *GetTimestampResponseBadRequest) WithPayload(payload *models.Error) *GetTimestampResponseBadRequest { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get timestamp response bad request response +func (o *GetTimestampResponseBadRequest) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetTimestampResponseBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(400) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetTimestampResponseNotImplementedCode is the HTTP code returned for type GetTimestampResponseNotImplemented +const GetTimestampResponseNotImplementedCode int = 501 + +/*GetTimestampResponseNotImplemented The content requested is not implemented + +swagger:response getTimestampResponseNotImplemented +*/ +type GetTimestampResponseNotImplemented struct { +} + +// NewGetTimestampResponseNotImplemented creates GetTimestampResponseNotImplemented with default headers values +func NewGetTimestampResponseNotImplemented() *GetTimestampResponseNotImplemented { + + return &GetTimestampResponseNotImplemented{} +} + +// WriteResponse to the client +func (o *GetTimestampResponseNotImplemented) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(501) +} + +/*GetTimestampResponseDefault There was an internal error in the server while processing the request + +swagger:response getTimestampResponseDefault +*/ +type GetTimestampResponseDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewGetTimestampResponseDefault creates GetTimestampResponseDefault with default headers values +func NewGetTimestampResponseDefault(code int) *GetTimestampResponseDefault { + if code <= 0 { + code = 500 + } + + return &GetTimestampResponseDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the get timestamp response default response +func (o *GetTimestampResponseDefault) WithStatusCode(code int) *GetTimestampResponseDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the get timestamp response default response +func (o *GetTimestampResponseDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the get timestamp response default response +func (o *GetTimestampResponseDefault) WithPayload(payload *models.Error) *GetTimestampResponseDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get timestamp response default response +func (o *GetTimestampResponseDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetTimestampResponseDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/pkg/generated/restapi/operations/timestamp/get_timestamp_response_urlbuilder.go b/pkg/generated/restapi/operations/timestamp/get_timestamp_response_urlbuilder.go new file mode 100644 index 0000000..9a3b72f --- /dev/null +++ b/pkg/generated/restapi/operations/timestamp/get_timestamp_response_urlbuilder.go @@ -0,0 +1,100 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// GetTimestampResponseURL generates an URL for the get timestamp response operation +type GetTimestampResponseURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetTimestampResponseURL) WithBasePath(bp string) *GetTimestampResponseURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetTimestampResponseURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetTimestampResponseURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/api/v1/timestamp" + + _basePath := o._basePath + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetTimestampResponseURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetTimestampResponseURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetTimestampResponseURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetTimestampResponseURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetTimestampResponseURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetTimestampResponseURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/pkg/generated/restapi/operations/timestamp/timestamp_response.go b/pkg/generated/restapi/operations/timestamp/timestamp_response.go new file mode 100644 index 0000000..4b895f2 --- /dev/null +++ b/pkg/generated/restapi/operations/timestamp/timestamp_response.go @@ -0,0 +1,72 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// TimestampResponseHandlerFunc turns a function with the right signature into a timestamp response handler +type TimestampResponseHandlerFunc func(TimestampResponseParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn TimestampResponseHandlerFunc) Handle(params TimestampResponseParams) middleware.Responder { + return fn(params) +} + +// TimestampResponseHandler interface for that can handle valid timestamp response params +type TimestampResponseHandler interface { + Handle(TimestampResponseParams) middleware.Responder +} + +// NewTimestampResponse creates a new http.Handler for the timestamp response operation +func NewTimestampResponse(ctx *middleware.Context, handler TimestampResponseHandler) *TimestampResponse { + return &TimestampResponse{Context: ctx, Handler: handler} +} + +/* TimestampResponse swagger:route POST /api/v1/tsr timestamp timestampResponse + +Generates a timestamp response + +*/ +type TimestampResponse struct { + Context *middleware.Context + Handler TimestampResponseHandler +} + +func (o *TimestampResponse) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewTimestampResponseParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/pkg/generated/restapi/operations/timestamp/timestamp_response_parameters.go b/pkg/generated/restapi/operations/timestamp/timestamp_response_parameters.go new file mode 100644 index 0000000..a4d6710 --- /dev/null +++ b/pkg/generated/restapi/operations/timestamp/timestamp_response_parameters.go @@ -0,0 +1,101 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "io" + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/validate" + + "github.com/sigstore/rekor/pkg/generated/models" +) + +// NewTimestampResponseParams creates a new TimestampResponseParams object +// +// There are no default values defined in the spec. +func NewTimestampResponseParams() TimestampResponseParams { + + return TimestampResponseParams{} +} + +// TimestampResponseParams contains all the bound params for the timestamp response operation +// typically these are obtained from a http.Request +// +// swagger:parameters timestampResponse +type TimestampResponseParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: body + */ + Query *models.TimestampRequest +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewTimestampResponseParams() beforehand. +func (o *TimestampResponseParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.TimestampRequest + if err := route.Consumer.Consume(r.Body, &body); err != nil { + if err == io.EOF { + res = append(res, errors.Required("query", "body", "")) + } else { + res = append(res, errors.NewParseError("query", "body", "", err)) + } + } else { + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + ctx := validate.WithOperationRequest(context.Background()) + if err := body.ContextValidate(ctx, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.Query = &body + } + } + } else { + res = append(res, errors.Required("query", "body", "")) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/generated/restapi/operations/timestamp/timestamp_response_responses.go b/pkg/generated/restapi/operations/timestamp/timestamp_response_responses.go new file mode 100644 index 0000000..88eb8e0 --- /dev/null +++ b/pkg/generated/restapi/operations/timestamp/timestamp_response_responses.go @@ -0,0 +1,176 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/sigstore/rekor/pkg/generated/models" +) + +// TimestampResponseOKCode is the HTTP code returned for type TimestampResponseOK +const TimestampResponseOKCode int = 200 + +/*TimestampResponseOK Returns a timestamp response + +swagger:response timestampResponseOK +*/ +type TimestampResponseOK struct { + + /* + In: Body + */ + Payload *models.TimestampResponse `json:"body,omitempty"` +} + +// NewTimestampResponseOK creates TimestampResponseOK with default headers values +func NewTimestampResponseOK() *TimestampResponseOK { + + return &TimestampResponseOK{} +} + +// WithPayload adds the payload to the timestamp response o k response +func (o *TimestampResponseOK) WithPayload(payload *models.TimestampResponse) *TimestampResponseOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the timestamp response o k response +func (o *TimestampResponseOK) SetPayload(payload *models.TimestampResponse) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *TimestampResponseOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// TimestampResponseBadRequestCode is the HTTP code returned for type TimestampResponseBadRequest +const TimestampResponseBadRequestCode int = 400 + +/*TimestampResponseBadRequest The content supplied to the server was invalid + +swagger:response timestampResponseBadRequest +*/ +type TimestampResponseBadRequest struct { + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewTimestampResponseBadRequest creates TimestampResponseBadRequest with default headers values +func NewTimestampResponseBadRequest() *TimestampResponseBadRequest { + + return &TimestampResponseBadRequest{} +} + +// WithPayload adds the payload to the timestamp response bad request response +func (o *TimestampResponseBadRequest) WithPayload(payload *models.Error) *TimestampResponseBadRequest { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the timestamp response bad request response +func (o *TimestampResponseBadRequest) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *TimestampResponseBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(400) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +/*TimestampResponseDefault There was an internal error in the server while processing the request + +swagger:response timestampResponseDefault +*/ +type TimestampResponseDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewTimestampResponseDefault creates TimestampResponseDefault with default headers values +func NewTimestampResponseDefault(code int) *TimestampResponseDefault { + if code <= 0 { + code = 500 + } + + return &TimestampResponseDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the timestamp response default response +func (o *TimestampResponseDefault) WithStatusCode(code int) *TimestampResponseDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the timestamp response default response +func (o *TimestampResponseDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the timestamp response default response +func (o *TimestampResponseDefault) WithPayload(payload *models.Error) *TimestampResponseDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the timestamp response default response +func (o *TimestampResponseDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *TimestampResponseDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/pkg/generated/restapi/operations/timestamp/timestamp_response_urlbuilder.go b/pkg/generated/restapi/operations/timestamp/timestamp_response_urlbuilder.go new file mode 100644 index 0000000..89d3d76 --- /dev/null +++ b/pkg/generated/restapi/operations/timestamp/timestamp_response_urlbuilder.go @@ -0,0 +1,100 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package timestamp + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// TimestampResponseURL generates an URL for the timestamp response operation +type TimestampResponseURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *TimestampResponseURL) WithBasePath(bp string) *TimestampResponseURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *TimestampResponseURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *TimestampResponseURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/api/v1/tsr" + + _basePath := o._basePath + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *TimestampResponseURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *TimestampResponseURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *TimestampResponseURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on TimestampResponseURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on TimestampResponseURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *TimestampResponseURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/pkg/pki/x509/x509.go b/pkg/pki/x509/x509.go index 6be398c..7bf2922 100644 --- a/pkg/pki/x509/x509.go +++ b/pkg/pki/x509/x509.go @@ -188,3 +188,48 @@ func (k PublicKey) EmailAddresses() []string { } return names } + +func CertChainToPEM(certChain []*x509.Certificate) ([]byte, error) { + var pemBytes bytes.Buffer + for _, cert := range certChain { + if err := pem.Encode(&pemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil { + return nil, err + } + } + return pemBytes.Bytes(), nil +} + +func ParseTimestampCertChain(pemBytes []byte) ([]*x509.Certificate, error) { + certChain := []*x509.Certificate{} + var block *pem.Block + block, pemBytes = pem.Decode(pemBytes) + for ; block != nil; block, pemBytes = pem.Decode(pemBytes) { + if block.Type == "CERTIFICATE" { + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, err + } + certChain = append(certChain, cert) + } else { + return nil, errors.New("invalid block type") + } + } + if len(certChain) == 0 { + return nil, errors.New("no valid certificates in chain") + } + // Verify cert chain for timestamping + roots := x509.NewCertPool() + intermediates := x509.NewCertPool() + for _, cert := range certChain[1:(len(certChain) - 1)] { + intermediates.AddCert(cert) + } + roots.AddCert(certChain[len(certChain)-1]) + if _, err := certChain[0].Verify(x509.VerifyOptions{ + Roots: roots, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}, + Intermediates: intermediates, + }); err != nil { + return nil, err + } + return certChain, nil +} diff --git a/pkg/pki/x509/x509_test.go b/pkg/pki/x509/x509_test.go index 19bf50d..3f81762 100644 --- a/pkg/pki/x509/x509_test.go +++ b/pkg/pki/x509/x509_test.go @@ -17,6 +17,7 @@ package x509 import ( "bytes" + "context" "crypto" "crypto/ecdsa" "crypto/ed25519" @@ -27,6 +28,8 @@ import ( "encoding/pem" "strings" "testing" + + "github.com/sigstore/rekor/pkg/signer" ) // Generated with: @@ -206,3 +209,44 @@ func TestSignature_VerifyFail(t *testing.T) { }) } } + +func TestNilCertChainToPEM(t *testing.T) { + certChain := []*x509.Certificate{} + if _, err := CertChainToPEM(certChain); err != nil { + t.Fatal(err) + } +} + +func TestCertChain_Verify(t *testing.T) { + mem, err := signer.NewMemory() + if err != nil { + t.Fatal(err) + } + // A properly created cert chain should encode to PEM OK. + ctx := context.Background() + pk, err := mem.PublicKey(ctx) + if err != nil { + t.Fatal(err) + } + certChain, err := signer.NewTimestampingCertWithSelfSignedCA(pk) + if err != nil { + t.Fatal(err) + } + certChainBytes, err := CertChainToPEM(certChain) + if err != nil { + t.Fatal(err) + } + + // Parse and verify timestamping cert chain + parsedCertChain, err := ParseTimestampCertChain(certChainBytes) + if err != nil { + t.Fatal(err) + } + + // Compare with original + for idx, cert := range parsedCertChain { + if !cert.Equal(certChain[idx]) { + t.Fatal("unexpected error comparing cert chain") + } + } +} diff --git a/pkg/signer/memory.go b/pkg/signer/memory.go index b9689e3..4cfe5e9 100644 --- a/pkg/signer/memory.go +++ b/pkg/signer/memory.go @@ -21,6 +21,12 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "math/big" + "net" + "time" "github.com/pkg/errors" "github.com/sigstore/sigstore/pkg/signature" @@ -33,6 +39,71 @@ type Memory struct { signature.ECDSASignerVerifier } +// create a self-signed CA and generate a timestamping certificate to rekor +func NewTimestampingCertWithSelfSignedCA(pub crypto.PublicKey) ([]*x509.Certificate, error) { + // generate self-signed CA + caPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, errors.Wrap(err, "generating private key") + } + ca := &x509.Certificate{ + SerialNumber: big.NewInt(2019), + Subject: pkix.Name{ + Organization: []string{"Root CA Test"}, + Country: []string{"US"}, + Province: []string{""}, + Locality: []string{"San Francisco"}, + StreetAddress: []string{"Golden Gate Bridge"}, + PostalCode: []string{"94016"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + IsCA: true, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } + caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey) + if err != nil { + return nil, err + } + timestampExt, err := asn1.Marshal([]asn1.ObjectIdentifier{{1, 3, 6, 1, 5, 5, 7, 3, 8}}) + if err != nil { + return nil, err + } + + cert := &x509.Certificate{ + SerialNumber: big.NewInt(1658), + Subject: pkix.Name{ + Organization: []string{"Rekor Test"}, + Country: []string{"US"}, + Province: []string{""}, + Locality: []string{"San Francisco"}, + StreetAddress: []string{"Golden Gate Bridge"}, + PostalCode: []string{"94016"}, + }, + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback}, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}, + KeyUsage: x509.KeyUsageContentCommitment, + IsCA: false, + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{2, 5, 29, 37}, + Critical: true, + Value: timestampExt, + }, + }, + BasicConstraintsValid: true, + } + certBytes, err := x509.CreateCertificate(rand.Reader, cert, ca, pub.(*ecdsa.PublicKey), caPrivKey) + if err != nil { + return nil, err + } + return x509.ParseCertificates(append(certBytes, caBytes...)) +} + func NewMemory() (*Memory, error) { // generate a keypair privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) diff --git a/pkg/signer/memory_test.go b/pkg/signer/memory_test.go index b5de0d1..83bcc20 100644 --- a/pkg/signer/memory_test.go +++ b/pkg/signer/memory_test.go @@ -20,6 +20,7 @@ import ( "context" "crypto" "crypto/ecdsa" + "crypto/x509" "testing" ) @@ -55,4 +56,29 @@ func TestMemory(t *testing.T) { if !ecdsa.VerifyASN1(pk, h.Sum(nil), signature) { t.Fatalf("unable to verify signature") } + + // verify signature using the cert's public key + certChain, err := NewTimestampingCertWithSelfSignedCA(pk) + pkCert, ok := certChain[0].PublicKey.(*ecdsa.PublicKey) + if !ok { + t.Fatalf("cert ecdsa public key: %v", err) + } + if !ecdsa.VerifyASN1(pkCert, h.Sum(nil), signature) { + t.Fatalf("unable to verify signature") + } + // verify that the cert chain is configured for timestamping + roots := x509.NewCertPool() + intermediates := x509.NewCertPool() + for _, cert := range certChain[1:(len(certChain) - 1)] { + intermediates.AddCert(cert) + } + roots.AddCert(certChain[len(certChain)-1]) + _, err = certChain[0].Verify(x509.VerifyOptions{ + Roots: roots, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}, + Intermediates: intermediates, + }) + if err != nil { + t.Fatalf("invalid timestamping cert chain") + } } diff --git a/pkg/util/rfc3161.go b/pkg/util/rfc3161.go new file mode 100644 index 0000000..6d80cd0 --- /dev/null +++ b/pkg/util/rfc3161.go @@ -0,0 +1,242 @@ +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "context" + "crypto" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "fmt" + "math/big" + "time" + + "github.com/sassoftware/relic/lib/pkcs7" + "github.com/sassoftware/relic/lib/pkcs9" + "github.com/sassoftware/relic/lib/x509tools" + "github.com/sigstore/sigstore/pkg/signature" +) + +type GeneralName struct { + Name asn1.RawValue `asn1:"optional,tag:4"` +} + +type IssuerNameAndSerial struct { + IssuerName GeneralName + SerialNumber *big.Int +} + +type EssCertIDv2 struct { + HashAlgorithm pkix.AlgorithmIdentifier `asn1:"optional"` // SHA256 + CertHash []byte + IssuerNameAndSerial IssuerNameAndSerial `asn1:"optional"` +} + +type SigningCertificateV2 struct { + Certs []EssCertIDv2 +} + +func createSigningCertificate(certificate *x509.Certificate) ([]byte, error) { + h := crypto.SHA256.New() // TODO: Get from certificate, defaults to 256 + _, err := h.Write(certificate.Raw) + if err != nil { + return nil, fmt.Errorf("failed to create hash") + } + signingCert := SigningCertificateV2{ + Certs: []EssCertIDv2{{ + CertHash: h.Sum(nil), + IssuerNameAndSerial: IssuerNameAndSerial{ + IssuerName: GeneralName{Name: asn1.RawValue{Tag: 4, Class: 2, IsCompound: true, Bytes: certificate.RawIssuer}}, + SerialNumber: certificate.SerialNumber, + }, + }}, + } + signingCertBytes, err := asn1.Marshal(signingCert) + if err != nil { + return nil, err + } + return signingCertBytes, nil +} + +func marshalCertificates(certs []*x509.Certificate) pkcs7.RawCertificates { + c := make(pkcs7.RawCertificates, len(certs)) + for i, cert := range certs { + c[i] = asn1.RawValue{FullBytes: cert.Raw} + } + return c +} + +func getPKIXPublicKeyAlgorithm(cert x509.Certificate) (*pkix.AlgorithmIdentifier, error) { + identifier := pkix.AlgorithmIdentifier{ + Parameters: asn1.NullRawValue, + } + switch alg := cert.PublicKeyAlgorithm; alg { + case x509.RSA: + identifier.Algorithm = x509tools.OidPublicKeyRSA + case x509.ECDSA: + identifier.Algorithm = x509tools.OidPublicKeyECDSA + case x509.Ed25519: + identifier.Algorithm = asn1.ObjectIdentifier{1, 3, 101, 112} + default: + return nil, fmt.Errorf("unknown public key algorithm") + } + + return &identifier, nil +} + +type TimestampRequestOptions struct { + // The policy that the client expects the TSA to use for creating the timestamp token. + // If no policy is specified the TSA uses its default policy. + TSAPolicyOid asn1.ObjectIdentifier + + // The nonce to specify in the request. + Nonce *big.Int + + // Hash function to use when constructing the timestamp request. Defaults to SHA-256. + Hash crypto.Hash +} + +func TimestampRequestFromDigest(digest []byte, opts TimestampRequestOptions) (*pkcs9.TimeStampReq, error) { + alg, _ := x509tools.PkixDigestAlgorithm(opts.Hash) + msg := pkcs9.TimeStampReq{ + Version: 1, + MessageImprint: pkcs9.MessageImprint{ + HashAlgorithm: alg, + HashedMessage: digest, + }, + CertReq: true, + } + if opts.Nonce != nil { + msg.Nonce = opts.Nonce + } + if opts.TSAPolicyOid != nil { + msg.ReqPolicy = opts.TSAPolicyOid + } + + return &msg, nil +} + +func ParseTimestampRequest(data []byte) (*pkcs9.TimeStampReq, error) { + msg := new(pkcs9.TimeStampReq) + if rest, err := asn1.Unmarshal(data, msg); err != nil { + return nil, fmt.Errorf("error umarshalling request") + } else if len(rest) != 0 { + return nil, fmt.Errorf("error umarshalling request, trailing bytes") + } + return msg, nil +} + +func CreateRfc3161Response(ctx context.Context, req pkcs9.TimeStampReq, certChain []*x509.Certificate, signer signature.Signer) (*pkcs9.TimeStampResp, error) { + // Populate TSTInfo. + genTimeBytes, err := asn1.MarshalWithParams(time.Now(), "generalized") + if err != nil { + return nil, err + } + policy := asn1.ObjectIdentifier{1, 2, 3, 4, 1} + if req.ReqPolicy.String() != "" { + policy = req.ReqPolicy + } + + info := pkcs9.TSTInfo{ + Version: req.Version, + MessageImprint: req.MessageImprint, + // directoryName is tag 4 https://datatracker.ietf.org/doc/html/rfc3280#section-4.2.1.7 + TSA: pkcs9.GeneralName{Value: asn1.RawValue{Tag: 4, Class: 2, IsCompound: true, Bytes: certChain[0].RawSubject}}, + // TODO: Ensure that every (SerialNumber, TSA name) identifies a unique token. + SerialNumber: x509tools.MakeSerial(), + GenTime: asn1.RawValue{FullBytes: genTimeBytes}, + Nonce: req.Nonce, + Policy: policy, + Extensions: req.Extensions, + } + + encoded, err := asn1.Marshal(info) + if err != nil { + return nil, err + } + contentInfo, err := pkcs7.NewContentInfo(pkcs9.OidTSTInfo, encoded) + if err != nil { + return nil, err + } + + // TODO: Does this need to match the hash algorithm in the request? + h := crypto.SHA256.New() + alg, _ := x509tools.PkixDigestAlgorithm(crypto.SHA256) + contentInfoBytes, _ := contentInfo.Bytes() + h.Write(contentInfoBytes) + digest := h.Sum(nil) + + // Create SignerInfo and signature. + signingCert, err := createSigningCertificate(certChain[0]) + if err != nil { + return nil, err + } + attributes := new(pkcs7.AttributeList) + if err := attributes.Add(pkcs7.OidAttributeContentType, contentInfo.ContentType); err != nil { + return nil, err + } + if err := attributes.Add(pkcs7.OidAttributeMessageDigest, digest); err != nil { + return nil, err + } + if err := attributes.Add(asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 47}, signingCert); err != nil { + return nil, err + } + + // The signature is over the entire authenticated attributes, not just the TstInfo. + attrBytes, err := attributes.Bytes() + if err != nil { + return nil, err + } + // Get signature. + signature, _, err := signer.Sign(ctx, attrBytes) + if err != nil { + return nil, err + } + + sigAlg, err := getPKIXPublicKeyAlgorithm(*certChain[0]) + if err != nil { + return nil, err + } + + response := pkcs9.TimeStampResp{ + Status: pkcs9.PKIStatusInfo{ + Status: 0, + }, + TimeStampToken: pkcs7.ContentInfoSignedData{ + ContentType: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}, // id-signedData + Content: pkcs7.SignedData{ + Version: 1, + DigestAlgorithmIdentifiers: []pkix.AlgorithmIdentifier{alg}, + ContentInfo: contentInfo, + Certificates: marshalCertificates(certChain), + CRLs: nil, + SignerInfos: []pkcs7.SignerInfo{{ + Version: 1, + IssuerAndSerialNumber: pkcs7.IssuerAndSerial{ + IssuerName: asn1.RawValue{FullBytes: certChain[0].RawIssuer}, + SerialNumber: certChain[0].SerialNumber, + }, + DigestAlgorithm: alg, + DigestEncryptionAlgorithm: *sigAlg, + AuthenticatedAttributes: *attributes, + EncryptedDigest: signature, + }}, + }, + }, + } + return &response, nil +} diff --git a/pkg/util/rfc3161_test.go b/pkg/util/rfc3161_test.go new file mode 100644 index 0000000..98c9dfa --- /dev/null +++ b/pkg/util/rfc3161_test.go @@ -0,0 +1,164 @@ +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "bytes" + "context" + "crypto" + "encoding/asn1" + "fmt" + "io/ioutil" + "math/big" + "testing" + + "github.com/sassoftware/relic/lib/pkcs9" + "github.com/sassoftware/relic/lib/x509tools" + "github.com/sigstore/rekor/pkg/signer" +) + +func TestCreateTimestampRequest(t *testing.T) { + type TestCase struct { + caseDesc string + entry []byte + expectSuccess bool + nonce *big.Int + policy asn1.ObjectIdentifier + } + + fileBytes, _ := ioutil.ReadFile("../../tests/test_file.txt") + testCases := []TestCase{ + { + caseDesc: "valid timestamp request", + entry: fileBytes, + expectSuccess: true, + nonce: x509tools.MakeSerial(), + }, + { + caseDesc: "valid timestamp request no nonce", + entry: fileBytes, + expectSuccess: true, + }, + { + caseDesc: "valid timestamp request with TSA policy id", + entry: fileBytes, + expectSuccess: true, + policy: asn1.ObjectIdentifier{1, 2, 3, 4, 5}, + }, + } + for _, tc := range testCases { + opts := TimestampRequestOptions{ + Hash: crypto.SHA256, + Nonce: tc.nonce, + TSAPolicyOid: tc.policy, + } + h := opts.Hash.New() + h.Write(tc.entry) + digest := h.Sum(nil) + req, err := TimestampRequestFromDigest(digest, opts) + if (err == nil) != tc.expectSuccess { + t.Errorf("unexpected error in test case '%v': %v", tc.caseDesc, err) + } + // Validate that the message hash matches the original file has. + if !bytes.Equal(digest, req.MessageImprint.HashedMessage) { + t.Errorf("unexpected error in test case '%v': %v", tc.caseDesc, "hashes do not match") + } + if tc.nonce != nil { + if tc.nonce.Cmp(req.Nonce) != 0 { + t.Errorf("unexpected error in test case '%v': %v", tc.caseDesc, "nonce does not match") + } + } else if req.Nonce != nil { + t.Errorf("unexpected error in test case '%v': %v", tc.caseDesc, fmt.Sprintf("nonce does not match got (%s) expected nil", req.Nonce.String())) + } + if tc.policy != nil { + if !tc.policy.Equal(req.ReqPolicy) { + t.Errorf("unexpected error in test case '%v': %v", tc.caseDesc, "policy does not match") + } + } else if req.ReqPolicy != nil { + t.Errorf("unexpected error in test case '%v': %v", tc.caseDesc, "policy does not match") + } + } +} + +func TestParseTimestampRequest(t *testing.T) { + type TestCase struct { + caseDesc string + entry []byte + expectSuccess bool + } + + requestBytes, _ := ioutil.ReadFile("../../tests/test_request.tsq") + fileBytes, _ := ioutil.ReadFile("../../tests/test_file.txt") + + testCases := []TestCase{ + { + caseDesc: "valid timestamp request", + entry: requestBytes, + expectSuccess: true, + }, + { + caseDesc: "invalid timestamp request", + entry: fileBytes, + expectSuccess: false, + }, + } + + for _, tc := range testCases { + if _, err := ParseTimestampRequest(tc.entry); (err == nil) != tc.expectSuccess { + t.Errorf("unexpected error in test case '%v': %v", tc.caseDesc, err) + } + } +} + +// Create an in-memory CA and TSA and verify the response. +func TestCreateRFC3161Response(t *testing.T) { + ctx := context.Background() + mem, err := signer.NewMemory() + if err != nil { + t.Error(err) + } + pk, err := mem.PublicKey(ctx) + if err != nil { + t.Fatal(err) + } + certChain, err := signer.NewTimestampingCertWithSelfSignedCA(pk) + if err != nil { + t.Error(err) + } + + fileBytes, _ := ioutil.ReadFile("../../tests/test_file.txt") + opts := TimestampRequestOptions{ + Hash: crypto.SHA256, + Nonce: x509tools.MakeSerial(), + } + h := opts.Hash.New() + h.Write(fileBytes) + digest := h.Sum(nil) + req, err := TimestampRequestFromDigest(digest, opts) + if err != nil { + t.Error(err) + } + + resp, err := CreateRfc3161Response(ctx, *req, certChain, mem) + if err != nil { + t.Error(err) + } + + _, err = pkcs9.Verify(&resp.TimeStampToken, fileBytes, certChain) + if err != nil { + t.Error(err) + } +} diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 8bb0ee1..7510ebc 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -18,6 +18,7 @@ package e2e import ( + "bytes" "context" "crypto" "crypto/ecdsa" @@ -40,7 +41,9 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/sigstore/rekor/cmd/rekor-cli/app" + "github.com/sigstore/rekor/pkg/generated/client" "github.com/sigstore/rekor/pkg/generated/client/entries" + "github.com/sigstore/rekor/pkg/generated/client/timestamp" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/signer" rekord "github.com/sigstore/rekor/pkg/types/rekord/v0.0.1" @@ -476,6 +479,70 @@ func TestSignedEntryTimestamp(t *testing.T) { } } +func TestTimestampResponseCLI(t *testing.T) { + ctx := context.Background() + payload := []byte("i am a cat") + // Create files for data, response, and CA. + + filePath := filepath.Join(t.TempDir(), "file.txt") + CAPath := filepath.Join(t.TempDir(), "ca.pem") + responsePath := filepath.Join(t.TempDir(), "response.tsr") + if err := ioutil.WriteFile(filePath, payload, 0644); err != nil { + t.Fatal(err) + } + + out := runCli(t, "timestamp", "--artifact", filePath, "--out", responsePath) + outputContains(t, out, "Wrote response to") + + rekorClient, err := app.GetRekorClient("http://localhost:3000") + if err != nil { + t.Fatal(err) + } + + certChain := rekorTimestampCertChain(t, ctx, rekorClient) + var rootCABytes bytes.Buffer + if err := pem.Encode(&rootCABytes, &pem.Block{Type: "CERTIFICATE", Bytes: certChain[len(certChain)-1].Raw}); err != nil { + t.Fatal(err) + } + if err := ioutil.WriteFile(CAPath, rootCABytes.Bytes(), 0644); err != nil { + t.Fatal(err) + } + + // Use openssl to verify + cmd := exec.Command("openssl", "ts", "-verify", "-data", filePath, "-in", responsePath, "-CAfile", CAPath) + errs := &bytes.Buffer{} + + cmd.Stderr = errs + if err := cmd.Run(); err != nil { + // Check that the result was OK. + if len(errs.Bytes()) > 0 { + t.Fatalf("error verifying with openssl %s", errs.String()) + } + + } + + // Now try with the digest. + h := crypto.SHA256.New() + if _, err := h.Write(payload); err != nil { + t.Fatalf("error creating digest") + } + digest := h.Sum(nil) + hexDigest := hex.EncodeToString(digest) + out = runCli(t, "timestamp", "--artifact-hash", hexDigest, "--out", responsePath) + outputContains(t, out, "Wrote response to") + cmd = exec.Command("openssl", "ts", "-verify", "-digest", hexDigest, "-in", responsePath, "-CAfile", CAPath) + errs = &bytes.Buffer{} + + cmd.Stderr = errs + if err := cmd.Run(); err != nil { + // Check that the result was OK. + if len(errs.Bytes()) > 0 { + t.Fatalf("error verifying with openssl %s", errs.String()) + } + + } +} + func TestGetNonExistantIndex(t *testing.T) { // this index is extremely likely to not exist out := runCliErr(t, "get", "--log-index", "100000000") @@ -487,3 +554,31 @@ func TestGetNonExistantUUID(t *testing.T) { out := runCliErr(t, "get", "--uuid", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") outputContains(t, out, "404") } + +func rekorTimestampCertChain(t *testing.T, ctx context.Context, c *client.Rekor) []*x509.Certificate { + resp, err := c.Timestamp.GetTimestampCertChain(×tamp.GetTimestampCertChainParams{Context: ctx}) + if err != nil { + t.Fatal(err) + } + certChainBytes := []byte(resp.GetPayload()) + + var block *pem.Block + block, certChainBytes = pem.Decode(certChainBytes) + certificates := []*x509.Certificate{} + for ; block != nil; block, certChainBytes = pem.Decode(certChainBytes) { + if block.Type == "CERTIFICATE" { + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + t.Fatal(err) + } + certificates = append(certificates, cert) + } else { + t.Fatal(err) + } + } + + if len(certificates) == 0 { + t.Fatal("could not find certificates") + } + return certificates +} diff --git a/tests/test_request.tsq b/tests/test_request.tsq new file mode 100644 index 0000000000000000000000000000000000000000..134012f642a2087f73c3e4842e3ee8c436d0a38d GIT binary patch literal 59 zcmV-B0L1?=IRXIzFflL<1_@w>NC9O71OfpC00bcI>aX+hcS_QThI4leZDa|4IUB=b R*Vg+qm@VH9{TRyu0srvx6(axu literal 0 HcmV?d00001 -- GitLab