aboutsummaryrefslogtreecommitdiff
path: root/type.go
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 /type.go
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.
Diffstat (limited to 'type.go')
-rw-r--r--type.go352
1 files changed, 197 insertions, 155 deletions
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,