aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--handler.go39
-rw-r--r--instance.go88
-rw-r--r--reqres.go25
-rw-r--r--server/main.go37
-rw-r--r--type.go10
-rw-r--r--x509.go43
6 files changed, 159 insertions, 83 deletions
diff --git a/handler.go b/handler.go
index 33c6979..6e5fe49 100644
--- a/handler.go
+++ b/handler.go
@@ -3,6 +3,7 @@ package stfe
import (
"context"
"fmt"
+ "time"
"net/http"
@@ -13,26 +14,26 @@ import (
// appHandler implements the http.Handler interface, and contains a reference
// to an STFE server instance as well as a function that uses it.
type appHandler struct {
- instance *instance // STFE server instance
+ instance *Instance // STFE server instance
endpoint string // e.g., add-entry
method string // e.g., GET
- handler func(context.Context, *instance, http.ResponseWriter, *http.Request) (int, error)
+ handler func(context.Context, *Instance, http.ResponseWriter, *http.Request) (int, error)
}
// ServeHTTP docdoc
func (a appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- ctx, cancel := context.WithDeadline(r.Context(), a.instance.timesource.Now().Add(a.instance.deadline))
+ ctx, cancel := context.WithDeadline(r.Context(), time.Now().Add(a.instance.Deadline))
defer cancel()
if r.Method != a.method {
- glog.Warningf("%s: got HTTP %s, wanted HTTP %s", a.instance.prefix+a.endpoint, r.Method, a.method)
+ glog.Warningf("%s: got HTTP %s, wanted HTTP %s", a.instance.LogParameters.Prefix+a.endpoint, r.Method, a.method)
a.sendHTTPError(w, http.StatusMethodNotAllowed, fmt.Errorf("method not allowed: %s", r.Method))
return
}
statusCode, err := a.handler(ctx, a.instance, w, r)
if err != nil {
- glog.Warningf("handler error %s/%s: %v", a.instance.prefix, a.endpoint, err)
+ glog.Warningf("handler error %s/%s: %v", a.instance.LogParameters.Prefix, a.endpoint, err)
a.sendHTTPError(w, statusCode, err)
}
}
@@ -42,26 +43,26 @@ func (a appHandler) sendHTTPError(w http.ResponseWriter, statusCode int, err err
http.Error(w, http.StatusText(statusCode), statusCode)
}
-func addEntry(ctx context.Context, i *instance, w http.ResponseWriter, r *http.Request) (int, error) {
+func addEntry(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) {
glog.Info("in addEntry")
request, err := NewAddEntryRequest(r)
if err != nil {
return http.StatusBadRequest, err
} // request can be decoded
- leaf, appendix, err := VerifyAddEntryRequest(i.anchors, request)
+ leaf, appendix, err := VerifyAddEntryRequest(i.LogParameters, request)
if err != nil {
return http.StatusBadRequest, err
} // valid add-entry request
trillianRequest := trillian.QueueLeafRequest{
- LogId: i.logID,
+ LogId: i.LogParameters.TreeId,
Leaf: &trillian.LogLeaf{
LeafValue: leaf,
ExtraData: appendix,
},
}
- trillianResponse, err := i.client.QueueLeaf(ctx, &trillianRequest)
+ trillianResponse, err := i.Client.QueueLeaf(ctx, &trillianRequest)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("backend QueueLeaf request failed: %v", err)
} // note: more detail could be provided here, see addChainInternal in ctfe
@@ -72,7 +73,7 @@ func addEntry(ctx context.Context, i *instance, w http.ResponseWriter, r *http.R
}
// getEntries provides a list of entries from the Trillian backend
-func getEntries(ctx context.Context, i *instance, w http.ResponseWriter, r *http.Request) (int, error) {
+func getEntries(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) {
glog.Info("in getEntries")
request, err := NewGetEntriesRequest(r)
if err != nil {
@@ -80,11 +81,11 @@ func getEntries(ctx context.Context, i *instance, w http.ResponseWriter, r *http
} // request can be decoded and is valid
trillianRequest := trillian.GetLeavesByRangeRequest{
- LogId: i.logID,
+ LogId: i.LogParameters.TreeId,
StartIndex: request.Start,
Count: request.End - request.Start + 1,
}
- trillianResponse, err := i.client.GetLeavesByRange(ctx, &trillianRequest)
+ trillianResponse, err := i.Client.GetLeavesByRange(ctx, &trillianRequest)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("backend GetLeavesByRange request failed: %v", err)
}
@@ -113,9 +114,9 @@ func getEntries(ctx context.Context, i *instance, w http.ResponseWriter, r *http
}
// getAnchors provides a list of configured trust anchors
-func getAnchors(_ context.Context, i *instance, w http.ResponseWriter, _ *http.Request) (int, error) {
+func getAnchors(_ context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) {
glog.Info("in getAnchors")
- data := NewGetAnchorsResponse(i.anchorsPool.RawCertificates())
+ data := NewGetAnchorsResponse(i.LogParameters.AnchorList)
if err := WriteJsonResponse(data, w); err != nil {
return http.StatusInternalServerError, err
}
@@ -123,7 +124,7 @@ func getAnchors(_ context.Context, i *instance, w http.ResponseWriter, _ *http.R
}
// getProofByHash provides an inclusion proof based on a given leaf hash
-func getProofByHash(ctx context.Context, i *instance, w http.ResponseWriter, r *http.Request) (int, error) {
+func getProofByHash(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) {
glog.Info("in getProofByHash")
request, err := NewGetProofByHashRequest(r)
if err != nil {
@@ -131,12 +132,12 @@ func getProofByHash(ctx context.Context, i *instance, w http.ResponseWriter, r *
} // request can be decoded and is valid
trillianRequest := trillian.GetInclusionProofByHashRequest{
- LogId: i.logID,
+ LogId: i.LogParameters.TreeId,
LeafHash: request.Hash,
TreeSize: request.TreeSize,
OrderBySequence: true,
}
- trillianResponse, err := i.client.GetInclusionProofByHash(ctx, &trillianRequest)
+ trillianResponse, err := i.Client.GetInclusionProofByHash(ctx, &trillianRequest)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("failed fetching inclusion proof from Trillian backend: %v", err)
}
@@ -159,13 +160,13 @@ 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) {
+func getConsistencyProof(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) {
glog.Info("in getConsistencyProof")
return http.StatusOK, nil // TODO
}
// getSth provides the most recent STH
-func getSth(ctx context.Context, i *instance, w http.ResponseWriter, r *http.Request) (int, error) {
+func getSth(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) {
glog.Info("in getSth")
return http.StatusOK, nil // TODO
}
diff --git a/instance.go b/instance.go
index c8aaca3..d4fc004 100644
--- a/instance.go
+++ b/instance.go
@@ -1,53 +1,81 @@
package stfe
import (
+ "crypto"
+ "crypto/x509"
+ "fmt"
"time"
+ "encoding/base64"
"net/http"
"github.com/golang/glog"
"github.com/google/trillian"
-
- "github.com/google/certificate-transparency-go/trillian/ctfe"
- ctutil "github.com/google/certificate-transparency-go/trillian/util"
)
-// instance groups information about a specific STFE instance.
-type instance struct {
- prefix string
- logID int64
- client trillian.TrillianLogClient
- deadline time.Duration
- anchors ctfe.CertValidationOpts
- anchorsPool ctfe.PEMCertPool // TODO: merge anchors and anchorsPool
- timesource ctutil.TimeSource
+// Instance is an instance of a particular log front-end
+type Instance struct {
+ LogParameters *LogParameters
+ Client trillian.TrillianLogClient
+ Deadline time.Duration
+}
+
+// LogParameters is a collection of log parameters
+type LogParameters struct {
+ LogId []byte // used externally by everyone
+ TreeId int64 // used internally by Trillian
+ Prefix string
+ AnchorPool *x509.CertPool // for chain verification
+ AnchorList []*x509.Certificate // for access to the raw certificates
+ Signer crypto.Signer
+}
+
+// NewInstance returns an initialized Instance
+func NewInstance(lp *LogParameters, client trillian.TrillianLogClient, deadline time.Duration, mux *http.ServeMux) (*Instance, error) {
+ i := &Instance{
+ LogParameters: lp,
+ Client: client,
+ Deadline: deadline,
+ }
+ i.registerHandlers(mux)
+ return i, nil
}
-// NewInstance returns a new STFE instance
-func NewInstance(prefix string, id int64, client trillian.TrillianLogClient, deadline time.Duration, timesource ctutil.TimeSource, anchors ctfe.CertValidationOpts, anchorsPool ctfe.PEMCertPool) *instance {
- return &instance{
- prefix: prefix,
- logID: id,
- client: client,
- deadline: deadline,
- timesource: timesource,
- anchors: anchors,
- anchorsPool: anchorsPool,
+// NewLogParameters returns an initialized LogParameters
+func NewLogParameters(logId []byte, treeId int64, prefix string, anchorPath string) (*LogParameters, error) {
+ anchorList, anchorPool, err := LoadTrustAnchors(anchorPath)
+ if err != nil {
+ return nil, err
}
+
+ return &LogParameters{
+ LogId: logId,
+ TreeId: treeId,
+ Prefix: prefix,
+ AnchorPool: anchorPool,
+ AnchorList: anchorList,
+ }, nil
+}
+
+func (i *Instance) String() string {
+ return fmt.Sprintf("%s Deadline(%v)\n", i.LogParameters, i.Deadline)
+}
+
+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))
}
-// addEndpoints registers STFE handler functions for the respective HTTP paths
-func (i *instance) AddEndpoints(mux *http.ServeMux) {
+func (i *Instance) registerHandlers(mux *http.ServeMux) {
for _, endpoint := range []struct {
path string
handler appHandler
}{
- {i.prefix + "/add-entry", appHandler{instance: i, handler: addEntry, endpoint: "add-entry", method: http.MethodPost}},
- {i.prefix + "/get-entries", appHandler{instance: i, handler: getEntries, endpoint: "get-entries", method: http.MethodGet}},
- {i.prefix + "/get-anchors", appHandler{instance: i, handler: getAnchors, endpoint: "get-anchors", method: http.MethodGet}},
- {i.prefix + "/get-proof-by-hash", appHandler{instance: i, handler: getProofByHash, endpoint: "get-proof-by-hash", method: http.MethodGet}},
- {i.prefix + "/get-consistency-proof", appHandler{instance: i, handler: getConsistencyProof, endpoint: "get-consistency-proof", method: http.MethodGet}},
- {i.prefix + "/get-sth", appHandler{instance: i, handler: getSth, endpoint: "get-sth", method: http.MethodGet}},
+ {i.LogParameters.Prefix + "/add-entry", appHandler{instance: i, handler: addEntry, endpoint: "add-entry", method: http.MethodPost}},
+ {i.LogParameters.Prefix + "/get-entries", appHandler{instance: i, handler: getEntries, endpoint: "get-entries", method: http.MethodGet}},
+ {i.LogParameters.Prefix + "/get-anchors", appHandler{instance: i, handler: getAnchors, endpoint: "get-anchors", method: http.MethodGet}},
+ {i.LogParameters.Prefix + "/get-proof-by-hash", appHandler{instance: i, handler: getProofByHash, endpoint: "get-proof-by-hash", method: http.MethodGet}},
+ {i.LogParameters.Prefix + "/get-consistency-proof", appHandler{instance: i, handler: getConsistencyProof, endpoint: "get-consistency-proof", method: http.MethodGet}},
+ {i.LogParameters.Prefix + "/get-sth", appHandler{instance: i, handler: getSth, endpoint: "get-sth", method: http.MethodGet}},
} {
glog.Infof("adding handler for %v", endpoint.path)
mux.Handle(endpoint.path, endpoint.handler)
diff --git a/reqres.go b/reqres.go
index 5d4c1bc..a725363 100644
--- a/reqres.go
+++ b/reqres.go
@@ -6,14 +6,13 @@ import (
"crypto/ecdsa"
"crypto/rsa"
+ "crypto/x509"
"encoding/base64"
"encoding/json"
"io/ioutil"
"net/http"
"github.com/google/certificate-transparency-go/tls"
- "github.com/google/certificate-transparency-go/trillian/ctfe"
- "github.com/google/certificate-transparency-go/x509"
"github.com/google/trillian"
)
@@ -137,9 +136,9 @@ func NewGetEntryResponse(leaf, appendix []byte) (GetEntryResponse, error) {
}
return GetEntryResponse{
- Leaf: base64.StdEncoding.EncodeToString(leaf),
+ Leaf: base64.StdEncoding.EncodeToString(leaf),
Signature: base64.StdEncoding.EncodeToString(app.Signature),
- Chain: chain,
+ Chain: chain,
}, nil
}
@@ -178,7 +177,7 @@ func NewGetAnchorsResponse(anchors []*x509.Certificate) GetAnchorsResponse {
// VerifyAddEntryRequest determines whether a well-formed AddEntryRequest should
// be inserted into the log. The corresponding leaf and appendix is returned.
-func VerifyAddEntryRequest(anchors ctfe.CertValidationOpts, r AddEntryRequest) ([]byte, []byte, error) {
+func VerifyAddEntryRequest(ld *LogParameters, r AddEntryRequest) ([]byte, []byte, error) {
item, err := StItemFromB64(r.Item)
if err != nil {
return nil, nil, fmt.Errorf("failed decoding StItem: %v", err)
@@ -193,13 +192,21 @@ func VerifyAddEntryRequest(anchors ctfe.CertValidationOpts, r AddEntryRequest) (
if err != nil {
return nil, nil, fmt.Errorf("failed decoding certificate: %v", err)
}
- chain := make([][]byte, 0, 1)
- chain = append(chain, certificate)
- x509chain, err := ctfe.ValidateChain(chain, anchors)
+ c, err := x509.ParseCertificate(certificate)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed decoding certificate: %v", err)
+ }
+ opts := x509.VerifyOptions{
+ Roots: ld.AnchorPool,
+ }
+ chains, err := c.Verify(opts)
if err != nil {
return nil, nil, fmt.Errorf("chain verification failed: %v", err)
}
- c := x509chain[0]
+ if len(chains) == 0 {
+ return nil, nil, fmt.Errorf("chain verification failed: no chain")
+ }
+ x509chain := chains[0]
signature, err := base64.StdEncoding.DecodeString(r.Signature)
if err != nil {
diff --git a/server/main.go b/server/main.go
index 618d40b..84d92ea 100644
--- a/server/main.go
+++ b/server/main.go
@@ -1,4 +1,4 @@
-// Package main provides an STFE binary
+// Package main provides an STFE server binary
package main
import (
@@ -11,19 +11,15 @@ import (
"github.com/google/trillian"
"github.com/system-transparency/stfe"
"google.golang.org/grpc"
-
- "github.com/google/certificate-transparency-go/trillian/ctfe"
- ctutil "github.com/google/certificate-transparency-go/trillian/util"
- "github.com/google/certificate-transparency-go/x509"
)
var (
- httpEndpoint = flag.String("http_endpoint", "localhost:6965", "host:port specification of where stfe serves clients")
- rpcBackend = flag.String("log_rpc_server", "localhost:6962", "host:port specification of where Trillian serves clients")
- prefix = flag.String("prefix", "/st/v1", "a prefix that proceeds each endpoint path")
- trillianID = flag.Int64("trillian_id", 5991359069696313945, "log identifier in the Trillian database")
- rpcDeadline = flag.Duration("rpc_deadline", time.Second*10, "deadline for backend RPC requests")
- anchorsPemFile = flag.String("anchors_file", "testdata/chain/rgdd-root.pem", "path to a file containing PEM-encoded X.509 root certificates")
+ httpEndpoint = flag.String("http_endpoint", "localhost:6965", "host:port specification of where stfe serves clients")
+ rpcBackend = flag.String("log_rpc_server", "localhost:6962", "host:port specification of where Trillian serves clients")
+ prefix = flag.String("prefix", "/st/v1", "a prefix that proceeds each endpoint path")
+ trillianID = flag.Int64("trillian_id", 5991359069696313945, "log identifier in the Trillian database")
+ rpcDeadline = flag.Duration("rpc_deadline", time.Second*10, "deadline for backend RPC requests")
+ anchorPath = flag.String("anchor_path", "testdata/chain/rgdd-root.pem", "path to a file containing PEM-encoded X.509 root certificates")
)
func main() {
@@ -35,21 +31,22 @@ func main() {
if err != nil {
glog.Fatal(err)
}
+ client := trillian.NewTrillianLogClient(conn)
glog.Info("Creating HTTP request multiplexer")
mux := http.NewServeMux()
http.Handle("/", mux)
- // TODO: proper setup
- glog.Info("Loading trust anchors")
- cert_pool := ctfe.NewPEMCertPool()
- cert_pool.AppendCertsFromPEMFile(*anchorsPemFile)
- anchors := ctfe.NewCertValidationOpts(cert_pool, time.Now(), true, false, nil, nil, false, []x509.ExtKeyUsage{})
- glog.Infof("%v", cert_pool.Subjects())
+ lp, err := stfe.NewLogParameters([]byte("rgdd"), *trillianID, *prefix, *anchorPath)
+ if err != nil {
+ glog.Fatalf("failed setting up log parameters: %v", err)
+ }
- glog.Info("Creating STFE server instance")
- stfe_server := stfe.NewInstance(*prefix, *trillianID, trillian.NewTrillianLogClient(conn), *rpcDeadline, new(ctutil.SystemTimeSource), anchors, *cert_pool)
- stfe_server.AddEndpoints(mux)
+ i, err := stfe.NewInstance(lp, client, *rpcDeadline, mux)
+ if err != nil {
+ glog.Fatalf("failed setting up log instance: %v", err)
+ }
+ glog.Infof("Configured: %s", i)
glog.Infof("Serving on %v%v", *httpEndpoint, *prefix)
srv := http.Server{Addr: *httpEndpoint}
diff --git a/type.go b/type.go
index f691f34..a629259 100644
--- a/type.go
+++ b/type.go
@@ -3,10 +3,10 @@ package stfe
import (
"fmt"
+ "crypto/x509"
"encoding/base64"
"github.com/google/certificate-transparency-go/tls"
- "github.com/google/certificate-transparency-go/x509"
"github.com/google/trillian"
)
@@ -140,8 +140,8 @@ func StItemFromB64(s string) (StItem, error) {
// Appendix is extra data that Trillian can store about a leaf
type Appendix struct {
- Signature []byte `tls:"minlen:0,maxlen:16383"`
- Chain []RawCertificate `tls:"minlen:0,maxlen:65535"`
+ Signature []byte `tls:"minlen:0,maxlen:16383"`
+ Chain []RawCertificate `tls:"minlen:0,maxlen:65535"`
}
// RawCertificate is a serialized X.509 certificate
@@ -153,7 +153,7 @@ type RawCertificate struct {
func NewAppendix(x509Chain []*x509.Certificate, signature []byte) Appendix {
chain := make([]RawCertificate, 0, 2) // TODO: base length on config param
for _, c := range x509Chain {
- chain = append(chain, RawCertificate{ c.Raw })
+ chain = append(chain, RawCertificate{c.Raw})
}
- return Appendix{ Signature: signature, Chain: chain }
+ return Appendix{Signature: signature, Chain: chain}
}
diff --git a/x509.go b/x509.go
new file mode 100644
index 0000000..cdcd523
--- /dev/null
+++ b/x509.go
@@ -0,0 +1,43 @@
+package stfe
+
+import (
+ "fmt"
+
+ "crypto/x509"
+ "encoding/pem"
+ "io/ioutil"
+)
+
+// LoadTrustAnchors loads a list of PEM-encoded certificates from file
+func LoadTrustAnchors(path string) ([]*x509.Certificate, *x509.CertPool, error) {
+ rest, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed reading trust anchors: %v", err)
+ }
+
+ pool := x509.NewCertPool()
+ var anchors []*x509.Certificate
+ for len(rest) > 0 {
+ var block *pem.Block
+ block, rest = pem.Decode(rest)
+ if block == nil {
+ break
+ }
+ if block.Type != "CERTIFICATE" {
+ return nil, nil, fmt.Errorf("unexpected PEM block type: %s", block.Type)
+ }
+
+ certificate, err := x509.ParseCertificate(block.Bytes)
+ if err != nil {
+ return nil, nil, fmt.Errorf("invalid trust anchor before rest(%s): %v", rest, err)
+ }
+
+ anchors = append(anchors, certificate)
+ pool.AddCert(certificate)
+ }
+
+ if len(anchors) == 0 {
+ return nil, nil, fmt.Errorf("found no valid trust anchor in: %s", path)
+ }
+ return anchors, pool, nil
+}