aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--handler.go5
-rw-r--r--reqres.go119
-rw-r--r--type.go204
-rw-r--r--type_test.go19
4 files changed, 184 insertions, 163 deletions
diff --git a/handler.go b/handler.go
index 10defba..b6a52f8 100644
--- a/handler.go
+++ b/handler.go
@@ -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
+}
diff --git a/type.go b/type.go
index a734411..d47996e 100644
--- a/type.go
+++ b/type.go
@@ -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=)
}