aboutsummaryrefslogtreecommitdiff
path: root/instance.go
blob: 8a0007aeb576e2d257e6bfa779be80991fed8313 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package stfe

import (
	"crypto"
	"fmt"
	"strings"
	"time"

	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"io/ioutil"
	"net/http"

	"github.com/golang/glog"
	"github.com/google/trillian"
	"github.com/system-transparency/stfe/x509util"
)

type Endpoint string

const (
	EndpointAddEntry            = Endpoint("add-entry")
	EndpointGetEntries          = Endpoint("get-entries")
	EndpointGetAnchors          = Endpoint("get-anchors")
	EndpointGetProofByHash      = Endpoint("get-proof-by-hash")
	EndpointGetConsistencyProof = Endpoint("get-consistency-proof")
	EndpointGetSth              = Endpoint("get-sth")
)

// Instance is an instance of a particular log front-end
type Instance struct {
	LogParameters *LogParameters
	Client        trillian.TrillianLogClient
	Deadline      time.Duration
}

// LogParameters is a collection of log parameters
type LogParameters struct {
	LogId      []byte              // used externally by everyone
	TreeId     int64               // used internally by Trillian
	Prefix     string              // e.g., "test" for <base>/test
	MaxRange   int64               // max entries per get-entries request
	MaxChain   int64               // max submitter certificate chain length
	AnchorPool *x509.CertPool      // for chain verification
	AnchorList []*x509.Certificate // for access to the raw certificates
	KeyUsage   []x509.ExtKeyUsage  // which extended key usages are accepted
	Signer     crypto.Signer
	HashType   crypto.Hash // hash function used by Trillian
}

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 *LogParameters) id() string {
	return base64.StdEncoding.EncodeToString(i.LogId)
}

// 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,
		Client:        client,
		Deadline:      deadline,
	}
	i.registerHandlers(mux)
	return i, nil
}

// NewLogParameters initializes log parameters, assuming ed25519 signatures.
func NewLogParameters(treeId int64, prefix string, anchorPath, keyPath string, maxRange, maxChain int64) (*LogParameters, error) {
	anchorList, anchorPool, err := loadTrustAnchors(anchorPath)
	if err != nil {
		return nil, err
	}

	pem, err := ioutil.ReadFile(keyPath)
	if err != nil {
		return nil, fmt.Errorf("failed reading %s: %v", keyPath, err)
	}
	key, err := x509util.NewEd25519PrivateKey(pem)
	if err != nil {
		return nil, err
	}

	pub, err := x509.MarshalPKIXPublicKey(key.Public())
	if err != nil {
		return nil, fmt.Errorf("failed DER encoding SubjectPublicKeyInfo: %v", err)
	}
	hasher := sha256.New()
	hasher.Write(pub)
	logId := hasher.Sum(nil)

	return &LogParameters{
		LogId:      logId,
		TreeId:     treeId,
		Prefix:     prefix,
		MaxRange:   maxRange,
		MaxChain:   maxChain,
		AnchorPool: anchorPool,
		AnchorList: anchorList,
		KeyUsage:   []x509.ExtKeyUsage{}, // placeholder, must be tested if used
		Signer:     key,
		HashType:   crypto.SHA256,
	}, nil
}

func (i *Instance) registerHandlers(mux *http.ServeMux) {
	for _, endpoint := range []struct {
		path    string
		handler handler
	}{
		{
			strings.Join([]string{"", i.LogParameters.Prefix, string(EndpointAddEntry)}, "/"),
			handler{instance: i, handler: addEntry, endpoint: EndpointAddEntry, method: http.MethodPost},
		},
		{
			strings.Join([]string{"", i.LogParameters.Prefix, string(EndpointGetEntries)}, "/"),
			handler{instance: i, handler: getEntries, endpoint: EndpointGetEntries, method: http.MethodGet},
		},
		{
			strings.Join([]string{"", i.LogParameters.Prefix, string(EndpointGetAnchors)}, "/"),
			handler{instance: i, handler: getAnchors, endpoint: EndpointGetAnchors, method: http.MethodGet},
		},
		{
			strings.Join([]string{"", i.LogParameters.Prefix, string(EndpointGetProofByHash)}, "/"),
			handler{instance: i, handler: getProofByHash, endpoint: EndpointGetProofByHash, method: http.MethodGet},
		},
		{
			strings.Join([]string{"", i.LogParameters.Prefix, string(EndpointGetConsistencyProof)}, "/"),
			handler{instance: i, handler: getConsistencyProof, endpoint: EndpointGetConsistencyProof, method: http.MethodGet},
		},
		{
			strings.Join([]string{"", i.LogParameters.Prefix, string(EndpointGetSth)}, "/"),
			handler{instance: i, handler: getSth, endpoint: EndpointGetSth, method: http.MethodGet},
		},
	} {
		glog.Infof("adding handler for %v", endpoint.path)
		mux.Handle(endpoint.path, endpoint.handler)
	}
}

// loadTrustAnchors loads a list of PEM-encoded certificates from file
func loadTrustAnchors(path string) ([]*x509.Certificate, *x509.CertPool, error) {
	pem, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, nil, fmt.Errorf("failed reading trust anchors: %v", err)
	}
	anchorList, err := x509util.NewCertificateList(pem)
	if err != nil || len(anchorList) == 0 {
		return nil, nil, fmt.Errorf("failed parsing trust anchors: %v", err)
	}
	return anchorList, x509util.NewCertPool(anchorList), nil
}