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
160
161
|
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")
)
// TODO: type EndpointParam string?
// 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
}
|