diff options
| -rw-r--r-- | handler.go | 10 | ||||
| -rw-r--r-- | instance.go | 21 | ||||
| -rw-r--r-- | reqres.go | 15 | ||||
| -rw-r--r-- | server/testdata/entry/main.go | 6 | ||||
| -rw-r--r-- | trillian.go | 2 | ||||
| -rw-r--r-- | type.go | 352 | ||||
| -rw-r--r-- | type_test.go | 14 | ||||
| -rw-r--r-- | x509.go | 20 | 
8 files changed, 232 insertions, 208 deletions
| @@ -20,7 +20,6 @@ type appHandler struct {  	handler  func(context.Context, *Instance, http.ResponseWriter, *http.Request) (int, error)  } -// ServeHTTP docdoc  func (a appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {  	ctx, cancel := context.WithDeadline(r.Context(), time.Now().Add(a.instance.Deadline))  	defer cancel() @@ -38,7 +37,6 @@ func (a appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {  	}  } -// sendHTTPError replies to a request with an error message and a status code.  func (a appHandler) sendHTTPError(w http.ResponseWriter, statusCode int, err error) {  	http.Error(w, http.StatusText(statusCode), statusCode)  } @@ -69,7 +67,7 @@ func addEntry(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.R  	if err != nil {  		return http.StatusInternalServerError, fmt.Errorf("failed creating signed debug info: %v", err)  	} -	rsp, err := StItemToB64(sdi) +	rsp, err := sdi.MarshalB64()  	if err != nil {  		return http.StatusInternalServerError, err  	} @@ -142,7 +140,7 @@ func getProofByHash(ctx context.Context, i *Instance, w http.ResponseWriter, r *  		return status, err  	} -	rsp, err := StItemToB64(NewInclusionProofV1(i.LogParameters.LogId, uint64(req.TreeSize), trsp.Proof[0])) +	rsp, err := NewInclusionProofV1(i.LogParameters.LogId, uint64(req.TreeSize), trsp.Proof[0]).MarshalB64()  	if err != nil {  		return http.StatusInternalServerError, err  	} @@ -173,7 +171,7 @@ func getConsistencyProof(ctx context.Context, i *Instance, w http.ResponseWriter  		return status, err  	} -	rsp, err := StItemToB64(NewConsistencyProofV1(i.LogParameters.LogId, req.First, req.Second, trsp.Proof)) +	rsp, err := NewConsistencyProofV1(i.LogParameters.LogId, req.First, req.Second, trsp.Proof).MarshalB64()  	if err != nil {  		return http.StatusInternalServerError, err  	} @@ -205,7 +203,7 @@ func getSth(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Req  	if err != nil {  		return http.StatusInternalServerError, fmt.Errorf("failed creating signed tree head: %v", err)  	} -	rsp, err := StItemToB64(sth) +	rsp, err := sth.MarshalB64()  	if err != nil {  		return http.StatusInternalServerError, err  	} diff --git a/instance.go b/instance.go index b13bdbe..461ab6c 100644 --- a/instance.go +++ b/instance.go @@ -7,7 +7,6 @@ import (  	"crypto/sha256"  	"crypto/x509" -  	"encoding/base64"  	"net/http" @@ -34,7 +33,15 @@ type LogParameters struct {  	HashType   crypto.Hash // hash function used by Trillian  } -// NewInstance returns an initialized Instance +func (i Instance) String() string { +	return fmt.Sprintf("%s Deadline(%v)\n", i.LogParameters, i.Deadline) +} + +func (p LogParameters) String() string { +	return fmt.Sprintf("LogId(%s) TreeId(%d) Prefix(%s) NumAnchors(%d)", base64.StdEncoding.EncodeToString(p.LogId), p.TreeId, p.Prefix, len(p.AnchorList)) +} + +// NewInstance returns a new STFE Instance  func NewInstance(lp *LogParameters, client trillian.TrillianLogClient, deadline time.Duration, mux *http.ServeMux) (*Instance, error) {  	i := &Instance{  		LogParameters: lp, @@ -45,7 +52,7 @@ func NewInstance(lp *LogParameters, client trillian.TrillianLogClient, deadline  	return i, nil  } -// NewLogParameters returns initialized log parameters using only ed25519 +// NewLogParameters initializes log parameters, assuming ed25519 signatures.  func NewLogParameters(treeId int64, prefix string, anchorPath, keyPath string) (*LogParameters, error) {  	anchorList, anchorPool, err := LoadTrustAnchors(anchorPath)  	if err != nil { @@ -77,14 +84,6 @@ func NewLogParameters(treeId int64, prefix string, anchorPath, keyPath string) (  	}, nil  } -func (i *Instance) String() string { -	return fmt.Sprintf("%s Deadline(%v)\n", i.LogParameters, i.Deadline) -} - -func (p *LogParameters) String() string { -	return fmt.Sprintf("LogId(%s) TreeId(%d) Prefix(%s) NumAnchors(%d)", base64.StdEncoding.EncodeToString(p.LogId), p.TreeId, p.Prefix, len(p.AnchorList)) -} -  func (i *Instance) registerHandlers(mux *http.ServeMux) {  	for _, endpoint := range []struct {  		path    string @@ -59,8 +59,8 @@ func NewAddEntryRequest(lp *LogParameters, r *http.Request) ([]byte, []byte, err  		return nil, nil, err  	} -	item, err := StItemFromB64(entry.Item) -	if err != nil { +	var item StItem +	if err := item.UnmarshalB64(entry.Item); err != nil {  		return nil, nil, fmt.Errorf("StItem(%s): %v", item.Format, err)  	}  	if item.Format != StFormatChecksumV1 { @@ -84,9 +84,9 @@ func NewAddEntryRequest(lp *LogParameters, r *http.Request) ([]byte, []byte, err  		return nil, nil, fmt.Errorf("invalid signature: %v", err)  	} -	extra, err := tls.Marshal(NewAppendix(chain, signature, entry.SignatureScheme)) +	extra, err := NewAppendix(chain, signature, entry.SignatureScheme).Marshal()  	if err != nil { -		return nil, nil, fmt.Errorf("failed tls marshaling appendix: %v", err) +		return nil, nil, fmt.Errorf("failed marshaling appendix: %v", err)  	}  	return serialized, extra, nil @@ -159,11 +159,8 @@ func NewGetConsistencyProofRequest(httpRequest *http.Request) (GetConsistencyPro  // NewGetEntryResponse assembles a log entry and its appendix  func NewGetEntryResponse(leaf, appendix []byte) (GetEntryResponse, error) {  	var app Appendix -	extra, err := tls.Unmarshal(appendix, &app) -	if err != nil { -		return GetEntryResponse{}, fmt.Errorf("failed tls unmarshaling appendix: %v (%v)", err, extra) -	} else if len(extra) > 0 { -		return GetEntryResponse{}, fmt.Errorf("tls umarshal found extra data for appendix: %v", extra) +	if err := app.Unmarshal(appendix); err != nil { +		return GetEntryResponse{}, err  	}  	chain := make([]string, 0, len(app.Chain)) diff --git a/server/testdata/entry/main.go b/server/testdata/entry/main.go index a849c50..7ab6d4d 100644 --- a/server/testdata/entry/main.go +++ b/server/testdata/entry/main.go @@ -8,7 +8,6 @@ import (  	"io/ioutil"  	"github.com/golang/glog" -	"github.com/google/certificate-transparency-go/tls"  	"github.com/system-transparency/stfe"  ) @@ -26,10 +25,9 @@ func main() {  	checksum := hasher.Sum(nil)  	// Create and serialize an StItem of type checksum_v1 -	item := stfe.NewChecksumV1([]byte(*name), checksum) -	serialized, err := tls.Marshal(item) +	serialized, err := stfe.NewChecksumV1([]byte(*name), checksum).Marshal()  	if err != nil { -		glog.Fatalf("tls marshal failed: %v", err) +		glog.Fatalf("%v", err)  	}  	// Store the serialized item in *dir/name diff --git a/trillian.go b/trillian.go index 0912af7..57494b1 100644 --- a/trillian.go +++ b/trillian.go @@ -15,8 +15,6 @@ func checkQueueLeaf(rsp *trillian.QueueLeafResponse) (int, error) {  	if codes.Code(rsp.QueuedLeaf.GetStatus().GetCode()) == codes.AlreadyExists {  		// no need to report this as an invalid request, just (re)issue sdi  		glog.V(3).Infof("queued leaf is a duplicate => %X", rsp.QueuedLeaf.Leaf.LeafValue) -	} else { -		glog.V(3).Infof("queued leaf => %X", rsp.QueuedLeaf.Leaf.LeafValue)  	}  	return 0, nil  } @@ -2,10 +2,10 @@ package stfe  import (  	"fmt" +	"time"  	"crypto/x509"  	"encoding/base64" -	"time"  	"github.com/google/certificate-transparency-go/tls"  	"github.com/google/trillian" @@ -24,7 +24,7 @@ const (  	StFormatChecksumV1                  = 5  ) -// StItem references a versioned item based on a given format specifier. +// StItem references a versioned item based on a given format specifier  type StItem struct {  	Format             StFormat            `tls:"maxval:65535"`  	SignedTreeHeadV1   *SignedTreeHeadV1   `tls:"selector:Format,val:1"` @@ -34,42 +34,15 @@ type StItem struct {  	ChecksumV1         *ChecksumV1         `tls:"selector:Format,val:5"`  } -type ConsistencyProofV1 struct { -	LogId           []byte `tls:"minlen:32,maxlen:32"` -	TreeSize1       uint64 -	TreeSize2       uint64 -	ConsistencyPath []NodeHash `tls:"minlen:1,maxlen:65535"` -} - +// SignedTreeHeadV1 is a signed tree head as defined by RFC 6962/bis, §4.10  type SignedTreeHeadV1 struct {  	LogId     []byte `tls:"minlen:32,maxlen:32"`  	TreeHead  TreeHeadV1  	Signature []byte `tls:"minlen:1,maxlen:65535"`  } -type TreeHeadV1 struct { -	Timestamp uint64 -	TreeSize  uint64 -	RootHash  NodeHash -	Extension []byte `tls:"minlen:0,maxlen:65535"` -} - -// ChecksumV1 associates a package name with an arbitrary checksum value -type ChecksumV1 struct { -	Package  []byte `tls:"minlen:1,maxlen:255"` -	Checksum []byte `tls:"minlen:1,maxlen:64"` -} - -// InclusionProofV1 is a Merkle tree inclusion proof, see RFC 6962/bis (§4.12) -type InclusionProofV1 struct { -	LogID         []byte `tls:"minlen:32,maxlen:32"` -	TreeSize      uint64 -	LeafIndex     uint64 -	InclusionPath []NodeHash `tls:"minlen:1,maxlen:65535"` -} -  // SignedDebugInfoV1 is a signed statement that we intend (but do not promise) -// to insert an entry into the log.  Only Ed25519 signatures are supported. +// to insert an entry into the log as defined by markdown/api.md  // TODO: double-check that crypto/ed25519 encodes signature as in RFC 8032  type SignedDebugInfoV1 struct {  	LogId     []byte `tls:"minlen:32,maxlen:32"` @@ -77,102 +50,51 @@ type SignedDebugInfoV1 struct {  	Signature []byte `tls:"minlen:1,maxlen:65535"`  } -// NodeHash is a hashed Merkle tree node, see RFC 6962/bis (§4.9) -type NodeHash struct { -	Data []byte `tls:"minlen:32,maxlen:255"` +// ConsistencyProofV1 is a consistency proof as defined by RFC 6962/bis, §4.11 +type ConsistencyProofV1 struct { +	LogId           []byte `tls:"minlen:32,maxlen:32"` +	TreeSize1       uint64 +	TreeSize2       uint64 +	ConsistencyPath []NodeHash `tls:"minlen:1,maxlen:65535"`  } -func NewSignedTreeHeadV1(th TreeHeadV1, logId, signature []byte) StItem { -	return StItem{ -		Format: StFormatSignedTreeHeadV1, -		SignedTreeHeadV1: &SignedTreeHeadV1{ -			LogId:     logId, -			TreeHead:  th, -			Signature: signature, -		}, -	} +// InclusionProofV1 is an inclusion proof as defined by RFC 6962/bis, §4.12 +type InclusionProofV1 struct { +	LogId         []byte `tls:"minlen:32,maxlen:32"` +	TreeSize      uint64 +	LeafIndex     uint64 +	InclusionPath []NodeHash `tls:"minlen:1,maxlen:65535"`  } -// NewTreeHead converts a Trillian-signed log root to a tree head without -// verifying any signature.  In other words, Trillian <-> STFE is trusted. -func NewTreeHeadV1(lp *LogParameters, slr *trillian.SignedLogRoot) (TreeHeadV1, error) { -	if slr == nil { -		return TreeHeadV1{}, fmt.Errorf("Trillian returned no tree head") -	} - -	var lr types.LogRootV1 -	if err := lr.UnmarshalBinary(slr.GetLogRoot()); err != nil { -		return TreeHeadV1{}, fmt.Errorf("failed unmarshaling Trillian slr: %v", err) -	} -	if lp.HashType.Size() != len(lr.RootHash) { -		return TreeHeadV1{}, fmt.Errorf("invalid Trillian root hash: %v", lr.RootHash) -	} - -	return TreeHeadV1{ -		Timestamp: uint64(lr.TimestampNanos / 1000 / 1000), -		TreeSize:  uint64(lr.TreeSize), -		RootHash: NodeHash{ -			Data: lr.RootHash, -		}, -		Extension: nil, // no known extensions -	}, nil +// ChecksumV1 associates a leaf type as defined by markdown/api.md +type ChecksumV1 struct { +	Package  []byte `tls:"minlen:1,maxlen:255"` +	Checksum []byte `tls:"minlen:1,maxlen:64"`  } -func NewSignedDebugInfoV1(logId, message, signature []byte) StItem { -	return StItem{ -		Format: StFormatSignedDebugInfoV1, -		SignedDebugInfoV1: &SignedDebugInfoV1{ -			LogId:     logId, -			Message:   message, -			Signature: signature, -		}, -	} +// TreeHeadV1 is a tree head as defined by RFC 6962/bis, §4.10 +type TreeHeadV1 struct { +	Timestamp uint64 +	TreeSize  uint64 +	RootHash  NodeHash +	Extension []byte `tls:"minlen:0,maxlen:65535"`  } -// NewChecksumV1 creates a new StItem of type checksum_v1 -func NewChecksumV1(identifier []byte, checksum []byte) StItem { -	return StItem{ -		Format: StFormatChecksumV1, -		ChecksumV1: &ChecksumV1{ -			Package:  identifier, -			Checksum: checksum, -		}, -	} +// NodeHash is a Merkle tree hash as defined by RFC 6962/bis, §4.9 +type NodeHash struct { +	Data []byte `tls:"minlen:32,maxlen:255"`  } -// NewInclusionProofV1 creates a new StItem of type inclusion_proof_v1 -func NewInclusionProofV1(logID []byte, treeSize uint64, proof *trillian.Proof) StItem { -	inclusionPath := make([]NodeHash, 0, len(proof.Hashes)) -	for _, hash := range proof.Hashes { -		inclusionPath = append(inclusionPath, NodeHash{Data: hash}) -	} - -	return StItem{ -		Format: StFormatInclusionProofV1, -		InclusionProofV1: &InclusionProofV1{ -			LogID:         logID, -			TreeSize:      treeSize, -			LeafIndex:     uint64(proof.LeafIndex), -			InclusionPath: inclusionPath, -		}, -	} +// RawCertificate is a serialized X.509 certificate +type RawCertificate struct { +	Data []byte `tls:"minlen:0,maxlen:65535"`  } -func NewConsistencyProofV1(logId []byte, first, second int64, proof *trillian.Proof) StItem { -	path := make([]NodeHash, 0, len(proof.Hashes)) -	for _, hash := range proof.Hashes { -		path = append(path, NodeHash{Data: hash}) -	} - -	return StItem{ -		Format: StFormatConsistencyProofV1, -		ConsistencyProofV1: &ConsistencyProofV1{ -			LogId:           logId, -			TreeSize1:       uint64(first), -			TreeSize2:       uint64(second), -			ConsistencyPath: path, -		}, -	} +// Appendix is extra leaf data that is not stored in the log's Merkle tree +type Appendix struct { +	Signature       []byte `tls:"minlen:0,maxlen:16383"` +	SignatureScheme uint16 +	Chain           []RawCertificate `tls:"minlen:0,maxlen:65535"`  }  func (f StFormat) String() string { @@ -197,24 +119,20 @@ func (f StFormat) String() string {  func (i StItem) String() string {  	switch i.Format {  	case StFormatChecksumV1: -		return fmt.Sprintf("Format(%s): %s", i.Format, *i.ChecksumV1) +		return fmt.Sprintf("Format(%s): %s", i.Format, i.ChecksumV1)  	case StFormatConsistencyProofV1: -		return fmt.Sprintf("Format(%s): %s", i.Format, *i.ConsistencyProofV1) +		return fmt.Sprintf("Format(%s): %s", i.Format, i.ConsistencyProofV1)  	case StFormatInclusionProofV1: -		return fmt.Sprintf("Format(%s): %s", i.Format, *i.InclusionProofV1) +		return fmt.Sprintf("Format(%s): %s", i.Format, i.InclusionProofV1)  	case StFormatSignedDebugInfoV1: -		return fmt.Sprintf("Format(%s): %s", i.Format, *i.SignedDebugInfoV1) +		return fmt.Sprintf("Format(%s): %s", i.Format, i.SignedDebugInfoV1)  	case StFormatSignedTreeHeadV1: -		return fmt.Sprintf("Format(%s): %s", i.Format, *i.SignedTreeHeadV1) +		return fmt.Sprintf("Format(%s): %s", i.Format, i.SignedTreeHeadV1)  	default:  		return fmt.Sprintf("unknown StItem: %s", i.Format)  	}  } -func (th TreeHeadV1) String() string { -	return fmt.Sprintf("Timestamp(%s) TreeSize(%d) RootHash(%s)", time.Unix(int64(th.Timestamp/1000), 0), th.TreeSize, base64.StdEncoding.EncodeToString(th.RootHash.Data)) -} -  func (i SignedTreeHeadV1) String() string {  	return fmt.Sprintf("LogId(%s) TreeHead(%s) Signature(%s)", base64.StdEncoding.EncodeToString(i.LogId), i.TreeHead, base64.StdEncoding.EncodeToString(i.Signature))  } @@ -223,8 +141,13 @@ func (i SignedDebugInfoV1) String() string {  	return fmt.Sprintf("LogId(%s) Message(%s) Signature(%s)", base64.StdEncoding.EncodeToString(i.LogId), string(i.Message), base64.StdEncoding.EncodeToString(i.Signature))  } -func (i ChecksumV1) String() string { -	return fmt.Sprintf("Package(%v) Checksum(%v)", string(i.Package), base64.StdEncoding.EncodeToString(i.Checksum)) +func (i ConsistencyProofV1) String() string { +	path := make([]string, 0, len(i.ConsistencyPath)) +	for _, hash := range i.ConsistencyPath { +		path = append(path, base64.StdEncoding.EncodeToString(hash.Data)) +	} + +	return fmt.Sprintf("LogID(%s) TreeSize1(%d) TreeSize2(%d) ConsistencyPath(%v)", base64.StdEncoding.EncodeToString(i.LogId), i.TreeSize1, i.TreeSize2, path)  }  func (i InclusionProofV1) String() string { @@ -233,63 +156,182 @@ func (i InclusionProofV1) String() string {  		path = append(path, base64.StdEncoding.EncodeToString(hash.Data))  	} -	return fmt.Sprintf("LogID(%s) TreeSize(%d) LeafIndex(%d) AuditPath(%v)", base64.StdEncoding.EncodeToString(i.LogID), i.TreeSize, i.LeafIndex, path) +	return fmt.Sprintf("LogID(%s) TreeSize(%d) LeafIndex(%d) AuditPath(%v)", base64.StdEncoding.EncodeToString(i.LogId), i.TreeSize, i.LeafIndex, path)  } -func (i ConsistencyProofV1) String() string { -	path := make([]string, 0, len(i.ConsistencyPath)) -	for _, hash := range i.ConsistencyPath { -		path = append(path, base64.StdEncoding.EncodeToString(hash.Data)) +func (i ChecksumV1) String() string { +	return fmt.Sprintf("Package(%s) Checksum(%s)", string(i.Package), base64.StdEncoding.EncodeToString(i.Checksum)) +} + +func (th TreeHeadV1) String() string { +	return fmt.Sprintf("Timestamp(%s) TreeSize(%d) RootHash(%s)", time.Unix(int64(th.Timestamp/1000), 0), th.TreeSize, base64.StdEncoding.EncodeToString(th.RootHash.Data)) +} + +// Marshal serializes an Stitem as defined by RFC 5246 +func (i *StItem) Marshal() ([]byte, error) { +	serialized, err := tls.Marshal(*i) +	if err != nil { +		return nil, fmt.Errorf("marshal failed for StItem(%s): %v", i.Format, err)  	} +	return serialized, nil +} -	return fmt.Sprintf("LogID(%s) TreeSize1(%d) TreeSize2(%d) ConsistencyPath(%v)", base64.StdEncoding.EncodeToString(i.LogId), i.TreeSize1, i.TreeSize2, path) +// MarshalB64 base64-encodes a serialized StItem +func (i *StItem) MarshalB64() (string, error) { +	serialized, err := i.Marshal() +	if err != nil { +		return "", err +	} +	return base64.StdEncoding.EncodeToString(serialized), nil +} + +// Unmarshal unpacks a serialized StItem +func (i *StItem) Unmarshal(serialized []byte) error { +	extra, err := tls.Unmarshal(serialized, i) +	if err != nil { +		return fmt.Errorf("unmarshal failed for StItem(%s): %v", i.Format, err) +	} else if len(extra) > 0 { +		return fmt.Errorf("unmarshal found extra data for StItem(%s): %v", i.Format, extra) +	} +	return nil  } -// StItemFromB64 creates an StItem from a serialized and base64-encoded string -func StItemFromB64(s string) (StItem, error) { -	b, err := base64.StdEncoding.DecodeString(s) +// UnmarshalB64 unpacks a base64-encoded serialized StItem +func (i *StItem) UnmarshalB64(s string) error { +	serialized, err := base64.StdEncoding.DecodeString(s)  	if err != nil { -		return StItem{}, fmt.Errorf("base64 decoding failed: %v", err) +		return fmt.Errorf("base64 decoding failed for StItem(%s): %v", i.Format, err)  	} +	return i.Unmarshal(serialized) +} -	var item StItem -	extra, err := tls.Unmarshal(b, &item) +// Marshal serializes an Appendix as defined by RFC 5246 +func (a *Appendix) Marshal() ([]byte, error) { +	serialized, err := tls.Marshal(*a)  	if err != nil { -		return StItem{}, fmt.Errorf("tls unmarshal failed: %v", err) +		return nil, fmt.Errorf("marshal failed for Appendix(%v): %v", a, err) +	} +	return serialized, nil +} + +// Unmarshal unpacks an serialized Appendix +func (a *Appendix) Unmarshal(serialized []byte) error { +	extra, err := tls.Unmarshal(serialized, a) +	if err != nil { +		return fmt.Errorf("unmarshal failed for Appendix(%v): %v", a, err)  	} else if len(extra) > 0 { -		return StItem{}, fmt.Errorf("tls unmarshal found extra data: %v", extra) +		return fmt.Errorf("unmarshal found extra data for Appendix(%v): %v", a, extra)  	} -	return item, nil +	return nil  } -func StItemToB64(i StItem) (string, error) { -	b, err := tls.Marshal(i) +// Marshal serializes a TreeHeadV1 as defined by RFC 5246 +func (th *TreeHeadV1) Marshal() ([]byte, error) { +	serialized, err := tls.Marshal(*th)  	if err != nil { -		return "", fmt.Errorf("StItem(%v) tls marshal failed: %v", i.Format, err) +		return nil, fmt.Errorf("marshal failed for TreeHeadV1: %v", err)  	} -	return base64.StdEncoding.EncodeToString(b), nil +	return serialized, nil  } -// Appendix is extra data that Trillian can store about a leaf -type Appendix struct { -	Signature       []byte `tls:"minlen:0,maxlen:16383"` -	SignatureScheme uint16 -	Chain           []RawCertificate `tls:"minlen:0,maxlen:65535"` +// NewSignedTreeHead creates a new StItem of type signed_tree_head_v1 +func NewSignedTreeHeadV1(th *TreeHeadV1, logId, signature []byte) *StItem { +	return &StItem{ +		Format: StFormatSignedTreeHeadV1, +		SignedTreeHeadV1: &SignedTreeHeadV1{ +			LogId:     logId, +			TreeHead:  *th, +			Signature: signature, +		}, +	}  } -// RawCertificate is a serialized X.509 certificate -type RawCertificate struct { -	Data []byte `tls:"minlen:0,maxlen:65535"` +// NewSignedDebugInfoV1 creates a new StItem of type inclusion_proof_v1 +func NewSignedDebugInfoV1(logId, message, signature []byte) *StItem { +	return &StItem{ +		Format: StFormatSignedDebugInfoV1, +		SignedDebugInfoV1: &SignedDebugInfoV1{ +			LogId:     logId, +			Message:   message, +			Signature: signature, +		}, +	} +} + +// NewInclusionProofV1 creates a new StItem of type inclusion_proof_v1 +func NewInclusionProofV1(logID []byte, treeSize uint64, proof *trillian.Proof) *StItem { +	inclusionPath := make([]NodeHash, 0, len(proof.Hashes)) +	for _, hash := range proof.Hashes { +		inclusionPath = append(inclusionPath, NodeHash{Data: hash}) +	} +	return &StItem{ +		Format: StFormatInclusionProofV1, +		InclusionProofV1: &InclusionProofV1{ +			LogId:         logID, +			TreeSize:      treeSize, +			LeafIndex:     uint64(proof.LeafIndex), +			InclusionPath: inclusionPath, +		}, +	} +} + +// NewConsistencyProofV1 creates a new StItem of type consistency_proof_v1 +func NewConsistencyProofV1(logId []byte, first, second int64, proof *trillian.Proof) *StItem { +	path := make([]NodeHash, 0, len(proof.Hashes)) +	for _, hash := range proof.Hashes { +		path = append(path, NodeHash{Data: hash}) +	} +	return &StItem{ +		Format: StFormatConsistencyProofV1, +		ConsistencyProofV1: &ConsistencyProofV1{ +			LogId:           logId, +			TreeSize1:       uint64(first), +			TreeSize2:       uint64(second), +			ConsistencyPath: path, +		}, +	} +} + +// NewChecksumV1 creates a new StItem of type checksum_v1 +func NewChecksumV1(identifier []byte, checksum []byte) *StItem { +	return &StItem{ +		Format: StFormatChecksumV1, +		ChecksumV1: &ChecksumV1{ +			Package:  identifier, +			Checksum: checksum, +		}, +	} +} + +// NewTreeHead creates a new TreeHeadV1 from a Trillian-signed log root without +// verifying any signature.  In other words, Trillian <-> STFE must be trusted. +func NewTreeHeadV1(lp *LogParameters, slr *trillian.SignedLogRoot) (*TreeHeadV1, error) { +	var lr types.LogRootV1 +	if err := lr.UnmarshalBinary(slr.GetLogRoot()); err != nil { +		return nil, fmt.Errorf("failed unmarshaling Trillian slr: %v", err) +	} +	if lp.HashType.Size() != len(lr.RootHash) { +		return nil, fmt.Errorf("invalid Trillian root hash: %v", lr.RootHash) +	} + +	return &TreeHeadV1{ +		Timestamp: uint64(lr.TimestampNanos / 1000 / 1000), +		TreeSize:  uint64(lr.TreeSize), +		RootHash: NodeHash{ +			Data: lr.RootHash, +		}, +		Extension: nil, // no known extensions +	}, nil  }  // NewAppendix creates a new leaf Appendix for an X.509 chain and signature -func NewAppendix(x509Chain []*x509.Certificate, signature []byte, signatureScheme uint16) Appendix { +func NewAppendix(x509Chain []*x509.Certificate, signature []byte, signatureScheme uint16) *Appendix {  	chain := make([]RawCertificate, 0, len(x509Chain))  	for _, c := range x509Chain {  		chain = append(chain, RawCertificate{c.Raw})  	} -	return Appendix{ +	return &Appendix{  		Signature:       signature,  		Chain:           chain,  		SignatureScheme: signatureScheme, diff --git a/type_test.go b/type_test.go index f4e7b40..c6fa687 100644 --- a/type_test.go +++ b/type_test.go @@ -4,8 +4,6 @@ import (  	"fmt"  	"crypto/sha256" - -	"github.com/google/certificate-transparency-go/tls"  )  func ExampleNewChecksumV1() { @@ -21,9 +19,9 @@ func ExampleNewChecksumV1() {  func ExampleMarshalChecksumV1() {  	item := NewChecksumV1([]byte("foobar-1.2.3"), make([]byte, 32)) -	b, err := tls.Marshal(item) +	b, err := item.Marshal()  	if err != nil { -		fmt.Printf("tls.Marshal() failed: %v", err) +		fmt.Printf("%v", err)  		return  	}  	fmt.Printf("%v\n", b) @@ -34,12 +32,8 @@ func ExampleUnmarshalChecksumV1() {  	b := []byte{0, 5, 12, 102, 111, 111, 98, 97, 114, 45, 49, 46, 50, 46, 51, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}  	var item StItem -	extra, err := tls.Unmarshal(b, &item) -	if err != nil { -		fmt.Printf("tls.Unmarshal() failed: %v (%v)", err, extra) -		return -	} else if len(extra) > 0 { -		fmt.Printf("tls.Unmarshal() found extra data: %v", extra) +	if err := item.Unmarshal(b); err != nil { +		fmt.Printf("%v", err)  		return  	}  	fmt.Printf("%v\n", item) @@ -8,13 +8,11 @@ import (  	"crypto/ed25519"  	"crypto/rand"  	"crypto/rsa" -	stdtls "crypto/tls" +	"crypto/tls"  	"crypto/x509"  	"encoding/base64"  	"encoding/pem"  	"io/ioutil" - -	"github.com/google/certificate-transparency-go/tls"  )  // LoadTrustAnchors loads a list of PEM-encoded certificates from file @@ -115,25 +113,25 @@ func VerifySignature(leaf, signature []byte, certificate *x509.Certificate) erro  	return nil  } -func GenV1SDI(ld *LogParameters, leaf []byte) (StItem, error) { +func GenV1SDI(ld *LogParameters, leaf []byte) (*StItem, error) {  	// Note that ed25519 does not use the passed io.Reader  	sig, err := ld.Signer.Sign(rand.Reader, leaf, crypto.Hash(0))  	if err != nil { -		return StItem{}, fmt.Errorf("ed25519 signature failed: %v", err) +		return nil, fmt.Errorf("ed25519 signature failed: %v", err)  	}  	return NewSignedDebugInfoV1(ld.LogId, []byte("reserved"), sig), nil  } -func GenV1STH(ld *LogParameters, th TreeHeadV1) (StItem, error) { -	serialized, err := tls.Marshal(th) +func GenV1STH(ld *LogParameters, th *TreeHeadV1) (*StItem, error) { +	serialized, err := th.Marshal()  	if err != nil { -		return StItem{}, fmt.Errorf("failed tls marshaling tree head: %v", err) +		return nil, fmt.Errorf("failed tls marshaling tree head: %v", err)  	}  	// Note that ed25519 does not use the passed io.Reader  	sig, err := ld.Signer.Sign(rand.Reader, serialized, crypto.Hash(0))  	if err != nil { -		return StItem{}, fmt.Errorf("ed25519 signature failed: %v", err) +		return nil, fmt.Errorf("ed25519 signature failed: %v", err)  	}  	return NewSignedTreeHeadV1(th, ld.LogId, sig), nil  } @@ -194,8 +192,8 @@ func buildChainFromB64List(lp *LogParameters, b64chain []string) ([]*x509.Certif  // verifySignature checks if signature is valid for some serialized data.  The  // only supported signature scheme is ecdsa_secp256r1_sha256(0x0403), see §4.3.2  // in RFC 8446.  TODO: replace ECDSA with ed25519(0x0807) -func verifySignature(_ *LogParameters, certificate *x509.Certificate, scheme stdtls.SignatureScheme, serialized, signature []byte) error { -	if scheme != stdtls.ECDSAWithP256AndSHA256 { +func verifySignature(_ *LogParameters, certificate *x509.Certificate, scheme tls.SignatureScheme, serialized, signature []byte) error { +	if scheme != tls.ECDSAWithP256AndSHA256 {  		return fmt.Errorf("unsupported signature scheme: %v", scheme)  	}  	if err := certificate.CheckSignature(x509.ECDSAWithSHA256, serialized, signature); err != nil { | 
