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 { |