diff options
| -rw-r--r-- | client/client.go | 188 | ||||
| -rw-r--r-- | client/cmd/add-entry/main.go | 4 | ||||
| -rwxr-xr-x | client/cmd/example.sh | 33 | ||||
| -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/flag.go | 55 | ||||
| -rw-r--r-- | client/verify.go | 52 | 
8 files changed, 487 insertions, 64 deletions
| diff --git a/client/client.go b/client/client.go index ead2c77..1a3470f 100644 --- a/client/client.go +++ b/client/client.go @@ -4,13 +4,8 @@ import (  	"bytes"  	"context"  	"crypto" -	"flag"  	"fmt" -	"reflect" -	"crypto/ed25519" -	"crypto/rand" -	"encoding/base64"  	"io/ioutil"  	"net/http" @@ -21,12 +16,13 @@ import (  	"golang.org/x/net/context/ctxhttp"  ) -var ( -	logId      = flag.String("log_id", "AAEsY0retj4wa3S2fjsOCJCTVHab7ipEiMdqtW1uJ6Jvmg==", "base64-encoded log identifier") -	logUrl     = flag.String("log_url", "http://localhost:6965/st/v1", "log url") -	ed25519_sk = flag.String("ed25519_sk", "d8i6nud7PS1vdO0sIk9H+W0nyxbM63Y3/mSeUPRafWaFh8iH8QXvL7NaAYn2RZPrnEey+FdpmTYXE47OFO70eg==", "base64-encoded ed25519 signing key") -) +// 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 @@ -34,56 +30,116 @@ type Client struct {  	Log        *Descriptor      // log's public identity  } -type Descriptor struct { -	Namespace *types.Namespace // log identifier is a namespace -	Url       string           // log url, e.g., http://example.com/st/v1 +// 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  } -func NewClientFromFlags() (*Client, error) { -	var err error -	c := Client{ -		HttpClient: &http.Client{}, +// 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)  	} -	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) -		} +	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 c.Log, err = NewDescriptorFromFlags(); err != nil { -		return nil, fmt.Errorf("NewDescriptorFromFlags: %v", err) +	if got, want := item.Format, types.StFormatInclusionProofV1; got != want { +		return nil, fmt.Errorf("unexpected StItem format: %v", item.Format)  	} -	return &c, nil +	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  } -func NewDescriptorFromFlags() (*Descriptor, error) { -	b, err := base64.StdEncoding.DecodeString(*logId) +// 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("LogId: DecodeString: %v", err) +		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)  	} -	var namespace types.Namespace -	if err := types.Unmarshal(b, &namespace); err != nil { -		return nil, fmt.Errorf("LogId: Unmarshal: %v", err) +	if err := VerifyConsistencyProofV1(item, sth1, sth2); err != nil { +		return nil, fmt.Errorf("invalid inclusion proof: %v", err)  	} -	return &Descriptor{ -		Namespace: &namespace, -		Url:       *logUrl, -	}, nil +	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, which can be used to verify inclusion. +// 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(rand.Reader, msg, crypto.Hash(0)) +	sig, err := c.Signer.Sign(nil, msg, crypto.Hash(0))  	if err != nil {  		return nil, fmt.Errorf("failed signing ChecksumV1: %v", err)  	} @@ -94,7 +150,7 @@ func (c *Client) AddEntry(ctx context.Context, data *types.ChecksumV1) ([]byte,  	if err != nil {  		return nil, fmt.Errorf("failed marshaling SignedChecksumV1: %v", err)  	} -	glog.V(9).Infof("signed: %v", data) +	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)) @@ -113,34 +169,42 @@ func (c *Client) AddEntry(ctx context.Context, data *types.ChecksumV1) ([]byte,  	return rfc6962.DefaultHasher.HashLeaf(leaf), nil  } -func (c *Client) GetLatestSth(ctx context.Context) (*types.StItem, error) { -	url := stfe.EndpointGetLatestSth.Path(c.Log.Url) -	req, err := http.NewRequest("GET", url, 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) -	item, err := c.doRequestWithStItemResponse(ctx, req) +	body, err := c.doRequest(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 got, want := &item.SignedTreeHeadV1.Signature.Namespace, c.Log.Namespace; !reflect.DeepEqual(got, want) { -		return nil, fmt.Errorf("unexpected log id: %v", want) +		return nil, fmt.Errorf("doRequest: %v", err)  	} - -	th, err := types.Marshal(item.SignedTreeHeadV1.TreeHead) -	if err != nil { -		return nil, fmt.Errorf("failed marshaling tree head: %v", err) +	var list types.StItemList +	if err := types.Unmarshal(body, &list); err != nil { +		return nil, fmt.Errorf("Unmarshal: %v", err)  	} -	if err := c.Log.Namespace.Verify(th, item.SignedTreeHeadV1.Signature.Signature); err != nil { -		return nil, fmt.Errorf("signature verification failed: %v", err) +	ret := make([]*types.StItem, 0, len(list.Items)) +	for _, item := range list.Items { +		if got, want := item.Format, types.StFormatSignedChecksumV1; got != want { +			return nil, fmt.Errorf("unexpected StItem format: %v", got) +		} +		ret = append(ret, &item)  	} -	glog.V(3).Infof("verified sth") -	return item, nil +	return ret, nil  }  // doRequest sends an HTTP request and outputs the raw body diff --git a/client/cmd/add-entry/main.go b/client/cmd/add-entry/main.go index 03844fa..a29d01f 100644 --- a/client/cmd/add-entry/main.go +++ b/client/cmd/add-entry/main.go @@ -13,8 +13,8 @@ import (  )  var ( -	identifier = flag.String("identifier", "foobar-1.2.3", "checksum identifier") -	checksum   = flag.String("checksum", "50e7967bce266a506f8f614bb5096beba580d205046b918f47d23b2ec626d75e", "base64-encoded checksum") +	identifier = flag.String("identifier", "", "checksum identifier") +	checksum   = flag.String("checksum", "", "base64-encoded checksum")  )  func main() { diff --git a/client/cmd/example.sh b/client/cmd/example.sh new file mode 100755 index 0000000..beac26e --- /dev/null +++ b/client/cmd/example.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +echo "fetching sth..." +go run get-sth/main.go --logtostderr -v 3 | tee sth1.output +echo "" && sleep 1 + +echo "adding an entry..." +go run add-entry/main.go --logtostderr -v 3 \ +	--identifier "example.sh v0.0.1-$(cat /dev/urandom | base64 | head -c 10)" \ +	--checksum $(sha256sum example.sh) | tee add-entry.output +echo "" && sleep 1 + +echo "fetching another sth..." +go run get-sth/main.go --logtostderr -v 3 | tee sth2.output +echo "" && sleep 1 + +echo "verifying inclusion..." +go run get-proof-by-hash/main.go --logtostderr -v 3 \ +	--leaf_hash $(cat add-entry.output | awk '{print $3}') \ +	--sth $(cat sth2.output | awk '{print $2}') +echo "" && sleep 1 + +echo "verifying consistency..." +go run get-consistency-proof/main.go --logtostderr -v 3 \ +	--first $(cat sth1.output | awk '{print $2}') \ +	--second $(cat sth2.output | awk '{print $2}') +echo "" && sleep 1 + +echo "fetching the log's first entry..." +go run get-entries/main.go --logtostderr -v 3 --start 0 --end 0 +echo "" + +rm *.output diff --git a/client/cmd/get-consistency-proof/main.go b/client/cmd/get-consistency-proof/main.go new file mode 100644 index 0000000..bb8a7a6 --- /dev/null +++ b/client/cmd/get-consistency-proof/main.go @@ -0,0 +1,70 @@ +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 new file mode 100644 index 0000000..f32fdbf --- /dev/null +++ b/client/cmd/get-entries/main.go @@ -0,0 +1,83 @@ +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 new file mode 100644 index 0000000..1f4f304 --- /dev/null +++ b/client/cmd/get-proof-by-hash/main.go @@ -0,0 +1,66 @@ +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/flag.go b/client/flag.go new file mode 100644 index 0000000..1e5e395 --- /dev/null +++ b/client/flag.go @@ -0,0 +1,55 @@ +package client + +import ( +	"flag" +	"fmt" + +	"crypto/ed25519" +	"encoding/base64" +	"net/http" + +	"github.com/system-transparency/stfe/types" +) + +var ( +	logId      = flag.String("log_id", "AAEsY0retj4wa3S2fjsOCJCTVHab7ipEiMdqtW1uJ6Jvmg==", "base64-encoded log identifier") +	logUrl     = flag.String("log_url", "http://localhost:6965/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 new file mode 100644 index 0000000..c95828c --- /dev/null +++ b/client/verify.go @@ -0,0 +1,52 @@ +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, +	) +} | 
