1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17
18
19 package signhandler
20
21 import (
22 "context"
23 "fmt"
24 "log"
25 "net/http"
26 "strings"
27 "sync"
28
29 "perkeep.org/internal/httputil"
30 "perkeep.org/internal/osutil"
31 "perkeep.org/pkg/blob"
32 "perkeep.org/pkg/blobserver"
33 "perkeep.org/pkg/blobserver/gethandler"
34 "perkeep.org/pkg/blobserver/memory"
35 "perkeep.org/pkg/jsonsign"
36 "perkeep.org/pkg/schema"
37 "perkeep.org/pkg/types/camtypes"
38
39 "go4.org/jsonconfig"
40 "golang.org/x/crypto/openpgp"
41 )
42
43 const maxJSONLength = 1024 * 1024
44
45
46
47
48 type Handler struct {
49
50 secretRing string
51
52 pubKey string
53 pubKeyBlobRef blob.Ref
54 pubKeyFetcher blob.Fetcher
55
56 pubKeyBlobRefServeSuffix string
57 pubKeyHandler http.Handler
58
59 pubKeyDest blobserver.Storage
60
61 pubKeyUploadMu sync.RWMutex
62 pubKeyUploaded bool
63
64 entity *openpgp.Entity
65 signer *schema.Signer
66 }
67
68 func (h *Handler) Signer() *schema.Signer { return h.signer }
69
70 func (h *Handler) secretRingPath() string {
71 if h.secretRing != "" {
72 return h.secretRing
73 }
74 return osutil.SecretRingFile()
75 }
76
77 func init() {
78 blobserver.RegisterHandlerConstructor("jsonsign", newJSONSignFromConfig)
79 }
80
81 func newJSONSignFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (http.Handler, error) {
82 var (
83
84 keyId = conf.RequiredString("keyId")
85
86 pubKeyDestPrefix = conf.OptionalString("publicKeyDest", "")
87 secretRing = conf.OptionalString("secretRing", "")
88 )
89 if err := conf.Validate(); err != nil {
90 return nil, err
91 }
92
93 h := &Handler{
94 secretRing: secretRing,
95 }
96
97 var err error
98 h.entity, err = jsonsign.EntityFromSecring(keyId, h.secretRingPath())
99 if err != nil {
100 return nil, err
101 }
102
103 h.pubKey, err = jsonsign.ArmoredPublicKey(h.entity)
104
105 ctx := context.Background()
106
107 ms := &memory.Storage{}
108 h.pubKeyBlobRef = blob.RefFromString(h.pubKey)
109 if _, err := ms.ReceiveBlob(ctx, h.pubKeyBlobRef, strings.NewReader(h.pubKey)); err != nil {
110 return nil, fmt.Errorf("could not store pub key blob: %v", err)
111 }
112 h.pubKeyFetcher = ms
113
114 if pubKeyDestPrefix != "" {
115 sto, err := ld.GetStorage(pubKeyDestPrefix)
116 if err != nil {
117 return nil, err
118 }
119 h.pubKeyDest = sto
120 }
121 h.pubKeyBlobRefServeSuffix = "camli/" + h.pubKeyBlobRef.String()
122 h.pubKeyHandler = &gethandler.Handler{
123 Fetcher: ms,
124 }
125
126 h.signer, err = schema.NewSigner(h.pubKeyBlobRef, strings.NewReader(h.pubKey), h.entity)
127 if err != nil {
128 return nil, err
129 }
130
131 return h, nil
132 }
133
134
135
136 func (h *Handler) UploadPublicKey(ctx context.Context) error {
137 h.pubKeyUploadMu.RLock()
138 if h.pubKeyUploaded {
139 h.pubKeyUploadMu.RUnlock()
140 return nil
141 }
142 h.pubKeyUploadMu.RUnlock()
143
144 sto := h.pubKeyDest
145
146 h.pubKeyUploadMu.Lock()
147 defer h.pubKeyUploadMu.Unlock()
148 if h.pubKeyUploaded {
149 return nil
150 }
151 _, err := blobserver.StatBlob(ctx, sto, h.pubKeyBlobRef)
152 if err == nil {
153 h.pubKeyUploaded = true
154 return nil
155 }
156 _, err = blobserver.Receive(ctx, sto, h.pubKeyBlobRef, strings.NewReader(h.pubKey))
157 h.pubKeyUploaded = (err == nil)
158 return err
159 }
160
161
162 func (h *Handler) Discovery(base string) *camtypes.SignDiscovery {
163 sd := &camtypes.SignDiscovery{
164 PublicKeyID: h.entity.PrimaryKey.KeyIdString(),
165 SignHandler: base + "camli/sig/sign",
166 VerifyHandler: base + "camli/sig/verify",
167 }
168 if h.pubKeyBlobRef.Valid() {
169 sd.PublicKeyBlobRef = h.pubKeyBlobRef
170 sd.PublicKey = base + h.pubKeyBlobRefServeSuffix
171 }
172 return sd
173 }
174
175 func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
176 base := httputil.PathBase(req)
177 subPath := httputil.PathSuffix(req)
178 switch req.Method {
179 case "GET", "HEAD":
180 switch subPath {
181 case "":
182 http.Redirect(rw, req, base+"camli/sig/discovery", http.StatusFound)
183 return
184 case h.pubKeyBlobRefServeSuffix:
185 h.pubKeyHandler.ServeHTTP(rw, req)
186 return
187 case "camli/sig/sign":
188 fallthrough
189 case "camli/sig/verify":
190 http.Error(rw, "POST required", http.StatusBadRequest)
191 return
192 case "camli/sig/discovery":
193 httputil.ReturnJSON(rw, h.Discovery(base))
194 return
195 }
196 case "POST":
197 switch subPath {
198 case "camli/sig/sign":
199 h.handleSign(rw, req)
200 return
201 case "camli/sig/verify":
202 h.handleVerify(rw, req)
203 return
204 }
205 }
206 http.Error(rw, "Unsupported path or method.", http.StatusBadRequest)
207 }
208
209 func (h *Handler) handleVerify(rw http.ResponseWriter, req *http.Request) {
210 req.ParseForm()
211 sjson := req.FormValue("sjson")
212 if sjson == "" {
213 http.Error(rw, "missing \"sjson\" parameter", http.StatusBadRequest)
214 return
215 }
216
217
218
219 fetcher := h.pubKeyFetcher
220
221 var res camtypes.VerifyResponse
222 vreq := jsonsign.NewVerificationRequest(sjson, fetcher)
223 _, err := vreq.Verify(req.Context())
224 if err != nil {
225 res.SignatureValid = false
226 res.ErrorMessage = err.Error()
227 } else {
228 res.SignatureValid = true
229 res.SignerKeyId = vreq.SignerKeyId
230 res.VerifiedData = vreq.PayloadMap
231 }
232
233 rw.WriteHeader(http.StatusOK)
234 httputil.ReturnJSON(rw, &res)
235 }
236
237 func (h *Handler) handleSign(rw http.ResponseWriter, req *http.Request) {
238 req.ParseForm()
239
240 badReq := func(s string) {
241 http.Error(rw, s, http.StatusBadRequest)
242 log.Printf("bad request: %s", s)
243 return
244 }
245
246 jsonStr := req.FormValue("json")
247 if jsonStr == "" {
248 badReq("missing \"json\" parameter")
249 return
250 }
251 if len(jsonStr) > maxJSONLength {
252 badReq("parameter \"json\" too large")
253 return
254 }
255
256 sreq := &jsonsign.SignRequest{
257 UnsignedJSON: jsonStr,
258 Fetcher: h.pubKeyFetcher,
259 ServerMode: true,
260 SecretKeyringPath: h.secretRing,
261 }
262 ctx := req.Context()
263 signedJSON, err := sreq.Sign(ctx)
264 if err != nil {
265
266 badReq(fmt.Sprintf("%v", err))
267 return
268 }
269 if err := h.UploadPublicKey(ctx); err != nil {
270 log.Printf("signing handler failed to upload public key: %v", err)
271 }
272 rw.Write([]byte(signedJSON))
273 }
274
275 func (h *Handler) Sign(ctx context.Context, bb *schema.Builder) (string, error) {
276 bb.SetSigner(h.pubKeyBlobRef)
277 unsigned, err := bb.JSON()
278 if err != nil {
279 return "", err
280 }
281 sreq := &jsonsign.SignRequest{
282 UnsignedJSON: unsigned,
283 Fetcher: h.pubKeyFetcher,
284 ServerMode: true,
285 SecretKeyringPath: h.secretRing,
286 }
287 claimTime, err := bb.Blob().ClaimDate()
288 if err != nil {
289 if !schema.IsMissingField(err) {
290 return "", err
291 }
292 } else {
293 sreq.SignatureTime = claimTime
294 }
295 if err := h.UploadPublicKey(ctx); err != nil {
296 log.Printf("signing handler failed to upload public key: %v", err)
297 }
298 return sreq.Sign(ctx)
299 }