aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--types/ascii.go413
-rw-r--r--types/ascii_test.go465
-rw-r--r--types/http.go188
-rw-r--r--types/http_test.go331
-rw-r--r--types/trunnel.go3
-rw-r--r--types/trunnel_test.go6
-rw-r--r--types/types.go63
7 files changed, 938 insertions, 531 deletions
diff --git a/types/ascii.go b/types/ascii.go
new file mode 100644
index 0000000..88b372e
--- /dev/null
+++ b/types/ascii.go
@@ -0,0 +1,413 @@
+package types
+
+import (
+ "bytes"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "strconv"
+)
+
+const (
+ // Delim is a key-value separator
+ Delim = "="
+
+ // EOL is a line sepator
+ EOL = "\n"
+
+ // NumField* is the number of unique keys in an incoming ASCII message
+ NumFieldLeaf = 4
+ NumFieldSignedTreeHead = 5
+ NumFieldConsistencyProof = 3
+ NumFieldInclusionProof = 3
+ NumFieldLeavesRequest = 2
+ NumFieldInclusionProofRequest = 2
+ NumFieldConsistencyProofRequest = 2
+ NumFieldLeafRequest = 5
+ NumFieldCosignatureRequest = 2
+
+ // New leaf keys
+ ShardHint = "shard_hint"
+ Checksum = "checksum"
+ SignatureOverMessage = "signature_over_message"
+ VerificationKey = "verification_key"
+ DomainHint = "domain_hint"
+
+ // Inclusion proof keys
+ LeafHash = "leaf_hash"
+ LeafIndex = "leaf_index"
+ InclusionPath = "inclusion_path"
+
+ // Consistency proof keys
+ NewSize = "new_size"
+ OldSize = "old_size"
+ ConsistencyPath = "consistency_path"
+
+ // Range of leaves keys
+ StartSize = "start_size"
+ EndSize = "end_size"
+
+ // Tree head keys
+ Timestamp = "timestamp"
+ TreeSize = "tree_size"
+ RootHash = "root_hash"
+
+ // Signature and signer-identity keys
+ Signature = "signature"
+ KeyHash = "key_hash"
+)
+
+// MessageASCI is a wrapper that manages ASCII key-value pairs
+type MessageASCII struct {
+ m map[string][]string
+}
+
+// NewMessageASCII unpacks an incoming ASCII message
+func NewMessageASCII(r io.Reader, numFieldExpected int) (*MessageASCII, error) {
+ buf, err := io.ReadAll(r)
+ if err != nil {
+ return nil, fmt.Errorf("ReadAll: %v", err)
+ }
+ lines := bytes.Split(buf, []byte(EOL))
+ if len(lines) <= 1 {
+ return nil, fmt.Errorf("Not enough lines: empty")
+ }
+ lines = lines[:len(lines)-1] // valid message => split gives empty last line
+
+ msg := MessageASCII{make(map[string][]string)}
+ for _, line := range lines {
+ split := bytes.Index(line, []byte(Delim))
+ if split == -1 {
+ return nil, fmt.Errorf("invalid line: %v", string(line))
+ }
+
+ key := string(line[:split])
+ value := string(line[split+len(Delim):])
+ values, ok := msg.m[key]
+ if !ok {
+ values = nil
+ msg.m[key] = values
+ }
+ msg.m[key] = append(values, value)
+ }
+
+ if msg.NumField() != numFieldExpected {
+ return nil, fmt.Errorf("Unexpected number of keys: %v", msg.NumField())
+ }
+ return &msg, nil
+}
+
+// NumField returns the number of unique keys
+func (msg *MessageASCII) NumField() int {
+ return len(msg.m)
+}
+
+// GetStrings returns a list of strings
+func (msg *MessageASCII) GetStrings(key string) []string {
+ strs, ok := msg.m[key]
+ if !ok {
+ return nil
+ }
+ return strs
+}
+
+// GetString unpacks a string
+func (msg *MessageASCII) GetString(key string) (string, error) {
+ strs := msg.GetStrings(key)
+ if len(strs) != 1 {
+ return "", fmt.Errorf("expected one string: %v", strs)
+ }
+ return strs[0], nil
+}
+
+// GetUint64 unpacks an uint64
+func (msg *MessageASCII) GetUint64(key string) (uint64, error) {
+ str, err := msg.GetString(key)
+ if err != nil {
+ return 0, fmt.Errorf("GetString: %v", err)
+ }
+ num, err := strconv.ParseUint(str, 10, 64)
+ if err != nil {
+ return 0, fmt.Errorf("ParseUint: %v", err)
+ }
+ return num, nil
+}
+
+// GetHash unpacks a hash
+func (msg *MessageASCII) GetHash(key string) (*[HashSize]byte, error) {
+ str, err := msg.GetString(key)
+ if err != nil {
+ return nil, fmt.Errorf("GetString: %v", err)
+ }
+
+ var hash [HashSize]byte
+ if err := decodeHex(str, hash[:]); err != nil {
+ return nil, fmt.Errorf("decodeHex: %v", err)
+ }
+ return &hash, nil
+}
+
+// GetSignature unpacks a signature
+func (msg *MessageASCII) GetSignature(key string) (*[SignatureSize]byte, error) {
+ str, err := msg.GetString(key)
+ if err != nil {
+ return nil, fmt.Errorf("GetString: %v", err)
+ }
+
+ var signature [SignatureSize]byte
+ if err := decodeHex(str, signature[:]); err != nil {
+ return nil, fmt.Errorf("decodeHex: %v", err)
+ }
+ return &signature, nil
+}
+
+// GetVerificationKey unpacks a verification key
+func (msg *MessageASCII) GetVerificationKey(key string) (*[VerificationKeySize]byte, error) {
+ str, err := msg.GetString(key)
+ if err != nil {
+ return nil, fmt.Errorf("GetString: %v", err)
+ }
+
+ var vk [VerificationKeySize]byte
+ if err := decodeHex(str, vk[:]); err != nil {
+ return nil, fmt.Errorf("decodeHex: %v", err)
+ }
+ return &vk, nil
+}
+
+// decodeHex decodes a hex-encoded string into an already-sized byte slice
+func decodeHex(str string, out []byte) error {
+ buf, err := hex.DecodeString(str)
+ if err != nil {
+ return fmt.Errorf("DecodeString: %v", err)
+ }
+ if len(buf) != len(out) {
+ return fmt.Errorf("invalid length: %v", len(buf))
+ }
+ copy(out, buf)
+ return nil
+}
+
+/*
+ *
+ * MarshalASCII wrappers for types that the log server outputs
+ *
+ */
+func (l *Leaf) MarshalASCII(w io.Writer) error {
+ if err := writeASCII(w, ShardHint, strconv.FormatUint(l.ShardHint, 10)); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ if err := writeASCII(w, Checksum, hex.EncodeToString(l.Checksum[:])); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ if err := writeASCII(w, Signature, hex.EncodeToString(l.Signature[:])); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ if err := writeASCII(w, KeyHash, hex.EncodeToString(l.KeyHash[:])); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ return nil
+}
+
+func (sth *SignedTreeHead) MarshalASCII(w io.Writer) error {
+ if err := writeASCII(w, Timestamp, strconv.FormatUint(sth.Timestamp, 10)); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ if err := writeASCII(w, TreeSize, strconv.FormatUint(sth.TreeSize, 10)); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ if err := writeASCII(w, RootHash, hex.EncodeToString(sth.RootHash[:])); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ for _, sigident := range sth.SigIdent {
+ if err := writeASCII(w, Signature, hex.EncodeToString(sigident.Signature[:])); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ if err := writeASCII(w, KeyHash, hex.EncodeToString(sigident.KeyHash[:])); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ }
+ return nil
+}
+
+func (p *ConsistencyProof) MarshalASCII(w io.Writer) error {
+ if err := writeASCII(w, NewSize, strconv.FormatUint(p.NewSize, 10)); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ if err := writeASCII(w, OldSize, strconv.FormatUint(p.OldSize, 10)); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ for _, hash := range p.Path {
+ if err := writeASCII(w, ConsistencyPath, hex.EncodeToString(hash[:])); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ }
+ return nil
+}
+
+func (p *InclusionProof) MarshalASCII(w io.Writer) error {
+ if err := writeASCII(w, TreeSize, strconv.FormatUint(p.TreeSize, 10)); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ if err := writeASCII(w, LeafIndex, strconv.FormatUint(p.LeafIndex, 10)); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ for _, hash := range p.Path {
+ if err := writeASCII(w, InclusionPath, hex.EncodeToString(hash[:])); err != nil {
+ return fmt.Errorf("writeASCII: %v", err)
+ }
+ }
+ return nil
+}
+
+func writeASCII(w io.Writer, key, value string) error {
+ if _, err := fmt.Fprintf(w, "%s%s%s%s", key, Delim, value, EOL); err != nil {
+ return fmt.Errorf("Fprintf: %v", err)
+ }
+ return nil
+}
+
+/*
+ *
+ * Unmarshal ASCII wrappers that the log server and/or log clients receive.
+ *
+ */
+func (ll *LeafList) UnmarshalASCII(r io.Reader) error {
+ return nil
+}
+
+func (sth *SignedTreeHead) UnmarshalASCII(r io.Reader) error {
+ msg, err := NewMessageASCII(r, NumFieldSignedTreeHead)
+ if err != nil {
+ return fmt.Errorf("NewMessageASCII: %v", err)
+ }
+
+ // TreeHead
+ if sth.Timestamp, err = msg.GetUint64(Timestamp); err != nil {
+ return fmt.Errorf("GetUint64(Timestamp): %v", err)
+ }
+ if sth.TreeSize, err = msg.GetUint64(TreeSize); err != nil {
+ return fmt.Errorf("GetUint64(TreeSize): %v", err)
+ }
+ if sth.RootHash, err = msg.GetHash(RootHash); err != nil {
+ return fmt.Errorf("GetHash(RootHash): %v", err)
+ }
+
+ // SigIdent
+ signatures := msg.GetStrings(Signature)
+ if len(signatures) == 0 {
+ return fmt.Errorf("no signer")
+ }
+ keyHashes := msg.GetStrings(KeyHash)
+ if len(signatures) != len(keyHashes) {
+ return fmt.Errorf("mismatched signature-signer count")
+ }
+ sth.SigIdent = make([]*SigIdent, 0, len(signatures))
+ for i, n := 0, len(signatures); i < n; i++ {
+ var signature [SignatureSize]byte
+ if err := decodeHex(signatures[i], signature[:]); err != nil {
+ return fmt.Errorf("decodeHex: %v", err)
+ }
+ var hash [HashSize]byte
+ if err := decodeHex(keyHashes[i], hash[:]); err != nil {
+ return fmt.Errorf("decodeHex: %v", err)
+ }
+ sth.SigIdent = append(sth.SigIdent, &SigIdent{
+ Signature: &signature,
+ KeyHash: &hash,
+ })
+ }
+ return nil
+}
+
+func (p *InclusionProof) UnmarshalASCII(r io.Reader) error {
+ return nil
+}
+
+func (p *ConsistencyProof) UnmarshalASCII(r io.Reader) error {
+ return nil
+}
+
+func (req *InclusionProofRequest) UnmarshalASCII(r io.Reader) error {
+ msg, err := NewMessageASCII(r, NumFieldInclusionProofRequest)
+ if err != nil {
+ return fmt.Errorf("NewMessageASCII: %v", err)
+ }
+
+ if req.LeafHash, err = msg.GetHash(LeafHash); err != nil {
+ return fmt.Errorf("GetHash(LeafHash): %v", err)
+ }
+ if req.TreeSize, err = msg.GetUint64(TreeSize); err != nil {
+ return fmt.Errorf("GetUint64(TreeSize): %v", err)
+ }
+ return nil
+}
+
+func (req *ConsistencyProofRequest) UnmarshalASCII(r io.Reader) error {
+ msg, err := NewMessageASCII(r, NumFieldConsistencyProofRequest)
+ if err != nil {
+ return fmt.Errorf("NewMessageASCII: %v", err)
+ }
+
+ if req.NewSize, err = msg.GetUint64(NewSize); err != nil {
+ return fmt.Errorf("GetUint64(NewSize): %v", err)
+ }
+ if req.OldSize, err = msg.GetUint64(OldSize); err != nil {
+ return fmt.Errorf("GetUint64(OldSize): %v", err)
+ }
+ return nil
+}
+
+func (req *LeavesRequest) UnmarshalASCII(r io.Reader) error {
+ msg, err := NewMessageASCII(r, NumFieldLeavesRequest)
+ if err != nil {
+ return fmt.Errorf("NewMessageASCII: %v", err)
+ }
+
+ if req.StartSize, err = msg.GetUint64(StartSize); err != nil {
+ return fmt.Errorf("GetUint64(StartSize): %v", err)
+ }
+ if req.EndSize, err = msg.GetUint64(EndSize); err != nil {
+ return fmt.Errorf("GetUint64(EndSize): %v", err)
+ }
+ return nil
+}
+
+func (req *LeafRequest) UnmarshalASCII(r io.Reader) error {
+ msg, err := NewMessageASCII(r, NumFieldLeafRequest)
+ if err != nil {
+ return fmt.Errorf("NewMessageASCII: %v", err)
+ }
+
+ if req.ShardHint, err = msg.GetUint64(ShardHint); err != nil {
+ return fmt.Errorf("GetUint64(ShardHint): %v", err)
+ }
+ if req.Checksum, err = msg.GetHash(Checksum); err != nil {
+ return fmt.Errorf("GetHash(Checksum): %v", err)
+ }
+ if req.Signature, err = msg.GetSignature(Signature); err != nil {
+ return fmt.Errorf("GetSignature: %v", err)
+ }
+ if req.VerificationKey, err = msg.GetVerificationKey(VerificationKey); err != nil {
+ return fmt.Errorf("GetVerificationKey: %v", err)
+ }
+ if req.DomainHint, err = msg.GetString(DomainHint); err != nil {
+ return fmt.Errorf("GetString(DomainHint): %v", err)
+ }
+ return nil
+}
+
+func (req *CosignatureRequest) UnmarshalASCII(r io.Reader) error {
+ msg, err := NewMessageASCII(r, NumFieldCosignatureRequest)
+ if err != nil {
+ return fmt.Errorf("NewMessageASCII: %v", err)
+ }
+
+ if req.Signature, err = msg.GetSignature(Signature); err != nil {
+ return fmt.Errorf("GetSignature: %v", err)
+ }
+ if req.KeyHash, err = msg.GetHash(KeyHash); err != nil {
+ return fmt.Errorf("GetHash(KeyHash): %v", err)
+ }
+ return nil
+}
diff --git a/types/ascii_test.go b/types/ascii_test.go
new file mode 100644
index 0000000..74a1e37
--- /dev/null
+++ b/types/ascii_test.go
@@ -0,0 +1,465 @@
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "testing"
+)
+
+/*
+ *
+ * MessageASCII methods and helpers
+ *
+ */
+func TestNewMessageASCII(t *testing.T) {
+ for _, table := range []struct {
+ description string
+ input io.Reader
+ wantErr bool
+ wantMap map[string][]string
+ }{
+ {
+ description: "invalid: not enough lines",
+ input: bytes.NewBufferString(""),
+ wantErr: true,
+ },
+ {
+ description: "invalid: lines must end with new line",
+ input: bytes.NewBufferString("k1=v1\nk2=v2"),
+ wantErr: true,
+ },
+ {
+ description: "invalid: lines must not be empty",
+ input: bytes.NewBufferString("k1=v1\n\nk2=v2\n"),
+ wantErr: true,
+ },
+ {
+ description: "invalid: wrong number of fields",
+ input: bytes.NewBufferString("k1=v1\n"),
+ wantErr: true,
+ },
+ {
+ description: "valid",
+ input: bytes.NewBufferString("k1=v1\nk2=v2\nk2=v3=4\n"),
+ wantMap: map[string][]string{
+ "k1": []string{"v1"},
+ "k2": []string{"v2", "v3=4"},
+ },
+ },
+ } {
+ msg, err := NewMessageASCII(table.input, len(table.wantMap))
+ 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 := msg.m, table.wantMap; !reflect.DeepEqual(got, want) {
+ t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
+ }
+ }
+}
+
+func TestNumField(t *testing.T) {}
+func TestGetStrings(t *testing.T) {}
+func TestGetString(t *testing.T) {}
+func TestGetUint64(t *testing.T) {}
+func TestGetHash(t *testing.T) {}
+func TestGetSignature(t *testing.T) {}
+func TestGetVerificationKey(t *testing.T) {}
+func TestDecodeHex(t *testing.T) {}
+
+/*
+ *
+ * MarshalASCII methods and helpers
+ *
+ */
+func TestLeafMarshalASCII(t *testing.T) {
+ description := "valid: two leaves"
+ leafList := []*Leaf{
+ &Leaf{
+ Message: Message{
+ ShardHint: 123,
+ Checksum: testBuffer32,
+ },
+ SigIdent: SigIdent{
+ Signature: testBuffer64,
+ KeyHash: testBuffer32,
+ },
+ },
+ &Leaf{
+ Message: Message{
+ ShardHint: 456,
+ Checksum: testBuffer32,
+ },
+ SigIdent: SigIdent{
+ Signature: testBuffer64,
+ KeyHash: testBuffer32,
+ },
+ },
+ }
+ wantBuf := bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+
+ "%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s",
+ // Leaf 1
+ ShardHint, Delim, 123, EOL,
+ Checksum, Delim, testBuffer32[:], EOL,
+ Signature, Delim, testBuffer64[:], EOL,
+ KeyHash, Delim, testBuffer32[:], EOL,
+ // Leaf 2
+ ShardHint, Delim, 456, EOL,
+ Checksum, Delim, testBuffer32[:], EOL,
+ Signature, Delim, testBuffer64[:], EOL,
+ KeyHash, Delim, testBuffer32[:], EOL,
+ ))
+ buf := bytes.NewBuffer(nil)
+ for _, leaf := range leafList {
+ if err := leaf.MarshalASCII(buf); err != nil {
+ t.Errorf("expected error %v but got %v in test %q: %v", false, true, description, err)
+ return
+ }
+ }
+ if got, want := buf.Bytes(), wantBuf.Bytes(); !bytes.Equal(got, want) {
+ t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", string(got), string(want), description)
+ }
+}
+
+func TestSignedTreeHeadMarshalASCII(t *testing.T) {
+ description := "valid"
+ sth := &SignedTreeHead{
+ TreeHead: TreeHead{
+ Timestamp: 123,
+ TreeSize: 456,
+ RootHash: testBuffer32,
+ },
+ SigIdent: []*SigIdent{
+ &SigIdent{
+ Signature: testBuffer64,
+ KeyHash: testBuffer32,
+ },
+ &SigIdent{
+ Signature: testBuffer64,
+ KeyHash: testBuffer32,
+ },
+ },
+ }
+ wantBuf := bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%d%s"+"%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s",
+ Timestamp, Delim, 123, EOL,
+ TreeSize, Delim, 456, EOL,
+ RootHash, Delim, testBuffer32[:], EOL,
+ Signature, Delim, testBuffer64[:], EOL,
+ KeyHash, Delim, testBuffer32[:], EOL,
+ Signature, Delim, testBuffer64[:], EOL,
+ KeyHash, Delim, testBuffer32[:], EOL,
+ ))
+ buf := bytes.NewBuffer(nil)
+ if err := sth.MarshalASCII(buf); err != nil {
+ t.Errorf("expected error %v but got %v in test %q", false, true, description)
+ return
+ }
+ if got, want := buf.Bytes(), wantBuf.Bytes(); !bytes.Equal(got, want) {
+ t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", string(got), string(want), description)
+ }
+}
+
+func TestInclusionProofMarshalASCII(t *testing.T) {
+ description := "valid"
+ proof := InclusionProof{
+ TreeSize: 321,
+ LeafIndex: 123,
+ Path: []*[HashSize]byte{
+ testBuffer32,
+ testBuffer32,
+ },
+ }
+ wantBuf := bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%d%s"+"%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s",
+ TreeSize, Delim, 321, EOL,
+ LeafIndex, Delim, 123, EOL,
+ InclusionPath, Delim, testBuffer32[:], EOL,
+ InclusionPath, Delim, testBuffer32[:], EOL,
+ ))
+ buf := bytes.NewBuffer(nil)
+ if err := proof.MarshalASCII(buf); err != nil {
+ t.Errorf("expected error %v but got %v in test %q", false, true, description)
+ return
+ }
+ if got, want := buf.Bytes(), wantBuf.Bytes(); !bytes.Equal(got, want) {
+ t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", string(got), string(want), description)
+ }
+}
+
+func TestConsistencyProofMarshalASCII(t *testing.T) {
+ description := "valid"
+ proof := ConsistencyProof{
+ NewSize: 321,
+ OldSize: 123,
+ Path: []*[HashSize]byte{
+ testBuffer32,
+ testBuffer32,
+ },
+ }
+ wantBuf := bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%d%s"+"%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s",
+ NewSize, Delim, 321, EOL,
+ OldSize, Delim, 123, EOL,
+ ConsistencyPath, Delim, testBuffer32[:], EOL,
+ ConsistencyPath, Delim, testBuffer32[:], EOL,
+ ))
+ buf := bytes.NewBuffer(nil)
+ if err := proof.MarshalASCII(buf); err != nil {
+ t.Errorf("expected error %v but got %v in test %q", false, true, description)
+ return
+ }
+ if got, want := buf.Bytes(), wantBuf.Bytes(); !bytes.Equal(got, want) {
+ t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", string(got), string(want), description)
+ }
+}
+
+func TestWriteASCII(t *testing.T) {
+}
+
+/*
+ *
+ * UnmarshalASCII methods and helpers
+ *
+ */
+func TestLeafListUnmarshalASCII(t *testing.T) {}
+
+func TestSignedTreeHeadUnmarshalASCII(t *testing.T) {
+ for _, table := range []struct {
+ description string
+ buf io.Reader
+ wantErr bool
+ wantSth *SignedTreeHead
+ }{
+ {
+ description: "valid",
+ buf: bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%d%s"+"%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s",
+ Timestamp, Delim, 123, EOL,
+ TreeSize, Delim, 456, EOL,
+ RootHash, Delim, testBuffer32[:], EOL,
+ Signature, Delim, testBuffer64[:], EOL,
+ KeyHash, Delim, testBuffer32[:], EOL,
+ Signature, Delim, testBuffer64[:], EOL,
+ KeyHash, Delim, testBuffer32[:], EOL,
+ )),
+ wantSth: &SignedTreeHead{
+ TreeHead: TreeHead{
+ Timestamp: 123,
+ TreeSize: 456,
+ RootHash: testBuffer32,
+ },
+ SigIdent: []*SigIdent{
+ &SigIdent{
+ Signature: testBuffer64,
+ KeyHash: testBuffer32,
+ },
+ &SigIdent{
+ Signature: testBuffer64,
+ KeyHash: testBuffer32,
+ },
+ },
+ },
+ },
+ } {
+ var sth SignedTreeHead
+ err := sth.UnmarshalASCII(table.buf)
+ 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\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
+ }
+ }
+}
+
+func TestInclusionProofUnmarshalASCII(t *testing.T) {}
+func TestConsistencyProofUnmarshalASCII(t *testing.T) {}
+
+func TestInclusionProofRequestUnmarshalASCII(t *testing.T) {
+ for _, table := range []struct {
+ description string
+ buf io.Reader
+ wantErr bool
+ wantReq *InclusionProofRequest
+ }{
+ {
+ description: "valid",
+ buf: bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%x%s"+"%s%s%d%s",
+ LeafHash, Delim, testBuffer32[:], EOL,
+ TreeSize, Delim, 123, EOL,
+ )),
+ wantReq: &InclusionProofRequest{
+ LeafHash: testBuffer32,
+ TreeSize: 123,
+ },
+ },
+ } {
+ var req InclusionProofRequest
+ err := req.UnmarshalASCII(table.buf)
+ 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 := &req, table.wantReq; !reflect.DeepEqual(got, want) {
+ t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
+ }
+ }
+}
+
+func TestConsistencyProofRequestUnmarshalASCII(t *testing.T) {
+ for _, table := range []struct {
+ description string
+ buf io.Reader
+ wantErr bool
+ wantReq *ConsistencyProofRequest
+ }{
+ {
+ description: "valid",
+ buf: bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%d%s"+"%s%s%d%s",
+ NewSize, Delim, 321, EOL,
+ OldSize, Delim, 123, EOL,
+ )),
+ wantReq: &ConsistencyProofRequest{
+ NewSize: 321,
+ OldSize: 123,
+ },
+ },
+ } {
+ var req ConsistencyProofRequest
+ err := req.UnmarshalASCII(table.buf)
+ 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 := &req, table.wantReq; !reflect.DeepEqual(got, want) {
+ t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
+ }
+ }
+}
+
+func TestLeavesRequestUnmarshalASCII(t *testing.T) {
+ for _, table := range []struct {
+ description string
+ buf io.Reader
+ wantErr bool
+ wantReq *LeavesRequest
+ }{
+ {
+ description: "valid",
+ buf: bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%d%s"+"%s%s%d%s",
+ StartSize, Delim, 123, EOL,
+ EndSize, Delim, 456, EOL,
+ )),
+ wantReq: &LeavesRequest{
+ StartSize: 123,
+ EndSize: 456,
+ },
+ },
+ } {
+ var req LeavesRequest
+ err := req.UnmarshalASCII(table.buf)
+ 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 := &req, table.wantReq; !reflect.DeepEqual(got, want) {
+ t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
+ }
+ }
+}
+
+func TestLeafRequestUnmarshalASCII(t *testing.T) {
+ for _, table := range []struct {
+ description string
+ buf io.Reader
+ wantErr bool
+ wantReq *LeafRequest
+ }{
+ {
+ description: "valid",
+ buf: bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%s%s",
+ ShardHint, Delim, 123, EOL,
+ Checksum, Delim, testBuffer32[:], EOL,
+ Signature, Delim, testBuffer64[:], EOL,
+ VerificationKey, Delim, testBuffer32[:], EOL,
+ DomainHint, Delim, "example.com", EOL,
+ )),
+ wantReq: &LeafRequest{
+ Message: Message{
+ ShardHint: 123,
+ Checksum: testBuffer32,
+ },
+ Signature: testBuffer64,
+ VerificationKey: testBuffer32,
+ DomainHint: "example.com",
+ },
+ },
+ } {
+ var req LeafRequest
+ err := req.UnmarshalASCII(table.buf)
+ 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 := &req, table.wantReq; !reflect.DeepEqual(got, want) {
+ t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
+ }
+ }
+}
+
+func TestCosignatureRequestUnmarshalASCII(t *testing.T) {
+ for _, table := range []struct {
+ description string
+ buf io.Reader
+ wantErr bool
+ wantReq *CosignatureRequest
+ }{
+ {
+ description: "valid",
+ buf: bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%x%s"+"%s%s%x%s",
+ Signature, Delim, testBuffer64[:], EOL,
+ KeyHash, Delim, testBuffer32[:], EOL,
+ )),
+ wantReq: &CosignatureRequest{
+ SigIdent: SigIdent{
+ Signature: testBuffer64,
+ KeyHash: testBuffer32,
+ },
+ },
+ },
+ } {
+ var req CosignatureRequest
+ err := req.UnmarshalASCII(table.buf)
+ 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 := &req, table.wantReq; !reflect.DeepEqual(got, want) {
+ t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
+ }
+ }
+}
diff --git a/types/http.go b/types/http.go
deleted file mode 100644
index 8bbe26d..0000000
--- a/types/http.go
+++ /dev/null
@@ -1,188 +0,0 @@
-package types
-
-import (
- "bytes"
- "encoding/hex"
- "fmt"
- "net/http"
- "strconv"
- "strings"
-)
-
-const (
- // HeaderPrefix is the start of every ST log HTTP header key
- HeaderPrefix = "stlog-"
-
- // New leaf
- HeaderShardHint = HeaderPrefix + "shard_hint"
- HeaderChecksum = HeaderPrefix + "checksum"
- HeaderSignatureOverMessage = HeaderPrefix + "signature_over_message"
- HeaderVerificationKey = HeaderPrefix + "verification_key"
- HeaderDomainHint = HeaderPrefix + "domain_hint"
-
- // Inclusion proof
- HeaderLeafHash = HeaderPrefix + "leaf_hash"
- HeaderLeafIndex = HeaderPrefix + "leaf_index"
- HeaderInclusionPath = HeaderPrefix + "inclusion_path"
-
- // Consistency proof
- HeaderNewSize = HeaderPrefix + "new_size"
- HeaderOldSize = HeaderPrefix + "old_size"
- HeaderConsistencyPath = HeaderPrefix + "consistency_path"
-
- // Range of leaves
- HeaderStartSize = HeaderPrefix + "start_size"
- HeaderEndSize = HeaderPrefix + "end_size"
-
- // Tree head
- HeaderTimestamp = HeaderPrefix + "timestamp"
- HeaderTreeSize = HeaderPrefix + "tree_size"
- HeaderRootHash = HeaderPrefix + "root_hash"
-
- // Signature and signer identity
- HeaderSignature = HeaderPrefix + "signature"
- HeaderKeyHash = HeaderPrefix + "key_hash"
-)
-
-// ToHTTP returns a signed tree-head as HTTP key-value pairs
-func (sth *SignedTreeHead) ToHTTP() ([]byte, error) {
- hdr := http.Header{}
- hdr.Add(HeaderTimestamp, strconv.FormatUint(sth.Timestamp, 10))
- hdr.Add(HeaderTreeSize, strconv.FormatUint(sth.TreeSize, 10))
- hdr.Add(HeaderRootHash, hex.EncodeToString(sth.RootHash[:]))
- for _, sigident := range sth.SigIdent {
- hdr.Add(HeaderSignature, hex.EncodeToString(sigident.Signature[:]))
- hdr.Add(HeaderKeyHash, hex.EncodeToString(sigident.KeyHash[:]))
- }
- return headerToBytes(hdr)
-}
-
-// ToHTTP returns a consistency proof as HTTP key-value pairs
-func (p *ConsistencyProof) ToHTTP() ([]byte, error) {
- hdr := http.Header{}
- hdr.Add(HeaderNewSize, strconv.FormatUint(p.NewSize, 10))
- hdr.Add(HeaderOldSize, strconv.FormatUint(p.OldSize, 10))
- for _, hash := range p.Path {
- hdr.Add(HeaderConsistencyPath, hex.EncodeToString(hash[:]))
- }
- return headerToBytes(hdr)
-}
-
-// ToHTTP returns an inclusion proof as HTTP key-value pairs
-func (p *InclusionProof) ToHTTP() ([]byte, error) {
- hdr := http.Header{}
- hdr.Add(HeaderTreeSize, strconv.FormatUint(p.TreeSize, 10))
- hdr.Add(HeaderLeafIndex, strconv.FormatUint(p.LeafIndex, 10))
- for _, hash := range p.Path {
- hdr.Add(HeaderInclusionPath, hex.EncodeToString(hash[:]))
- }
- return headerToBytes(hdr)
-}
-
-// ToHTTP returns a leaf as HTTP key-value pairs
-func (l *Leaf) ToHTTP() ([]byte, error) {
- hdr := http.Header{}
- hdr.Add(HeaderShardHint, strconv.FormatUint(l.ShardHint, 10))
- hdr.Add(HeaderChecksum, hex.EncodeToString(l.Checksum[:]))
- hdr.Add(HeaderSignature, hex.EncodeToString(l.Signature[:]))
- hdr.Add(HeaderKeyHash, hex.EncodeToString(l.KeyHash[:]))
- return headerToBytes(hdr)
-}
-
-// SignedTreeHeadFromHTTP parses a signed tree head from HTTP key-value pairs
-func SignedTreeHeadFromHTTP(buf []byte) (*SignedTreeHead, error) {
- hdr, err := headerFromBuf(buf)
- if err != nil {
- return nil, fmt.Errorf("headerFromBuf(): %v", err)
- }
-
- // TreeHead
- var sth SignedTreeHead
- sth.Timestamp, err = strconv.ParseUint(hdr.Get(HeaderTimestamp), 10, 64)
- if err != nil {
- return nil, fmt.Errorf("invalid timestamp: %v", err)
- }
- sth.TreeSize, err = strconv.ParseUint(hdr.Get(HeaderTreeSize), 10, 64)
- if err != nil {
- return nil, fmt.Errorf("invalid tree size: %v", err)
- }
- if err := decodeHex(hdr.Get(HeaderRootHash), sth.RootHash[:]); err != nil {
- return nil, fmt.Errorf("decodeHex(): %v", err)
- }
-
- // SigIdent
- signatures := hdr.Values(HeaderSignature)
- keyHashes := hdr.Values(HeaderKeyHash)
- if len(signatures) == 0 {
- return nil, fmt.Errorf("no signer")
- }
- if len(signatures) != len(keyHashes) {
- return nil, fmt.Errorf("mismatched signature-signer count")
- }
- for i := 0; i < len(signatures); i++ {
- var sigident SigIdent
- if err := decodeHex(signatures[i], sigident.Signature[:]); err != nil {
- return nil, fmt.Errorf("decodeHex(): %v", err)
- }
- if err := decodeHex(keyHashes[i], sigident.KeyHash[:]); err != nil {
- return nil, fmt.Errorf("decodeHex(): %v", err)
- }
- sth.SigIdent = append(sth.SigIdent, sigident)
- }
- return &sth, nil
-}
-
-// ConsistencyProofFromHTTP parses a consistency proof from HTTP key-value pairs
-func ConsistencyProofFromHTTP(buf []byte) (*ConsistencyProof, error) {
- return nil, nil // TODO
-}
-
-// InclusionProofFromHTTP parses an inclusion proof from HTTP key-value pairs
-func InclusionProofFromHTTP(buf []byte) (*InclusionProof, error) {
- return nil, nil // TODO
-}
-
-// LeavesFromHTTP parses a list of leaves from HTTP key-value pairs
-func LeavesFromHTTP(buf []byte) ([]*Leaf, error) {
- return nil, nil // TODO
-}
-
-// headerFromBuf parses ST log HTTP header key-value pairs from a response body
-func headerFromBuf(buf []byte) (http.Header, error) {
- hdr := http.Header{}
- lines := strings.Split(string(buf), "\r\n")
- lines = lines[:len(lines)-1] // skip the final empty line
- for _, line := range lines {
- split := strings.Split(line, ":")
- if len(split) != 2 {
- return nil, fmt.Errorf("invalid ST log HTTP header: %s", line)
- }
- if !strings.HasPrefix(strings.ToLower(split[0]), HeaderPrefix) {
- return nil, fmt.Errorf("invalid ST log HTTP header prefix: %s", line)
- }
- hdr.Add(split[0], strings.TrimSpace(split[1]))
- }
- return hdr, nil
-}
-
-// decodeHex decodes a hex-encoded string into a fixed-size output slice
-func decodeHex(str string, out []byte) error {
- buf, err := hex.DecodeString(str)
- if err != nil {
- return fmt.Errorf("hex.DecodeString(): %v", err)
- }
- if len(buf) != len(out) {
- return fmt.Errorf("invalid length: %v", len(buf))
- }
- copy(out, buf)
- return nil
-}
-
-// headerToBytes encodes a header as HTTP key-value pairs
-func headerToBytes(hdr http.Header) ([]byte, error) {
- buf := bytes.NewBuffer(nil)
- if err := hdr.Write(buf); err != nil {
- return nil, fmt.Errorf("hdr.Write(): %v", err) // should not happen
- }
- return buf.Bytes(), nil
-}
diff --git a/types/http_test.go b/types/http_test.go
deleted file mode 100644
index 527bcdf..0000000
--- a/types/http_test.go
+++ /dev/null
@@ -1,331 +0,0 @@
-package types
-
-import (
- "bytes"
- "encoding/hex"
- "net/http"
- "reflect"
- "strings"
- "testing"
-)
-
-var (
- testZeroBuffer32 = [32]byte{}
- testZeroBuffer64 = [64]byte{}
-)
-
-func TestSignedTreeHeadToHTTP(t *testing.T) {
- description := "valid: cosigned tree head with two signatures"
- sth := &SignedTreeHead{
- TreeHead: TreeHead{
- Timestamp: 0,
- TreeSize: 0,
- RootHash: testBuffer32,
- },
- SigIdent: []SigIdent{
- SigIdent{
- Signature: testZeroBuffer64,
- KeyHash: testZeroBuffer32,
- },
- SigIdent{
- Signature: testBuffer64,
- KeyHash: testBuffer32,
- },
- },
- }
- want := map[string][]string{
- HeaderTimestamp: []string{"0"},
- HeaderTreeSize: []string{"0"},
- HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])},
- HeaderSignature: []string{
- hex.EncodeToString(testZeroBuffer64[:]),
- hex.EncodeToString(testBuffer64[:]),
- },
- HeaderKeyHash: []string{
- hex.EncodeToString(testZeroBuffer32[:]),
- hex.EncodeToString(testBuffer32[:]),
- },
- }
- buf, err := sth.ToHTTP()
- if err != nil {
- t.Fatalf("sth.ToHTTP: %v", err)
- }
- hdr, err := headerFromBuf(buf)
- if err != nil {
- t.Fatalf("headerFromBuf: %v", err)
- }
- compareHeaderWithMap(t, description, hdr, want)
-}
-
-func TestConsistencyProofToHTTP(t *testing.T) { // TODO
-}
-
-func TestInclusionProofToHTTP(t *testing.T) { // TODO
-}
-
-func TestLeafToHTTP(t *testing.T) { // TODO
-}
-
-func TestSignedTreeHeadFromHTTP(t *testing.T) {
- for _, table := range []struct {
- description string
- buf []byte
- wantErr bool
- wantSth *SignedTreeHead
- }{
- {
- description: "invalid: not ST log HTTP header",
- buf: newHeaderBuf(t, map[string][]string{
- "user-agent": []string{"secret"},
- }),
- wantErr: true,
- },
- {
- description: "invalid: timestamp",
- buf: newHeaderBuf(t, map[string][]string{
- HeaderTreeSize: []string{"0"},
- HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])},
- HeaderSignature: []string{hex.EncodeToString(testBuffer64[:])},
- HeaderKeyHash: []string{hex.EncodeToString(testBuffer32[:])},
- }),
- wantErr: true,
- },
- {
- description: "invalid: tree size",
- buf: newHeaderBuf(t, map[string][]string{
- HeaderTimestamp: []string{"0"},
- HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])},
- HeaderSignature: []string{hex.EncodeToString(testBuffer64[:])},
- HeaderKeyHash: []string{hex.EncodeToString(testBuffer32[:])},
- }),
- wantErr: true,
- },
- {
- description: "invalid: root hash",
- buf: newHeaderBuf(t, map[string][]string{
- HeaderTimestamp: []string{"0"},
- HeaderTreeSize: []string{"0"},
- HeaderSignature: []string{hex.EncodeToString(testBuffer64[:])},
- HeaderKeyHash: []string{hex.EncodeToString(testBuffer32[:])},
- }),
- wantErr: true,
- },
- {
- description: "invalid: signature",
- buf: newHeaderBuf(t, map[string][]string{
- HeaderTimestamp: []string{"0"},
- HeaderTreeSize: []string{"0"},
- HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])},
- HeaderSignature: []string{hex.EncodeToString(testBuffer32[:])},
- HeaderKeyHash: []string{hex.EncodeToString(testBuffer32[:])},
- }),
- wantErr: true,
- },
- {
- description: "invalid: key hash",
- buf: newHeaderBuf(t, map[string][]string{
- HeaderTimestamp: []string{"0"},
- HeaderTreeSize: []string{"0"},
- HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])},
- HeaderSignature: []string{hex.EncodeToString(testBuffer64[:])},
- HeaderKeyHash: []string{hex.EncodeToString(testBuffer64[:])},
- }),
- wantErr: true,
- },
- {
- description: "invalid: sigident count",
- buf: newHeaderBuf(t, map[string][]string{
- HeaderTimestamp: []string{"0"},
- HeaderTreeSize: []string{"0"},
- HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])},
- HeaderSignature: []string{hex.EncodeToString(testBuffer64[:])},
- HeaderKeyHash: []string{
- hex.EncodeToString(testZeroBuffer32[:]),
- hex.EncodeToString(testBuffer32[:]),
- },
- }),
- wantErr: true,
- },
- {
- description: "invalid: no signer",
- buf: newHeaderBuf(t, map[string][]string{
- HeaderTimestamp: []string{"0"},
- HeaderTreeSize: []string{"0"},
- HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])},
- }),
- wantErr: true,
- },
- {
- description: "valid: cosigned tree head with two signatures",
- buf: newHeaderBuf(t, map[string][]string{
- HeaderTimestamp: []string{"0"},
- HeaderTreeSize: []string{"0"},
- HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])},
- HeaderSignature: []string{
- hex.EncodeToString(testZeroBuffer64[:]),
- hex.EncodeToString(testBuffer64[:]),
- },
- HeaderKeyHash: []string{
- hex.EncodeToString(testZeroBuffer32[:]),
- hex.EncodeToString(testBuffer32[:]),
- },
- }),
- wantSth: &SignedTreeHead{
- TreeHead: TreeHead{
- Timestamp: 0,
- TreeSize: 0,
- RootHash: testBuffer32,
- },
- SigIdent: []SigIdent{
- SigIdent{
- Signature: testZeroBuffer64,
- KeyHash: testZeroBuffer32,
- },
- SigIdent{
- Signature: testBuffer64,
- KeyHash: testBuffer32,
- },
- },
- },
- },
- } {
- sth, err := SignedTreeHeadFromHTTP(table.buf)
- 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 // nothing more to check on error
- }
- if got, want := sth, table.wantSth; !reflect.DeepEqual(got, want) {
- t.Errorf("got signed tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- }
-}
-
-func TestHeaderFromBuf(t *testing.T) {
- for _, table := range []struct {
- description string
- buf []byte
- wantErr bool
- wantMap map[string][]string
- }{
- {
- description: "invalid: split",
- buf: []byte(HeaderPrefix + "k1: v1:v2\r\n"),
- wantErr: true,
- },
- {
- description: "invalid: prefix",
- buf: []byte("user-agent: secret\r\n"),
- wantErr: true,
- },
- {
- description: "valid: one key with funky case",
- buf: []byte(funkyCase(t, HeaderPrefix) + "k1: v1\r\n"),
- wantMap: map[string][]string{
- HeaderPrefix + "k1": []string{"v1"},
- },
- },
- {
- description: "valid: two keys where one has multiple values",
- buf: []byte(
- HeaderPrefix + "k1: v1 \r\n" +
- HeaderPrefix + "k2: v2\r\n" +
- HeaderPrefix + "k2: v3\r\n",
- ),
- wantMap: map[string][]string{
- HeaderPrefix + "k1": []string{"v1"},
- HeaderPrefix + "k2": []string{"v2", "v3"},
- },
- },
- } {
- hdr, err := headerFromBuf(table.buf)
- 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 // nothing more to check on error
- }
- compareHeaderWithMap(t, table.description, hdr, table.wantMap)
- }
-}
-
-func TestDecodeHex(t *testing.T) {
- for _, table := range []struct {
- description string
- hex string
- wantErr bool
- wantBuf [4]byte
- }{
- {
- description: "invalid: too short input",
- hex: "000102",
- wantErr: true,
- },
- {
- description: "invalid: too large input",
- hex: "0001020304",
- wantErr: true,
- },
- {
- description: "invalid: not hex (1/2)",
- hex: "000102030",
- wantErr: true,
- },
- {
- description: "invalid: not hex (2/2)",
- hex: "0001020q",
- wantErr: true,
- },
- {
- description: "valid",
- hex: "00010203",
- wantBuf: [4]byte{0, 1, 2, 3},
- },
- } {
- var buf [4]byte
- err := decodeHex(table.hex, buf[:])
- 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 // nothing more to check on error
- }
- if got, want := buf[:], table.wantBuf[:]; !bytes.Equal(got, want) {
- t.Errorf("got buf %v but wanted %v in test %q", got, want, table.description)
- }
- }
-}
-
-func newHeaderBuf(t *testing.T, kv map[string][]string) []byte {
- t.Helper()
- hdr := http.Header{}
- for key, values := range kv {
- for _, value := range values {
- hdr.Add(key, value)
- }
- }
- buf := bytes.NewBuffer(nil)
- if err := hdr.Write(buf); err != nil {
- t.Fatalf("hdr.Write(): %v", err)
- }
- return buf.Bytes()
-}
-
-func compareHeaderWithMap(t *testing.T, description string, hdr http.Header, wantMap map[string][]string) {
- t.Helper()
- if got, want := len(hdr), len(wantMap); got != want {
- t.Errorf("got %d keys but wanted %d in test %q", got, want, description)
- }
- for key, value := range wantMap {
- if got, want := hdr.Values(key), value; !reflect.DeepEqual(got, want) {
- t.Errorf("got value %v but wanted %v for key %v in test %q", got, want, key, description)
- }
- }
-}
-
-func funkyCase(t *testing.T, str string) string {
- t.Helper()
- splitIndex := len(str) / 2
- return strings.ToLower(str[:splitIndex]) + strings.ToUpper(str[splitIndex:])
-}
diff --git a/types/trunnel.go b/types/trunnel.go
index 72ae68d..268f6f7 100644
--- a/types/trunnel.go
+++ b/types/trunnel.go
@@ -46,12 +46,15 @@ func (l *Leaf) Unmarshal(buf []byte) error {
l.ShardHint = binary.BigEndian.Uint64(buf)
offset := 8
// Checksum
+ l.Checksum = &[HashSize]byte{}
copy(l.Checksum[:], buf[offset:offset+HashSize])
offset += HashSize
// Signature
+ l.Signature = &[SignatureSize]byte{}
copy(l.Signature[:], buf[offset:offset+SignatureSize])
offset += SignatureSize
// KeyHash
+ l.KeyHash = &[HashSize]byte{}
copy(l.KeyHash[:], buf[offset:])
return nil
}
diff --git a/types/trunnel_test.go b/types/trunnel_test.go
index 0fa7656..297578c 100644
--- a/types/trunnel_test.go
+++ b/types/trunnel_test.go
@@ -7,8 +7,8 @@ import (
)
var (
- testBuffer32 = [32]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
- testBuffer64 = [64]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}
+ testBuffer32 = &[32]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
+ testBuffer64 = &[64]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}
)
func TestMarshalMessage(t *testing.T) {
@@ -105,7 +105,7 @@ func TestUnmarshalLeaf(t *testing.T) {
t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err)
}
if err != nil {
- continue // nothing more to check on error
+ continue
}
if got, want := &leaf, table.want; !reflect.DeepEqual(got, want) {
t.Errorf("got leaf\n\t%v\nbut wanted\n\t%v\nin test %q\n", got, want, table.description)
diff --git a/types/types.go b/types/types.go
index 483dac0..2da40da 100644
--- a/types/types.go
+++ b/types/types.go
@@ -6,8 +6,9 @@ import (
)
const (
- HashSize = sha256.Size
- SignatureSize = ed25519.SignatureSize
+ HashSize = sha256.Size
+ SignatureSize = ed25519.SignatureSize
+ VerificationKeySize = ed25519.PublicKeySize
)
// Leaf is the log's Merkle tree leaf.
@@ -22,14 +23,14 @@ type Leaf struct {
// these values to fit the log's shard interval and the opaque data in question.
type Message struct {
ShardHint uint64
- Checksum [HashSize]byte
+ Checksum *[HashSize]byte
}
// SigIdent is composed of a signature-signer pair. The signature is computed
// over the Trunnel-serialized leaf message. KeyHash identifies the signer.
type SigIdent struct {
- Signature [SignatureSize]byte
- KeyHash [HashSize]byte
+ Signature *[SignatureSize]byte
+ KeyHash *[HashSize]byte
}
// SignedTreeHead is composed of a tree head and a list of signature-signer
@@ -40,7 +41,7 @@ type SigIdent struct {
// Ref: https://github.com/system-transparency/stfe/blob/design/doc/api.md#get-tree-head-latest
type SignedTreeHead struct {
TreeHead
- SigIdent []SigIdent
+ SigIdent []*SigIdent
}
// TreeHead is the log's tree head.
@@ -49,7 +50,7 @@ type SignedTreeHead struct {
type TreeHead struct {
Timestamp uint64
TreeSize uint64
- RootHash [HashSize]byte
+ RootHash *[HashSize]byte
}
// ConsistencyProof is a consistency proof that proves the log's append-only
@@ -59,7 +60,7 @@ type TreeHead struct {
type ConsistencyProof struct {
NewSize uint64
OldSize uint64
- Path [][HashSize]byte
+ Path []*[HashSize]byte
}
// InclusionProof is an inclusion proof that proves a leaf is included in the
@@ -69,5 +70,49 @@ type ConsistencyProof struct {
type InclusionProof struct {
TreeSize uint64
LeafIndex uint64
- Path [][HashSize]byte
+ Path []*[HashSize]byte
+}
+
+// LeafList is a list of leaves
+type LeafList []*Leaf
+
+// ConsistencyProofRequest is a get-consistency-proof request
+//
+// Ref: https://github.com/system-transparency/stfe/blob/design/doc/api.md#get-consistency-proof
+type ConsistencyProofRequest struct {
+ NewSize uint64
+ OldSize uint64
+}
+
+// InclusionProofRequest is a get-proof-by-hash request
+//
+// Ref: https://github.com/system-transparency/stfe/blob/design/doc/api.md#get-proof-by-hash
+type InclusionProofRequest struct {
+ LeafHash *[HashSize]byte
+ TreeSize uint64
+}
+
+// LeavesRequest is a get-leaves request
+//
+// Ref: https://github.com/system-transparency/stfe/blob/design/doc/api.md#get-leaves
+type LeavesRequest struct {
+ StartSize uint64
+ EndSize uint64
+}
+
+// LeafRequest is an add-leaf request
+//
+// Ref: https://github.com/system-transparency/stfe/blob/design/doc/api.md#add-leaf
+type LeafRequest struct {
+ Message
+ Signature *[SignatureSize]byte
+ VerificationKey *[VerificationKeySize]byte
+ DomainHint string
+}
+
+// CosignatureRequest is an add-cosignature request
+//
+// Ref: https://github.com/system-transparency/stfe/blob/design/doc/api.md#add-cosignature
+type CosignatureRequest struct {
+ SigIdent
}