From bbe071ed654d385e2557e90657bfab5a0b0a32fc Mon Sep 17 00:00:00 2001
From: Bob Callaway <bcallawa@redhat.com>
Date: Fri, 4 Dec 2020 20:11:52 -0500
Subject: [PATCH] add log_proof impl

---
 Makefile                                      |   7 +-
 cmd/cli/app/log_info.go                       |  15 ++-
 cmd/cli/app/log_proof.go                      | 105 ++++++++++++++++++
 cmd/cli/app/root.go                           |  23 ++++
 cmd/cli/app/upload.go                         |  12 ++
 cmd/cli/app/verify.go                         |  11 ++
 openapi.yaml                                  |  33 +++---
 pkg/api/tlog.go                               |  38 ++++++-
 pkg/api/trillian_client.go                    |  35 +++++-
 .../client/tlog/get_log_proof_parameters.go   |  10 +-
 .../client/tlog/get_log_proof_responses.go    |  39 +++++++
 pkg/generated/models/consistency_proof.go     |  32 ++----
 pkg/generated/models/inclusion_proof.go       |  12 +-
 pkg/generated/models/log_entry.go             |   5 +
 pkg/generated/models/log_info.go              |  10 ++
 pkg/generated/models/search_log_query.go      |   5 +
 pkg/generated/restapi/embedded_spec.go        |  91 +++++++++------
 .../get_log_entry_by_index_parameters.go      |  15 +++
 .../tlog/get_log_proof_parameters.go          |  14 +--
 .../tlog/get_log_proof_responses.go           |  44 ++++++++
 20 files changed, 459 insertions(+), 97 deletions(-)
 create mode 100644 cmd/cli/app/log_proof.go

diff --git a/Makefile b/Makefile
index ad3317a..6c40e6e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,8 @@
 .PHONY: all test clean lint gosec
 
+NONGENSRCS := $(shell find cmd -name "*.go") $(shell find pkg -name "*.go"|grep -v "pkg/generated") pkg/generated/restapi/configure_rekor_server.go
 GENSRCS := $(shell find pkg/generated -name "*.go"|grep -v "configure_rekor_server.go")
-SRCS := $(wildcard cmd/**/**.go) ${GENSRCS} $(shell find pkg -name "*.go"|grep -v "pkg/generated") pkg/generated/restapi/configure_rekor_server.go
+SRCS := $(NONGENSRCS) ${GENSRCS}
 
 all: cli server
 
@@ -15,10 +16,10 @@ lint: $(SRCS)
 gosec: $(SRCS)
 	$(GOBIN)/gosec ./...
 
-cli: $(SRCS)
+cli: $(SRCS) openapi.yaml
 	go build ./cmd/cli
 
-server: $(SRCS)
+server: $(SRCS) openapi.yaml
 	go build ./cmd/server
 
 test:
diff --git a/cmd/cli/app/log_info.go b/cmd/cli/app/log_info.go
index d2132e2..45734f0 100644
--- a/cmd/cli/app/log_info.go
+++ b/cmd/cli/app/log_info.go
@@ -18,18 +18,27 @@ package app
 import (
 	"fmt"
 	"net/url"
+	"os"
 
 	"github.com/projectrekor/rekor/pkg/generated/client"
+	"github.com/projectrekor/rekor/pkg/generated/client/tlog"
 	"github.com/projectrekor/rekor/pkg/log"
 	"github.com/spf13/cobra"
 	"github.com/spf13/viper"
 )
 
