From ec4741e374beeb085579ba896fdee2cd6f0f8848 Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Wed, 21 Oct 2020 18:18:43 +0200 Subject: added start on addEntry code path If the POSTed StItem can be parsed without errors it is handed over to the Trillian back-end. --- handler.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- type.go | 23 ++++++++++++++++++++ 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/handler.go b/handler.go index 90dbc27..7b986c1 100644 --- a/handler.go +++ b/handler.go @@ -4,9 +4,13 @@ import ( "context" "fmt" + "encoding/json" + "io/ioutil" "net/http" "github.com/golang/glog" + "github.com/google/certificate-transparency-go/tls" + "github.com/google/trillian" ) // appHandler implements the http.Handler interface, and contains a reference @@ -31,7 +35,7 @@ func (a appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { statusCode, err := a.handler(ctx, a.instance, w, r) if err != nil { - glog.Warningf("handler error %s: %v", a.instance.prefix+a.endpoint, err) + glog.Warningf("handler error %s/%s: %v", a.instance.prefix, a.endpoint, err) a.sendHTTPError(w, statusCode, err) } } @@ -41,10 +45,71 @@ func (a appHandler) sendHTTPError(w http.ResponseWriter, statusCode int, err err http.Error(w, http.StatusText(statusCode), statusCode) } -// addEntry adds an entry to the Trillian backend func addEntry(ctx context.Context, i *instance, w http.ResponseWriter, r *http.Request) (int, error) { glog.Info("in addEntry") - return http.StatusOK, nil // TODO + var request AddEntryRequest + if err := unpackRequest(r, &request); err != nil { + return http.StatusBadRequest, err + } + + item, err := verifyAddEntryRequest(request) + if err != nil { + return http.StatusBadRequest, err + } + glog.Infof("got item: %s", item) + + serializedItem, err := tls.Marshal(*item) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("tls marshal failed: %v", err) + } + trillianRequest := trillian.QueueLeafRequest{ + LogId: i.logID, + Leaf: &trillian.LogLeaf{ + LeafValue: serializedItem, + //TODO: add appendix here w/ chain + signature + }, + } + + trillianResponse, err := i.client.QueueLeaf(ctx, &trillianRequest) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("backend QueueLeaf request failed: %v", err) + } + if trillianResponse == nil { + return http.StatusInternalServerError, fmt.Errorf("missing QueueLeaf response") + } + // TODO: check that we got gRPC OK as specified in Trillian's API doc + + queuedLeaf := trillianResponse.QueuedLeaf + glog.Infof("Queued leaf: %v", queuedLeaf.Leaf.LeafValue) + // TODO: respond with an SDI + + return http.StatusOK, nil +} + +// verifyAddEntryRequest +func verifyAddEntryRequest(r AddEntryRequest) (*StItem, error) { + item, err := StItemFromB64(r.Item) + if err != nil { + return nil, fmt.Errorf("failed decoding StItem: %v", err) + } + if item.Format != StFormatChecksumV1 { + return nil, fmt.Errorf("invalid StItem format: %s", item.Format) + } + // TODO: verify checksum length + // TODO: verify r.Signature and r.Certificate + return item, nil +} + +// unpackRequest tries to unpack a json-encoded HTTP POST request into `unpack` +func unpackRequest(r *http.Request, unpack interface{}) error { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return fmt.Errorf("failed reading request body: %v", err) + } + if err := json.Unmarshal(body, &unpack); err != nil { + return fmt.Errorf("failed parsing json body: %v", err) + } + return nil } // getEntries provides with a list of entries from the Trillian backend diff --git a/type.go b/type.go index e9b5ef2..031ae8d 100644 --- a/type.go +++ b/type.go @@ -55,6 +55,22 @@ func (i StItem) String() string { } } +func StItemFromB64(s string) (*StItem, error) { + b, err := base64.StdEncoding.DecodeString(s) + if err != nil { + return nil, fmt.Errorf("base64 decoding failed: %v", err) + } + + var item StItem + extra, err := tls.Unmarshal(b, &item) + if err != nil { + return nil, fmt.Errorf("tls unmarshal failed: %v", err) + } else if len(extra) > 0 { + return nil, fmt.Errorf("tls unmarshal found extra data: %v", extra) + } + return &item, nil +} + // ChecksumV1 associates a package name with an arbitrary checksum value type ChecksumV1 struct { Package []byte `tls:"minlen:0,maxlen:255"` @@ -75,3 +91,10 @@ func NewChecksumV1(name string, checksum []byte) (StItem, error) { func (i ChecksumV1) String() string { return fmt.Sprintf("%v %v", string(i.Package), base64.StdEncoding.EncodeToString(i.Checksum)) } + +// AddEntryRequest is a collection of add-entry input parameters +type AddEntryRequest struct { + Item string `json:"item"` + Signature string `json:"signature"` + Certificate string `json:"certificate"` +} -- cgit v1.2.3