From caca215c9516e8dc236a8510a6a5467c267d2331 Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Wed, 28 Oct 2020 14:56:11 +0100 Subject: added consistency-proof code path --- handler.go | 24 +++++++++++++++++ reqres.go | 44 ++++++++++++++++++++++++++++++- server/testdata/cmd/get-consistency-proof | 16 +++++++++++ type.go | 37 +++++++++++++++++++++++++- 4 files changed, 119 insertions(+), 2 deletions(-) create mode 100755 server/testdata/cmd/get-consistency-proof diff --git a/handler.go b/handler.go index ee1eae6..119f65c 100644 --- a/handler.go +++ b/handler.go @@ -174,6 +174,30 @@ func getProofByHash(ctx context.Context, i *Instance, w http.ResponseWriter, r * // getConsistencyProof provides a consistency proof between two STHs func getConsistencyProof(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { glog.Info("in getConsistencyProof") + request, err := NewGetConsistencyProofRequest(r) + if err != nil { + return http.StatusBadRequest, err + } // request can be decoded and is valid + + trillianRequest := trillian.GetConsistencyProofRequest{ + LogId: i.LogParameters.TreeId, + FirstTreeSize: int64(request.First), + SecondTreeSize: int64(request.Second), + } + trillianResponse, err := i.Client.GetConsistencyProof(ctx, &trillianRequest) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("failed fetching consistency proof from Trillian backend: %v", err) + } + // TODO: santity-checks? + + response, err := NewGetConsistencyProofResponse(i.LogParameters.LogId, request.First, request.Second, trillianResponse.Proof) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("failed creating get-consistency-proof response: %v", err) + } + if err := WriteJsonResponse(response, w); err != nil { + return http.StatusInternalServerError, err + } + return http.StatusOK, nil return http.StatusOK, nil // TODO } diff --git a/reqres.go b/reqres.go index e223fd9..d0257ab 100644 --- a/reqres.go +++ b/reqres.go @@ -33,6 +33,13 @@ type GetProofByHashRequest struct { TreeSize int64 `json:"tree_size"` // Tree head size to base proof on } +// GetConsistencyProofRequest is a collection of get-consistency-proof input +// parameters +type GetConsistencyProofRequest struct { + First int64 `json:"first"` + Second int64 `json:"second"` +} + // AddEntryResponse is an assembled add-entry response type AddEntryResponse struct { SignedDebugInfo string `json:"sdi"` @@ -55,6 +62,10 @@ type GetProofByHashResponse struct { InclusionProof string `json:"inclusion_proof"` // base64-encoded StItem } +type GetConsistencyProofResponse struct { + ConsistencyProof string `json:"consistency_proof"` // base64-encoded StItem +} + // GetAnchorsResponse is an assembled get-anchor response type GetAnchorsResponse struct { Certificates []string `json:"certificates"` @@ -102,7 +113,7 @@ func NewGetEntriesRequest(httpRequest *http.Request) (GetEntriesRequest, error) return GetEntriesRequest{}, fmt.Errorf("bad parameters: start(%v) must have a non-negative value", start) } if start > end { - return GetEntriesRequest{}, fmt.Errorf("bad parameters: start(%v) must be larger than end(%v)", start, end) + return GetEntriesRequest{}, fmt.Errorf("bad parameters: start(%v) must be less than or equal to end(%v)", start, end) } // TODO: check that range is not larger than the max range. Yes -> truncate // TODO: check that end is not past the most recent STH. Yes -> truncate @@ -128,6 +139,26 @@ func NewGetProofByHashRequest(httpRequest *http.Request) (GetProofByHashRequest, return GetProofByHashRequest{TreeSize: treeSize, Hash: hash}, nil } +func NewGetConsistencyProofRequest(httpRequest *http.Request) (GetConsistencyProofRequest, error) { + first, err := strconv.ParseInt(httpRequest.FormValue("first"), 10, 64) + if err != nil { + return GetConsistencyProofRequest{}, fmt.Errorf("bad first parameter: %v", err) + } + second, err := strconv.ParseInt(httpRequest.FormValue("second"), 10, 64) + if err != nil { + return GetConsistencyProofRequest{}, fmt.Errorf("bad second parameter: %v", err) + } + + if first < 1 { + return GetConsistencyProofRequest{}, fmt.Errorf("bad parameters: first(%v) must be a natural number", first) + } + if first >= second { + return GetConsistencyProofRequest{}, fmt.Errorf("bad parameters: second(%v) must be larger than first(%v)", first, second) + } + + return GetConsistencyProofRequest{ First: first, Second: second}, nil +} + // NewAddEntryResponse assembles an add-entry response from an SDI func NewAddEntryResponse(sdi StItem) (AddEntryResponse, error) { b, err := tls.Marshal(sdi) @@ -186,6 +217,17 @@ func NewGetProofByHashResponse(treeSize uint64, inclusionProof *trillian.Proof) }, nil } +func NewGetConsistencyProofResponse(logId []byte, first, second int64, consistencyProof *trillian.Proof) (*GetConsistencyProofResponse, error) { + item := NewConsistencyProofV1(logId, first, second, consistencyProof) + b, err := tls.Marshal(item) + if err != nil { + return nil, fmt.Errorf("tls marshal failed: %v", err) + } + return &GetConsistencyProofResponse{ + ConsistencyProof: base64.StdEncoding.EncodeToString(b), + }, nil +} + func NewGetAnchorsResponse(anchors []*x509.Certificate) GetAnchorsResponse { certificates := make([]string, 0, len(anchors)) for _, certificate := range anchors { diff --git a/server/testdata/cmd/get-consistency-proof b/server/testdata/cmd/get-consistency-proof new file mode 100755 index 0000000..b5b749c --- /dev/null +++ b/server/testdata/cmd/get-consistency-proof @@ -0,0 +1,16 @@ +#!/bin/bash + +set -eo pipefail +source config + +first="1" +second="2" +if [[ ! -z $1 ]] && [[ ! -z $2 ]]; then + first=$1 + second=$2 +fi + +info "sending get-consistency-proof request" +curl -G -d "first=$first" -d "second=$second" $base_url/get-consistency-proof +newline +# TODO: try decoding and verifying proof diff --git a/type.go b/type.go index be2571e..729f54f 100644 --- a/type.go +++ b/type.go @@ -28,11 +28,18 @@ type StItem struct { Format StFormat `tls:"maxval:65535"` SignedTreeHeadV1 *SignedTreeHeadV1 `tls:"selector:Format,val:1"` SignedDebugInfoV1 *SignedDebugInfoV1 `tls:"selector:Format,val:2"` - // TODO: add consistency proof + ConsistencyProofV1 *ConsistencyProofV1 `tls:"selector:Format,val:3"` InclusionProofV1 *InclusionProofV1 `tls:"selector:Format,val:4"` ChecksumV1 *ChecksumV1 `tls:"selector:Format,val:5"` } +type ConsistencyProofV1 struct { + LogId []byte `tls:"minlen:2,maxlen:127"` + TreeSize1 uint64 + TreeSize2 uint64 + ConsistencyPath []NodeHash `tls:"minlen:1,maxlen:65535"` +} + type SignedTreeHeadV1 struct { LogId []byte `tls:"minlen:2,maxlen:127"` TreeHead TreeHeadV1 `tls:minlen:0, maxlen:65535` // what should maxlen be? @@ -137,6 +144,23 @@ func NewInclusionProofV1(logID []byte, treeSize uint64, proof *trillian.Proof) S } } +func NewConsistencyProofV1(logId []byte, first, second int64, proof *trillian.Proof) StItem { + path := make([]NodeHash, 0, len(proof.Hashes)) + for _, hash := range proof.Hashes { + path = append(path, NodeHash{Data: hash}) + } + + return StItem{ + Format: StFormatConsistencyProofV1, + ConsistencyProofV1: &ConsistencyProofV1{ + LogId: logId, + TreeSize1: uint64(first), + TreeSize2: uint64(second), + ConsistencyPath: path, + }, + } +} + func (f StFormat) String() string { switch f { case StFormatReserved: @@ -160,6 +184,8 @@ func (i StItem) String() string { switch i.Format { case StFormatChecksumV1: return fmt.Sprintf("Format(%s): %s", i.Format, *i.ChecksumV1) + case StFormatConsistencyProofV1: + return fmt.Sprintf("Format(%s): %s", i.Format, *i.ConsistencyProofV1) case StFormatInclusionProofV1: return fmt.Sprintf("Format(%s): %s", i.Format, *i.InclusionProofV1) case StFormatSignedDebugInfoV1: @@ -196,6 +222,15 @@ func (i InclusionProofV1) String() string { return fmt.Sprintf("LogID(%s) TreeSize(%d) LeafIndex(%d) AuditPath(%v)", base64.StdEncoding.EncodeToString(i.LogID), i.TreeSize, i.LeafIndex, path) } +func (i ConsistencyProofV1) String() string { + path := make([]string, 0, len(i.ConsistencyPath)) + for _, hash := range i.ConsistencyPath { + path = append(path, base64.StdEncoding.EncodeToString(hash.Data)) + } + + return fmt.Sprintf("LogID(%s) TreeSize1(%d) TreeSize2(%d) ConsistencyPath(%v)", base64.StdEncoding.EncodeToString(i.LogId), i.TreeSize1, i.TreeSize2, path) +} + // StItemFromB64 creates an StItem from a serialized and base64-encoded string func StItemFromB64(s string) (StItem, error) { b, err := base64.StdEncoding.DecodeString(s) -- cgit v1.2.3