aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRasmus Dahlberg <rasmus.dahlberg@kau.se>2020-10-28 13:38:39 +0100
committerRasmus Dahlberg <rasmus.dahlberg@kau.se>2020-10-28 13:38:39 +0100
commitd752d967335e1418f27e03e0389b01178b28f232 (patch)
treeabc3b6f2e3b64af67f19ca50f6e5c3609d829fb9
parente7801b268c97c6b72bfcd76549ce5fd50ab0b1b5 (diff)
added signed tree head and get-sth code path
-rw-r--r--handler.go35
-rw-r--r--reqres.go17
-rwxr-xr-xserver/testdata/cmd/get-sth9
-rw-r--r--type.go49
-rw-r--r--x509.go16
5 files changed, 122 insertions, 4 deletions
diff --git a/handler.go b/handler.go
index 2a23dbb..ee1eae6 100644
--- a/handler.go
+++ b/handler.go
@@ -9,6 +9,7 @@ import (
"github.com/golang/glog"
"github.com/google/trillian"
+ "github.com/google/trillian/types"
)
// appHandler implements the http.Handler interface, and contains a reference
@@ -177,7 +178,37 @@ func getConsistencyProof(ctx context.Context, i *Instance, w http.ResponseWriter
}
// 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, _ *http.Request) (int, error) {
glog.Info("in getSth")
- return http.StatusOK, nil // TODO
+ trillianRequest := trillian.GetLatestSignedLogRootRequest{
+ LogId: i.LogParameters.TreeId,
+ }
+ trillianResponse, err := i.Client.GetLatestSignedLogRoot(ctx, &trillianRequest)
+ if err != nil {
+ return http.StatusInternalServerError, fmt.Errorf("failed fetching signed tree head from Trillian backend: %v", err)
+ }
+ if trillianResponse.SignedLogRoot == nil {
+ return http.StatusInternalServerError, fmt.Errorf("Trillian returned no tree head")
+ }
+
+ var lr types.LogRootV1
+ if err := lr.UnmarshalBinary(trillianResponse.SignedLogRoot.GetLogRoot()); err != nil {
+ return http.StatusInternalServerError, fmt.Errorf("failed unmarshaling tree head: %v", err)
+ }
+
+ th := NewTreeHeadV1(uint64(lr.TimestampNanos / 1000 / 1000), uint64(lr.TreeSize), lr.RootHash)
+ sth, err := GenV1STH(i.LogParameters, th)
+ if err != nil {
+ return http.StatusInternalServerError, fmt.Errorf("failed creating signed tree head: %v", err)
+ }
+ glog.Infof("%v", sth)
+
+ response, err := NewGetSthResponse(sth)
+ if err != nil {
+ return http.StatusInternalServerError, fmt.Errorf("failed creating GetSthResponse: %v", err)
+ }
+ if err := WriteJsonResponse(response, w); err != nil {
+ return http.StatusInternalServerError, err
+ }
+ return http.StatusOK, nil
}
diff --git a/reqres.go b/reqres.go
index fe79d51..e223fd9 100644
--- a/reqres.go
+++ b/reqres.go
@@ -55,11 +55,16 @@ type GetProofByHashResponse struct {
InclusionProof string `json:"inclusion_proof"` // base64-encoded StItem
}
-// GetAnchorsResponse
+// GetAnchorsResponse is an assembled get-anchor response
type GetAnchorsResponse struct {
Certificates []string `json:"certificates"`
}
+// GetSthResponse is an assembled get-sth response
+type GetSthResponse struct {
+ SignedTreeHead string `json:"sth"` // base64-encoded StItem
+}
+
// NewAddEntryRequest parses and sanitizes the JSON-encoded add-entry
// parameters from an incoming HTTP post. The resulting AddEntryRequest is
// well-formed, but not necessarily trusted (further sanitization is needed).
@@ -189,6 +194,16 @@ func NewGetAnchorsResponse(anchors []*x509.Certificate) GetAnchorsResponse {
return GetAnchorsResponse{Certificates: certificates}
}
+func NewGetSthResponse(sth StItem) (GetSthResponse, error) {
+ b, err := tls.Marshal(sth)
+ if err != nil {
+ return GetSthResponse{}, fmt.Errorf("tls marshal failed: %v", err)
+ }
+ return GetSthResponse{
+ SignedTreeHead: base64.StdEncoding.EncodeToString(b),
+ }, nil
+}
+
// VerifyAddEntryRequest determines whether a well-formed AddEntryRequest should
// be inserted into the log. The corresponding leaf and appendix is returned.
func VerifyAddEntryRequest(ld *LogParameters, r AddEntryRequest) ([]byte, []byte, error) {
diff --git a/server/testdata/cmd/get-sth b/server/testdata/cmd/get-sth
new file mode 100755
index 0000000..9060633
--- /dev/null
+++ b/server/testdata/cmd/get-sth
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -eo pipefail
+source config
+
+info "fetching signed tree head"
+curl -G $base_url/get-sth
+newline
+# TODO: try decoding and verifying signature
diff --git a/type.go b/type.go
index 726b215..be2571e 100644
--- a/type.go
+++ b/type.go
@@ -5,6 +5,7 @@ import (
"crypto/x509"
"encoding/base64"
+ "time"
"github.com/google/certificate-transparency-go/tls"
"github.com/google/trillian"
@@ -25,10 +26,24 @@ const (
// StItem references a versioned item based on a given format specifier.
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
InclusionProofV1 *InclusionProofV1 `tls:"selector:Format,val:4"`
ChecksumV1 *ChecksumV1 `tls:"selector:Format,val:5"`
- // TODO: add more items
+}
+
+type SignedTreeHeadV1 struct {
+ LogId []byte `tls:"minlen:2,maxlen:127"`
+ TreeHead TreeHeadV1 `tls:minlen:0, maxlen:65535` // what should maxlen be?
+ Signature []byte `tls:"minlen:0,maxlen:65535"`
+}
+
+type TreeHeadV1 struct {
+ Timestamp uint64
+ TreeSize uint64
+ RootHash NodeHash `tls:minlen:32,maxlen:255`
+ Extension []byte `tls:"minlen:0,maxlen:65535"`
}
// ChecksumV1 associates a package name with an arbitrary checksum value
@@ -60,6 +75,28 @@ type NodeHash struct {
Data []byte `tls:"minlen:32,maxlen:255"`
}
+func NewSignedTreeHeadV1(th TreeHeadV1, logId, signature []byte) StItem {
+ return StItem{
+ Format: StFormatSignedTreeHeadV1,
+ SignedTreeHeadV1: &SignedTreeHeadV1{
+ LogId: logId,
+ TreeHead: th,
+ Signature: signature,
+ },
+ }
+}
+
+func NewTreeHeadV1(timestamp, treeSize uint64, rootHash []byte) TreeHeadV1 {
+ return TreeHeadV1{
+ Timestamp: timestamp,
+ TreeSize: treeSize,
+ RootHash: NodeHash{
+ Data: rootHash,
+ },
+ Extension: nil,
+ }
+}
+
func NewSignedDebugInfoV1(logId, message, signature []byte) StItem {
return StItem{
Format: StFormatSignedDebugInfoV1,
@@ -127,11 +164,21 @@ func (i StItem) String() string {
return fmt.Sprintf("Format(%s): %s", i.Format, *i.InclusionProofV1)
case StFormatSignedDebugInfoV1:
return fmt.Sprintf("Format(%s): %s", i.Format, *i.SignedDebugInfoV1)
+ case StFormatSignedTreeHeadV1:
+ return fmt.Sprintf("Format(%s): %s", i.Format, *i.SignedTreeHeadV1)
default:
return fmt.Sprintf("unknown StItem: %s", i.Format)
}
}
+func (th TreeHeadV1) String() string {
+ return fmt.Sprintf("Timestamp(%s) TreeSize(%d) RootHash(%s)", time.Unix(int64(th.Timestamp/1000), 0), th.TreeSize, base64.StdEncoding.EncodeToString(th.RootHash.Data))
+}
+
+func (i SignedTreeHeadV1) String() string {
+ return fmt.Sprintf("LogId(%s) TreeHead(%s) Signature(%s)", base64.StdEncoding.EncodeToString(i.LogId), i.TreeHead, base64.StdEncoding.EncodeToString(i.Signature))
+}
+
func (i SignedDebugInfoV1) String() string {
return fmt.Sprintf("LogId(%s) Message(%s) Signature(%s)", base64.StdEncoding.EncodeToString(i.LogId), string(i.Message), base64.StdEncoding.EncodeToString(i.Signature))
}
diff --git a/x509.go b/x509.go
index 1e443a1..841b477 100644
--- a/x509.go
+++ b/x509.go
@@ -11,6 +11,8 @@ import (
"crypto/x509"
"encoding/pem"
"io/ioutil"
+
+ "github.com/google/certificate-transparency-go/tls"
)
// LoadTrustAnchors loads a list of PEM-encoded certificates from file
@@ -121,3 +123,17 @@ func GenV1SDI(ld *LogParameters, leaf []byte) (StItem, error) {
}
return NewSignedDebugInfoV1(ld.LogId, []byte("reserved"), sig), nil
}
+
+func GenV1STH(ld *LogParameters, th TreeHeadV1) (StItem, error) {
+ serialized, err := tls.Marshal(th)
+ if err != nil {
+ return StItem{}, fmt.Errorf("failed tls marshaling tree head: %v", err)
+ }
+
+ // Note that ed25519 does not use the passed io.Reader
+ sig, err := ld.Signer.Sign(rand.Reader, serialized, crypto.Hash(0))
+ if err != nil {
+ return StItem{}, fmt.Errorf("ed25519 signature failed: %v", err)
+ }
+ return NewSignedTreeHeadV1(th, ld.LogId, sig), nil
+}