diff options
| author | Rasmus Dahlberg <rasmus.dahlberg@kau.se> | 2020-10-23 11:46:58 +0200 | 
|---|---|---|
| committer | Rasmus Dahlberg <rasmus.dahlberg@kau.se> | 2020-10-23 11:46:58 +0200 | 
| commit | bf521e82e0128a2cc31a51f866fd5a86dc677a87 (patch) | |
| tree | 9eaeb0dbb8eda2c22a7c48027796c2400ef15f51 | |
| parent | 6bd17ab810ec1eaa1f93ef7136ee4008337a2f14 (diff) | |
refactored type.go
Moved structures for in/out HTTP data into reqres.go and added basic
doc comments.  A few minor edits as well to make things consistent.
| -rw-r--r-- | handler.go | 5 | ||||
| -rw-r--r-- | reqres.go | 119 | ||||
| -rw-r--r-- | type.go | 204 | ||||
| -rw-r--r-- | type_test.go | 19 | 
4 files changed, 184 insertions, 163 deletions
| @@ -115,9 +115,8 @@ func unpackRequest(r *http.Request, unpack interface{}) error {  // getEntries provides a list of entries from the Trillian backend  func getEntries(ctx context.Context, i *instance, w http.ResponseWriter, r *http.Request) (int, error) {  	glog.Info("in getEntries") - -	var request GetEntriesRequest -	if err := request.Unpack(r); err != nil { +	request, err := NewGetEntriesRequest(r) +	if err != nil {  		return http.StatusBadRequest, err  	} diff --git a/reqres.go b/reqres.go new file mode 100644 index 0000000..a228c76 --- /dev/null +++ b/reqres.go @@ -0,0 +1,119 @@ +package stfe + +import ( +	"fmt" +	"strconv" + +	"encoding/base64" +	"net/http" + +	"github.com/google/certificate-transparency-go/tls" +	"github.com/google/trillian" +) + +// AddEntryRequest is a collection of add-entry input parameters +type AddEntryRequest struct { +	Item        string `json:"item"` +	Signature   string `json:"signature"` +	Certificate string `json:"certificate"` +} + +// GetEntriesRequest is a collection of get-entry input parameters +type GetEntriesRequest struct { +	Start int64 `json:"start"` +	End   int64 `json:"end"` +} + +// GetProofByHashRequest is a collection of get-proof-by-hash input parameters +type GetProofByHashRequest struct { +	Hash     []byte `json:"hash"` +	TreeSize int64  `json:"tree_size"` +} + +// GetEntryResponse is an assembled log entry and its associated appendix +type GetEntryResponse struct { +	Leaf      string   `json:"leaf"`      // base64-encoded StItem +	Signature string   `json:"signature"` // base64-encoded DigitallySigned +	Chain     []string `json:chain`       // base64-encoded X.509 certificates +} + +// GetEntriesResponse is an assembled get-entries responses +type GetEntriesResponse struct { +	Entries []GetEntryResponse `json:"entries"` +} + +// GetProofByHashResponse is an assembled inclusion proof response +type GetProofByHashResponse struct { +	InclusionProof string `json:"inclusion_proof"` // base64-encoded StItem +} + +// NewGetEntriesRequest parses and sanitizes the URL-encoded get-entries +// parameters from an incoming HTTP request. +func NewGetEntriesRequest(httpRequest *http.Request) (GetEntriesRequest, error) { +	start, err := strconv.ParseInt(httpRequest.FormValue("start"), 10, 64) +	if err != nil { +		return GetEntriesRequest{}, fmt.Errorf("bad start parameter: %v", err) +	} +	end, err := strconv.ParseInt(httpRequest.FormValue("end"), 10, 64) +	if err != nil { +		return GetEntriesRequest{}, fmt.Errorf("bad end parameter: %v", err) +	} + +	if start < 0 { +		return GetEntriesRequest{}, fmt.Errorf("bad parameters: start(%v) must have a non-negative value", start) +	} +	if start > end { +		return GetEntriesRequest{}, fmt.Errorf("bad parameters: start(%v) must be larger than end(%v)", start, end) +	} +	// TODO: check that range is not larger than the max range. Yes -> truncate +	// TODO: check that end is not past the most recent STH. Yes -> truncate +	return GetEntriesRequest{Start: start, End: end}, nil +} + +// NewGetProofByHashRequest parses and sanitizes the URL-encoded +// get-proof-by-hash parameters from an incoming HTTP request. +func NewGetProofByHashRequest(httpRequest *http.Request) (GetProofByHashRequest, error) { +	treeSize, err := strconv.ParseInt(httpRequest.FormValue("tree_size"), 10, 64) +	if err != nil { +		return GetProofByHashRequest{}, fmt.Errorf("bad tree_size parameter: %v", err) +	} +	if treeSize < 0 { +		return GetProofByHashRequest{}, fmt.Errorf("bad tree_size parameter: negative value") +	} +	// TODO: check that tree size is not past STH.tree_size + +	hash, err := base64.StdEncoding.DecodeString(httpRequest.FormValue("hash")) +	if err != nil { +		return GetProofByHashRequest{}, fmt.Errorf("bad hash parameter: %v", err) +	} +	return GetProofByHashRequest{TreeSize: treeSize, Hash: hash}, nil +} + +// NewGetEntryResponse assembles a log entry and its appendix +func NewGetEntryResponse(leaf []byte) GetEntryResponse { +	return GetEntryResponse{ +		Leaf: base64.StdEncoding.EncodeToString(leaf), +		// TODO: add signature and chain +	} +} + +// NewGetEntriesResponse assembles a get-entries response +func NewGetEntriesResponse(leaves []*trillian.LogLeaf) (GetEntriesResponse, error) { +	entries := make([]GetEntryResponse, 0, len(leaves)) +	for _, leaf := range leaves { +		entries = append(entries, NewGetEntryResponse(leaf.GetLeafValue())) // TODO: add signature and chain +	} +	return GetEntriesResponse{entries}, nil +} + +// NewGetProofByHashResponse assembles a get-proof-by-hash response +func NewGetProofByHashResponse(treeSize uint64, inclusionProof *trillian.Proof) (*GetProofByHashResponse, error) { +	item := NewInclusionProofV1([]byte("TODO: add log ID"), treeSize, inclusionProof) +	b, err := tls.Marshal(item) +	if err != nil { +		return nil, fmt.Errorf("tls marshal failed: %v", err) +	} +	return &GetProofByHashResponse{ +		InclusionProof: base64.StdEncoding.EncodeToString(b), +	}, nil +} @@ -2,10 +2,8 @@ package stfe  import (  	"fmt" -	"strconv"  	"encoding/base64" -	"net/http"  	"github.com/google/certificate-transparency-go/tls"  	"github.com/google/trillian" @@ -23,25 +21,6 @@ const (  	StFormatChecksumV1                  = 5  ) -func (f StFormat) String() string { -	switch f { -	case StFormatReserved: -		return "reserved" -	case StFormatSignedTreeHeadV1: -		return "signed_tree_head_v1" -	case StFormatSignedDebugInfoV1: -		return "signed_debug_info_v1" -	case StFormatConsistencyProofV1: -		return "consistency_proof_v1" -	case StFormatInclusionProofV1: -		return "inclusion_proof_v1" -	case StFormatChecksumV1: -		return "checksum_v1" -	default: -		return fmt.Sprintf("Unknown StFormat: %d", f) -	} -} -  // StItem references a versioned item based on a given format specifier.  type StItem struct {  	Format           StFormat          `tls:"maxval:65535"` @@ -50,63 +29,37 @@ type StItem struct {  	// TODO: add more items  } -func (i StItem) String() string { -	switch i.Format { -	case StFormatChecksumV1: -		return fmt.Sprintf("%s %s", i.Format, *i.ChecksumV1) -	default: -		return fmt.Sprintf("unknown StItem: %s", i.Format) -	} -} - -func StItemFromB64(s string) (*StItem, error) { -	b, err := base64.StdEncoding.DecodeString(s) -	if err != nil { -		return nil, fmt.Errorf("base64 decoding failed: %v", err) -	} - -	var item StItem -	extra, err := tls.Unmarshal(b, &item) -	if err != nil { -		return nil, fmt.Errorf("tls unmarshal failed: %v", err) -	} else if len(extra) > 0 { -		return nil, fmt.Errorf("tls unmarshal found extra data: %v", extra) -	} -	return &item, nil -} -  // ChecksumV1 associates a package name with an arbitrary checksum value  type ChecksumV1 struct {  	Package  []byte `tls:"minlen:0,maxlen:255"`  	Checksum []byte `tls:"minlen:32,maxlen:255"`  } -// NewChecksumV1 creates a new StItem of type checksum_v1 -func NewChecksumV1(name string, checksum []byte) (StItem, error) { -	return StItem{ -		Format: StFormatChecksumV1, -		ChecksumV1: &ChecksumV1{ -			Package:  []byte(name), -			Checksum: checksum, -		}, -	}, nil // TODO: error handling -} - -func (i ChecksumV1) String() string { -	return fmt.Sprintf("%v %v", string(i.Package), base64.StdEncoding.EncodeToString(i.Checksum)) +// InclusionProofV1 is a Merkle tree inclusion proof, see RFC 6962/bis (§4.12) +type InclusionProofV1 struct { +	LogID         []byte `tls:"minlen:2,maxlen:127"` +	TreeSize      uint64 +	LeafIndex     uint64 +	InclusionPath []NodeHash `tls:"minlen:1,maxlen:65535"`  } +// NodeHash is a hashed Merkle tree node, see RFC 6962/bis (§4.9)  type NodeHash struct {  	Data []byte `tls:"minlen:32,maxlen:255"`  } -type InclusionProofV1 struct { -	LogID         []byte `tls:"minlen:2,maxlen:127"` -	TreeSize      uint64 -	LeafIndex     uint64 -	InclusionPath []NodeHash `tls:"minlen:1,maxlen:65535"` +// NewChecksumV1 creates a new StItem of type checksum_v1 +func NewChecksumV1(identifier []byte, checksum []byte) StItem { +	return StItem{ +		Format: StFormatChecksumV1, +		ChecksumV1: &ChecksumV1{ +			Package:  identifier, +			Checksum: checksum, +		}, +	}  } +// NewInclusionProofV1 creates a new StItem of type inclusion_proof_v1  func NewInclusionProofV1(logID []byte, treeSize uint64, proof *trillian.Proof) StItem {  	inclusionPath := make([]NodeHash, 0, len(proof.Hashes))  	for _, hash := range proof.Hashes { @@ -124,103 +77,62 @@ func NewInclusionProofV1(logID []byte, treeSize uint64, proof *trillian.Proof) S  	}  } -// AddEntryRequest is a collection of add-entry input parameters -type AddEntryRequest struct { -	Item        string `json:"item"` -	Signature   string `json:"signature"` -	Certificate string `json:"certificate"` -} - -// GetEntriesRequest is a collection of get-entry input parameters -type GetEntriesRequest struct { -	Start int64 -	End   int64 -} - -func (r *GetEntriesRequest) Unpack(httpRequest *http.Request) error { -	var err error - -	r.Start, err = strconv.ParseInt(httpRequest.FormValue("start"), 10, 64) -	if err != nil { -		return fmt.Errorf("bad start parameter: %v", err) -	} -	r.End, err = strconv.ParseInt(httpRequest.FormValue("end"), 10, 64) -	if err != nil { -		return fmt.Errorf("bad end parameter: %v", err) -	} - -	if r.Start < 0 { -		return fmt.Errorf("bad parameters: start(%v) must have a non-negative value", r.Start) -	} -	if r.Start > r.End { -		return fmt.Errorf("bad parameters: start(%v) must be larger than end(%v)", r.Start, r.End) +func (f StFormat) String() string { +	switch f { +	case StFormatReserved: +		return "reserved" +	case StFormatSignedTreeHeadV1: +		return "signed_tree_head_v1" +	case StFormatSignedDebugInfoV1: +		return "signed_debug_info_v1" +	case StFormatConsistencyProofV1: +		return "consistency_proof_v1" +	case StFormatInclusionProofV1: +		return "inclusion_proof_v1" +	case StFormatChecksumV1: +		return "checksum_v1" +	default: +		return fmt.Sprintf("Unknown StFormat: %d", f)  	} -	// TODO: check that range is not larger than the max range. Yes -> truncate -	// TODO: check that end is not past the most recent STH. Yes -> truncate -	return nil -} - -type GetEntryResponse struct { -	Leaf      string   `json:"leaf"` -	Signature string   `json:"signature"` -	Chain     []string `json:chain`  } -func NewGetEntryResponse(leaf []byte) GetEntryResponse { -	return GetEntryResponse{ -		Leaf: base64.StdEncoding.EncodeToString(leaf), -		// TODO: add signature and chain +func (i StItem) String() string { +	switch i.Format { +	case StFormatChecksumV1: +		return fmt.Sprintf("Format(%s): %s", i.Format, *i.ChecksumV1) +	case StFormatInclusionProofV1: +		return fmt.Sprintf("Format(%s): %s", i.Format, *i.InclusionProofV1) +	default: +		return fmt.Sprintf("unknown StItem: %s", i.Format)  	}  } -type GetEntriesResponse struct { -	Entries []GetEntryResponse `json:"entries"` +func (i ChecksumV1) String() string { +	return fmt.Sprintf("Package(%v) Checksum(%v)", string(i.Package), base64.StdEncoding.EncodeToString(i.Checksum))  } -func NewGetEntriesResponse(leaves []*trillian.LogLeaf) (GetEntriesResponse, error) { -	entries := make([]GetEntryResponse, 0, len(leaves)) -	for _, leaf := range leaves { -		entries = append(entries, NewGetEntryResponse(leaf.GetLeafValue())) // TODO: add signature and chain +func (i InclusionProofV1) String() string { +	path := make([]string, 0, len(i.InclusionPath)) +	for _, hash := range i.InclusionPath { +		path = append(path, base64.StdEncoding.EncodeToString(hash.Data))  	} -	return GetEntriesResponse{entries}, nil -} -type GetProofByHashRequest struct { -	Hash     []byte -	TreeSize int64 +	return fmt.Sprintf("LogID(%s) TreeSize(%d) LeafIndex(%d) AuditPath(%v)", base64.StdEncoding.EncodeToString(i.LogID), i.TreeSize, i.LeafIndex, path)  } -func NewGetProofByHashRequest(httpRequest *http.Request) (*GetProofByHashRequest, error) { -	var r GetProofByHashRequest -	var err error - -	r.TreeSize, err = strconv.ParseInt(httpRequest.FormValue("tree_size"), 10, 64) -	if err != nil { -		return nil, fmt.Errorf("bad tree_size parameter: %v", err) -	} -	if r.TreeSize < 0 { -		return nil, fmt.Errorf("bad tree_size parameter: negative value") -	} -	// TODO: check that tree size is not past STH.tree_size - -	r.Hash, err = base64.StdEncoding.DecodeString(httpRequest.FormValue("hash")) +// StItemFromB64 creates an StItem from a serialized and base64-encoded string +func StItemFromB64(s string) (*StItem, error) { +	b, err := base64.StdEncoding.DecodeString(s)  	if err != nil { -		return nil, fmt.Errorf("bad hash parameter: %v", err) +		return nil, fmt.Errorf("base64 decoding failed: %v", err)  	} -	return &r, nil -} -type GetProofByHashResponse struct { -	InclusionProof string `json:"inclusion_proof"` -} - -func NewGetProofByHashResponse(treeSize uint64, inclusionProof *trillian.Proof) (*GetProofByHashResponse, error) { -	item := NewInclusionProofV1([]byte("TODO: add log ID"), treeSize, inclusionProof) -	b, err := tls.Marshal(item) +	var item StItem +	extra, err := tls.Unmarshal(b, &item)  	if err != nil { -		return nil, fmt.Errorf("tls marshal failed: %v", err) +		return nil, fmt.Errorf("tls unmarshal failed: %v", err) +	} else if len(extra) > 0 { +		return nil, fmt.Errorf("tls unmarshal found extra data: %v", extra)  	} -	return &GetProofByHashResponse{ -		InclusionProof: base64.StdEncoding.EncodeToString(b), -	}, nil +	return &item, nil  } diff --git a/type_test.go b/type_test.go index bcd66e6..f4e7b40 100644 --- a/type_test.go +++ b/type_test.go @@ -9,27 +9,18 @@ import (  )  func ExampleNewChecksumV1() { -	name := "foobar-1.2.3" +	name := []byte("foobar-1.2.3")  	hasher := sha256.New()  	hasher.Write([]byte(name))  	checksum := hasher.Sum(nil) // hash of package name -	item, err := NewChecksumV1(name, checksum) -	if err != nil { -		fmt.Printf("failed creating checksum item: %v", err) -		return -	} +	item := NewChecksumV1(name, checksum)  	fmt.Printf("%s\n", item) -	// Output: checksum_v1 foobar-1.2.3 UOeWe84malBvj2FLtQlr66WA0gUEa5GPR9I7LsYm114= +	// Output: Format(checksum_v1): Package(foobar-1.2.3) Checksum(UOeWe84malBvj2FLtQlr66WA0gUEa5GPR9I7LsYm114=)  }  func ExampleMarshalChecksumV1() { -	item, err := NewChecksumV1("foobar-1.2.3", make([]byte, 32)) -	if err != nil { -		fmt.Printf("failed creating checksum item: %v", err) -		return -	} - +	item := NewChecksumV1([]byte("foobar-1.2.3"), make([]byte, 32))  	b, err := tls.Marshal(item)  	if err != nil {  		fmt.Printf("tls.Marshal() failed: %v", err) @@ -52,5 +43,5 @@ func ExampleUnmarshalChecksumV1() {  		return  	}  	fmt.Printf("%v\n", item) -	// Output: checksum_v1 foobar-1.2.3 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= +	// Output: Format(checksum_v1): Package(foobar-1.2.3) Checksum(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=)  } | 