-// verifyCmd represents the get command
+// logInfoCmd represents the current information about the transparency log
 var logInfoCmd = &cobra.Command{
 	Use:   "loginfo",
 	Short: "Rekor loginfo command",
 	Long:  `Prints info about the transparency log`,
+	PreRun: func(cmd *cobra.Command, args []string) {
+		if err := validateRekorServerURL(); err != nil {
+			log.Logger.Error(err)
+			_ = cmd.Help()
+			os.Exit(1)
+		}
+	},
 	Run: func(cmd *cobra.Command, args []string) {
 		log := log.Logger
 		rekorServer := viper.GetString("rekor_server")
@@ -42,7 +51,9 @@ var logInfoCmd = &cobra.Command{
 		tc := client.DefaultTransportConfig().WithHost(url.Host)
 		rc := client.NewHTTPClientWithConfig(nil, tc)
 
-		result, err := rc.Tlog.GetLogInfo(nil)
+		params := tlog.NewGetLogInfoParams()
+
+		result, err := rc.Tlog.GetLogInfo(params)
 		if err != nil {
 			log.Fatal(err)
 		}
diff --git a/cmd/cli/app/log_proof.go b/cmd/cli/app/log_proof.go
new file mode 100644
index 0000000..edd732a
--- /dev/null
+++ b/cmd/cli/app/log_proof.go
@@ -0,0 +1,105 @@
+/*
+Copyright © 2020 Bob Callaway <bcallawa@redhat.com>
+
+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 (
+	"fmt"
+	"net/url"
+	"os"
+
+	"github.com/projectrekor/rekor/pkg/generated/client"
+	"github.com/projectrekor/rekor/pkg/generated/client/tlog"
+	"github.com/projectrekor/rekor/pkg/log"
+	"github.com/spf13/cobra"
+	"github.com/spf13/viper"
+)
+
+// logProof represents the consistency proof
+var logProofCmd = &cobra.Command{
+	Use:   "logproof",
+	Short: "Rekor logproof command",
+	Long:  `Prints information required to compute the consistency proof of the transparency log`,
+	PreRun: func(cmd *cobra.Command, args []string) {
+		// these are bound here so that they are not overwritten by other commands
+		if err := viper.BindPFlags(cmd.Flags()); err != nil {
+			log.Logger.Fatal("Error initializing cmd line args: ", err)
+		}
+		if err := validateRekorServerURL(); err != nil {
+			log.Logger.Error(err)
+			_ = cmd.Help()
+			os.Exit(1)
+		}
+		if viper.GetUint64("first-size") > viper.GetUint64("last-size") {
+			log.Logger.Error("last-size must be >= to first-size")
+			os.Exit(1)
+		}
+		if viper.GetUint64("first-size") == 0 {
+			log.Logger.Error("first-size must be > 0")
+			os.Exit(1)
+		}
+		if viper.GetUint64("last-size") == 0 {
+			log.Logger.Error("last-size must be > 0")
+			os.Exit(1)
+		}
+	},
+	Run: func(cmd *cobra.Command, args []string) {
+		log := log.Logger
+		rekorServer := viper.GetString("rekor_server")
+
+		url, err := url.Parse(rekorServer)
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		tc := client.DefaultTransportConfig().WithHost(url.Host)
+		rc := client.NewHTTPClientWithConfig(nil, tc)
+
+		firstSize := int64(viper.GetUint64("first-size"))
+		lastSize := int64(viper.GetUint64("last-size"))
+
+		params := tlog.NewGetLogProofParams()
+		params.FirstSize = &firstSize
+		params.LastSize = lastSize
+
+		result, err := rc.Tlog.GetLogProof(params)
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		consistencyProof := result.GetPayload()
+		fmt.Printf("Root Hash: %v\n", *consistencyProof.RootHash)
+		fmt.Printf("Hashes: [")
+		for i, hash := range consistencyProof.Hashes {
+			if i+1 == len(consistencyProof.Hashes) {
+				fmt.Printf("%v", hash)
+			} else {
+				fmt.Printf("%v,", hash)
+			}
+		}
+		fmt.Printf("]\n")
+	},
+}
+
+func init() {
+	logProofCmd.Flags().Uint64("first-size", 1, "the size of the log where the proof should begin")
+	logProofCmd.Flags().Uint64("last-size", 1, "the size of the log where the proof should end")
+	if err := logProofCmd.MarkFlagRequired("last-size"); err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+
+	rootCmd.AddCommand(logProofCmd)
+}
diff --git a/cmd/cli/app/root.go b/cmd/cli/app/root.go
index 78c31f9..b7b5cf8 100644
--- a/cmd/cli/app/root.go
+++ b/cmd/cli/app/root.go
@@ -16,8 +16,11 @@ limitations under the License.
 package app
 
 import (
+	"errors"
 	"fmt"
+	"net/url"
 	"os"
+	"strings"
 
 	"github.com/spf13/cobra"
 
@@ -88,3 +91,23 @@ func initConfig() {
 		fmt.Println("Using config file:", viper.ConfigFileUsed())
 	}
 }
+
+func validateRekorServerURL() error {
+	rekorServerURL := viper.GetString("rekor_server")
+	if rekorServerURL != "" {
+		url, err := url.Parse(rekorServerURL)
+		if err != nil {
+			return fmt.Errorf("malformed rekor_server URL: %w", err)
+		}
+		if !url.IsAbs() {
+			return errors.New("rekor_server URL must be absolute")
+		}
+		lowercaseScheme := strings.ToLower(url.Scheme)
+		if lowercaseScheme != "http" && lowercaseScheme != "https" {
+			return errors.New("rekor_server must be a valid HTTP or HTTPS URL")
+		}
+	} else {
+		return errors.New("rekor_server must be specified")
+	}
+	return nil
+}
diff --git a/cmd/cli/app/upload.go b/cmd/cli/app/upload.go
index 124b40d..82b8ea3 100644
--- a/cmd/cli/app/upload.go
+++ b/cmd/cli/app/upload.go
@@ -21,6 +21,7 @@ import (
 	"encoding/json"
 	"io/ioutil"
 	"net/http"
+	"os"
 	"path/filepath"
 	"time"
 
@@ -47,6 +48,17 @@ var uploadCmd = &cobra.Command{
 	Short: "Upload a rekord file",
 	Long: `This command takes the public key, signature and URL
 of the release artifact and uploads it to the rekor server.`,
+	PreRun: func(cmd *cobra.Command, args []string) {
+		// these are bound here so that they are not overwritten by other commands
+		if err := viper.BindPFlags(cmd.Flags()); err != nil {
+			log.Logger.Fatal("Error initializing cmd line args: ", err)
+		}
+		if err := validateRekorServerURL(); err != nil {
+			log.Logger.Error(err)
+			_ = cmd.Help()
+			os.Exit(1)
+		}
+	},
 	Run: func(cmd *cobra.Command, args []string) {
 		log := log.Logger
 		rekorServerURL := viper.GetString("rekor_server") + "/api/v1/add"
diff --git a/cmd/cli/app/verify.go b/cmd/cli/app/verify.go
index 4c1c093..35d28b4 100644
--- a/cmd/cli/app/verify.go
+++ b/cmd/cli/app/verify.go
@@ -35,6 +35,17 @@ var verifyCmd = &cobra.Command{
 	Use:   "verify",
 	Short: "Rekor verify command",
 	Long:  `Verifies a signature and checks that it exists in the transparency log`,
+	PreRun: func(cmd *cobra.Command, args []string) {
+		// these are bound here so that they are not overwritten by other commands
+		if err := viper.BindPFlags(cmd.Flags()); err != nil {
+			log.Logger.Fatal("Error initializing cmd line args: ", err)
+		}
+		if err := validateRekorServerURL(); err != nil {
+			log.Logger.Error(err)
+			_ = cmd.Help()
+			os.Exit(1)
+		}
+	},
 	Run: func(cmd *cobra.Command, args []string) {
 		log := log.Logger
 		rekorServer := viper.GetString("rekor_server")
diff --git a/openapi.yaml b/openapi.yaml
index a2aa6bb..daf0530 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -36,22 +36,24 @@ paths:
         - in: query
           name: firstSize
           type: integer
-          default: 0
-          minimum: 0
+          default: 1
+          minimum: 1
           description: >
-            The size of the tree that you wish to prove consistency from (0 means the beginning of the log)
-            Defaults to 0 if not specified
+            The size of the tree that you wish to prove consistency from (1 means the beginning of the log)
+            Defaults to 1 if not specified
         - in: query
           name: lastSize
           type: integer
           required: true
-          minimum: 0
+          minimum: 1
           description: The size of the tree that you wish to prove consistency to
       responses:
         200:
           description: All hashes required to compute the consistency proof
           schema:
             $ref: '#/definitions/ConsistencyProof'
+        400:
+          $ref: '#/responses/BadContent'
         default:
           $ref: '#/responses/InternalServerError'
 
@@ -93,6 +95,7 @@ paths:
           name: logIndex
           type: integer
           required: true
+          minimum: 1
           description: specifies the index of the entry in the transparency log to be retrieved
       responses:
         200:
@@ -245,6 +248,7 @@ definitions:
       properties:
         logIndex:
           type: integer
+          minimum: 1
         signature:
           type: object
           properties:
@@ -285,6 +289,7 @@ definitions:
         items:
           type: integer
           minItems: 1
+          minimum: 1
       entries:
         type: array
         items:
@@ -297,9 +302,11 @@ definitions:
       rootHash:
         type: string
         description: The current hash value stored at the root of the merkle tree
+        pattern: '^[0-9a-fA-F]{64}$'
       treeSize:
         type: integer
         description: The current number of nodes in the merkle tree
+        minimum: 1
     required:
       - rootHash
       - treeSize
@@ -307,20 +314,18 @@ definitions:
   ConsistencyProof:
     type: object
     properties:
-      firstRootHash:
-        type: string
-        description: The hash value stored at the root of the merkle tree at the first size specified
-      lastRootHash:
+      rootHash:
         type: string
-        description: The hash value stored at the root of the merkle tree at the last size specified
+        description: The hash value stored at the root of the merkle tree at time the proof was generated
+        pattern: '^[0-9a-fA-F]{64}$'
       hashes:
         type: array
         items:
           type: string
+          description: SHA256 hash value expressed in hexadecimal format
           pattern: '^[0-9a-fA-F]{64}$'
     required:
-      - firstRootHash
-      - lastRootHash
+      - rootHash
       - hashes
 
   InclusionProof:
@@ -329,13 +334,15 @@ definitions:
       logIndex:
         type: integer
         description: The index of the entry in the transparency log
+        minimum: 1
       rootHash:
-        description: The hash value stored at the root of the merkle tree
+        description: The hash value stored at the root of the merkle tree at the time the proof was generated
         type: string
         pattern: '^[0-9a-fA-F]{64}$'
       treeSize:
         type: integer
         description: The size of the merkle tree at the time the inclusion proof was generated
+        minimum: 1
       hashes:
         description: A list of hashes required to compute the inclusion proof, sorted in order from leaf to root
         type: array
diff --git a/pkg/api/tlog.go b/pkg/api/tlog.go
index 7916510..777ce1f 100644
--- a/pkg/api/tlog.go
+++ b/pkg/api/tlog.go
@@ -22,7 +22,7 @@ import (
 	"github.com/projectrekor/rekor/pkg/generated/models"
 
 	"github.com/go-openapi/runtime/middleware"
-	ttypes "github.com/google/trillian/types"
+	"github.com/google/trillian/types"
 	"github.com/projectrekor/rekor/pkg/generated/restapi/operations/tlog"
 )
 
@@ -36,7 +36,7 @@ func GetLogInfoHandler(params tlog.GetLogInfoParams) middleware.Responder {
 		return tlog.NewGetLogInfoDefault(http.StatusInternalServerError).WithPayload(errorMsg("title", "type", err.Error(), http.StatusInternalServerError))
 	}
 
-	var root ttypes.LogRootV1
+	var root types.LogRootV1
 	if err := root.UnmarshalBinary(resp.getLatestResult.SignedLogRoot.LogRoot); err != nil {
 		return tlog.NewGetLogInfoDefault(http.StatusInternalServerError).WithPayload(errorMsg("title", "type", err.Error(), http.StatusInternalServerError))
 	}
@@ -52,5 +52,37 @@ func GetLogInfoHandler(params tlog.GetLogInfoParams) middleware.Responder {
 }
 
 func GetLogProofHandler(params tlog.GetLogProofParams) middleware.Responder {
-	return middleware.NotImplemented("getLogProof not yet implemented")
+	if *params.FirstSize > params.LastSize {
+		return tlog.NewGetLogProofBadRequest().WithPayload(errorMsg("title", "type", "firstSize must be greater than or equal to lastSize", http.StatusBadRequest))
+	}
+	api, _ := NewAPI()
+
+	server := serverInstance(api.logClient, api.tLogID)
+
+	cpResp, err := server.getConsistencyProof(api.tLogID, *params.FirstSize, params.LastSize)
+	if err != nil {
+		return tlog.NewGetLogProofDefault(http.StatusInternalServerError).WithPayload(errorMsg("title", "type", err.Error(), http.StatusInternalServerError))
+	}
+	result := cpResp.getConsistencyProofResult
+
+	var root types.LogRootV1
+	if err := root.UnmarshalBinary(result.SignedLogRoot.LogRoot); err != nil {
+		return tlog.NewGetLogProofDefault(http.StatusInternalServerError).WithPayload(errorMsg("title", "type", err.Error(), http.StatusInternalServerError))
+	}
+
+	hashString := hex.EncodeToString(root.RootHash)
+	proofHashes := []string{}
+
+	if proof := result.GetProof(); proof != nil {
+		for _, hash := range proof.Hashes {
+			proofHashes = append(proofHashes, hex.EncodeToString(hash))
+		}
+	}
+
+	consistencyProof := models.ConsistencyProof{
+		RootHash: &hashString,
+		Hashes:   proofHashes,
+	}
+
+	return tlog.NewGetLogProofOK().WithPayload(&consistencyProof)
 }
diff --git a/pkg/api/trillian_client.go b/pkg/api/trillian_client.go
index 30a8364..2ce71bc 100644
--- a/pkg/api/trillian_client.go
+++ b/pkg/api/trillian_client.go
@@ -41,11 +41,12 @@ type trillianclient struct {
 }
 
 type Response struct {
-	status               codes.Code
-	getLeafResult        *trillian.GetLeavesByHashResponse
-	getProofResult       *trillian.GetInclusionProofByHashResponse
-	getLeafByIndexResult *trillian.GetLeavesByIndexResponse
-	getLatestResult      *trillian.GetLatestSignedLogRootResponse
+	status                    codes.Code
+	getLeafResult             *trillian.GetLeavesByHashResponse
+	getProofResult            *trillian.GetInclusionProofByHashResponse
+	getLeafByIndexResult      *trillian.GetLeavesByIndexResponse
+	getLatestResult           *trillian.GetLatestSignedLogRootResponse
+	getConsistencyProofResult *trillian.GetConsistencyProofResponse
 }
 
 func serverInstance(client trillian.TrillianLogClient, tLogID int64) *trillianclient {
@@ -174,6 +175,9 @@ func (s *trillianclient) getLatest(tLogID int64, leafSizeInt int64) (*Response,
 			LogId:         tLogID,
 			FirstTreeSize: leafSizeInt,
 		})
+	if err != nil {
+		return nil, err
+	}
 
 	return &Response{
 		status:          status.Code(err),
@@ -181,6 +185,27 @@ func (s *trillianclient) getLatest(tLogID int64, leafSizeInt int64) (*Response,
 	}, nil
 }
 
+func (s *trillianclient) getConsistencyProof(tLogID int64, firstSize, lastSize int64) (*Response, error) {
+
+	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
+	defer cancel()
+
+	resp, err := s.client.GetConsistencyProof(ctx,
+		&trillian.GetConsistencyProofRequest{
+			LogId:          tLogID,
+			FirstTreeSize:  firstSize,
+			SecondTreeSize: lastSize,
+		})
+	if err != nil {
+		return nil, err
+	}
+
+	return &Response{
+		status:                    status.Code(err),
+		getConsistencyProofResult: resp,
+	}, nil
+}
+
 func createAndInitTree(ctx context.Context, adminClient trillian.TrillianAdminClient, logClient trillian.TrillianLogClient) (*trillian.Tree, error) {
 	// First look for and use an existing tree
 	trees, err := adminClient.ListTrees(ctx, &trillian.ListTreesRequest{})
diff --git a/pkg/generated/client/tlog/get_log_proof_parameters.go b/pkg/generated/client/tlog/get_log_proof_parameters.go
index 4e97a44..736e434 100644
--- a/pkg/generated/client/tlog/get_log_proof_parameters.go
+++ b/pkg/generated/client/tlog/get_log_proof_parameters.go
@@ -21,7 +21,7 @@ import (
 // with the default values initialized.
 func NewGetLogProofParams() *GetLogProofParams {
 	var (
-		firstSizeDefault = int64(0)
+		firstSizeDefault = int64(1)
 	)
 	return &GetLogProofParams{
 		FirstSize: &firstSizeDefault,
@@ -34,7 +34,7 @@ func NewGetLogProofParams() *GetLogProofParams {
 // with the default values initialized, and the ability to set a timeout on a request
 func NewGetLogProofParamsWithTimeout(timeout time.Duration) *GetLogProofParams {
 	var (
-		firstSizeDefault = int64(0)
+		firstSizeDefault = int64(1)
 	)
 	return &GetLogProofParams{
 		FirstSize: &firstSizeDefault,
@@ -47,7 +47,7 @@ func NewGetLogProofParamsWithTimeout(timeout time.Duration) *GetLogProofParams {
 // with the default values initialized, and the ability to set a context for a request
 func NewGetLogProofParamsWithContext(ctx context.Context) *GetLogProofParams {
 	var (
-		firstSizeDefault = int64(0)
+		firstSizeDefault = int64(1)
 	)
 	return &GetLogProofParams{
 		FirstSize: &firstSizeDefault,
@@ -60,7 +60,7 @@ func NewGetLogProofParamsWithContext(ctx context.Context) *GetLogProofParams {
 // with the default values initialized, and the ability to set a custom HTTPClient for a request
 func NewGetLogProofParamsWithHTTPClient(client *http.Client) *GetLogProofParams {
 	var (
-		firstSizeDefault = int64(0)
+		firstSizeDefault = int64(1)
 	)
 	return &GetLogProofParams{
 		FirstSize:  &firstSizeDefault,
@@ -74,7 +74,7 @@ for the get log proof operation typically these are written to a http.Request
 type GetLogProofParams struct {
 
 	/*FirstSize
-	  The size of the tree that you wish to prove consistency from (0 means the beginning of the log) Defaults to 0 if not specified
+	  The size of the tree that you wish to prove consistency from (1 means the beginning of the log) Defaults to 1 if not specified
 
 
 	*/
diff --git a/pkg/generated/client/tlog/get_log_proof_responses.go b/pkg/generated/client/tlog/get_log_proof_responses.go
index d99b1d8..d542d73 100644
--- a/pkg/generated/client/tlog/get_log_proof_responses.go
+++ b/pkg/generated/client/tlog/get_log_proof_responses.go
@@ -29,6 +29,12 @@ func (o *GetLogProofReader) ReadResponse(response runtime.ClientResponse, consum
 			return nil, err
 		}
 		return result, nil
+	case 400:
+		result := NewGetLogProofBadRequest()
+		if err := result.readResponse(response, consumer, o.formats); err != nil {
+			return nil, err
+		}
+		return nil, result
 	default:
 		result := NewGetLogProofDefault(response.Code())
 		if err := result.readResponse(response, consumer, o.formats); err != nil {
@@ -74,6 +80,39 @@ func (o *GetLogProofOK) readResponse(response runtime.ClientResponse, consumer r
 	return nil
 }
 
+// NewGetLogProofBadRequest creates a GetLogProofBadRequest with default headers values
+func NewGetLogProofBadRequest() *GetLogProofBadRequest {
+	return &GetLogProofBadRequest{}
+}
+
+/*GetLogProofBadRequest handles this case with default header values.
+
+The content supplied to the server was invalid
+*/
+type GetLogProofBadRequest struct {
+	Payload *models.Error
+}
+
+func (o *GetLogProofBadRequest) Error() string {
+	return fmt.Sprintf("[GET /api/v1/log/proof][%d] getLogProofBadRequest  %+v", 400, o.Payload)
+}
+
+func (o *GetLogProofBadRequest) GetPayload() *models.Error {
+	return o.Payload
+}
+
+func (o *GetLogProofBadRequest) 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
+}
+
 // NewGetLogProofDefault creates a GetLogProofDefault with default headers values
 func NewGetLogProofDefault(code int) *GetLogProofDefault {
 	return &GetLogProofDefault{
diff --git a/pkg/generated/models/consistency_proof.go b/pkg/generated/models/consistency_proof.go
index 2da0f59..5b50e27 100644
--- a/pkg/generated/models/consistency_proof.go
+++ b/pkg/generated/models/consistency_proof.go
@@ -19,32 +19,25 @@ import (
 // swagger:model ConsistencyProof
 type ConsistencyProof struct {
 
-	// The hash value stored at the root of the merkle tree at the first size specified
-	// Required: true
-	FirstRootHash *string `json:"firstRootHash"`
-
 	// hashes
 	// Required: true
 	Hashes []string `json:"hashes"`
 
-	// The hash value stored at the root of the merkle tree at the last size specified
+	// The hash value stored at the root of the merkle tree at time the proof was generated
 	// Required: true
-	LastRootHash *string `json:"lastRootHash"`
+	// Pattern: ^[0-9a-fA-F]{64}$
+	RootHash *string `json:"rootHash"`
 }
 
 // Validate validates this consistency proof
 func (m *ConsistencyProof) Validate(formats strfmt.Registry) error {
 	var res []error
 
-	if err := m.validateFirstRootHash(formats); err != nil {
-		res = append(res, err)
-	}
-
 	if err := m.validateHashes(formats); err != nil {
 		res = append(res, err)
 	}
 
-	if err := m.validateLastRootHash(formats); err != nil {
+	if err := m.validateRootHash(formats); err != nil {
 		res = append(res, err)
 	}
 
@@ -54,15 +47,6 @@ func (m *ConsistencyProof) Validate(formats strfmt.Registry) error {
 	return nil
 }
 
-func (m *ConsistencyProof) validateFirstRootHash(formats strfmt.Registry) error {
-
-	if err := validate.Required("firstRootHash", "body", m.FirstRootHash); err != nil {
-		return err
-	}
-
-	return nil
-}
-
 func (m *ConsistencyProof) validateHashes(formats strfmt.Registry) error {
 
 	if err := validate.Required("hashes", "body", m.Hashes); err != nil {
@@ -80,9 +64,13 @@ func (m *ConsistencyProof) validateHashes(formats strfmt.Registry) error {
 	return nil
 }
 
-func (m *ConsistencyProof) validateLastRootHash(formats strfmt.Registry) error {
+func (m *ConsistencyProof) validateRootHash(formats strfmt.Registry) error {
+
+	if err := validate.Required("rootHash", "body", m.RootHash); err != nil {
+		return err
+	}
 
-	if err := validate.Required("lastRootHash", "body", m.LastRootHash); err != nil {
+	if err := validate.Pattern("rootHash", "body", string(*m.RootHash), `^[0-9a-fA-F]{64}$`); err != nil {
 		return err
 	}
 
diff --git a/pkg/generated/models/inclusion_proof.go b/pkg/generated/models/inclusion_proof.go
index 061f59d..66c8e8e 100644
--- a/pkg/generated/models/inclusion_proof.go
+++ b/pkg/generated/models/inclusion_proof.go
@@ -25,15 +25,17 @@ type InclusionProof struct {
 
 	// The index of the entry in the transparency log
 	// Required: true
+	// Minimum: 1
 	LogIndex *int64 `json:"logIndex"`
 
-	// The hash value stored at the root of the merkle tree
+	// The hash value stored at the root of the merkle tree at the time the proof was generated
 	// Required: true
 	// Pattern: ^[0-9a-fA-F]{64}$
 	RootHash *string `json:"rootHash"`
 
 	// The size of the merkle tree at the time the inclusion proof was generated
 	// Required: true
+	// Minimum: 1
 	TreeSize *int64 `json:"treeSize"`
 }
 
@@ -86,6 +88,10 @@ func (m *InclusionProof) validateLogIndex(formats strfmt.Registry) error {
 		return err
 	}
 
+	if err := validate.MinimumInt("logIndex", "body", int64(*m.LogIndex), 1, false); err != nil {
+		return err
+	}
+
 	return nil
 }
 
@@ -108,6 +114,10 @@ func (m *InclusionProof) validateTreeSize(formats strfmt.Registry) error {
 		return err
 	}
 
+	if err := validate.MinimumInt("treeSize", "body", int64(*m.TreeSize), 1, false); err != nil {
+		return err
+	}
+
 	return nil
 }
 
diff --git a/pkg/generated/models/log_entry.go b/pkg/generated/models/log_entry.go
index acb8df5..9808746 100644
--- a/pkg/generated/models/log_entry.go
+++ b/pkg/generated/models/log_entry.go
@@ -50,6 +50,7 @@ type LogEntryAnon struct {
 
 	// log index
 	// Required: true
+	// Minimum: 1
 	LogIndex *int64 `json:"logIndex"`
 
 	// signature
@@ -90,6 +91,10 @@ func (m *LogEntryAnon) validateLogIndex(formats strfmt.Registry) error {
 		return err
 	}
 
+	if err := validate.MinimumInt("logIndex", "body", int64(*m.LogIndex), 1, false); err != nil {
+		return err
+	}
+
 	return nil
 }
 
diff --git a/pkg/generated/models/log_info.go b/pkg/generated/models/log_info.go
index 9ad8adf..12a6b08 100644
--- a/pkg/generated/models/log_info.go
+++ b/pkg/generated/models/log_info.go
@@ -19,10 +19,12 @@ type LogInfo struct {
 
 	// The current hash value stored at the root of the merkle tree
 	// Required: true
+	// Pattern: ^[0-9a-fA-F]{64}$
 	RootHash *string `json:"rootHash"`
 
 	// The current number of nodes in the merkle tree
 	// Required: true
+	// Minimum: 1
 	TreeSize *int64 `json:"treeSize"`
 }
 
@@ -50,6 +52,10 @@ func (m *LogInfo) validateRootHash(formats strfmt.Registry) error {
 		return err
 	}
 
+	if err := validate.Pattern("rootHash", "body", string(*m.RootHash), `^[0-9a-fA-F]{64}$`); err != nil {
+		return err
+	}
+
 	return nil
 }
 
@@ -59,6 +65,10 @@ func (m *LogInfo) validateTreeSize(formats strfmt.Registry) error {
 		return err
 	}
 
+	if err := validate.MinimumInt("treeSize", "body", int64(*m.TreeSize), 1, false); err != nil {
+		return err
+	}
+
 	return nil
 }
 
diff --git a/pkg/generated/models/search_log_query.go b/pkg/generated/models/search_log_query.go
index 2264b19..bf31437 100644
--- a/pkg/generated/models/search_log_query.go
+++ b/pkg/generated/models/search_log_query.go
@@ -11,6 +11,7 @@ import (
 	"github.com/go-openapi/errors"
 	"github.com/go-openapi/strfmt"
 	"github.com/go-openapi/swag"
+	"github.com/go-openapi/validate"
 )
 
 // SearchLogQuery search log query
@@ -91,6 +92,10 @@ func (m *SearchLogQuery) validateLogIndexes(formats strfmt.Registry) error {
 
 	for i := 0; i < len(m.LogIndexes); i++ {
 
+		if err := validate.MinimumInt("logIndexes"+"."+strconv.Itoa(i), "body", int64(m.LogIndexes[i]), 1, false); err != nil {
+			return err
+		}
+
 	}
 
 	return nil
diff --git a/pkg/generated/restapi/embedded_spec.go b/pkg/generated/restapi/embedded_spec.go
index 8156b90..c82cca5 100644
--- a/pkg/generated/restapi/embedded_spec.go
+++ b/pkg/generated/restapi/embedded_spec.go
@@ -61,6 +61,7 @@ func init() {
         "operationId": "getLogEntryByIndex",
         "parameters": [
           {
+            "minimum": 1,
             "type": "integer",
             "description": "specifies the index of the entry in the transparency log to be retrieved",
             "name": "logIndex",
@@ -230,13 +231,15 @@ func init() {
         "operationId": "getLogProof",
         "parameters": [
           {
+            "minimum": 1,
             "type": "integer",
-            "default": 0,
-            "description": "The size of the tree that you wish to prove consistency from (0 means the beginning of the log) Defaults to 0 if not specified\n",
+            "default": 1,
+            "description": "The size of the tree that you wish to prove consistency from (1 means the beginning of the log) Defaults to 1 if not specified\n",
             "name": "firstSize",
             "in": "query"
           },
           {
+            "minimum": 1,
             "type": "integer",
             "description": "The size of the tree that you wish to prove consistency to",
             "name": "lastSize",
@@ -251,6 +254,9 @@ func init() {
               "$ref": "#/definitions/ConsistencyProof"
             }
           },
+          "400": {
+            "$ref": "#/responses/BadContent"
+          },
           "default": {
             "$ref": "#/responses/InternalServerError"
           }
@@ -262,25 +268,22 @@ func init() {
     "ConsistencyProof": {
       "type": "object",
       "required": [
-        "firstRootHash",
-        "lastRootHash",
+        "rootHash",
         "hashes"
       ],
       "properties": {
-        "firstRootHash": {
-          "description": "The hash value stored at the root of the merkle tree at the first size specified",
-          "type": "string"
-        },
         "hashes": {
           "type": "array",
           "items": {
+            "description": "SHA256 hash value expressed in hexadecimal format",
             "type": "string",
             "pattern": "^[0-9a-fA-F]{64}$"
           }
         },
-        "lastRootHash": {
-          "description": "The hash value stored at the root of the merkle tree at the last size specified",
-          "type": "string"
+        "rootHash": {
+          "description": "The hash value stored at the root of the merkle tree at time the proof was generated",
+          "type": "string",
+          "pattern": "^[0-9a-fA-F]{64}$"
         }
       }
     },
@@ -326,16 +329,18 @@ func init() {
         },
         "logIndex": {
           "description": "The index of the entry in the transparency log",
-          "type": "integer"
+          "type": "integer",
+          "minimum": 1
         },
         "rootHash": {
-          "description": "The hash value stored at the root of the merkle tree",
+          "description": "The hash value stored at the root of the merkle tree at the time the proof was generated",
           "type": "string",
           "pattern": "^[0-9a-fA-F]{64}$"
         },
         "treeSize": {
           "description": "The size of the merkle tree at the time the inclusion proof was generated",
-          "type": "integer"
+          "type": "integer",
+          "minimum": 1
         }
       }
     },
@@ -356,7 +361,8 @@ func init() {
             }
           },
           "logIndex": {
-            "type": "integer"
+            "type": "integer",
+            "minimum": 1
           },
           "signature": {
             "type": "object",
@@ -395,11 +401,13 @@ func init() {
       "properties": {
         "rootHash": {
           "description": "The current hash value stored at the root of the merkle tree",
-          "type": "string"
+          "type": "string",
+          "pattern": "^[0-9a-fA-F]{64}$"
         },
         "treeSize": {
           "description": "The current number of nodes in the merkle tree",
-          "type": "integer"
+          "type": "integer",
+          "minimum": 1
         }
       }
     },
@@ -484,6 +492,7 @@ func init() {
           "type": "array",
           "items": {
             "type": "integer",
+            "minimum": 1,
             "minItems": 1
           }
         }
@@ -565,6 +574,7 @@ func init() {
         "operationId": "getLogEntryByIndex",
         "parameters": [
           {
+            "minimum": 1,
             "type": "integer",
             "description": "specifies the index of the entry in the transparency log to be retrieved",
             "name": "logIndex",
@@ -761,15 +771,15 @@ func init() {
         "operationId": "getLogProof",
         "parameters": [
           {
-            "minimum": 0,
+            "minimum": 1,
             "type": "integer",
-            "default": 0,
-            "description": "The size of the tree that you wish to prove consistency from (0 means the beginning of the log) Defaults to 0 if not specified\n",
+            "default": 1,
+            "description": "The size of the tree that you wish to prove consistency from (1 means the beginning of the log) Defaults to 1 if not specified\n",
             "name": "firstSize",
             "in": "query"
           },
           {
-            "minimum": 0,
+            "minimum": 1,
             "type": "integer",
             "description": "The size of the tree that you wish to prove consistency to",
             "name": "lastSize",
@@ -784,6 +794,12 @@ func init() {
               "$ref": "#/definitions/ConsistencyProof"
             }
           },
+          "400": {
+            "description": "The content supplied to the server was invalid",
+            "schema": {
+              "$ref": "#/definitions/Error"
+            }
+          },
           "default": {
             "description": "There was an internal error in the server while processing the request",
             "schema": {
@@ -798,25 +814,22 @@ func init() {
     "ConsistencyProof": {
       "type": "object",
       "required": [
-        "firstRootHash",
-        "lastRootHash",
+        "rootHash",
         "hashes"
       ],
       "properties": {
-        "firstRootHash": {
-          "description": "The hash value stored at the root of the merkle tree at the first size specified",
-          "type": "string"
-        },
         "hashes": {
           "type": "array",
           "items": {
+            "description": "SHA256 hash value expressed in hexadecimal format",
             "type": "string",
             "pattern": "^[0-9a-fA-F]{64}$"
           }
         },
-        "lastRootHash": {
-          "description": "The hash value stored at the root of the merkle tree at the last size specified",
-          "type": "string"
+        "rootHash": {
+          "description": "The hash value stored at the root of the merkle tree at time the proof was generated",
+          "type": "string",
+          "pattern": "^[0-9a-fA-F]{64}$"
         }
       }
     },
@@ -862,16 +875,18 @@ func init() {
         },
         "logIndex": {
           "description": "The index of the entry in the transparency log",
-          "type": "integer"
+          "type": "integer",
+          "minimum": 1
         },
         "rootHash": {
-          "description": "The hash value stored at the root of the merkle tree",
+          "description": "The hash value stored at the root of the merkle tree at the time the proof was generated",
           "type": "string",
           "pattern": "^[0-9a-fA-F]{64}$"
         },
         "treeSize": {
           "description": "The size of the merkle tree at the time the inclusion proof was generated",
-          "type": "integer"
+          "type": "integer",
+          "minimum": 1
         }
       }
     },
@@ -896,7 +911,8 @@ func init() {
           }
         },
         "logIndex": {
-          "type": "integer"
+          "type": "integer",
+          "minimum": 1
         },
         "signature": {
           "type": "object",
@@ -955,11 +971,13 @@ func init() {
       "properties": {
         "rootHash": {
           "description": "The current hash value stored at the root of the merkle tree",
-          "type": "string"
+          "type": "string",
+          "pattern": "^[0-9a-fA-F]{64}$"
         },
         "treeSize": {
           "description": "The current number of nodes in the merkle tree",
-          "type": "integer"
+          "type": "integer",
+          "minimum": 1
         }
       }
     },
@@ -1114,6 +1132,7 @@ func init() {
           "type": "array",
           "items": {
             "type": "integer",
+            "minimum": 1,
             "minItems": 1
           }
         }
diff --git a/pkg/generated/restapi/operations/entries/get_log_entry_by_index_parameters.go b/pkg/generated/restapi/operations/entries/get_log_entry_by_index_parameters.go
index 266d5e6..bd2b13a 100644
--- a/pkg/generated/restapi/operations/entries/get_log_entry_by_index_parameters.go
+++ b/pkg/generated/restapi/operations/entries/get_log_entry_by_index_parameters.go
@@ -34,6 +34,7 @@ type GetLogEntryByIndexParams struct {
 
 	/*specifies the index of the entry in the transparency log to be retrieved
 	  Required: true
+	  Minimum: 1
 	  In: query
 	*/
 	LogIndex int64
@@ -83,5 +84,19 @@ func (o *GetLogEntryByIndexParams) bindLogIndex(rawData []string, hasKey bool, f
 	}
 	o.LogIndex = value
 
+	if err := o.validateLogIndex(formats); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// validateLogIndex carries on validations for parameter LogIndex
+func (o *GetLogEntryByIndexParams) validateLogIndex(formats strfmt.Registry) error {
+
+	if err := validate.MinimumInt("logIndex", "query", int64(o.LogIndex), 1, false); err != nil {
+		return err
+	}
+
 	return nil
 }
diff --git a/pkg/generated/restapi/operations/tlog/get_log_proof_parameters.go b/pkg/generated/restapi/operations/tlog/get_log_proof_parameters.go
index ddd4671..3ec8406 100644
--- a/pkg/generated/restapi/operations/tlog/get_log_proof_parameters.go
+++ b/pkg/generated/restapi/operations/tlog/get_log_proof_parameters.go
@@ -23,7 +23,7 @@ func NewGetLogProofParams() GetLogProofParams {
 	var (
 		// initialize parameters with default values
 
-		firstSizeDefault = int64(0)
+		firstSizeDefault = int64(1)
 	)
 
 	return GetLogProofParams{
@@ -40,16 +40,16 @@ type GetLogProofParams struct {
 	// HTTP Request Object
 	HTTPRequest *http.Request `json:"-"`
 
-	/*The size of the tree that you wish to prove consistency from (0 means the beginning of the log) Defaults to 0 if not specified
+	/*The size of the tree that you wish to prove consistency from (1 means the beginning of the log) Defaults to 1 if not specified
 
-	  Minimum: 0
+	  Minimum: 1
 	  In: query
-	  Default: 0
+	  Default: 1
 	*/
 	FirstSize *int64
 	/*The size of the tree that you wish to prove consistency to
 	  Required: true
-	  Minimum: 0
+	  Minimum: 1
 	  In: query
 	*/
 	LastSize int64
@@ -112,7 +112,7 @@ func (o *GetLogProofParams) bindFirstSize(rawData []string, hasKey bool, formats
 // validateFirstSize carries on validations for parameter FirstSize
 func (o *GetLogProofParams) validateFirstSize(formats strfmt.Registry) error {
 
-	if err := validate.MinimumInt("firstSize", "query", int64(*o.FirstSize), 0, false); err != nil {
+	if err := validate.MinimumInt("firstSize", "query", int64(*o.FirstSize), 1, false); err != nil {
 		return err
 	}
 
@@ -151,7 +151,7 @@ func (o *GetLogProofParams) bindLastSize(rawData []string, hasKey bool, formats
 // validateLastSize carries on validations for parameter LastSize
 func (o *GetLogProofParams) validateLastSize(formats strfmt.Registry) error {
 
-	if err := validate.MinimumInt("lastSize", "query", int64(o.LastSize), 0, false); err != nil {
+	if err := validate.MinimumInt("lastSize", "query", int64(o.LastSize), 1, false); err != nil {
 		return err
 	}
 
diff --git a/pkg/generated/restapi/operations/tlog/get_log_proof_responses.go b/pkg/generated/restapi/operations/tlog/get_log_proof_responses.go
index 1e842ea..8f50202 100644
--- a/pkg/generated/restapi/operations/tlog/get_log_proof_responses.go
+++ b/pkg/generated/restapi/operations/tlog/get_log_proof_responses.go
@@ -57,6 +57,50 @@ func (o *GetLogProofOK) WriteResponse(rw http.ResponseWriter, producer runtime.P
 	}
 }
 
+// GetLogProofBadRequestCode is the HTTP code returned for type GetLogProofBadRequest
+const GetLogProofBadRequestCode int = 400
+
+/*GetLogProofBadRequest The content supplied to the server was invalid
+
+swagger:response getLogProofBadRequest
+*/
+type GetLogProofBadRequest struct {
+
+	/*
+	  In: Body
+	*/
+	Payload *models.Error `json:"body,omitempty"`
+}
+
+// NewGetLogProofBadRequest creates GetLogProofBadRequest with default headers values
+func NewGetLogProofBadRequest() *GetLogProofBadRequest {
+
+	return &GetLogProofBadRequest{}
+}
+
+// WithPayload adds the payload to the get log proof bad request response
+func (o *GetLogProofBadRequest) WithPayload(payload *models.Error) *GetLogProofBadRequest {
+	o.Payload = payload
+	return o
+}
+
+// SetPayload sets the payload to the get log proof bad request response
+func (o *GetLogProofBadRequest) SetPayload(payload *models.Error) {
+	o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *GetLogProofBadRequest) 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
+		}
+	}
+}
+
 /*GetLogProofDefault There was an internal error in the server while processing the request
 
 swagger:response getLogProofDefault
-- 
GitLab