diff options
-rw-r--r-- | client/client.go | 242 | ||||
-rw-r--r-- | client/cmd/add-entry/main.go | 52 | ||||
-rwxr-xr-x | client/cmd/example.sh | 49 | ||||
-rw-r--r-- | client/cmd/get-consistency-proof/main.go | 70 | ||||
-rw-r--r-- | client/cmd/get-entries/main.go | 83 | ||||
-rw-r--r-- | client/cmd/get-proof-by-hash/main.go | 66 | ||||
-rw-r--r-- | client/cmd/get-sth/main.go | 35 | ||||
-rw-r--r-- | client/flag.go | 55 | ||||
-rw-r--r-- | client/verify.go | 52 | ||||
-rw-r--r-- | cmd/siglog_server/.gitignore (renamed from server/.gitignore) | 0 | ||||
-rw-r--r-- | cmd/siglog_server/README.md (renamed from server/README.md) | 0 | ||||
-rw-r--r-- | cmd/siglog_server/main.go (renamed from server/main.go) | 91 | ||||
-rw-r--r-- | cmd/tmp/README.md | 2 | ||||
-rw-r--r-- | cmd/tmp/cosign/main.go (renamed from client/cmd/cosign/main.go) | 2 | ||||
-rw-r--r-- | cmd/tmp/keygen/main.go (renamed from client/cmd/keygen/main.go) | 0 | ||||
-rw-r--r-- | cmd/tmp/submit/main.go (renamed from client/cmd/submit/main.go) | 3 | ||||
-rw-r--r-- | doc.go | 3 | ||||
-rw-r--r-- | endpoint.go | 163 | ||||
-rw-r--r-- | go.sum | 3 | ||||
-rw-r--r-- | instance.go | 75 | ||||
-rw-r--r-- | log_parameters.go | 47 | ||||
-rw-r--r-- | log_parameters_test.go | 99 | ||||
-rw-r--r-- | pkg/instance/endpoint.go | 122 | ||||
-rw-r--r-- | pkg/instance/endpoint_test.go (renamed from endpoint_test.go) | 9 | ||||
-rw-r--r-- | pkg/instance/instance.go | 90 | ||||
-rw-r--r-- | pkg/instance/instance_test.go (renamed from instance_test.go) | 7 | ||||
-rw-r--r-- | pkg/instance/metric.go (renamed from metric.go) | 0 | ||||
-rw-r--r-- | pkg/instance/request.go (renamed from request.go) | 36 | ||||
-rw-r--r-- | pkg/instance/request_test.go (renamed from request_test.go) | 4 | ||||
-rw-r--r-- | pkg/mocks/crypto.go (renamed from mocks/crypto.go) | 0 | ||||
-rw-r--r-- | pkg/mocks/stfe.go (renamed from mocks/stfe.go) | 2 | ||||
-rw-r--r-- | pkg/mocks/trillian.go (renamed from mocks/trillian.go) | 0 | ||||
-rw-r--r-- | pkg/state/state_manager.go (renamed from state/state_manager.go) | 6 | ||||
-rw-r--r-- | pkg/state/state_manager_test.go (renamed from state/state_manager_test.go) | 6 | ||||
-rw-r--r-- | pkg/trillian/client.go (renamed from trillian/client.go) | 2 | ||||
-rw-r--r-- | pkg/trillian/client_test.go (renamed from trillian/client_test.go) | 4 | ||||
-rw-r--r-- | pkg/trillian/util.go (renamed from trillian/util.go) | 2 | ||||
-rw-r--r-- | pkg/types/ascii.go (renamed from types/ascii.go) | 0 | ||||
-rw-r--r-- | pkg/types/ascii_test.go (renamed from types/ascii_test.go) | 0 | ||||
-rw-r--r-- | pkg/types/trunnel.go (renamed from types/trunnel.go) | 0 | ||||
-rw-r--r-- | pkg/types/trunnel_test.go (renamed from types/trunnel_test.go) | 0 | ||||
-rw-r--r-- | pkg/types/types.go (renamed from types/types.go) | 0 | ||||
-rw-r--r-- | pkg/types/types_test.go (renamed from types/types_test.go) | 0 | ||||
-rw-r--r-- | pkg/types/util.go (renamed from types/util.go) | 0 | ||||
-rw-r--r-- | sth.go | 143 | ||||
-rw-r--r-- | sth_test.go | 466 | ||||
-rw-r--r-- | testdata/data.go | 287 | ||||
-rw-r--r-- | trillian.go | 125 | ||||
-rw-r--r-- | trillian_test.go | 282 | ||||
-rw-r--r-- | util.go | 27 | ||||
-rw-r--r-- | util_test.go | 17 |
51 files changed, 307 insertions, 2522 deletions
diff --git a/client/client.go b/client/client.go deleted file mode 100644 index ba81f4d..0000000 --- a/client/client.go +++ /dev/null @@ -1,242 +0,0 @@ -package client - -import ( - "bytes" - "context" - "crypto" - "fmt" - - "io/ioutil" - "net/http" - - "github.com/golang/glog" - "github.com/google/trillian/merkle/rfc6962" - "github.com/system-transparency/stfe" - "github.com/system-transparency/stfe/types" - "golang.org/x/net/context/ctxhttp" -) - -// Descriptor is a log descriptor -type Descriptor struct { - Namespace *types.Namespace // log identifier is a namespace - Url string // log url, e.g., http://example.com/st/v1 -} - -// Client is a log client -type Client struct { - HttpClient *http.Client - Signer crypto.Signer // client's private identity - Namespace *types.Namespace // client's public identity - Log *Descriptor // log's public identity -} - -// GetLatestSth fetches and verifies the signature of the most recent STH. -// Outputs the resulting STH. -func (c *Client) GetLatestSth(ctx context.Context) (*types.StItem, error) { - url := stfe.EndpointGetLatestSth.Path(c.Log.Url) - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, fmt.Errorf("failed creating http request: %v", err) - } - glog.V(3).Infof("created http request: %s %s", req.Method, req.URL) - - item, err := c.doRequestWithStItemResponse(ctx, req) - if err != nil { - return nil, err - } - if got, want := item.Format, types.StFormatSignedTreeHeadV1; got != want { - return nil, fmt.Errorf("unexpected StItem format: %v", got) - } - if err := VerifySignedTreeHeadV1(c.Log.Namespace, item); err != nil { - return nil, fmt.Errorf("signature verification failed: %v", err) - } - glog.V(3).Infof("verified sth") - return item, nil -} - -// GetProofByHash fetches and verifies an inclusion proof for a leaf hash -// against an STH. Outputs the resulting proof. -func (c *Client) GetProofByHash(ctx context.Context, leafHash []byte, sth *types.StItem) (*types.StItem, error) { - if err := VerifySignedTreeHeadV1(c.Log.Namespace, sth); err != nil { - return nil, fmt.Errorf("invalid sth: %v", err) - } - glog.V(3).Infof("verified sth") - params := types.GetProofByHashV1{ - TreeSize: sth.SignedTreeHeadV1.TreeHead.TreeSize, - } - copy(params.Hash[:], leafHash) - buf, err := types.Marshal(params) - if err != nil { - return nil, fmt.Errorf("req: Marshal: %v", err) - } - - url := stfe.EndpointGetProofByHash.Path(c.Log.Url) - req, err := http.NewRequest("POST", url, bytes.NewBuffer(buf)) - if err != nil { - return nil, fmt.Errorf("failed creating http request: %v", err) - } - req.Header.Set("Content-Type", "application/octet-stream") - glog.V(3).Infof("created http request: %s %s", req.Method, req.URL) - - item, err := c.doRequestWithStItemResponse(ctx, req) - if err != nil { - return nil, fmt.Errorf("doRequestWithStItemResponse: %v", err) - } - if got, want := item.Format, types.StFormatInclusionProofV1; got != want { - return nil, fmt.Errorf("unexpected StItem format: %v", item.Format) - } - if err := VerifyInclusionProofV1(item, sth, params.Hash[:]); err != nil { - return nil, fmt.Errorf("invalid inclusion proof: %v", err) - } - glog.V(3).Infof("verified inclusion proof") - return item, nil -} - -// GetConsistencyProof fetches and verifies a consistency proof betweeen two -// STHs. Outputs the resulting proof. -func (c *Client) GetConsistencyProof(ctx context.Context, sth1, sth2 *types.StItem) (*types.StItem, error) { - if err := VerifySignedTreeHeadV1(c.Log.Namespace, sth1); err != nil { - return nil, fmt.Errorf("invalid first sth: %v", err) - } - if err := VerifySignedTreeHeadV1(c.Log.Namespace, sth2); err != nil { - return nil, fmt.Errorf("invalid second sth: %v", err) - } - glog.V(3).Infof("verified sths") - buf, err := types.Marshal(types.GetConsistencyProofV1{ - First: sth1.SignedTreeHeadV1.TreeHead.TreeSize, - Second: sth2.SignedTreeHeadV1.TreeHead.TreeSize, - }) - if err != nil { - return nil, fmt.Errorf("req: Marshal: %v", err) - } - - url := stfe.EndpointGetConsistencyProof.Path(c.Log.Url) - req, err := http.NewRequest("POST", url, bytes.NewBuffer(buf)) - if err != nil { - return nil, fmt.Errorf("failed creating http request: %v", err) - } - req.Header.Set("Content-Type", "application/octet-stream") - glog.V(3).Infof("created http request: %s %s", req.Method, req.URL) - - item, err := c.doRequestWithStItemResponse(ctx, req) - if err != nil { - return nil, fmt.Errorf("doRequestWithStItemResponse: %v", err) - } - if got, want := item.Format, types.StFormatConsistencyProofV1; got != want { - return nil, fmt.Errorf("unexpected StItem format: %v", item.Format) - } - if err := VerifyConsistencyProofV1(item, sth1, sth2); err != nil { - return nil, fmt.Errorf("invalid inclusion proof: %v", err) - } - glog.V(3).Infof("verified inclusion proof") - return item, nil -} - -// AddEntry signs and submits a checksum_v1 entry to the log. Outputs the -// resulting leaf-hash on success. -func (c *Client) AddEntry(ctx context.Context, data *types.ChecksumV1) ([]byte, error) { - msg, err := types.Marshal(*data) - if err != nil { - return nil, fmt.Errorf("failed marshaling ChecksumV1: %v", err) - } - sig, err := c.Signer.Sign(nil, msg, crypto.Hash(0)) - if err != nil { - return nil, fmt.Errorf("failed signing ChecksumV1: %v", err) - } - leaf, err := types.Marshal(*types.NewSignedChecksumV1(data, &types.SignatureV1{ - Namespace: *c.Namespace, - Signature: sig, - })) - if err != nil { - return nil, fmt.Errorf("failed marshaling SignedChecksumV1: %v", err) - } - glog.V(3).Infof("signed checksum entry for identifier %q", string(data.Identifier)) - - url := stfe.EndpointAddEntry.Path(c.Log.Url) - req, err := http.NewRequest("POST", url, bytes.NewBuffer(leaf)) - if err != nil { - return nil, fmt.Errorf("failed creating http request: %v", err) - } - req.Header.Set("Content-Type", "application/octet-stream") - glog.V(3).Infof("created http request: %s %s", req.Method, req.URL) - - if rsp, err := c.doRequest(ctx, req); err != nil { - return nil, fmt.Errorf("doRequest: %v", err) - } else if len(rsp) != 0 { - return nil, fmt.Errorf("extra data: %v", err) - } - glog.V(3).Infof("add-entry succeded") - return rfc6962.DefaultHasher.HashLeaf(leaf), nil -} - -// GetEntries fetches a range of entries from the log, verifying that they are -// of type signed_checksum_v1 but nothing more than that. Outputs the resulting -// range that may be truncated by the log if [start,end] is too large. -func (c *Client) GetEntries(ctx context.Context, start, end uint64) ([]*types.StItem, error) { - buf, err := types.Marshal(types.GetEntriesV1{ - Start: start, - End: end, - }) - if err != nil { - return nil, fmt.Errorf("Marshal: %v", err) - } - url := stfe.EndpointGetEntries.Path(c.Log.Url) - req, err := http.NewRequest("POST", url, bytes.NewBuffer(buf)) - if err != nil { - return nil, fmt.Errorf("failed creating http request: %v", err) - } - req.Header.Set("Content-Type", "application/octet-stream") - glog.V(3).Infof("created http request: %s %s", req.Method, req.URL) - glog.V(3).Infof("request data: start(%d), end(%d)", start, end) - - body, err := c.doRequest(ctx, req) - if err != nil { - return nil, fmt.Errorf("doRequest: %v", err) - } - var list types.StItemList - if err := types.Unmarshal(body, &list); err != nil { - return nil, fmt.Errorf("Unmarshal: %v", err) - } - ret := make([]*types.StItem, 0, len(list.Items)) - for i, _ := range list.Items { - item := list.Items[i] - if got, want := item.Format, types.StFormatSignedChecksumV1; got != want { - return nil, fmt.Errorf("unexpected StItem format: %v", got) - } - ret = append(ret, &item) - } - return ret, nil -} - -// doRequest sends an HTTP request and outputs the raw body -func (c *Client) doRequest(ctx context.Context, req *http.Request) ([]byte, error) { - rsp, err := ctxhttp.Do(ctx, c.HttpClient, req) - if err != nil { - return nil, fmt.Errorf("no response: %v", err) - } - defer rsp.Body.Close() - if got, want := rsp.StatusCode, http.StatusOK; got != want { - return nil, fmt.Errorf("bad http status: %v", got) - } - body, err := ioutil.ReadAll(rsp.Body) - if err != nil { - return nil, fmt.Errorf("cannot read body: %v", err) - } - return body, nil -} - -// -// doRequestWithStItemResponse sends an HTTP request and returns a decoded -// StItem that the resulting HTTP response contained json:ed and marshaled -func (c *Client) doRequestWithStItemResponse(ctx context.Context, req *http.Request) (*types.StItem, error) { - body, err := c.doRequest(ctx, req) - if err != nil { - return nil, err - } - var item types.StItem - if err := types.Unmarshal(body, &item); err != nil { - return nil, fmt.Errorf("failed decoding StItem: %v", err) - } - glog.V(9).Infof("got StItem: %v", item) - return &item, nil -} diff --git a/client/cmd/add-entry/main.go b/client/cmd/add-entry/main.go deleted file mode 100644 index a29d01f..0000000 --- a/client/cmd/add-entry/main.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - - "encoding/base64" - - "github.com/golang/glog" - "github.com/system-transparency/stfe/client" - "github.com/system-transparency/stfe/types" -) - -var ( - identifier = flag.String("identifier", "", "checksum identifier") - checksum = flag.String("checksum", "", "base64-encoded checksum") -) - -func main() { - flag.Parse() - defer glog.Flush() - - client, err := client.NewClientFromFlags() - if err != nil { - glog.Errorf("NewClientFromFlags: %v", err) - return - } - data, err := NewChecksumV1FromFlags() - if err != nil { - glog.Errorf("NewChecksumV1FromFlags: %v", err) - return - } - leafHash, err := client.AddEntry(context.Background(), data) - if err != nil { - glog.Errorf("AddEntry: %v", err) - return - } - fmt.Println("leaf hash:", base64.StdEncoding.EncodeToString(leafHash)) -} - -func NewChecksumV1FromFlags() (*types.ChecksumV1, error) { - var err error - data := types.ChecksumV1{ - Identifier: []byte(*identifier), - } - data.Checksum, err = base64.StdEncoding.DecodeString(*checksum) - if err != nil { - return nil, fmt.Errorf("entry_checksum: DecodeString: %v", err) - } - return &data, nil -} diff --git a/client/cmd/example.sh b/client/cmd/example.sh deleted file mode 100755 index d790712..0000000 --- a/client/cmd/example.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -set -eu - -log_url=http://tlog-poc.system-transparency.org:4780/st/v1 -log_id=AAG+ZW+UesWdMFytUGkp28csBcziomSB3U2vvkAW55MVZQ== -tmpdir=$(mktemp -dt stfe.XXXXXXXX) -cp $0 $tmpdir/ -cd $tmpdir - -commonargs="--log_id $log_id --log_url $log_url" # --logtostderr -v 3 -pause="sleep 1" - -echo "arguments used:" -echo $commonargs -echo "" - -echo "fetching sth..." -get-sth $commonargs | tee sth1.output -echo "" && $pause - -echo "adding an entry..." -add-entry $commonargs \ - --identifier "example.sh v0.0.1-$(cat /dev/urandom | base64 | head -c 10)" \ - --checksum $(sha256sum "$0") | tee add-entry.output -echo "" && $pause - -echo "fetching another sth..." -get-sth $commonargs | tee sth2.output -echo "" && $pause - -echo "verifying inclusion..." -get-proof-by-hash $commonargs \ - --leaf_hash $(cat add-entry.output | awk '{print $3}') \ - --sth $(cat sth2.output | awk '{print $2}') -echo "" && $pause - -echo "verifying consistency..." -get-consistency-proof $commonargs \ - --first $(cat sth1.output | awk '{print $2}') \ - --second $(cat sth2.output | awk '{print $2}') -echo "" && $pause - -echo "fetching the log's first entry..." -get-entries $commonargs --start 0 --end 0 -echo "" - -rm *.output $0 -cd -rmdir $tmpdir diff --git a/client/cmd/get-consistency-proof/main.go b/client/cmd/get-consistency-proof/main.go deleted file mode 100644 index bb8a7a6..0000000 --- a/client/cmd/get-consistency-proof/main.go +++ /dev/null @@ -1,70 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - - "encoding/base64" - - "github.com/golang/glog" - "github.com/system-transparency/stfe/client" - "github.com/system-transparency/stfe/types" -) - -var ( - first = flag.String("first", "", "base64-encoded sth") - second = flag.String("second", "", "base64-encoded sth") -) - -func main() { - flag.Parse() - defer glog.Flush() - - client, err := client.NewClientFromFlags() - if err != nil { - glog.Errorf("NewClientFromFlags: %v", err) - return - } - sth1, sth2, err := newParamsFromFlags() - if err != nil { - glog.Errorf("NewRequestFromFlags: %v", err) - return - } - - proof, err := client.GetConsistencyProof(context.Background(), sth1, sth2) - if err != nil { - glog.Errorf("GetConsistencyProof: %v", err) - return - } - serialized, err := types.Marshal(*proof) - if err != nil { - glog.Errorf("Marshal: %v", err) - return - } - fmt.Println("proof:", base64.StdEncoding.EncodeToString(serialized)) -} - -func newParamsFromFlags() (*types.StItem, *types.StItem, error) { - sth1, err := decodeSthStr(*first) - if err != nil { - return nil, nil, fmt.Errorf("first: decodeSthStr: %v", err) - } - sth2, err := decodeSthStr(*second) - if err != nil { - return nil, nil, fmt.Errorf("second: decodeSthStr: %v", err) - } - return sth1, sth2, nil -} - -func decodeSthStr(sthStr string) (*types.StItem, error) { - serialized, err := base64.StdEncoding.DecodeString(sthStr) - if err != nil { - return nil, fmt.Errorf("DecodeString: %v", err) - } - var item types.StItem - if err = types.Unmarshal(serialized, &item); err != nil { - return nil, fmt.Errorf("Unmarshal: %v", err) - } - return &item, nil -} diff --git a/client/cmd/get-entries/main.go b/client/cmd/get-entries/main.go deleted file mode 100644 index f32fdbf..0000000 --- a/client/cmd/get-entries/main.go +++ /dev/null @@ -1,83 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - - "encoding/base64" - - "github.com/golang/glog" - "github.com/google/trillian/merkle/rfc6962" - "github.com/system-transparency/stfe/client" - "github.com/system-transparency/stfe/types" -) - -var ( - start = flag.Uint64("start", 0, "inclusive start index to download") - end = flag.Uint64("end", 0, "inclusive stop index to download") -) - -func main() { - flag.Parse() - defer glog.Flush() - - client, err := client.NewClientFromFlags() - if err != nil { - glog.Errorf("NewClientFromFlags: %v", err) - return - } - items, err := getRange(client, *start, *end) - if err != nil { - glog.Errorf("getRange: %v", err) - return - } - if err := printRange(items); err != nil { - glog.Errorf("printRange: %v", err) - return - } -} - -func getRange(client *client.Client, start, end uint64) ([]*types.StItem, error) { - items := make([]*types.StItem, 0, end-start+1) - for len(items) != cap(items) { - rsp, err := client.GetEntries(context.Background(), start, end) - if err != nil { - return nil, fmt.Errorf("fetching entries failed: %v", err) - } - items = append(items, rsp...) - start += uint64(len(rsp)) - } - return items, nil -} - -func printRange(items []*types.StItem) error { - for i, item := range items { - var status string - msg, err := types.Marshal(item.SignedChecksumV1.Data) - if err != nil { - return fmt.Errorf("Marshal data failed: %v", err) - } - sig := item.SignedChecksumV1.Signature.Signature - namespace := &item.SignedChecksumV1.Signature.Namespace - if err := namespace.Verify(msg, sig); err != nil { - status = "unverified signature" - } else { - status = "verified signature" - } - serializedNamespace, err := types.Marshal(*namespace) - if err != nil { - return fmt.Errorf("Marshal namespace failed: %v", err) - } - serializedLeaf, err := types.Marshal(*item) - if err != nil { - return fmt.Errorf("Marshal item on index %d: %v", *start+uint64(i), err) - } - fmt.Printf("Index(%d) - %s\n", *start+uint64(i), status) - fmt.Printf("-> Namespace: %s\n", base64.StdEncoding.EncodeToString(serializedNamespace)) - fmt.Printf("-> Identifier: %s\n", string(item.SignedChecksumV1.Data.Identifier)) - fmt.Printf("-> Checksum: %s\n", base64.StdEncoding.EncodeToString(item.SignedChecksumV1.Data.Checksum)) - fmt.Printf("-> Leaf hash: %s\n", base64.StdEncoding.EncodeToString(rfc6962.DefaultHasher.HashLeaf(serializedLeaf))) - } - return nil -} diff --git a/client/cmd/get-proof-by-hash/main.go b/client/cmd/get-proof-by-hash/main.go deleted file mode 100644 index 1f4f304..0000000 --- a/client/cmd/get-proof-by-hash/main.go +++ /dev/null @@ -1,66 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - - "encoding/base64" - - "github.com/golang/glog" - "github.com/system-transparency/stfe/client" - "github.com/system-transparency/stfe/types" -) - -var ( - sthStr = flag.String("sth", "", "base64-encoded StItem of type StFormatSignedTreeHeadV1 (default: fetch new sth)") - leafHashStr = flag.String("leaf_hash", "", "base64-encoded leaf hash") -) - -func main() { - flag.Parse() - defer glog.Flush() - - client, err := client.NewClientFromFlags() - if err != nil { - glog.Errorf("NewClientFromFlags: %v", err) - return - } - leafHash, sth, err := newParamsFromFlags(client) - if err != nil { - glog.Errorf("NewRequestFromFlags: %v", err) - return - } - - proof, err := client.GetProofByHash(context.Background(), leafHash, sth) - if err != nil { - glog.Errorf("GetProofByHash: %v", err) - return - } - serialized, err := types.Marshal(*proof) - if err != nil { - glog.Errorf("Marshal: %v", err) - } - fmt.Println("proof:", base64.StdEncoding.EncodeToString(serialized)) -} - -func newParamsFromFlags(client *client.Client) ([]byte, *types.StItem, error) { - serialized, err := base64.StdEncoding.DecodeString(*sthStr) - if err != nil { - return nil, nil, fmt.Errorf("sth: DecodeString: %v", err) - } - var item types.StItem - if err = types.Unmarshal(serialized, &item); err != nil { - return nil, nil, fmt.Errorf("sth: Unmarshal: %v", err) - } else if got, want := item.Format, types.StFormatSignedTreeHeadV1; got != want { - return nil, nil, fmt.Errorf("unexpected StItem format: %v", got) - } - leafHash, err := base64.StdEncoding.DecodeString(*leafHashStr) - if err != nil { - return nil, nil, fmt.Errorf("leaf_hash: DecodeString: %v", err) - } else if got, want := len(leafHash), 32; got != want { - return nil, nil, fmt.Errorf("leaf_hash: unexpected size: %v", got) - } - glog.V(3).Infof("created request parameters TreeSize(%d) and LeafHash(%s)", item.SignedTreeHeadV1.TreeHead.TreeSize, *leafHashStr) - return leafHash, &item, nil -} diff --git a/client/cmd/get-sth/main.go b/client/cmd/get-sth/main.go deleted file mode 100644 index 6b23b06..0000000 --- a/client/cmd/get-sth/main.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - - "encoding/base64" - - "github.com/golang/glog" - "github.com/system-transparency/stfe/client" - "github.com/system-transparency/stfe/types" -) - -func main() { - flag.Parse() - defer glog.Flush() - - client, err := client.NewClientFromFlags() - if err != nil { - glog.Errorf("NewClientFromFlags: %v", err) - return - } - sth, err := client.GetLatestSth(context.Background()) - if err != nil { - glog.Errorf("GetLatestSth: %v", err) - return - } - serialized, err := types.Marshal(*sth) - if err != nil { - glog.Errorf("Marshal: %v", err) - return - } - fmt.Println("sth:", base64.StdEncoding.EncodeToString(serialized)) -} diff --git a/client/flag.go b/client/flag.go deleted file mode 100644 index 8ba7a10..0000000 --- a/client/flag.go +++ /dev/null @@ -1,55 +0,0 @@ -package client - -import ( - "flag" - "fmt" - - "crypto/ed25519" - "encoding/base64" - "net/http" - - "github.com/system-transparency/stfe/types" -) - -var ( - logId = flag.String("log_id", "AAG+ZW+UesWdMFytUGkp28csBcziomSB3U2vvkAW55MVZQ==", "base64-encoded log identifier") - logUrl = flag.String("log_url", "http://tlog-poc.system-transparency.org:4780/st/v1", "log url") - ed25519_sk = flag.String("ed25519_sk", "d8i6nud7PS1vdO0sIk9H+W0nyxbM63Y3/mSeUPRafWaFh8iH8QXvL7NaAYn2RZPrnEey+FdpmTYXE47OFO70eg==", "base64-encoded ed25519 signing key") -) - -func NewClientFromFlags() (*Client, error) { - var err error - c := Client{ - HttpClient: &http.Client{}, - } - if len(*ed25519_sk) != 0 { - sk, err := base64.StdEncoding.DecodeString(*ed25519_sk) - if err != nil { - return nil, fmt.Errorf("ed25519_sk: DecodeString: %v", err) - } - c.Signer = ed25519.PrivateKey(sk) - c.Namespace, err = types.NewNamespaceEd25519V1([]byte(ed25519.PrivateKey(sk).Public().(ed25519.PublicKey))) - if err != nil { - return nil, fmt.Errorf("ed25519_vk: NewNamespaceEd25519V1: %v", err) - } - } - if c.Log, err = NewDescriptorFromFlags(); err != nil { - return nil, fmt.Errorf("NewDescriptorFromFlags: %v", err) - } - return &c, nil -} - -func NewDescriptorFromFlags() (*Descriptor, error) { - b, err := base64.StdEncoding.DecodeString(*logId) - if err != nil { - return nil, fmt.Errorf("LogId: DecodeString: %v", err) - } - var namespace types.Namespace - if err := types.Unmarshal(b, &namespace); err != nil { - return nil, fmt.Errorf("LogId: Unmarshal: %v", err) - } - return &Descriptor{ - Namespace: &namespace, - Url: *logUrl, - }, nil -} diff --git a/client/verify.go b/client/verify.go deleted file mode 100644 index c95828c..0000000 --- a/client/verify.go +++ /dev/null @@ -1,52 +0,0 @@ -package client - -import ( - "fmt" - "reflect" - - "github.com/google/trillian/merkle" - "github.com/google/trillian/merkle/rfc6962" - "github.com/system-transparency/stfe/types" -) - -func VerifySignedTreeHeadV1(namespace *types.Namespace, sth *types.StItem) error { - if got, want := &sth.SignedTreeHeadV1.Signature.Namespace, namespace; !reflect.DeepEqual(got, want) { - return fmt.Errorf("unexpected log id: %v", want) - } - th, err := types.Marshal(sth.SignedTreeHeadV1.TreeHead) - if err != nil { - return fmt.Errorf("Marshal: %v", err) - } - if err := namespace.Verify(th, sth.SignedTreeHeadV1.Signature.Signature); err != nil { - return fmt.Errorf("Verify: %v", err) - } - return nil -} - -func VerifyConsistencyProofV1(proof, first, second *types.StItem) error { - path := make([][]byte, 0, len(proof.ConsistencyProofV1.ConsistencyPath)) - for _, nh := range proof.ConsistencyProofV1.ConsistencyPath { - path = append(path, nh.Data) - } - return merkle.NewLogVerifier(rfc6962.DefaultHasher).VerifyConsistencyProof( - int64(proof.ConsistencyProofV1.TreeSize1), - int64(proof.ConsistencyProofV1.TreeSize2), - first.SignedTreeHeadV1.TreeHead.RootHash.Data, - second.SignedTreeHeadV1.TreeHead.RootHash.Data, - path, - ) -} - -func VerifyInclusionProofV1(proof, sth *types.StItem, leafHash []byte) error { - path := make([][]byte, 0, len(proof.InclusionProofV1.InclusionPath)) - for _, nh := range proof.InclusionProofV1.InclusionPath { - path = append(path, nh.Data) - } - return merkle.NewLogVerifier(rfc6962.DefaultHasher).VerifyInclusionProof( - int64(proof.InclusionProofV1.LeafIndex), - int64(proof.InclusionProofV1.TreeSize), - path, - sth.SignedTreeHeadV1.TreeHead.RootHash.Data, - leafHash, - ) -} diff --git a/server/.gitignore b/cmd/siglog_server/.gitignore index 254defd..254defd 100644 --- a/server/.gitignore +++ b/cmd/siglog_server/.gitignore diff --git a/server/README.md b/cmd/siglog_server/README.md index 71bb3ac..71bb3ac 100644 --- a/server/README.md +++ b/cmd/siglog_server/README.md diff --git a/server/main.go b/cmd/siglog_server/main.go index 1fecb43..368b0a7 100644 --- a/server/main.go +++ b/cmd/siglog_server/main.go @@ -19,15 +19,17 @@ import ( "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/types" + stfe "github.com/system-transparency/stfe/pkg/instance" + "github.com/system-transparency/stfe/pkg/state" + trillianWrapper "github.com/system-transparency/stfe/pkg/trillian" + "github.com/system-transparency/stfe/pkg/types" "google.golang.org/grpc" ) 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/v0", "a prefix that proceeds each endpoint path") + prefix = flag.String("prefix", "", "a prefix that proceeds /st/v0/<endpoint>") trillianID = flag.Int64("trillian_id", 0, "log identifier in the Trillian database") deadline = flag.Duration("deadline", time.Second*10, "deadline for backend requests") key = flag.String("key", "", "hex-encoded Ed25519 signing key") @@ -53,13 +55,13 @@ func main() { return } - glog.V(3).Infof("spawning SthSource") + glog.V(3).Infof("spawning state manager") go func() { wg.Add(1) defer wg.Done() - instance.SthSource.Run(ctx) - glog.Errorf("SthSource shutdown") - cancel() // must have SthSource running + instance.Stateman.Run(ctx) + glog.Errorf("state manager shutdown") + cancel() // must have state manager running }() glog.V(3).Infof("spawning await") @@ -83,53 +85,62 @@ func main() { // SetupInstance sets up a new STFE instance from flags func setupInstanceFromFlags() (*stfe.Instance, error) { - // Trillian gRPC connection - dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(*deadline)} - conn, err := grpc.Dial(*rpcBackend, dialOpts...) + var i stfe.Instance + var err error + + // Setup log configuration + i.Signer, i.LogID, err = newLogIdentity(*key) if err != nil { - return nil, fmt.Errorf("Dial: %v", err) + return nil, fmt.Errorf("newLogIdentity: %v", err) } - client := trillian.NewTrillianLogClient(conn) - // HTTP multiplexer - mux := http.NewServeMux() - http.Handle("/", mux) - // Prometheus metrics - glog.V(3).Infof("Adding prometheus handler on path: /metrics") - http.Handle("/metrics", promhttp.Handler()) - // Trusted witnesses - witnesses, err := newWitnessMap(*witnesses) + i.TreeID = *trillianID + i.Prefix = *prefix + i.MaxRange = *maxRange + i.Deadline = *deadline + i.Interval = *interval + i.Witnesses, err = newWitnessMap(*witnesses) if err != nil { return nil, fmt.Errorf("newWitnessMap: %v", err) } - // Secret signing key - sk, err := hex.DecodeString(*key) + + // Setup log client + dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(i.Deadline)} + conn, err := grpc.Dial(*rpcBackend, dialOpts...) if err != nil { - return nil, fmt.Errorf("sk: DecodeString: %v", err) + return nil, fmt.Errorf("Dial: %v", err) } - // Setup log parameters - lp := &stfe.LogParameters{ - LogId: hex.EncodeToString([]byte(ed25519.PrivateKey(sk).Public().(ed25519.PublicKey))), - TreeId: *trillianID, - Prefix: *prefix, - MaxRange: *maxRange, - Deadline: *deadline, - Interval: *interval, - HashType: crypto.SHA256, - Signer: ed25519.PrivateKey(sk), - Witnesses: witnesses, + i.Client = &trillianWrapper.TrillianClient{ + TreeID: i.TreeID, + GRPC: trillian.NewTrillianLogClient(conn), } - // Setup STH source - source, err := stfe.NewActiveSthSource(client, lp) + + // Setup state manager + i.Stateman, err = state.NewStateManagerSingle(i.Client, i.Signer, i.Interval, i.Deadline) if err != nil { - return nil, fmt.Errorf("NewActiveSthSource: %v", err) + return nil, fmt.Errorf("NewStateManager: %v", err) } - // Setup log instance - i := &stfe.Instance{client, lp, source} + + // Register HTTP endpoints + mux := http.NewServeMux() + http.Handle("/", mux) for _, handler := range i.Handlers() { glog.V(3).Infof("adding handler: %s", handler.Path()) mux.Handle(handler.Path(), handler) } - return i, nil + glog.V(3).Infof("Adding prometheus handler on path: /metrics") + http.Handle("/metrics", promhttp.Handler()) + + return &i, nil +} + +func newLogIdentity(key string) (crypto.Signer, string, error) { + buf, err := hex.DecodeString(key) + if err != nil { + return nil, "", fmt.Errorf("DecodeString: %v", err) + } + sk := crypto.Signer(ed25519.PrivateKey(buf)) + vk := sk.Public().(ed25519.PublicKey) + return sk, hex.EncodeToString([]byte(vk[:])), nil } // newWitnessMap creates a new map of trusted witnesses diff --git a/cmd/tmp/README.md b/cmd/tmp/README.md new file mode 100644 index 0000000..30d5317 --- /dev/null +++ b/cmd/tmp/README.md @@ -0,0 +1,2 @@ +# Warning +These basic commands will be moved or replaced by proper tooling. diff --git a/client/cmd/cosign/main.go b/cmd/tmp/cosign/main.go index e86842b..a51f17d 100644 --- a/client/cmd/cosign/main.go +++ b/cmd/tmp/cosign/main.go @@ -9,7 +9,7 @@ import ( "log" "net/http" - "github.com/system-transparency/stfe/types" + "github.com/system-transparency/stfe/pkg/types" ) var ( diff --git a/client/cmd/keygen/main.go b/cmd/tmp/keygen/main.go index c1c1b58..c1c1b58 100644 --- a/client/cmd/keygen/main.go +++ b/cmd/tmp/keygen/main.go diff --git a/client/cmd/submit/main.go b/cmd/tmp/submit/main.go index 36c7271..3dcaa97 100644 --- a/client/cmd/submit/main.go +++ b/cmd/tmp/submit/main.go @@ -6,7 +6,8 @@ import ( "crypto/ed25519" "crypto/rand" "fmt" - "github.com/system-transparency/stfe/types" + + "github.com/system-transparency/stfe/pkg/types" ) func main() { @@ -1,3 +0,0 @@ -// Package stfe implements a System Transparency Front-End (STFE) personality -// for the Trillian log server gRPC API. -package stfe diff --git a/endpoint.go b/endpoint.go deleted file mode 100644 index 9be55b4..0000000 --- a/endpoint.go +++ /dev/null @@ -1,163 +0,0 @@ -package stfe - -import ( - "context" - "crypto/ed25519" - "fmt" - "net/http" - - "github.com/golang/glog" - "github.com/google/trillian" - "github.com/system-transparency/stfe/types" -) - -func addEntry(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { - glog.V(3).Info("handling add-entry request") - leaf, err := i.LogParameters.parseAddEntryV1Request(r) - if err != nil { - return http.StatusBadRequest, fmt.Errorf("parseAddEntryV1Request: %v", err) - } - trsp, err := i.Client.QueueLeaf(ctx, &trillian.QueueLeafRequest{ - LogId: i.LogParameters.TreeId, - Leaf: &trillian.LogLeaf{ - LeafValue: leaf.Marshal(), - ExtraData: nil, - }, - }) - if errInner := checkQueueLeaf(trsp, err); errInner != nil { - return http.StatusInternalServerError, fmt.Errorf("bad QueueLeafResponse: %v", errInner) - } - return http.StatusOK, nil -} - -func addCosignature(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { - glog.V(3).Info("handling add-cosignature request") - req, err := i.LogParameters.parseAddCosignatureRequest(r) - if err != nil { - return http.StatusBadRequest, fmt.Errorf("parseAddCosignatureRequest: %v", err) - } - vk := i.LogParameters.Witnesses[*req.KeyHash] - if err := i.SthSource.AddCosignature(ctx, ed25519.PublicKey(vk[:]), req.Signature); err != nil { - return http.StatusBadRequest, fmt.Errorf("AddCosignature: %v", err) - } - return http.StatusOK, nil -} - -func getLatestSth(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) { - glog.V(3).Info("handling get-latest-sth request") - sth, err := i.SthSource.Latest(ctx) - if err != nil { - return http.StatusInternalServerError, fmt.Errorf("Latest: %v", err) - } - if err := sth.MarshalASCII(w); err != nil { - return http.StatusInternalServerError, fmt.Errorf("MarshalASCII: %v", err) - } - return http.StatusOK, nil -} - -func getStableSth(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) { - glog.V(3).Info("handling get-stable-sth request") - sth, err := i.SthSource.Stable(ctx) - if err != nil { - return http.StatusInternalServerError, fmt.Errorf("Latest: %v", err) - } - if err := sth.MarshalASCII(w); err != nil { - return http.StatusInternalServerError, fmt.Errorf("MarshalASCII: %v", err) - } - return http.StatusOK, nil -} - -func getCosignedSth(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) { - glog.V(3).Info("handling get-cosigned-sth request") - sth, err := i.SthSource.Cosigned(ctx) - if err != nil { - return http.StatusInternalServerError, fmt.Errorf("Cosigned: %v", err) - } - if err := sth.MarshalASCII(w); err != nil { - return http.StatusInternalServerError, fmt.Errorf("MarshalASCII: %v", err) - } - return http.StatusOK, nil -} - -func getConsistencyProof(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { - glog.V(3).Info("handling get-consistency-proof request") - req, err := i.LogParameters.parseGetConsistencyProofRequest(r) - if err != nil { - return http.StatusBadRequest, err - } - - trsp, err := i.Client.GetConsistencyProof(ctx, &trillian.GetConsistencyProofRequest{ - LogId: i.LogParameters.TreeId, - FirstTreeSize: int64(req.OldSize), - SecondTreeSize: int64(req.NewSize), - }) - if errInner := checkGetConsistencyProof(i.LogParameters, trsp, err); errInner != nil { - return http.StatusInternalServerError, fmt.Errorf("bad GetConsistencyProofResponse: %v", errInner) - } - - proof := &types.ConsistencyProof{ - NewSize: req.NewSize, - OldSize: req.OldSize, - Path: NodePathFromHashes(trsp.Proof.Hashes), - } - if err := proof.MarshalASCII(w); err != nil { - return http.StatusInternalServerError, fmt.Errorf("MarshalASCII: %v", err) - } - return http.StatusOK, nil -} - -func getProofByHash(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { - glog.V(3).Info("handling get-proof-by-hash request") - req, err := i.LogParameters.parseGetProofByHashRequest(r) - if err != nil { - return http.StatusBadRequest, err - } - - trsp, err := i.Client.GetInclusionProofByHash(ctx, &trillian.GetInclusionProofByHashRequest{ - LogId: i.LogParameters.TreeId, - LeafHash: req.LeafHash[:], - TreeSize: int64(req.TreeSize), - OrderBySequence: true, - }) - if errInner := checkGetInclusionProofByHash(i.LogParameters, trsp, err); errInner != nil { - return http.StatusInternalServerError, fmt.Errorf("bad GetInclusionProofByHashResponse: %v", errInner) - } - - proof := &types.InclusionProof{ - TreeSize: req.TreeSize, - LeafIndex: uint64(trsp.Proof[0].LeafIndex), - Path: NodePathFromHashes(trsp.Proof[0].Hashes), - } - if err := proof.MarshalASCII(w); err != nil { - return http.StatusInternalServerError, fmt.Errorf("MarshalASCII: %v", err) - } - return http.StatusOK, nil -} - -func getEntries(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { - glog.V(3).Info("handling get-entries request") - req, err := i.LogParameters.parseGetEntriesRequest(r) - if err != nil { - return http.StatusBadRequest, err - } - - trsp, err := i.Client.GetLeavesByRange(ctx, &trillian.GetLeavesByRangeRequest{ - LogId: i.LogParameters.TreeId, - StartIndex: int64(req.StartSize), - Count: int64(req.EndSize-req.StartSize) + 1, - }) - if errInner := checkGetLeavesByRange(req, trsp, err); errInner != nil { - return http.StatusInternalServerError, fmt.Errorf("checkGetLeavesByRangeResponse: %v", errInner) // there is one StatusBadRequest in here tho.. - } - - for _, serialized := range trsp.Leaves { - var leaf types.Leaf - if err := leaf.Unmarshal(serialized.LeafValue); err != nil { - return http.StatusInternalServerError, fmt.Errorf("Unmarshal: %v", err) - } - if err := leaf.MarshalASCII(w); err != nil { - return http.StatusInternalServerError, fmt.Errorf("MarshalASCII: %v", err) - } - } - return http.StatusOK, nil -} @@ -181,6 +181,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -461,6 +462,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -645,6 +647,7 @@ golang.org/x/tools v0.0.0-20200630154851-b2d8b0336632/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200706234117-b22de6825cf7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= diff --git a/instance.go b/instance.go deleted file mode 100644 index 4425770..0000000 --- a/instance.go +++ /dev/null @@ -1,75 +0,0 @@ -package stfe - -import ( - "context" - "fmt" - "time" - - "net/http" - - "github.com/golang/glog" - "github.com/google/trillian" - "github.com/system-transparency/stfe/types" -) - -// Instance is an instance of the system transparency front-end -type Instance struct { - Client trillian.TrillianLogClient - LogParameters *LogParameters - SthSource SthSource -} - -// Handlers returns a list of STFE handlers -func (i *Instance) Handlers() []Handler { - return []Handler{ - Handler{Instance: i, Handler: addEntry, Endpoint: types.EndpointAddLeaf, Method: http.MethodPost}, - Handler{Instance: i, Handler: addCosignature, Endpoint: types.EndpointAddCosignature, Method: http.MethodPost}, - Handler{Instance: i, Handler: getLatestSth, Endpoint: types.EndpointGetTreeHeadLatest, Method: http.MethodGet}, - Handler{Instance: i, Handler: getStableSth, Endpoint: types.EndpointGetTreeHeadToSign, Method: http.MethodGet}, - Handler{Instance: i, Handler: getCosignedSth, Endpoint: types.EndpointGetTreeHeadCosigned, Method: http.MethodGet}, - Handler{Instance: i, Handler: getProofByHash, Endpoint: types.EndpointGetProofByHash, Method: http.MethodPost}, - Handler{Instance: i, Handler: getConsistencyProof, Endpoint: types.EndpointGetConsistencyProof, Method: http.MethodPost}, - Handler{Instance: i, Handler: getEntries, Endpoint: types.EndpointGetLeaves, Method: http.MethodPost}, - } -} - -// 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 { - Instance *Instance - Endpoint types.Endpoint - Method string - Handler func(context.Context, *Instance, http.ResponseWriter, *http.Request) (int, error) -} - -// 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 - defer func() { - rspcnt.Inc(a.Instance.LogParameters.LogId, string(a.Endpoint), fmt.Sprintf("%d", statusCode)) - latency.Observe(time.Now().Sub(now).Seconds(), a.Instance.LogParameters.LogId, string(a.Endpoint), fmt.Sprintf("%d", statusCode)) - }() - reqcnt.Inc(a.Instance.LogParameters.LogId, string(a.Endpoint)) - - ctx, cancel := context.WithDeadline(r.Context(), now.Add(a.Instance.LogParameters.Deadline)) - defer cancel() - - if r.Method != a.Method { - glog.Warningf("%s/%s: got HTTP %s, wanted HTTP %s", a.Instance.LogParameters.Prefix, string(a.Endpoint), r.Method, a.Method) - http.Error(w, "", http.StatusMethodNotAllowed) - return - } - - statusCode, err := a.Handler(ctx, a.Instance, w, r) - if err != nil { - glog.Warningf("handler error %s/%s: %v", a.Instance.LogParameters.Prefix, a.Endpoint, err) - http.Error(w, fmt.Sprintf("%s%s%s%s", "Error", types.Delim, err.Error(), types.EOL), statusCode) - } -} diff --git a/log_parameters.go b/log_parameters.go deleted file mode 100644 index aceff3e..0000000 --- a/log_parameters.go +++ /dev/null @@ -1,47 +0,0 @@ -package stfe - -import ( - "crypto" - "crypto/ed25519" - "fmt" - "time" - - "github.com/system-transparency/stfe/types" -) - -// LogParameters is a collection of log parameters -type LogParameters struct { - LogId string // serialized log id (hex) - TreeId int64 // used internally by Trillian - Prefix string // e.g., "test" for <base>/test - MaxRange int64 // max entries per get-entries request - Deadline time.Duration // gRPC deadline - Interval time.Duration // cosigning sth frequency - HashType crypto.Hash // hash function used by Trillian - Signer crypto.Signer // access to Ed25519 private key - - // Witnesses map trusted witness identifiers to public verification keys - Witnesses map[[types.HashSize]byte][types.VerificationKeySize]byte -} - -// Sign signs a tree head -func (lp *LogParameters) Sign(th *types.TreeHead) (*types.SignedTreeHead, error) { - sig, err := lp.Signer.Sign(nil, th.Marshal(), crypto.Hash(0)) - if err != nil { - return nil, fmt.Errorf("Sign failed: %v", err) - } - lastSthTimestamp.Set(float64(time.Now().Unix()), lp.LogId) - lastSthSize.Set(float64(th.TreeSize), lp.LogId) - - sigident := types.SigIdent{ - KeyHash: types.Hash(lp.Signer.Public().(ed25519.PublicKey)[:]), - Signature: &[types.SignatureSize]byte{}, - } - copy(sigident.Signature[:], sig) - return &types.SignedTreeHead{ - TreeHead: *th, - SigIdent: []*types.SigIdent{ - &sigident, - }, - }, nil -} diff --git a/log_parameters_test.go b/log_parameters_test.go deleted file mode 100644 index 88e83ad..0000000 --- a/log_parameters_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package stfe - -import ( - "crypto" - "fmt" - "reflect" - "testing" - - cttestdata "github.com/google/certificate-transparency-go/trillian/testdata" - "github.com/system-transparency/stfe/testdata" - "github.com/system-transparency/stfe/types" -) - -// newLogParameters must create new log parameters with an optional log signer -// based on the parameters in "github.com/system-transparency/stfe/testdata". -// The log's namespace is initialized with testdata.LogEd25519Vk, the submmiter -// namespace list is initialized with testdata.SubmmiterEd25519, and the witness -// namespace list is initialized with testdata.WitnessEd25519Vk. The log's -// submitter and witness policies are set to reject unregistered namespace. -func newLogParameters(t *testing.T, signer crypto.Signer) *LogParameters { - t.Helper() - logId := testdata.NewNamespace(t, testdata.Ed25519VkLog) - witnessPool := testdata.NewNamespacePool(t, []*types.Namespace{ - testdata.NewNamespace(t, testdata.Ed25519VkWitness), - }) - submitPool := testdata.NewNamespacePool(t, []*types.Namespace{ - testdata.NewNamespace(t, testdata.Ed25519VkSubmitter), - }) - lp, err := NewLogParameters(signer, logId, testdata.TreeId, testdata.Prefix, submitPool, witnessPool, testdata.MaxRange, testdata.Interval, testdata.Deadline, true, true) - if err != nil { - t.Fatalf("must create new log parameters: %v", err) - } - return lp -} - -func TestNewLogParameters(t *testing.T) { - for _, table := range []struct { - description string - logId *types.Namespace - wantErr bool - }{ - { - description: "invalid: cannot marshal log id", - logId: &types.Namespace{ - Format: types.NamespaceFormatReserved, - }, - wantErr: true, - }, - { - description: "valid", - logId: testdata.NewNamespace(t, testdata.Ed25519VkLog), - }, - } { - _, err := NewLogParameters(nil, table.logId, testdata.TreeId, testdata.Prefix, nil, nil, testdata.MaxRange, testdata.Interval, testdata.Deadline, true, true) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) - } - } -} - -func TestSignTreeHeadV1(t *testing.T) { - for _, table := range []struct { - description string - th *types.TreeHeadV1 - signer crypto.Signer - wantErr bool - wantSth *types.StItem - }{ - { - description: "invalid: marshal failure", - th: types.NewTreeHeadV1(testdata.Timestamp, testdata.TreeSize, nil, testdata.Extension), - wantErr: true, - }, - { - description: "invalid: signature failure", - th: types.NewTreeHeadV1(testdata.Timestamp, testdata.TreeSize, testdata.NodeHash, testdata.Extension), - signer: cttestdata.NewSignerWithErr(nil, fmt.Errorf("signer failed")), - wantErr: true, - }, - { - description: "valid", - th: testdata.DefaultTh(t), - signer: cttestdata.NewSignerWithFixedSig(nil, testdata.Signature), - wantSth: testdata.DefaultSth(t, testdata.Ed25519VkLog), - }, - } { - sth, err := newLogParameters(t, table.signer).SignTreeHeadV1(table.th) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) - } - if err != nil { - continue - } - - if got, want := sth, table.wantSth; !reflect.DeepEqual(got, want) { - t.Errorf("got \n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description) - } - } -} diff --git a/pkg/instance/endpoint.go b/pkg/instance/endpoint.go new file mode 100644 index 0000000..5085c49 --- /dev/null +++ b/pkg/instance/endpoint.go @@ -0,0 +1,122 @@ +package stfe + +import ( + "context" + "net/http" + + "github.com/golang/glog" +) + +func addLeaf(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { + glog.V(3).Info("handling add-entry request") + req, err := i.leafRequestFromHTTP(r) + if err != nil { + return http.StatusBadRequest, err + } + if err := i.Client.AddLeaf(ctx, req); err != nil { + return http.StatusInternalServerError, err + } + return http.StatusOK, nil +} + +func addCosignature(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { + glog.V(3).Info("handling add-cosignature request") + req, err := i.cosignatureRequestFromHTTP(r) + if err != nil { + return http.StatusBadRequest, err + } + vk := i.Witnesses[*req.KeyHash] + if err := i.Stateman.AddCosignature(ctx, &vk, req.Signature); err != nil { + return http.StatusBadRequest, err + } + return http.StatusOK, nil +} + +func getTreeHeadLatest(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) { + glog.V(3).Info("handling get-tree-head-latest request") + sth, err := i.Stateman.Latest(ctx) + if err != nil { + return http.StatusInternalServerError, err + } + if err := sth.MarshalASCII(w); err != nil { + return http.StatusInternalServerError, err + } + return http.StatusOK, nil +} + +func getTreeHeadToSign(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) { + glog.V(3).Info("handling get-tree-head-to-sign request") + sth, err := i.Stateman.ToSign(ctx) + if err != nil { + return http.StatusInternalServerError, err + } + if err := sth.MarshalASCII(w); err != nil { + return http.StatusInternalServerError, err + } + return http.StatusOK, nil +} + +func getTreeHeadCosigned(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) { + glog.V(3).Info("handling get-tree-head-cosigned request") + sth, err := i.Stateman.Cosigned(ctx) + if err != nil { + return http.StatusInternalServerError, err + } + if err := sth.MarshalASCII(w); err != nil { + return http.StatusInternalServerError, err + } + return http.StatusOK, nil +} + +func getConsistencyProof(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { + glog.V(3).Info("handling get-consistency-proof request") + req, err := i.consistencyProofRequestFromHTTP(r) + if err != nil { + return http.StatusBadRequest, err + } + + proof, err := i.Client.GetConsistencyProof(ctx, req) + if err != nil { + return http.StatusInternalServerError, err + } + if err := proof.MarshalASCII(w); err != nil { + return http.StatusInternalServerError, err + } + return http.StatusOK, nil +} + +func getInclusionProof(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { + glog.V(3).Info("handling get-proof-by-hash request") + req, err := i.inclusionProofRequestFromHTTP(r) + if err != nil { + return http.StatusBadRequest, err + } + + proof, err := i.Client.GetInclusionProof(ctx, req) + if err != nil { + return http.StatusInternalServerError, err + } + if err := proof.MarshalASCII(w); err != nil { + return http.StatusInternalServerError, err + } + return http.StatusOK, nil +} + +func getLeaves(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { + glog.V(3).Info("handling get-leaves request") + req, err := i.leavesRequestFromHTTP(r) + if err != nil { + return http.StatusBadRequest, err + } + + leaves, err := i.Client.GetLeaves(ctx, req) + if err != nil { + return http.StatusInternalServerError, err + } + for _, leaf := range *leaves { + if err := leaf.MarshalASCII(w); err != nil { + return http.StatusInternalServerError, err + } + } + return http.StatusOK, nil +} diff --git a/endpoint_test.go b/pkg/instance/endpoint_test.go index e515635..8511b8d 100644 --- a/endpoint_test.go +++ b/pkg/instance/endpoint_test.go @@ -4,17 +4,16 @@ import ( "bytes" "context" "fmt" - "reflect" - "testing" - "net/http" "net/http/httptest" + "reflect" + "testing" "github.com/golang/mock/gomock" cttestdata "github.com/google/certificate-transparency-go/trillian/testdata" "github.com/google/trillian" - "github.com/system-transparency/stfe/testdata" - "github.com/system-transparency/stfe/types" + "github.com/system-transparency/stfe/pkg/testdata" + "github.com/system-transparency/stfe/pkg/types" ) func TestEndpointAddEntry(t *testing.T) { diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go new file mode 100644 index 0000000..3441a0a --- /dev/null +++ b/pkg/instance/instance.go @@ -0,0 +1,90 @@ +package stfe + +import ( + "context" + "crypto" + "fmt" + "net/http" + "time" + + "github.com/golang/glog" + "github.com/system-transparency/stfe/pkg/state" + "github.com/system-transparency/stfe/pkg/trillian" + "github.com/system-transparency/stfe/pkg/types" +) + +// Config is a collection of log parameters +type Config struct { + LogID string // H(public key), then hex-encoded + TreeID int64 // Merkle tree identifier used by Trillian + Prefix string // The portion between base URL and st/v0 (may be "") + MaxRange int64 // Maximum number of leaves per get-leaves request + Deadline time.Duration // Deadline used for gRPC requests + Interval time.Duration // Cosigning frequency + + // Witnesses map trusted witness identifiers to public verification keys + Witnesses map[[types.HashSize]byte][types.VerificationKeySize]byte +} + +// Instance is an instance of the log's front-end +type Instance struct { + Config // configuration parameters + Client trillian.Client // provides access to the Trillian backend + Signer crypto.Signer // provides access to Ed25519 private key + Stateman state.StateManager // coordinates access to (co)signed tree heads +} + +// 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 { + Instance *Instance + Endpoint types.Endpoint + Method string + Handler func(context.Context, *Instance, http.ResponseWriter, *http.Request) (int, error) +} + +// Handlers returns a list of STFE handlers +func (i *Instance) Handlers() []Handler { + return []Handler{ + Handler{Instance: i, Handler: addLeaf, Endpoint: types.EndpointAddLeaf, Method: http.MethodPost}, + Handler{Instance: i, Handler: addCosignature, Endpoint: types.EndpointAddCosignature, Method: http.MethodPost}, + Handler{Instance: i, Handler: getTreeHeadLatest, Endpoint: types.EndpointGetTreeHeadLatest, Method: http.MethodGet}, + Handler{Instance: i, Handler: getTreeHeadToSign, Endpoint: types.EndpointGetTreeHeadToSign, Method: http.MethodGet}, + Handler{Instance: i, Handler: getTreeHeadCosigned, Endpoint: types.EndpointGetTreeHeadCosigned, Method: http.MethodGet}, + Handler{Instance: i, Handler: getConsistencyProof, Endpoint: types.EndpointGetConsistencyProof, Method: http.MethodPost}, + Handler{Instance: i, Handler: getInclusionProof, Endpoint: types.EndpointGetProofByHash, Method: http.MethodPost}, + Handler{Instance: i, Handler: getLeaves, Endpoint: types.EndpointGetLeaves, Method: http.MethodPost}, + } +} + +// Path returns a path that should be configured for this handler +func (h Handler) Path() string { + return h.Endpoint.Path(h.Instance.Prefix, "st", "v0") +} + +// 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 + defer func() { + rspcnt.Inc(a.Instance.LogID, string(a.Endpoint), fmt.Sprintf("%d", statusCode)) + latency.Observe(time.Now().Sub(now).Seconds(), a.Instance.LogID, string(a.Endpoint), fmt.Sprintf("%d", statusCode)) + }() + reqcnt.Inc(a.Instance.LogID, string(a.Endpoint)) + + ctx, cancel := context.WithDeadline(r.Context(), now.Add(a.Instance.Deadline)) + defer cancel() + + if r.Method != a.Method { + glog.Warningf("%s/%s: got HTTP %s, wanted HTTP %s", a.Instance.Prefix, string(a.Endpoint), r.Method, a.Method) + http.Error(w, "", http.StatusMethodNotAllowed) + 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) + http.Error(w, fmt.Sprintf("%s%s%s%s", "Error", types.Delim, err.Error(), types.EOL), statusCode) + } +} diff --git a/instance_test.go b/pkg/instance/instance_test.go index de539a1..a7a3d8a 100644 --- a/instance_test.go +++ b/pkg/instance/instance_test.go @@ -2,15 +2,14 @@ package stfe import ( "crypto" - "testing" - "net/http" "net/http/httptest" + "testing" "github.com/golang/mock/gomock" "github.com/google/certificate-transparency-go/trillian/mockclient" - "github.com/system-transparency/stfe/testdata" - "github.com/system-transparency/stfe/types" + "github.com/system-transparency/stfe/pkg/testdata" + "github.com/system-transparency/stfe/pkg/types" ) type testInstance struct { diff --git a/metric.go b/pkg/instance/metric.go index 7e3e8b2..7e3e8b2 100644 --- a/metric.go +++ b/pkg/instance/metric.go diff --git a/request.go b/pkg/instance/request.go index 763d9ed..7475b26 100644 --- a/request.go +++ b/pkg/instance/request.go @@ -1,46 +1,42 @@ package stfe import ( - "fmt" - "crypto/ed25519" + "fmt" "net/http" - "github.com/system-transparency/stfe/types" + "github.com/system-transparency/stfe/pkg/types" ) -func (lp *LogParameters) parseAddEntryV1Request(r *http.Request) (*types.Leaf, error) { +func (i *Instance) leafRequestFromHTTP(r *http.Request) (*types.LeafRequest, error) { var req types.LeafRequest if err := req.UnmarshalASCII(r.Body); err != nil { return nil, fmt.Errorf("UnmarshalASCII: %v", err) } - if pub, msg, sig := ed25519.PublicKey(req.VerificationKey[:]), req.Message.Marshal(), req.Signature[:]; !ed25519.Verify(pub, msg, sig) { - return nil, fmt.Errorf("Invalid signature") + vk := ed25519.PublicKey(req.VerificationKey[:]) + msg := req.Message.Marshal() + sig := req.Signature[:] + if !ed25519.Verify(vk, msg, sig) { + return nil, fmt.Errorf("invalid signature") } // TODO: check shard hint // TODO: check domain hint - return &types.Leaf{ - Message: req.Message, - SigIdent: types.SigIdent{ - Signature: req.Signature, - KeyHash: types.Hash(req.VerificationKey[:]), - }, - }, nil + return &req, nil } -func (lp *LogParameters) parseAddCosignatureRequest(r *http.Request) (*types.CosignatureRequest, error) { +func (i *Instance) cosignatureRequestFromHTTP(r *http.Request) (*types.CosignatureRequest, error) { var req types.CosignatureRequest if err := req.UnmarshalASCII(r.Body); err != nil { return nil, fmt.Errorf("unpackOctetPost: %v", err) } - if _, ok := lp.Witnesses[*req.KeyHash]; !ok { + if _, ok := i.Witnesses[*req.KeyHash]; !ok { return nil, fmt.Errorf("Unknown witness: %x", req.KeyHash) } return &req, nil } -func (lp *LogParameters) parseGetConsistencyProofRequest(r *http.Request) (*types.ConsistencyProofRequest, error) { +func (i *Instance) consistencyProofRequestFromHTTP(r *http.Request) (*types.ConsistencyProofRequest, error) { var req types.ConsistencyProofRequest if err := req.UnmarshalASCII(r.Body); err != nil { return nil, fmt.Errorf("UnmarshalASCII: %v", err) @@ -54,7 +50,7 @@ func (lp *LogParameters) parseGetConsistencyProofRequest(r *http.Request) (*type return &req, nil } -func (lp *LogParameters) parseGetProofByHashRequest(r *http.Request) (*types.InclusionProofRequest, error) { +func (i *Instance) inclusionProofRequestFromHTTP(r *http.Request) (*types.InclusionProofRequest, error) { var req types.InclusionProofRequest if err := req.UnmarshalASCII(r.Body); err != nil { return nil, fmt.Errorf("UnmarshalASCII: %v", err) @@ -65,7 +61,7 @@ func (lp *LogParameters) parseGetProofByHashRequest(r *http.Request) (*types.Inc return &req, nil } -func (lp *LogParameters) parseGetEntriesRequest(r *http.Request) (*types.LeavesRequest, error) { +func (i *Instance) leavesRequestFromHTTP(r *http.Request) (*types.LeavesRequest, error) { var req types.LeavesRequest if err := req.UnmarshalASCII(r.Body); err != nil { return nil, fmt.Errorf("UnmarshalASCII: %v", err) @@ -74,8 +70,8 @@ func (lp *LogParameters) parseGetEntriesRequest(r *http.Request) (*types.LeavesR if req.StartSize > req.EndSize { return nil, fmt.Errorf("StartSize(%d) must be less than or equal to EndSize(%d)", req.StartSize, req.EndSize) } - if req.EndSize-req.StartSize+1 > uint64(lp.MaxRange) { - req.EndSize = req.StartSize + uint64(lp.MaxRange) - 1 + if req.EndSize-req.StartSize+1 > uint64(i.MaxRange) { + req.EndSize = req.StartSize + uint64(i.MaxRange) - 1 } return &req, nil } diff --git a/request_test.go b/pkg/instance/request_test.go index 102c56f..0a5a908 100644 --- a/request_test.go +++ b/pkg/instance/request_test.go @@ -9,8 +9,8 @@ import ( "net/http" - "github.com/system-transparency/stfe/testdata" - "github.com/system-transparency/stfe/types" + "github.com/system-transparency/stfe/pkg/testdata" + "github.com/system-transparency/stfe/pkg/types" ) func TestParseAddEntryV1Request(t *testing.T) { diff --git a/mocks/crypto.go b/pkg/mocks/crypto.go index 87c883a..87c883a 100644 --- a/mocks/crypto.go +++ b/pkg/mocks/crypto.go diff --git a/mocks/stfe.go b/pkg/mocks/stfe.go index e0fe7a9..def5bc6 100644 --- a/mocks/stfe.go +++ b/pkg/mocks/stfe.go @@ -9,7 +9,7 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" - types "github.com/system-transparency/stfe/types" + types "github.com/system-transparency/stfe/pkg/types" ) // MockClient is a mock of Client interface. diff --git a/mocks/trillian.go b/pkg/mocks/trillian.go index 8aa3a58..8aa3a58 100644 --- a/mocks/trillian.go +++ b/pkg/mocks/trillian.go diff --git a/state/state_manager.go b/pkg/state/state_manager.go index 3199e61..dfa73f4 100644 --- a/state/state_manager.go +++ b/pkg/state/state_manager.go @@ -1,4 +1,4 @@ -package stfe +package state import ( "context" @@ -10,8 +10,8 @@ import ( "github.com/golang/glog" "github.com/google/certificate-transparency-go/schedule" - "github.com/system-transparency/stfe/trillian" - "github.com/system-transparency/stfe/types" + "github.com/system-transparency/stfe/pkg/trillian" + "github.com/system-transparency/stfe/pkg/types" ) // StateManager coordinates access to the log's tree heads and (co)signatures diff --git a/state/state_manager_test.go b/pkg/state/state_manager_test.go index 348074c..08990cc 100644 --- a/state/state_manager_test.go +++ b/pkg/state/state_manager_test.go @@ -1,4 +1,4 @@ -package stfe +package state import ( "bytes" @@ -12,8 +12,8 @@ import ( "time" "github.com/golang/mock/gomock" - "github.com/system-transparency/stfe/mocks" - "github.com/system-transparency/stfe/types" + "github.com/system-transparency/stfe/pkg/mocks" + "github.com/system-transparency/stfe/pkg/types" ) var ( diff --git a/trillian/client.go b/pkg/trillian/client.go index 9ea6a4a..9523e56 100644 --- a/trillian/client.go +++ b/pkg/trillian/client.go @@ -7,7 +7,7 @@ import ( "github.com/golang/glog" "github.com/google/trillian" ttypes "github.com/google/trillian/types" - "github.com/system-transparency/stfe/types" + "github.com/system-transparency/stfe/pkg/types" "google.golang.org/grpc/codes" ) diff --git a/trillian/client_test.go b/pkg/trillian/client_test.go index e9f1ff5..6b3d881 100644 --- a/trillian/client_test.go +++ b/pkg/trillian/client_test.go @@ -9,8 +9,8 @@ import ( "github.com/golang/mock/gomock" "github.com/google/trillian" ttypes "github.com/google/trillian/types" - "github.com/system-transparency/stfe/mocks" - "github.com/system-transparency/stfe/types" + "github.com/system-transparency/stfe/pkg/mocks" + "github.com/system-transparency/stfe/pkg/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/trillian/util.go b/pkg/trillian/util.go index 87e64b6..4cf31fb 100644 --- a/trillian/util.go +++ b/pkg/trillian/util.go @@ -4,7 +4,7 @@ import ( "fmt" trillian "github.com/google/trillian/types" - siglog "github.com/system-transparency/stfe/types" + siglog "github.com/system-transparency/stfe/pkg/types" ) func treeHeadFromLogRoot(lr *trillian.LogRootV1) *siglog.TreeHead { diff --git a/types/ascii.go b/pkg/types/ascii.go index d27d79b..d27d79b 100644 --- a/types/ascii.go +++ b/pkg/types/ascii.go diff --git a/types/ascii_test.go b/pkg/types/ascii_test.go index 92732f9..92732f9 100644 --- a/types/ascii_test.go +++ b/pkg/types/ascii_test.go diff --git a/types/trunnel.go b/pkg/types/trunnel.go index 268f6f7..268f6f7 100644 --- a/types/trunnel.go +++ b/pkg/types/trunnel.go diff --git a/types/trunnel_test.go b/pkg/types/trunnel_test.go index 297578c..297578c 100644 --- a/types/trunnel_test.go +++ b/pkg/types/trunnel_test.go diff --git a/types/types.go b/pkg/types/types.go index 9ca7db8..9ca7db8 100644 --- a/types/types.go +++ b/pkg/types/types.go diff --git a/types/types_test.go b/pkg/types/types_test.go index da89c59..da89c59 100644 --- a/types/types_test.go +++ b/pkg/types/types_test.go diff --git a/types/util.go b/pkg/types/util.go index 3cd7dfa..3cd7dfa 100644 --- a/types/util.go +++ b/pkg/types/util.go @@ -1,143 +0,0 @@ -package stfe - -import ( - "context" - "crypto/ed25519" - "fmt" - "reflect" - "sync" - - "github.com/golang/glog" - "github.com/google/certificate-transparency-go/schedule" - "github.com/google/trillian" - ttypes "github.com/google/trillian/types" - "github.com/system-transparency/stfe/types" -) - -// SthSource provides access to the log's (co)signed tree heads -type SthSource interface { - Latest(context.Context) (*types.SignedTreeHead, error) - Stable(context.Context) (*types.SignedTreeHead, error) - Cosigned(context.Context) (*types.SignedTreeHead, error) - AddCosignature(context.Context, ed25519.PublicKey, *[types.SignatureSize]byte) error - Run(context.Context) -} - -// ActiveSthSource implements the SthSource interface for an STFE instance that -// accepts new logging requests, i.e., the log is running in read+write mode. -type ActiveSthSource struct { - client trillian.TrillianLogClient - logParameters *LogParameters - sync.RWMutex - - // cosigned is the current cosigned tree head that is served - cosigned types.SignedTreeHead - - // tosign is the current tree head that is being cosigned - tosign types.SignedTreeHead - - // cosignature keeps track of all collected cosignatures for tosign - cosignature map[[types.HashSize]byte]*types.SigIdent -} - -func NewActiveSthSource(cli trillian.TrillianLogClient, lp *LogParameters) (*ActiveSthSource, error) { - s := ActiveSthSource{ - client: cli, - logParameters: lp, - } - - ctx, _ := context.WithTimeout(context.Background(), lp.Deadline) - sth, err := s.Latest(ctx) - if err != nil { - return nil, fmt.Errorf("Latest: %v", err) - } - - s.cosigned = *sth - s.tosign = *sth - s.cosignature = make(map[[types.HashSize]byte]*types.SigIdent) - return &s, nil -} - -func (s *ActiveSthSource) Run(ctx context.Context) { - schedule.Every(ctx, s.logParameters.Interval, func(ctx context.Context) { - // get the next stable sth - ictx, _ := context.WithTimeout(ctx, s.logParameters.Deadline) - sth, err := s.Latest(ictx) - if err != nil { - glog.Warningf("cannot rotate without new sth: Latest: %v", err) - return - } - // rotate - s.Lock() - defer s.Unlock() - s.rotate(sth) - }) -} - -func (s *ActiveSthSource) Latest(ctx context.Context) (*types.SignedTreeHead, error) { - trsp, err := s.client.GetLatestSignedLogRoot(ctx, &trillian.GetLatestSignedLogRootRequest{ - LogId: s.logParameters.TreeId, - }) - var lr ttypes.LogRootV1 - if errInner := checkGetLatestSignedLogRoot(s.logParameters, trsp, err, &lr); errInner != nil { - return nil, fmt.Errorf("invalid signed log root response: %v", errInner) - } - return s.logParameters.Sign(NewTreeHeadFromLogRoot(&lr)) -} - -func (s *ActiveSthSource) Stable(_ context.Context) (*types.SignedTreeHead, error) { - s.RLock() - defer s.RUnlock() - return &s.tosign, nil -} - -func (s *ActiveSthSource) Cosigned(_ context.Context) (*types.SignedTreeHead, error) { - s.RLock() - defer s.RUnlock() - return &s.cosigned, nil -} - -func (s *ActiveSthSource) AddCosignature(_ context.Context, vk ed25519.PublicKey, sig *[types.SignatureSize]byte) error { - s.Lock() - defer s.Unlock() - - if msg := s.tosign.TreeHead.Marshal(); !ed25519.Verify(vk, msg, sig[:]) { - return fmt.Errorf("Invalid signature for tree head with timestamp: %d", s.tosign.TreeHead.Timestamp) - } - witness := types.Hash(vk[:]) - if _, ok := s.cosignature[*witness]; ok { - glog.V(3).Infof("received cosignature again (duplicate)") - return nil // duplicate - } - s.cosignature[*witness] = &types.SigIdent{ - Signature: sig, - KeyHash: witness, - } - glog.V(3).Infof("accepted new cosignature") - return nil -} - -// rotate rotates the log's cosigned and stable STH. The caller must aquire the -// source's read-write lock if there are concurrent reads and/or writes. -func (s *ActiveSthSource) rotate(next *types.SignedTreeHead) { - if reflect.DeepEqual(s.cosigned.TreeHead, s.tosign.TreeHead) { - for _, sigident := range s.cosigned.SigIdent[1:] { // skip log sigident - if _, ok := s.cosignature[*sigident.KeyHash]; !ok { - s.cosignature[*sigident.KeyHash] = sigident - } - } - } - var cosignatures []*types.SigIdent - for _, sigident := range s.cosignature { - cosignatures = append(cosignatures, sigident) - } // cosignatures contains all cosignatures, even if repeated tree head - - // Update cosigned tree head - s.cosigned.TreeHead = s.tosign.TreeHead - s.cosigned.SigIdent = append(s.tosign.SigIdent, cosignatures...) - - // Update to-sign tree head - s.tosign = *next - s.cosignature = make(map[[types.HashSize]byte]*types.SigIdent) - glog.V(3).Infof("rotated sth") -} diff --git a/sth_test.go b/sth_test.go deleted file mode 100644 index 0942ea1..0000000 --- a/sth_test.go +++ /dev/null @@ -1,466 +0,0 @@ -package stfe - -import ( - "context" - "crypto" - "fmt" - "reflect" - "testing" - - "github.com/golang/mock/gomock" - cttestdata "github.com/google/certificate-transparency-go/trillian/testdata" - "github.com/google/trillian" - "github.com/system-transparency/stfe/testdata" - "github.com/system-transparency/stfe/types" -) - -func TestNewActiveSthSource(t *testing.T) { - for _, table := range []struct { - description string - signer crypto.Signer - trsp *trillian.GetLatestSignedLogRootResponse - terr error - wantErr bool - wantCosi *types.StItem // current cosigned sth - wantStable *types.StItem // next stable sth that signatures are collected for - }{ - { - description: "invalid: no Trillian response", - signer: cttestdata.NewSignerWithFixedSig(nil, testdata.Signature), - terr: fmt.Errorf("internal server error"), - wantErr: true, - }, - { - description: "valid", - signer: cttestdata.NewSignerWithFixedSig(nil, testdata.Signature), - trsp: testdata.DefaultTSlr(t), - wantCosi: testdata.DefaultCosth(t, testdata.Ed25519VkLog, nil), - wantStable: testdata.DefaultCosth(t, testdata.Ed25519VkLog, nil), - }, - } { - func() { // run deferred functions at the end of each iteration - ti := newTestInstance(t, table.signer) - defer ti.ctrl.Finish() - ti.client.EXPECT().GetLatestSignedLogRoot(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr) - source, err := NewActiveSthSource(ti.client, ti.instance.LogParameters) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) - } - if err != nil { - return - } - - if got, want := source.currCosth, table.wantCosi; !reflect.DeepEqual(got, want) { - t.Errorf("got cosigned sth\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description) - } - if got, want := source.nextCosth, table.wantStable; !reflect.DeepEqual(got, want) { - t.Errorf("got stable sth\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description) - } - cosignatureFrom := make(map[[types.NamespaceFingerprintSize]byte]bool) - for _, cosig := range table.wantStable.CosignedTreeHeadV1.Cosignatures { - cosignatureFrom[testdata.Fingerprint(t, &cosig.Namespace)] = true - } - if got, want := source.cosignatureFrom, cosignatureFrom; !reflect.DeepEqual(got, want) { - if got == nil { - t.Errorf("got uninitialized witness map\n%v\n\tbut wanted\n%v\n\tin test %q", nil, want, table.description) - } else { - t.Errorf("got witness map\n%v\n\t but wanted\n%v\n\tin test %q", got, want, table.description) - } - } - }() - } -} - -func TestLatest(t *testing.T) { - for _, table := range []struct { - description string - signer crypto.Signer - trsp *trillian.GetLatestSignedLogRootResponse - terr error - wantErr bool - wantRsp *types.StItem - }{ - { - description: "invalid: no Trillian response", - signer: cttestdata.NewSignerWithFixedSig(nil, testdata.Signature), - terr: fmt.Errorf("internal server error"), - wantErr: true, - }, - { - description: "invalid: no signature", - signer: cttestdata.NewSignerWithErr(nil, fmt.Errorf("signing failed")), - terr: fmt.Errorf("internal server error"), - wantErr: true, - }, - { - description: "valid", - signer: cttestdata.NewSignerWithFixedSig(nil, testdata.Signature), - trsp: testdata.DefaultTSlr(t), - wantRsp: testdata.DefaultSth(t, testdata.Ed25519VkLog), - }, - } { - func() { // run deferred functions at the end of each iteration - ti := newTestInstance(t, table.signer) - defer ti.ctrl.Finish() - ti.client.EXPECT().GetLatestSignedLogRoot(gomock.Any(), gomock.Any()).Return(table.trsp, table.terr) // no deadline matcher because context is set by the caller of Latest(), i.e., this test on the line below - sth, err := ti.instance.SthSource.Latest(context.Background()) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) - } - if err != nil { - return - } - if got, want := sth, table.wantRsp; !reflect.DeepEqual(got, want) { - t.Errorf("got\n%v\n\tbut wanted\n%v\n\t in test %q", got, want, table.description) - } - }() - } -} - -func TestStable(t *testing.T) { - for _, table := range []struct { - description string - source SthSource - wantRsp *types.StItem - wantErr bool - }{ - { - description: "invalid: no stable sth", - source: &ActiveSthSource{}, - wantErr: true, - }, - { - description: "valid", - source: &ActiveSthSource{ - nextCosth: testdata.DefaultCosth(t, testdata.Ed25519VkLog, nil), - }, - wantRsp: testdata.DefaultSth(t, testdata.Ed25519VkLog), - }, - } { - sth, err := table.source.Stable(context.Background()) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) - } - if err != nil { - continue - } - if got, want := sth, table.wantRsp; !reflect.DeepEqual(got, want) { - t.Errorf("got\n%v\n\t but wanted\n%v\n\t in test %q", got, want, table.description) - } - } -} - -func TestCosigned(t *testing.T) { - for _, table := range []struct { - description string - source SthSource - wantRsp *types.StItem - wantErr bool - }{ - { - description: "invalid: no cosigned sth: nil", - source: &ActiveSthSource{}, - wantErr: true, - }, - { - description: "valid", - source: &ActiveSthSource{ - currCosth: testdata.DefaultCosth(t, testdata.Ed25519VkLog, [][32]byte{testdata.Ed25519VkWitness}), - }, - wantRsp: testdata.DefaultCosth(t, testdata.Ed25519VkLog, [][32]byte{testdata.Ed25519VkWitness}), - }, - } { - cosi, err := table.source.Cosigned(context.Background()) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) - } - if err != nil { - continue - } - if got, want := cosi, table.wantRsp; !reflect.DeepEqual(got, want) { - t.Errorf("got\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description) - } - } -} - -func TestAddCosignature(t *testing.T) { - for _, table := range []struct { - description string - source *ActiveSthSource - req *types.StItem - wantWit []*types.Namespace - wantErr bool - }{ - { - description: "invalid: cosignature must target the stable sth", - source: &ActiveSthSource{ - nextCosth: testdata.DefaultCosth(t, testdata.Ed25519VkLog, nil), - cosignatureFrom: make(map[[types.NamespaceFingerprintSize]byte]bool), - }, - req: testdata.DefaultCosth(t, testdata.Ed25519VkLog2, [][32]byte{testdata.Ed25519VkWitness}), - wantErr: true, - }, - { - description: "valid: adding duplicate into a pool of cosignatures", - source: &ActiveSthSource{ - nextCosth: testdata.DefaultCosth(t, testdata.Ed25519VkLog, [][32]byte{testdata.Ed25519VkWitness}), - cosignatureFrom: map[[types.NamespaceFingerprintSize]byte]bool{ - testdata.Fingerprint(t, testdata.NewNamespace(t, testdata.Ed25519VkWitness)): true, - }, - }, - req: testdata.DefaultCosth(t, testdata.Ed25519VkLog, [][32]byte{testdata.Ed25519VkWitness}), - wantWit: []*types.Namespace{testdata.NewNamespace(t, testdata.Ed25519VkWitness)}, - }, - { - description: "valid: adding into an empty pool of cosignatures", - source: &ActiveSthSource{ - nextCosth: testdata.DefaultCosth(t, testdata.Ed25519VkLog, nil), - cosignatureFrom: make(map[[types.NamespaceFingerprintSize]byte]bool), - }, - req: testdata.DefaultCosth(t, testdata.Ed25519VkLog, [][32]byte{testdata.Ed25519VkWitness}), - wantWit: []*types.Namespace{testdata.NewNamespace(t, testdata.Ed25519VkWitness)}, - }, - { - description: "valid: adding into a pool of cosignatures", - source: &ActiveSthSource{ - nextCosth: testdata.DefaultCosth(t, testdata.Ed25519VkLog, [][32]byte{testdata.Ed25519VkWitness}), - cosignatureFrom: map[[types.NamespaceFingerprintSize]byte]bool{ - testdata.Fingerprint(t, testdata.NewNamespace(t, testdata.Ed25519VkWitness)): true, - }, - }, - req: testdata.DefaultCosth(t, testdata.Ed25519VkLog, [][32]byte{testdata.Ed25519VkWitness2}), - wantWit: []*types.Namespace{testdata.NewNamespace(t, testdata.Ed25519VkWitness), testdata.NewNamespace(t, testdata.Ed25519VkWitness2)}, - }, - } { - err := table.source.AddCosignature(context.Background(), table.req) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) - } - if err != nil { - continue - } - - // Check that the next cosigned sth is updated - var sigs []types.SignatureV1 - for _, wit := range table.wantWit { - sigs = append(sigs, types.SignatureV1{ - Namespace: *wit, - Signature: testdata.Signature, - }) - } - if got, want := table.source.nextCosth, types.NewCosignedTreeHeadV1(testdata.DefaultSth(t, testdata.Ed25519VkLog).SignedTreeHeadV1, sigs); !reflect.DeepEqual(got, want) { - t.Errorf("got\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description) - } - // Check that the map tracking witness signatures is updated - if got, want := len(table.source.cosignatureFrom), len(table.wantWit); got != want { - t.Errorf("witness map got %d cosignatures but wanted %d in test %q", got, want, table.description) - } else { - for _, wit := range table.wantWit { - if _, ok := table.source.cosignatureFrom[testdata.Fingerprint(t, wit)]; !ok { - t.Errorf("missing signature from witness %X in test %q", testdata.Fingerprint(t, wit), table.description) - } - } - } - } -} - -func TestRotate(t *testing.T) { - // distinct sths - sth1 := testdata.DefaultSth(t, testdata.Ed25519VkLog) - sth2 := testdata.DefaultSth(t, testdata.Ed25519VkLog2) - sth3 := testdata.DefaultSth(t, testdata.Ed25519VkLog3) - // distinct witnesses - wit1 := testdata.NewNamespace(t, testdata.Ed25519VkWitness) - wit2 := testdata.NewNamespace(t, testdata.Ed25519VkWitness2) - wit3 := testdata.NewNamespace(t, testdata.Ed25519VkWitness3) - for _, table := range []struct { - description string - source *ActiveSthSource - fixedSth *types.StItem - wantCurrSth *types.StItem - wantNextSth *types.StItem - wantWit []*types.Namespace - }{ - { - description: "not repeated cosigned and not repeated stable", - source: &ActiveSthSource{ - currCosth: types.NewCosignedTreeHeadV1(sth1.SignedTreeHeadV1, nil), - nextCosth: types.NewCosignedTreeHeadV1(sth2.SignedTreeHeadV1, []types.SignatureV1{ - types.SignatureV1{ - Namespace: *wit1, - Signature: testdata.Signature, - }, - }), - cosignatureFrom: map[[types.NamespaceFingerprintSize]byte]bool{ - testdata.Fingerprint(t, wit1): true, - }, - }, - fixedSth: sth3, - wantCurrSth: types.NewCosignedTreeHeadV1(sth2.SignedTreeHeadV1, []types.SignatureV1{ - types.SignatureV1{ - Namespace: *wit1, - Signature: testdata.Signature, - }, - }), - wantNextSth: types.NewCosignedTreeHeadV1(sth3.SignedTreeHeadV1, nil), - wantWit: nil, // no cosignatures for the next stable sth yet - }, - { - description: "not repeated cosigned and repeated stable", - source: &ActiveSthSource{ - currCosth: types.NewCosignedTreeHeadV1(sth1.SignedTreeHeadV1, nil), - nextCosth: types.NewCosignedTreeHeadV1(sth2.SignedTreeHeadV1, []types.SignatureV1{ - types.SignatureV1{ - Namespace: *wit1, - Signature: testdata.Signature, - }, - }), - cosignatureFrom: map[[types.NamespaceFingerprintSize]byte]bool{ - testdata.Fingerprint(t, wit1): true, - }, - }, - fixedSth: sth2, - wantCurrSth: types.NewCosignedTreeHeadV1(sth2.SignedTreeHeadV1, []types.SignatureV1{ - types.SignatureV1{ - Namespace: *wit1, - Signature: testdata.Signature, - }, - }), - wantNextSth: types.NewCosignedTreeHeadV1(sth2.SignedTreeHeadV1, []types.SignatureV1{ - types.SignatureV1{ - Namespace: *wit1, - Signature: testdata.Signature, - }, - }), - wantWit: []*types.Namespace{wit1}, - }, - { - description: "repeated cosigned and not repeated stable", - source: &ActiveSthSource{ - currCosth: types.NewCosignedTreeHeadV1(sth1.SignedTreeHeadV1, []types.SignatureV1{ - types.SignatureV1{ - Namespace: *wit1, - Signature: testdata.Signature, - }, - types.SignatureV1{ - Namespace: *wit2, - Signature: testdata.Signature, - }, - }), - nextCosth: types.NewCosignedTreeHeadV1(sth1.SignedTreeHeadV1, []types.SignatureV1{ - types.SignatureV1{ - Namespace: *wit2, - Signature: testdata.Signature, - }, - types.SignatureV1{ - Namespace: *wit3, - Signature: testdata.Signature, - }, - }), - cosignatureFrom: map[[types.NamespaceFingerprintSize]byte]bool{ - testdata.Fingerprint(t, wit2): true, - testdata.Fingerprint(t, wit3): true, - }, - }, - fixedSth: sth3, - wantCurrSth: types.NewCosignedTreeHeadV1(sth1.SignedTreeHeadV1, []types.SignatureV1{ - types.SignatureV1{ - Namespace: *wit2, - Signature: testdata.Signature, - }, - types.SignatureV1{ - Namespace: *wit3, - Signature: testdata.Signature, - }, - types.SignatureV1{ - Namespace: *wit1, - Signature: testdata.Signature, - }, - }), - wantNextSth: types.NewCosignedTreeHeadV1(sth3.SignedTreeHeadV1, nil), - wantWit: nil, // no cosignatures for the next stable sth yet - }, - { - description: "repeated cosigned and repeated stable", - source: &ActiveSthSource{ - currCosth: types.NewCosignedTreeHeadV1(sth1.SignedTreeHeadV1, []types.SignatureV1{ - types.SignatureV1{ - Namespace: *wit1, - Signature: testdata.Signature, - }, - types.SignatureV1{ - Namespace: *wit2, - Signature: testdata.Signature, - }, - }), - nextCosth: types.NewCosignedTreeHeadV1(sth1.SignedTreeHeadV1, []types.SignatureV1{ - types.SignatureV1{ - Namespace: *wit2, - Signature: testdata.Signature, - }, - types.SignatureV1{ - Namespace: *wit3, - Signature: testdata.Signature, - }, - }), - cosignatureFrom: map[[types.NamespaceFingerprintSize]byte]bool{ - testdata.Fingerprint(t, wit2): true, - testdata.Fingerprint(t, wit3): true, - }, - }, - fixedSth: sth1, - wantCurrSth: types.NewCosignedTreeHeadV1(sth1.SignedTreeHeadV1, []types.SignatureV1{ - types.SignatureV1{ - Namespace: *wit2, - Signature: testdata.Signature, - }, - types.SignatureV1{ - Namespace: *wit3, - Signature: testdata.Signature, - }, - types.SignatureV1{ - Namespace: *wit1, - Signature: testdata.Signature, - }, - }), - wantNextSth: types.NewCosignedTreeHeadV1(sth1.SignedTreeHeadV1, []types.SignatureV1{ - types.SignatureV1{ - Namespace: *wit2, - Signature: testdata.Signature, - }, - types.SignatureV1{ - Namespace: *wit3, - Signature: testdata.Signature, - }, - types.SignatureV1{ - Namespace: *wit1, - Signature: testdata.Signature, - }, - }), - wantWit: []*types.Namespace{wit1, wit2, wit3}, - }, - } { - table.source.rotate(table.fixedSth) - if got, want := table.source.currCosth, table.wantCurrSth; !reflect.DeepEqual(got, want) { - t.Errorf("got currCosth\n%v\n\tbut wanted \n%v\n\tin test %q", got, want, table.description) - } - if got, want := table.source.nextCosth, table.wantNextSth; !reflect.DeepEqual(got, want) { - t.Errorf("got nextCosth\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description) - } - if got, want := len(table.source.cosignatureFrom), len(table.wantWit); got != want { - t.Errorf("witness map got %d cosignatures but wanted %d in test %q", got, want, table.description) - } else { - for _, wit := range table.wantWit { - if _, ok := table.source.cosignatureFrom[testdata.Fingerprint(t, wit)]; !ok { - t.Errorf("missing signature from witness %X in test %q", testdata.Fingerprint(t, wit), table.description) - } - } - } - // check that adding cosignatures to stable will not effect cosigned sth - wantLen := len(table.source.currCosth.CosignedTreeHeadV1.Cosignatures) - table.source.nextCosth.CosignedTreeHeadV1.Cosignatures = append(table.source.nextCosth.CosignedTreeHeadV1.Cosignatures, types.SignatureV1{Namespace: *wit1, Signature: testdata.Signature}) - if gotLen := len(table.source.currCosth.CosignedTreeHeadV1.Cosignatures); gotLen != wantLen { - t.Errorf("adding cosignatures to the stable sth modifies the fixated cosigned sth in test %q", table.description) - } - } -} diff --git a/testdata/data.go b/testdata/data.go deleted file mode 100644 index ac958e5..0000000 --- a/testdata/data.go +++ /dev/null @@ -1,287 +0,0 @@ -package testdata - -import ( - "bytes" - "testing" - "time" - - "crypto/ed25519" - - "github.com/google/trillian" - ttypes "github.com/google/trillian/types" - "github.com/system-transparency/stfe/types" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -var ( - Ed25519VkLog = [32]byte{} - Ed25519VkLog2 = [32]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} - Ed25519VkLog3 = [32]byte{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2} - //Ed25519VkWitness = [32]byte{3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3} - // Ed25519VkWitness2 = [32]byte{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4} - Ed25519VkWitness3 = [32]byte{5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5} - //Ed25519VkSubmitter = [32]byte{6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6} - - TreeId = int64(0) - Prefix = "test" - MaxRange = int64(3) - Interval = time.Second * 10 - Deadline = time.Second * 5 - - Timestamp = uint64(0) - TreeSize = uint64(0) - Extension = make([]byte, 0) - NodeHash = make([]byte, 32) - Signature = make([]byte, 64) - Identifier = []byte("foobar-1.2.3") - Checksum = make([]byte, 32) - Index = int64(0) - HashPath = [][]byte{ - NodeHash, - } - NodePath = []types.NodeHash{ - types.NodeHash{NodeHash}, - } - LeafHash = [32]byte{} - - // TODO: make these unique and load more pretty maybe - Ed25519SkWitness = [64]byte{230, 122, 195, 152, 194, 195, 147, 153, 80, 120, 153, 79, 102, 27, 52, 187, 136, 218, 150, 234, 107, 9, 167, 4, 92, 21, 11, 113, 42, 29, 129, 69, 75, 60, 249, 150, 229, 93, 75, 32, 103, 126, 244, 37, 53, 182, 68, 82, 249, 109, 49, 94, 10, 19, 146, 244, 58, 191, 169, 107, 78, 37, 45, 210} - Ed25519VkWitness = [32]byte{75, 60, 249, 150, 229, 93, 75, 32, 103, 126, 244, 37, 53, 182, 68, 82, 249, 109, 49, 94, 10, 19, 146, 244, 58, 191, 169, 107, 78, 37, 45, 210} - - Ed25519SkWitness2 = [64]byte{98, 65, 92, 117, 33, 167, 138, 36, 252, 147, 87, 173, 44, 62, 17, 66, 126, 70, 218, 87, 91, 148, 64, 194, 241, 248, 62, 90, 140, 122, 234, 76, 144, 6, 250, 185, 37, 217, 77, 201, 180, 42, 81, 37, 165, 27, 22, 32, 25, 8, 156, 228, 78, 207, 208, 18, 91, 77, 189, 51, 112, 31, 237, 6} - Ed25519VkWitness2 = [32]byte{144, 6, 250, 185, 37, 217, 77, 201, 180, 42, 81, 37, 165, 27, 22, 32, 25, 8, 156, 228, 78, 207, 208, 18, 91, 77, 189, 51, 112, 31, 237, 6} - - Ed25519SkSubmitter = [64]byte{230, 122, 195, 152, 194, 195, 147, 153, 80, 120, 153, 79, 102, 27, 52, 187, 136, 218, 150, 234, 107, 9, 167, 4, 92, 21, 11, 113, 42, 29, 129, 69, 75, 60, 249, 150, 229, 93, 75, 32, 103, 126, 244, 37, 53, 182, 68, 82, 249, 109, 49, 94, 10, 19, 146, 244, 58, 191, 169, 107, 78, 37, 45, 210} - Ed25519VkSubmitter = [32]byte{75, 60, 249, 150, 229, 93, 75, 32, 103, 126, 244, 37, 53, 182, 68, 82, 249, 109, 49, 94, 10, 19, 146, 244, 58, 191, 169, 107, 78, 37, 45, 210} - Ed25519SkSubmitter2 = [64]byte{98, 65, 92, 117, 33, 167, 138, 36, 252, 147, 87, 173, 44, 62, 17, 66, 126, 70, 218, 87, 91, 148, 64, 194, 241, 248, 62, 90, 140, 122, 234, 76, 144, 6, 250, 185, 37, 217, 77, 201, 180, 42, 81, 37, 165, 27, 22, 32, 25, 8, 156, 228, 78, 207, 208, 18, 91, 77, 189, 51, 112, 31, 237, 6} - Ed25519VkSubmitter2 = [32]byte{144, 6, 250, 185, 37, 217, 77, 201, 180, 42, 81, 37, 165, 27, 22, 32, 25, 8, 156, 228, 78, 207, 208, 18, 91, 77, 189, 51, 112, 31, 237, 6} -) - -// TODO: reorder and docdoc where need be -// -// Helpers that must create default values for different STFE types -// - -func DefaultCosth(t *testing.T, logVk [32]byte, witVk [][32]byte) *types.StItem { - t.Helper() - cosigs := make([]types.SignatureV1, 0) - for _, vk := range witVk { - cosigs = append(cosigs, types.SignatureV1{*NewNamespace(t, vk), Signature}) - } - return types.NewCosignedTreeHeadV1(DefaultSth(t, logVk).SignedTreeHeadV1, cosigs) -} - -func DefaultSth(t *testing.T, vk [32]byte) *types.StItem { - t.Helper() - return types.NewSignedTreeHeadV1(DefaultTh(t), DefaultSig(t, vk)) -} - -func DefaultSignedChecksum(t *testing.T, vk [32]byte) *types.StItem { - t.Helper() - return types.NewSignedChecksumV1(DefaultChecksum(t), DefaultSig(t, vk)) -} - -func DefaultTh(t *testing.T) *types.TreeHeadV1 { - t.Helper() - return types.NewTreeHeadV1(Timestamp, TreeSize, NodeHash, Extension) -} - -func DefaultSig(t *testing.T, vk [32]byte) *types.SignatureV1 { - t.Helper() - return &types.SignatureV1{*NewNamespace(t, vk), Signature} -} - -func DefaultChecksum(t *testing.T) *types.ChecksumV1 { - t.Helper() - return &types.ChecksumV1{Identifier, Checksum} -} - -func AddCosignatureBuffer(t *testing.T, sth *types.StItem, sk *[64]byte, vk *[32]byte) *bytes.Buffer { - t.Helper() - var cosigs []types.SignatureV1 - if vk != nil { - cosigs = []types.SignatureV1{ - types.SignatureV1{ - Namespace: *NewNamespace(t, *vk), - Signature: ed25519.Sign(ed25519.PrivateKey((*sk)[:]), marshal(t, *sth.SignedTreeHeadV1)), - }, - } - } - return bytes.NewBuffer(marshal(t, *types.NewCosignedTreeHeadV1(sth.SignedTreeHeadV1, cosigs))) -} - -func AddSignedChecksumBuffer(t *testing.T, sk [64]byte, vk [32]byte) *bytes.Buffer { - t.Helper() - data := DefaultChecksum(t) - return bytes.NewBuffer(marshal(t, *types.NewSignedChecksumV1( - data, - &types.SignatureV1{ - Namespace: *NewNamespace(t, vk), - Signature: ed25519.Sign(ed25519.PrivateKey(sk[:]), marshal(t, *data)), - }, - ))) -} - -func NewNamespacePool(t *testing.T, namespaces []*types.Namespace) *types.NamespacePool { - pool, err := types.NewNamespacePool(namespaces) - if err != nil { - t.Fatalf("must make namespace pool: %v", err) - } - return pool -} - -func NewNamespace(t *testing.T, vk [32]byte) *types.Namespace { - namespace, err := types.NewNamespaceEd25519V1(vk[:]) - if err != nil { - t.Fatalf("must make Ed25519V1 namespace: %v", err) - } - return namespace -} - -// -// Helpers that must create default values for different Trillian types -// - -// DefaultTLr creates a default Trillian log root -func DefaultTLr(t *testing.T) *ttypes.LogRootV1 { - t.Helper() - return Tlr(t, TreeSize, Timestamp, NodeHash) -} - -// Tlr creates a Trillian log root -func Tlr(t *testing.T, size, timestamp uint64, hash []byte) *ttypes.LogRootV1 { - t.Helper() - return &ttypes.LogRootV1{ - TreeSize: size, - RootHash: hash, - TimestampNanos: timestamp, - Revision: 0, // not used by stfe - Metadata: nil, // not used by stfe - } -} - -// DefaultTSlr creates a default Trillian signed log root -func DefaultTSlr(t *testing.T) *trillian.GetLatestSignedLogRootResponse { - t.Helper() - return Tslr(t, DefaultTLr(t)) -} - -// Tslr creates a Trillian signed log root -func Tslr(t *testing.T, lr *ttypes.LogRootV1) *trillian.GetLatestSignedLogRootResponse { - t.Helper() - b, err := lr.MarshalBinary() - if err != nil { - t.Fatalf("must marshal Trillian log root: %v", err) - } - return &trillian.GetLatestSignedLogRootResponse{ - SignedLogRoot: &trillian.SignedLogRoot{ - KeyHint: nil, // not used by stfe - LogRoot: b, - LogRootSignature: nil, // not used by stfe - }, - Proof: nil, // not used by stfe - } -} - -// DefaultTQlr creates a default Trillian queue leaf response -func DefaultTQlr(t *testing.T, withDupCode bool) *trillian.QueueLeafResponse { - t.Helper() - s := status.New(codes.OK, "ok").Proto() - if withDupCode { - s = status.New(codes.AlreadyExists, "duplicate").Proto() - } - return &trillian.QueueLeafResponse{ - QueuedLeaf: &trillian.QueuedLogLeaf{ - Leaf: &trillian.LogLeaf{ - MerkleLeafHash: nil, // not used by stfe - LeafValue: marshal(t, *DefaultSignedChecksum(t, Ed25519VkSubmitter)), - ExtraData: nil, // not used by stfe - LeafIndex: 0, // not applicable (log is not pre-ordered) - LeafIdentityHash: nil, // not used by stfe - }, - Status: s, - }, - } -} - -// DefaultTglbrr creates a default Trillian get leaves by range response -func DefaultTGlbrr(t *testing.T, start, end int64) *trillian.GetLeavesByRangeResponse { - t.Helper() - leaves := make([]*trillian.LogLeaf, 0, end-start+1) - for i, n := start, end+1; i < n; i++ { - leaves = append(leaves, &trillian.LogLeaf{ - MerkleLeafHash: nil, // not usedb y stfe - LeafValue: marshal(t, *DefaultSignedChecksum(t, Ed25519VkSubmitter)), - ExtraData: nil, // not used by stfe - LeafIndex: i, - LeafIdentityHash: nil, // not used by stfe - }) - } - return &trillian.GetLeavesByRangeResponse{ - Leaves: leaves, - SignedLogRoot: Tslr(t, Tlr(t, uint64(end)+1, Timestamp, NodeHash)).SignedLogRoot, - } -} - -func DefaultStItemList(t *testing.T, start, end uint64) *types.StItemList { - items := make([]types.StItem, 0, end-start+1) - for i, n := start, end+1; i < n; i++ { - items = append(items, *DefaultSignedChecksum(t, Ed25519VkSubmitter)) - } - return &types.StItemList{items} -} - -// DefaultTGipbhr creates a default Trillian get inclusion proof by hash response -func DefaultTGipbhr(t *testing.T) *trillian.GetInclusionProofByHashResponse { - t.Helper() - return &trillian.GetInclusionProofByHashResponse{ - Proof: []*trillian.Proof{ - &trillian.Proof{ - LeafIndex: Index, - Hashes: HashPath, - }, - }, - SignedLogRoot: nil, // not used by stfe - } -} - -func DefaultInclusionProof(t *testing.T, size uint64) *types.StItem { - return types.NewInclusionProofV1(NewNamespace(t, Ed25519VkLog), size, uint64(Index), NodePath) -} - -// DefaultTGcpr creates a default Trillian get consistency proof response -func DefaultTGcpr(t *testing.T) *trillian.GetConsistencyProofResponse { - t.Helper() - return &trillian.GetConsistencyProofResponse{ - Proof: &trillian.Proof{ - LeafIndex: 0, // not applicable for consistency proofs - Hashes: HashPath, - }, - SignedLogRoot: nil, // not used by stfe - } -} - -func DefaultConsistencyProof(t *testing.T, first, second uint64) *types.StItem { - return types.NewConsistencyProofV1(NewNamespace(t, Ed25519VkLog), first, second, NodePath) -} - -// -// Other helpers -// - -func Fingerprint(t *testing.T, namespace *types.Namespace) [types.NamespaceFingerprintSize]byte { - fpr, err := namespace.Fingerprint() - if err != nil { - t.Fatalf("must have namespace fingerprint: %v", err) - } - return *fpr -} - -func marshal(t *testing.T, i interface{}) []byte { - b, err := types.Marshal(i) - if err != nil { - t.Fatalf("must marshal interface: %v", err) - } - return b -} diff --git a/trillian.go b/trillian.go deleted file mode 100644 index f358d4d..0000000 --- a/trillian.go +++ /dev/null @@ -1,125 +0,0 @@ -package stfe - -import ( - "fmt" - - "github.com/golang/glog" - "github.com/google/trillian" - "github.com/google/trillian/types" - stfetypes "github.com/system-transparency/stfe/types" - "google.golang.org/grpc/codes" -) - -func checkQueueLeaf(rsp *trillian.QueueLeafResponse, err error) error { - if err != nil { - return fmt.Errorf("Trillian error: %v", err) - } - if rsp == nil { - return fmt.Errorf("Trillian error: empty response") - } - if rsp.QueuedLeaf == nil { - return fmt.Errorf("Trillian error: empty QueuedLeaf") - } - if codes.Code(rsp.QueuedLeaf.GetStatus().GetCode()) == codes.AlreadyExists { - glog.V(3).Infof("queued leaf is a duplicate => %X", rsp.QueuedLeaf.Leaf.LeafValue) - } - return nil -} - -func checkGetLeavesByRange(req *stfetypes.LeavesRequest, rsp *trillian.GetLeavesByRangeResponse, err error) error { - if err != nil { - return fmt.Errorf("Trillian Error: %v", err) - } - if rsp == nil { - return fmt.Errorf("Trillian error: empty response") - } - if rsp.SignedLogRoot == nil { - return fmt.Errorf("Trillian error: no signed log root") - } - if rsp.SignedLogRoot.LogRoot == nil { - return fmt.Errorf("Trillian error: no log root") - } - if len(rsp.Leaves) == 0 { - return fmt.Errorf("Trillian error: no leaves") - } - if len(rsp.Leaves) > int(req.EndSize-req.StartSize+1) { - return fmt.Errorf("too many leaves: %d for [%d,%d]", len(rsp.Leaves), req.StartSize, req.EndSize) - } - - // Ensure that a bad start parameter results in an error - var lr types.LogRootV1 - if err := lr.UnmarshalBinary(rsp.SignedLogRoot.LogRoot); err != nil { - return fmt.Errorf("cannot unmarshal log root: %v", err) - } - if uint64(req.StartSize) >= lr.TreeSize { - return fmt.Errorf("invalid start(%d): tree size is %d", req.StartSize, lr.TreeSize) - } - - // Ensure that we got and return expected leaf indices - for i, leaf := range rsp.Leaves { - if got, want := leaf.LeafIndex, int64(req.StartSize+uint64(i)); got != want { - return fmt.Errorf("invalid leaf index(%d): wanted %d", got, want) - } - } - return nil -} - -func checkGetInclusionProofByHash(lp *LogParameters, rsp *trillian.GetInclusionProofByHashResponse, err error) error { - if err != nil { - return fmt.Errorf("Trillian Error: %v", err) - } - if rsp == nil { - return fmt.Errorf("Trillian error: empty response") - } - if len(rsp.Proof) == 0 { - return fmt.Errorf("Trillian error: no proofs") - } - if rsp.Proof[0] == nil { - return fmt.Errorf("Trillian error: no proof") - } - return checkHashPath(lp.HashType.Size(), rsp.Proof[0].Hashes) -} - -func checkGetConsistencyProof(lp *LogParameters, rsp *trillian.GetConsistencyProofResponse, err error) error { - if err != nil { - return fmt.Errorf("Trillian Error: %v", err) - } - if rsp == nil { - return fmt.Errorf("Trillian error: empty response") - } - if rsp.Proof == nil { - return fmt.Errorf("Trillian error: no proof") - } - return checkHashPath(lp.HashType.Size(), rsp.Proof.Hashes) -} - -func checkGetLatestSignedLogRoot(lp *LogParameters, rsp *trillian.GetLatestSignedLogRootResponse, err error, out *types.LogRootV1) error { - if err != nil { - return fmt.Errorf("Trillian Error: %v", err) - } - if rsp == nil { - return fmt.Errorf("Trillian error: empty response") - } - if rsp.SignedLogRoot == nil { - return fmt.Errorf("Trillian error: no signed log root") - } - if rsp.SignedLogRoot.LogRoot == nil { - return fmt.Errorf("Trillian error: no log root") - } - if err := out.UnmarshalBinary(rsp.SignedLogRoot.LogRoot); err != nil { - return fmt.Errorf("cannot unmarshal log root: %v", err) - } - if len(out.RootHash) != lp.HashType.Size() { - return fmt.Errorf("invalid root hash: %v", out.RootHash) - } - return nil -} - -func checkHashPath(hashSize int, path [][]byte) error { - for _, hash := range path { - if len(hash) != hashSize { - return fmt.Errorf("invalid proof: %v", path) - } - } - return nil -} diff --git a/trillian_test.go b/trillian_test.go deleted file mode 100644 index 1b0c923..0000000 --- a/trillian_test.go +++ /dev/null @@ -1,282 +0,0 @@ -package stfe - -import ( - "fmt" - "testing" - - "github.com/google/trillian" - ttypes "github.com/google/trillian/types" - "github.com/system-transparency/stfe/testdata" - "github.com/system-transparency/stfe/types" -) - -func TestCheckQueueLeaf(t *testing.T) { - for _, table := range []struct { - description string - rsp *trillian.QueueLeafResponse - err error - wantErr bool - }{ - { - description: "invalid: no Trillian response: error", - err: fmt.Errorf("backend error"), - wantErr: true, - }, - { - description: "invalid: no Trillian response: nil", - wantErr: true, - }, - { - description: "invalid: no Trillian response: empty", - rsp: &trillian.QueueLeafResponse{}, - wantErr: true, - }, - { - description: "valid: gRPC status: duplicate", - rsp: testdata.DefaultTQlr(t, true), - }, - { - description: "valid: gRPC status: ok", - rsp: testdata.DefaultTQlr(t, false), - }, - } { - err := checkQueueLeaf(table.rsp, table.err) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q", got, want, table.description) - } - } -} - -func TestCheckGetLeavesByRange(t *testing.T) { - for _, table := range []struct { - description string - req *types.GetEntriesV1 - rsp *trillian.GetLeavesByRangeResponse - err error - wantErr bool - }{ - { - description: "invalid: no Trillian response: error", - req: &types.GetEntriesV1{Start: 0, End: 1}, - err: fmt.Errorf("backend error"), - wantErr: true, - }, - { - description: "invalid: no Trillian response: nil", - req: &types.GetEntriesV1{Start: 0, End: 1}, - wantErr: true, - }, - { - description: "invalid: bad Trillian response: no leaves", - req: &types.GetEntriesV1{Start: 0, End: 1}, - rsp: func(rsp *trillian.GetLeavesByRangeResponse) *trillian.GetLeavesByRangeResponse { - rsp.Leaves = nil - return rsp - }(testdata.DefaultTGlbrr(t, 0, 1)), - wantErr: true, - }, - { - description: "invalid: bad Trillian response: no signed log root", - req: &types.GetEntriesV1{Start: 0, End: 1}, - rsp: func(rsp *trillian.GetLeavesByRangeResponse) *trillian.GetLeavesByRangeResponse { - rsp.SignedLogRoot = nil - return rsp - }(testdata.DefaultTGlbrr(t, 0, 1)), - wantErr: true, - }, - { - description: "invalid: bad Trillian response: no log root", - req: &types.GetEntriesV1{Start: 0, End: 1}, - rsp: func(rsp *trillian.GetLeavesByRangeResponse) *trillian.GetLeavesByRangeResponse { - rsp.SignedLogRoot.LogRoot = nil - return rsp - }(testdata.DefaultTGlbrr(t, 0, 1)), - wantErr: true, - }, - { - description: "invalid: bad Trillian response: truncated log root", - req: &types.GetEntriesV1{Start: 0, End: 1}, - rsp: func(rsp *trillian.GetLeavesByRangeResponse) *trillian.GetLeavesByRangeResponse { - rsp.SignedLogRoot.LogRoot = rsp.SignedLogRoot.LogRoot[1:] - return rsp - }(testdata.DefaultTGlbrr(t, 0, 1)), - wantErr: true, - }, - { - description: "invalid: bad Trillian response: too many leaves", - req: &types.GetEntriesV1{Start: 0, End: 1}, - rsp: testdata.DefaultTGlbrr(t, 0, 2), - wantErr: true, - }, - { - description: "invalid: bad Trillian response: start is not a valid index", - req: &types.GetEntriesV1{Start: 10, End: 10}, - rsp: testdata.DefaultTGlbrr(t, 9, 9), - wantErr: true, - }, - { - description: "invalid: bad Trillian response: invalid leaf indices", - req: &types.GetEntriesV1{Start: 10, End: 11}, - rsp: testdata.DefaultTGlbrr(t, 11, 12), - wantErr: true, - }, - { - description: "valid", - req: &types.GetEntriesV1{Start: 10, End: 20}, - rsp: testdata.DefaultTGlbrr(t, 10, 20), - }, - } { - err := checkGetLeavesByRange(table.req, table.rsp, table.err) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q", got, want, table.description) - } - } -} - -func TestCheckGetInclusionProofByHash(t *testing.T) { - for _, table := range []struct { - description string - rsp *trillian.GetInclusionProofByHashResponse - err error - wantErr bool - }{ - { - description: "invalid: no Trillian response: error", - err: fmt.Errorf("backend failure"), - wantErr: true, - }, - { - description: "invalid: no Trillian response: nil", - wantErr: true, - }, - { - description: "invalid: bad Trillian response: no proofs", - rsp: &trillian.GetInclusionProofByHashResponse{}, - wantErr: true, - }, - { - description: "bad response: no proof", - rsp: func(rsp *trillian.GetInclusionProofByHashResponse) *trillian.GetInclusionProofByHashResponse { - rsp.Proof[0] = nil - return rsp - }(testdata.DefaultTGipbhr(t)), - wantErr: true, - }, - { - description: "bad response: proof with invalid node hash", - rsp: func(rsp *trillian.GetInclusionProofByHashResponse) *trillian.GetInclusionProofByHashResponse { - rsp.Proof[0].Hashes = append(rsp.Proof[0].Hashes, make([]byte, 0)) - return rsp - }(testdata.DefaultTGipbhr(t)), - wantErr: true, - }, - { - description: "valid", - rsp: testdata.DefaultTGipbhr(t), - }, - } { - err := checkGetInclusionProofByHash(newLogParameters(t, nil), table.rsp, table.err) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q", got, want, table.description) - } - } -} - -func TestCheckGetConsistencyProof(t *testing.T) { - for _, table := range []struct { - description string - rsp *trillian.GetConsistencyProofResponse - err error - wantErr bool - }{ - { - description: "invalid: no Trillian response: error", - err: fmt.Errorf("backend failure"), - wantErr: true, - }, - { - description: "invalid: no Trillian response: nil", - wantErr: true, - }, - { - description: "invalid: bad Trillian response: no proof", - rsp: &trillian.GetConsistencyProofResponse{}, - wantErr: true, - }, - { - description: "invalid: bad Trillian response: proof with invalid node hash", - rsp: func(rsp *trillian.GetConsistencyProofResponse) *trillian.GetConsistencyProofResponse { - rsp.Proof.Hashes = append(rsp.Proof.Hashes, make([]byte, 0)) - return rsp - }(testdata.DefaultTGcpr(t)), - wantErr: true, - }, - { - description: "valid", - rsp: testdata.DefaultTGcpr(t), - }, - } { - err := checkGetConsistencyProof(newLogParameters(t, nil), table.rsp, table.err) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q", got, want, table.description) - } - } -} - -func TestCheckGetLatestSignedLogRoot(t *testing.T) { - for _, table := range []struct { - description string - rsp *trillian.GetLatestSignedLogRootResponse - err error - wantErr bool - }{ - { - description: "invalid: no Trillian response: error", - err: fmt.Errorf("backend failure"), - wantErr: true, - }, - { - description: "invalid: no Trillian response: nil", - wantErr: true, - }, - { - description: "invalid: bad Trillian response: no signed log root", - rsp: func(rsp *trillian.GetLatestSignedLogRootResponse) *trillian.GetLatestSignedLogRootResponse { - rsp.SignedLogRoot = nil - return rsp - }(testdata.DefaultTSlr(t)), - wantErr: true, - }, - { - description: "invalid: bad Trillian response: no log root", - rsp: func(rsp *trillian.GetLatestSignedLogRootResponse) *trillian.GetLatestSignedLogRootResponse { - rsp.SignedLogRoot.LogRoot = nil - return rsp - }(testdata.DefaultTSlr(t)), - wantErr: true, - }, - { - description: "invalid: bad Trillian response: truncated log root", - rsp: func(rsp *trillian.GetLatestSignedLogRootResponse) *trillian.GetLatestSignedLogRootResponse { - rsp.SignedLogRoot.LogRoot = rsp.SignedLogRoot.LogRoot[1:] - return rsp - }(testdata.DefaultTSlr(t)), - wantErr: true, - }, - { - description: "invalid: bad Trillian response: truncated root hash", - rsp: testdata.Tslr(t, testdata.Tlr(t, testdata.TreeSize, testdata.Timestamp, make([]byte, 31))), - wantErr: true, - }, - { - description: "valid", - rsp: testdata.DefaultTSlr(t), - }, - } { - var lr ttypes.LogRootV1 - err := checkGetLatestSignedLogRoot(newLogParameters(t, nil), table.rsp, table.err, &lr) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q", got, want, table.description) - } - } -} diff --git a/util.go b/util.go deleted file mode 100644 index a8c918e..0000000 --- a/util.go +++ /dev/null @@ -1,27 +0,0 @@ -package stfe - -import ( - ttypes "github.com/google/trillian/types" - "github.com/system-transparency/stfe/types" -) - -func NewTreeHeadFromLogRoot(lr *ttypes.LogRootV1) *types.TreeHead { - var hash [types.HashSize]byte - th := types.TreeHead{ - Timestamp: uint64(lr.TimestampNanos / 1000 / 1000 / 1000), - TreeSize: uint64(lr.TreeSize), - RootHash: &hash, - } - copy(th.RootHash[:], lr.RootHash) - return &th -} - -func NodePathFromHashes(hashes [][]byte) []*[types.HashSize]byte { - var path []*[types.HashSize]byte - for _, hash := range hashes { - var h [types.HashSize]byte - copy(h[:], hash) - path = append(path, &h) - } - return path -} diff --git a/util_test.go b/util_test.go deleted file mode 100644 index b40a672..0000000 --- a/util_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package stfe - -import ( - "testing" -) - -// TODO: TestNewTreeHeadV1FromLogRoot -func TestNewTreeHeadV1FromLogRoot(t *testing.T) { -} - -// TODO: TestNewNodePathFromHashPath -func TestNewNodePathFromHashPath(t *testing.T) { -} - -// TODO: TestStItemListFromLeaves -func TestStItemListFromLeaves(t *testing.T) { -} |