diff options
Diffstat (limited to 'types')
| -rw-r--r-- | types/ascii.go | 413 | ||||
| -rw-r--r-- | types/ascii_test.go | 465 | ||||
| -rw-r--r-- | types/http.go | 188 | ||||
| -rw-r--r-- | types/http_test.go | 331 | ||||
| -rw-r--r-- | types/trunnel.go | 3 | ||||
| -rw-r--r-- | types/trunnel_test.go | 6 | ||||
| -rw-r--r-- | types/types.go | 63 | 
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  } | 
