Skip to content

Commit

Permalink
LogRoot format
Browse files Browse the repository at this point in the history
  • Loading branch information
gdbelvin committed Mar 2, 2018
1 parent 462d1c2 commit 3dd45fd
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 1 deletion.
67 changes: 66 additions & 1 deletion crypto/data_formats.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ package crypto

import (
"encoding/base64"
"encoding/binary"
"fmt"
"strconv"

"github.com/google/certificate-transparency-go/tls"

"github.com/benlaurie/objecthash/go/objecthash"
"github.com/google/trillian"
)

// This file contains struct specific mappings and data structures.
// TODO(gdbelvin): remove data-structure specific operations.

// Constants used as map keys when building input for ObjectHash. They must not be changed
// as this will change the output of hashRoot()
Expand Down Expand Up @@ -53,3 +55,66 @@ func HashLogRoot(root trillian.SignedLogRoot) ([]byte, error) {
}
return hash[:], nil
}

// LogRootV1 contains the fields verified by SignedLogRoot
type LogRootV1 struct {
Version uint16
TreeSize uint64
RootHash []byte `tls:"minlen:0,maxlen:128"`
TimestampNanos uint64
Revision uint64
Metadata []byte `tls:"minlen:0,maxlen:65535"`
}

// MapRootV1 contains the fields verified by SignedMapRoot
type MapRootV1 struct {
Version uint16
RootHash []byte `tls:"minlen:0,maxlen:128"`
TimestampNanos uint64
Revision uint64
Metadata []byte `tls:"minlen:0,maxlen:65535"`
}

// ParseLogRoot returns a *SignedLogRootV1
func ParseLogRoot(b []byte) (*LogRootV1, error) {
// Verify version
version := binary.BigEndian.Uint16(b)
if got, want := version, uint16(trillian.LogRootFormat_LOG_ROOT_FORMAT_V1); got != want {
return nil, fmt.Errorf("invalid LogRoot.Version: %v, want %v", got, want)
}

var logRoot LogRootV1
if _, err := tls.Unmarshal(b, &logRoot); err != nil {
return nil, err
}
return &logRoot, nil
}

// SerializeLogRoot returns a canonical TLS serialization of the log root.
func SerializeLogRoot(r *LogRootV1) ([]byte, error) {
root := *r
root.Version = uint16(trillian.LogRootFormat_LOG_ROOT_FORMAT_V1)
return tls.Marshal(root)
}

// ParseMapRoot returns a *SignedMapRootV1
func ParseMapRoot(b []byte) (*MapRootV1, error) {
// Verify version
version := binary.BigEndian.Uint16(b)
if got, want := version, uint16(trillian.MapRootFormat_MAP_ROOT_FORMAT_V1); got != want {
return nil, fmt.Errorf("invalid MapRoot.Version: %v, want %v", got, want)
}

var logRoot MapRootV1
if _, err := tls.Unmarshal(b, &logRoot); err != nil {
return nil, err
}
return &logRoot, nil
}

// SerializeMapRoot returns a canonical TLS serialization of the map root.
func SerializeMapRoot(r *MapRootV1) ([]byte, error) {
root := *r
root.Version = uint16(trillian.MapRootFormat_MAP_ROOT_FORMAT_V1)
return tls.Marshal(root)
}
79 changes: 79 additions & 0 deletions crypto/data_formats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,93 @@ package crypto

import (
"bytes"
"reflect"
"testing"

"github.com/google/trillian"
"github.com/google/trillian/testonly"
"github.com/kr/pretty"
)

var dh = testonly.MustHexDecode

func TestLogRoot(t *testing.T) {
for _, logRoot := range []*LogRootV1{
{
Version: 1,
RootHash: []byte("foo"),
Metadata: []byte{},
},
} {
b, err := SerializeLogRoot(logRoot)
if err != nil {
t.Errorf("SerializeLogRoot(%v): %v", logRoot, err)
}
r2, err := ParseLogRoot(b)
if err != nil {
t.Errorf("ParseLogRoot(): %v", err)
}
if got, want := r2, logRoot; !reflect.DeepEqual(got, want) {
t.Errorf("serialize/parse round trip failed. got %#v, want %#v", pretty.Formatter(got), pretty.Formatter(want))
}
}
}

func TestParseLogRoot(t *testing.T) {
for _, tc := range []struct {
logRoot []byte
wantErr bool
}{
{
logRoot: func() []byte {
b, _ := SerializeLogRoot(&LogRootV1{})
return b
}(),
},
{
logRoot: func() []byte {
b, _ := SerializeLogRoot(&LogRootV1{})
b[0] = 1
return b
}(),
wantErr: true,
},
{
logRoot: []byte("foo"),
wantErr: true,
},
} {
_, err := ParseLogRoot(tc.logRoot)
if got, want := err != nil, tc.wantErr; got != want {
t.Errorf("ParseLogRoot(): %v, wantErr: %v", err, want)
}
}
}

func TestMapRoot(t *testing.T) {
for _, tc := range []struct {
logRoot *MapRootV1
}{
{logRoot: &MapRootV1{
Version: 1,
RootHash: []byte("foo"),
Metadata: []byte{},
}},
} {
b, err := SerializeMapRoot(tc.logRoot)
if err != nil {
t.Errorf("SerializeMapRoot(%v): %v", tc.logRoot, err)
}
r2, err := ParseMapRoot(b)
if err != nil {
t.Errorf("ParseMapRoot(): %v", err)
}
if got, want := r2, tc.logRoot; !reflect.DeepEqual(got, want) {
t.Errorf("serialize/parse round trip failed. got %#v, want %#v", pretty.Formatter(got), pretty.Formatter(want))
}
}
}

// It's important that signatures don't change.
func TestHashLogRootKnownValue(t *testing.T) {
expectedSig := dh("5e6baba8dc3465de9c01d669059dda590b7ce123d6ccd436bcd898f1c79ff6d9")
Expand Down

0 comments on commit 3dd45fd

Please sign in to comment.