aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRasmus Dahlberg <rasmus.dahlberg@kau.se>2020-10-30 20:40:17 +0100
committerRasmus Dahlberg <rasmus.dahlberg@kau.se>2020-10-30 20:40:17 +0100
commitf367d220ff99eaee7debb234c3234de6c781359c (patch)
tree49fda266aaf121e725780d2b7e2d6eb70710c74c
parent5426f3bcd1a5ae4fc4b3b831b41c0d667a17e525 (diff)
refactor types and documentation
Structured files a bit better, added more documentation, switched to pointers as default (unless specifically motivated not to do so), and encapsulated TLS (un)marshaling for the respective types that use it.
-rw-r--r--handler.go10
-rw-r--r--instance.go21
-rw-r--r--reqres.go15
-rw-r--r--server/testdata/entry/main.go6
-rw-r--r--trillian.go2
-rw-r--r--type.go352
-rw-r--r--type_test.go14
-rw-r--r--x509.go20
8 files changed, 232 insertions, 208 deletions
diff --git a/handler.go b/handler.go
index 675c441..735d798 100644
--- a/handler.go
+++ b/handler.go
@@ -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
diff --git a/reqres.go b/reqres.go
index a1e0d78..1cad933 100644
--- a/reqres.go
+++ b/reqres.go
@@ -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
}
diff --git a/type.go b/type.go
index 5a32964..780d70e 100644
--- a/type.go
+++ b/type.go
@@ -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)
diff --git a/x509.go b/x509.go
index b78e5e5..329ce01 100644
--- a/x509.go
+++ b/x509.go
@@ -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 {