From 453a0c38516496052c5f570691c74516c8675e2d Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Tue, 16 Mar 2021 16:29:24 +0100 Subject: added additional basic client commands --- client/client.go | 188 +++++++++++++++++++++---------- client/cmd/add-entry/main.go | 4 +- client/cmd/example.sh | 33 ++++++ client/cmd/get-consistency-proof/main.go | 70 ++++++++++++ client/cmd/get-entries/main.go | 83 ++++++++++++++ client/cmd/get-proof-by-hash/main.go | 66 +++++++++++ client/flag.go | 55 +++++++++ client/verify.go | 52 +++++++++ 8 files changed, 487 insertions(+), 64 deletions(-) create mode 100755 client/cmd/example.sh create mode 100644 client/cmd/get-consistency-proof/main.go create mode 100644 client/cmd/get-entries/main.go create mode 100644 client/cmd/get-proof-by-hash/main.go create mode 100644 client/flag.go create mode 100644 client/verify.go 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, + ) +} -- cgit v1.2.3