diff options
| -rw-r--r-- | handler.go | 14 | ||||
| -rw-r--r-- | handler_test.go | 24 | ||||
| -rw-r--r-- | instance.go | 141 | ||||
| -rw-r--r-- | server/main.go | 45 | 
4 files changed, 112 insertions, 112 deletions
| @@ -12,16 +12,22 @@ import (  	"github.com/google/trillian/types"  ) -// handler implements the http.Handler interface, and contains a reference +// Handler implements the http.Handler interface, and contains a reference  // to an STFE server instance as well as a function that uses it. -type handler struct { +type Handler struct {  	instance *Instance // STFE server instance  	endpoint Endpoint  // e.g., add-entry  	method   string    // e.g., GET  	handler  func(context.Context, *Instance, http.ResponseWriter, *http.Request) (int, error)  } -func (a handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +// Path returns a path that should be configured for this handler +func (h Handler) Path() string { +	return h.endpoint.Path("", h.instance.LogParameters.Prefix) +} + +// ServeHTTP is part of the http.Handler interface +func (a Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {  	// export prometheus metrics  	var now time.Time = time.Now()  	var statusCode int @@ -47,7 +53,7 @@ func (a handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {  	}  } -func (a handler) sendHTTPError(w http.ResponseWriter, statusCode int, err error) { +func (a Handler) sendHTTPError(w http.ResponseWriter, statusCode int, err error) {  	http.Error(w, http.StatusText(statusCode), statusCode)  } diff --git a/handler_test.go b/handler_test.go index 689541b..3bcc702 100644 --- a/handler_test.go +++ b/handler_test.go @@ -43,17 +43,17 @@ func newTestHandler(t *testing.T, signer crypto.Signer) *testHandler {  	}  } -func (th *testHandler) getHandlers(t *testing.T) map[Endpoint]handler { -	return map[Endpoint]handler{ -		EndpointGetSth:              handler{instance: th.instance, handler: getSth, endpoint: EndpointGetSth, method: http.MethodGet}, -		EndpointGetConsistencyProof: handler{instance: th.instance, handler: getConsistencyProof, endpoint: EndpointGetConsistencyProof, method: http.MethodGet}, -		EndpointGetProofByHash:      handler{instance: th.instance, handler: getProofByHash, endpoint: EndpointGetProofByHash, method: http.MethodGet}, -		EndpointGetAnchors:          handler{instance: th.instance, handler: getAnchors, endpoint: EndpointGetAnchors, method: http.MethodGet}, -		EndpointGetEntries:          handler{instance: th.instance, handler: getEntries, endpoint: EndpointGetEntries, method: http.MethodGet}, +func (th *testHandler) getHandlers(t *testing.T) map[Endpoint]Handler { +	return map[Endpoint]Handler{ +		EndpointGetSth:              Handler{instance: th.instance, handler: getSth, endpoint: EndpointGetSth, method: http.MethodGet}, +		EndpointGetConsistencyProof: Handler{instance: th.instance, handler: getConsistencyProof, endpoint: EndpointGetConsistencyProof, method: http.MethodGet}, +		EndpointGetProofByHash:      Handler{instance: th.instance, handler: getProofByHash, endpoint: EndpointGetProofByHash, method: http.MethodGet}, +		EndpointGetAnchors:          Handler{instance: th.instance, handler: getAnchors, endpoint: EndpointGetAnchors, method: http.MethodGet}, +		EndpointGetEntries:          Handler{instance: th.instance, handler: getEntries, endpoint: EndpointGetEntries, method: http.MethodGet},  	}  } -func (th *testHandler) getHandler(t *testing.T, endpoint Endpoint) handler { +func (th *testHandler) getHandler(t *testing.T, endpoint Endpoint) Handler {  	handler, ok := th.getHandlers(t)[endpoint]  	if !ok {  		t.Fatalf("no such get endpoint: %s", endpoint) @@ -61,13 +61,13 @@ func (th *testHandler) getHandler(t *testing.T, endpoint Endpoint) handler {  	return handler  } -func (th *testHandler) postHandlers(t *testing.T) map[Endpoint]handler { -	return map[Endpoint]handler{ -		EndpointAddEntry: handler{instance: th.instance, handler: addEntry, endpoint: EndpointAddEntry, method: http.MethodPost}, +func (th *testHandler) postHandlers(t *testing.T) map[Endpoint]Handler { +	return map[Endpoint]Handler{ +		EndpointAddEntry: Handler{instance: th.instance, handler: addEntry, endpoint: EndpointAddEntry, method: http.MethodPost},  	}  } -func (th *testHandler) postHandler(t *testing.T, endpoint Endpoint) handler { +func (th *testHandler) postHandler(t *testing.T, endpoint Endpoint) Handler {  	handler, ok := th.postHandlers(t)[endpoint]  	if !ok {  		t.Fatalf("no such post endpoint: %s", endpoint) diff --git a/instance.go b/instance.go index 2c108aa..b0a2d9e 100644 --- a/instance.go +++ b/instance.go @@ -9,31 +9,12 @@ import (  	"crypto/sha256"  	"crypto/x509"  	"encoding/base64" -	"io/ioutil"  	"net/http" -	"github.com/golang/glog"  	"github.com/google/trillian"  	"github.com/system-transparency/stfe/x509util"  ) -type Endpoint string - -const ( -	EndpointAddEntry            = Endpoint("add-entry") -	EndpointGetEntries          = Endpoint("get-entries") -	EndpointGetAnchors          = Endpoint("get-anchors") -	EndpointGetProofByHash      = Endpoint("get-proof-by-hash") -	EndpointGetConsistencyProof = Endpoint("get-consistency-proof") -	EndpointGetSth              = Endpoint("get-sth") -) - -// Path joins a number of components to form a full endpoint path, e.g., base -// ("example.com"), prefix ("st/v1"), and the endpoint itself ("get-sth"). -func (e Endpoint) Path(components ...string) string { -	return strings.Join(append(components, string(e)), "/") -} -  // Instance is an instance of a particular log front-end  type Instance struct {  	LogParameters *LogParameters @@ -55,6 +36,18 @@ type LogParameters struct {  	HashType   crypto.Hash // hash function used by Trillian  } +// Endpoint is a named HTTP API endpoint +type Endpoint string + +const ( +	EndpointAddEntry            = Endpoint("add-entry") +	EndpointGetEntries          = Endpoint("get-entries") +	EndpointGetAnchors          = Endpoint("get-anchors") +	EndpointGetProofByHash      = Endpoint("get-proof-by-hash") +	EndpointGetConsistencyProof = Endpoint("get-consistency-proof") +	EndpointGetSth              = Endpoint("get-sth") +) +  func (i Instance) String() string {  	return fmt.Sprintf("%s Deadline(%v)\n", i.LogParameters, i.Deadline)  } @@ -63,103 +56,67 @@ func (p LogParameters) String() string {  	return fmt.Sprintf("LogId(%s) TreeId(%d) Prefix(%s) NumAnchors(%d)", base64.StdEncoding.EncodeToString(p.LogId), p.TreeId, p.Prefix, len(p.AnchorList))  } -func (i *LogParameters) id() string { -	return base64.StdEncoding.EncodeToString(i.LogId) +func (e Endpoint) String() string { +	return string(e)  } -// NewInstance returns a new STFE Instance -func NewInstance(lp *LogParameters, client trillian.TrillianLogClient, deadline time.Duration, mux *http.ServeMux) (*Instance, error) { -	i := &Instance{ +// NewInstance creates a new STFE instance +func NewInstance(lp *LogParameters, client trillian.TrillianLogClient, deadline time.Duration, mux *http.ServeMux) *Instance { +	return &Instance{  		LogParameters: lp,  		Client:        client,  		Deadline:      deadline,  	} -	i.registerHandlers(mux) -	return i, nil  } -// NewLogParameters initializes log parameters, assuming ed25519 signatures. -func NewLogParameters(treeId int64, prefix string, anchorPath, keyPath string, maxRange, maxChain int64) (*LogParameters, error) { -	anchorList, anchorPool, err := loadTrustAnchors(anchorPath) +// NewLogParameters creates new log parameters.  Note that the signer is +// assumed to be an ed25519 signing key.  Could be fixed at some point. +func NewLogParameters(treeId int64, prefix string, anchors []*x509.Certificate, signer crypto.Signer, maxRange, maxChain int64) (*LogParameters, error) { +	pub, err := x509.MarshalPKIXPublicKey(signer.Public())  	if err != nil { -		return nil, err -	} - -	pem, err := ioutil.ReadFile(keyPath) -	if err != nil { -		return nil, fmt.Errorf("failed reading %s: %v", keyPath, err) +		return nil, fmt.Errorf("failed DER encoding SubjectPublicKeyInfo: %v", err)  	} -	key, err := x509util.NewEd25519PrivateKey(pem) -	if err != nil { -		return nil, err +	if maxRange < 1 { +		return nil, fmt.Errorf("invalid max range: must be at least 1")  	} - -	pub, err := x509.MarshalPKIXPublicKey(key.Public()) -	if err != nil { -		return nil, fmt.Errorf("failed DER encoding SubjectPublicKeyInfo: %v", err) +	if maxChain < 1 { +		return nil, fmt.Errorf("invalid max chain: must be at least 1")  	}  	hasher := sha256.New()  	hasher.Write(pub) -	logId := hasher.Sum(nil) -  	return &LogParameters{ -		LogId:      logId, +		LogId:      hasher.Sum(nil),  		TreeId:     treeId,  		Prefix:     prefix,  		MaxRange:   maxRange,  		MaxChain:   maxChain, -		AnchorPool: anchorPool, -		AnchorList: anchorList, +		AnchorPool: x509util.NewCertPool(anchors), +		AnchorList: anchors,  		KeyUsage:   []x509.ExtKeyUsage{}, // placeholder, must be tested if used -		Signer:     key, -		HashType:   crypto.SHA256, +		Signer:     signer, +		HashType:   crypto.SHA256, // STFE assumes RFC 6962 hashing  	}, nil  } -func (i *Instance) registerHandlers(mux *http.ServeMux) { -	for _, endpoint := range []struct { -		path    string -		handler handler -	}{ -		{ -			EndpointAddEntry.Path("", i.LogParameters.Prefix), -			handler{instance: i, handler: addEntry, endpoint: EndpointAddEntry, method: http.MethodPost}, -		}, -		{ -			EndpointGetEntries.Path("", i.LogParameters.Prefix), -			handler{instance: i, handler: getEntries, endpoint: EndpointGetEntries, method: http.MethodGet}, -		}, -		{ -			EndpointGetAnchors.Path("", i.LogParameters.Prefix), -			handler{instance: i, handler: getAnchors, endpoint: EndpointGetAnchors, method: http.MethodGet}, -		}, -		{ -			EndpointGetProofByHash.Path("", i.LogParameters.Prefix), -			handler{instance: i, handler: getProofByHash, endpoint: EndpointGetProofByHash, method: http.MethodGet}, -		}, -		{ -			EndpointGetConsistencyProof.Path("", i.LogParameters.Prefix), -			handler{instance: i, handler: getConsistencyProof, endpoint: EndpointGetConsistencyProof, method: http.MethodGet}, -		}, -		{ -			EndpointGetSth.Path("", i.LogParameters.Prefix), -			handler{instance: i, handler: getSth, endpoint: EndpointGetSth, method: http.MethodGet}, -		}, -	} { -		glog.Infof("adding handler for %v", endpoint.path) -		mux.Handle(endpoint.path, endpoint.handler) -	} +// Path joins a number of components to form a full endpoint path, e.g., base +// ("example.com"), prefix ("st/v1"), and the endpoint itself ("get-sth"). +func (e Endpoint) Path(components ...string) string { +	return strings.Join(append(components, string(e)), "/")  } -// loadTrustAnchors loads a list of PEM-encoded certificates from file -func loadTrustAnchors(path string) ([]*x509.Certificate, *x509.CertPool, error) { -	pem, err := ioutil.ReadFile(path) -	if err != nil { -		return nil, nil, fmt.Errorf("failed reading trust anchors: %v", err) -	} -	anchorList, err := x509util.NewCertificateList(pem) -	if err != nil || len(anchorList) == 0 { -		return nil, nil, fmt.Errorf("failed parsing trust anchors: %v", err) +// TODO: id() docdoc +func (i *LogParameters) id() string { +	return base64.StdEncoding.EncodeToString(i.LogId) +} + +// Handlers returns a list of STFE handlers +func (i *Instance) Handlers() []Handler { +	return []Handler{ +		Handler{instance: i, handler: addEntry, endpoint: EndpointAddEntry, method: http.MethodPost}, +		Handler{instance: i, handler: getEntries, endpoint: EndpointGetEntries, method: http.MethodGet}, +		Handler{instance: i, handler: getAnchors, endpoint: EndpointGetAnchors, method: http.MethodGet}, +		Handler{instance: i, handler: getProofByHash, endpoint: EndpointGetProofByHash, method: http.MethodGet}, +		Handler{instance: i, handler: getConsistencyProof, endpoint: EndpointGetConsistencyProof, method: http.MethodGet}, +		Handler{instance: i, handler: getSth, endpoint: EndpointGetSth, method: http.MethodGet},  	} -	return anchorList, x509util.NewCertPool(anchorList), nil  } diff --git a/server/main.go b/server/main.go index d6a7aa5..c60f95d 100644 --- a/server/main.go +++ b/server/main.go @@ -3,14 +3,18 @@ package main  import (  	"flag" +	"fmt"  	"time" +	"crypto/x509" +	"io/ioutil"  	"net/http"  	"github.com/golang/glog"  	"github.com/google/trillian"  	"github.com/prometheus/client_golang/prometheus/promhttp"  	"github.com/system-transparency/stfe" +	"github.com/system-transparency/stfe/x509util"  	"google.golang.org/grpc"  ) @@ -44,14 +48,31 @@ func main() {  	glog.Info("Adding prometheus handler on path: /metrics")  	http.Handle("/metrics", promhttp.Handler()) -	lp, err := stfe.NewLogParameters(*trillianID, *prefix, *anchorPath, *keyPath, *maxRange, *maxChain) +	glog.Infof("Loading trust anchors from file: %s", *anchorPath) +	anchors, err := loadCertificates(*anchorPath)  	if err != nil { -		glog.Fatalf("failed setting up log parameters: %v", err) +		glog.Fatalf("no trust anchors: %v", err) +	} + +	glog.Infof("Loading Ed25519 signing key from file: %s", *keyPath) +	pem, err := ioutil.ReadFile(*keyPath) +	if err != nil { +		glog.Fatalf("no signing key: %v", err) +	} +	signer, err := x509util.NewEd25519PrivateKey(pem) +	if err != nil { +		glog.Fatalf("no signing key: %v", err)  	} -	i, err := stfe.NewInstance(lp, client, *rpcDeadline, mux) +	lp, err := stfe.NewLogParameters(*trillianID, *prefix, anchors, signer, *maxRange, *maxChain)  	if err != nil { -		glog.Fatalf("failed setting up log instance: %v", err) +		glog.Fatalf("failed setting up log parameters: %v", err) +	} + +	i := stfe.NewInstance(lp, client, *rpcDeadline, mux) +	for _, handler := range i.Handlers() { +		glog.Infof("adding handler: %s", handler.Path()) +		mux.Handle(handler.Path(), handler)  	}  	glog.Infof("Configured: %s", i) @@ -64,3 +85,19 @@ func main() {  	glog.Flush()  } + +// loadCertificates loads a non-empty list of PEM-encoded certificates from file +func loadCertificates(path string) ([]*x509.Certificate, error) { +	pem, err := ioutil.ReadFile(path) +	if err != nil { +		return nil, fmt.Errorf("failed reading %s: %v", path, err) +	} +	anchors, err := x509util.NewCertificateList(pem) +	if err != nil { +		return nil, fmt.Errorf("failed parsing: %v", err) +	} +	if len(anchors) == 0 { +		return nil, fmt.Errorf("no trust anchors") +	} +	return anchors, nil +} | 
