github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/jsonsign/signhandler/sig.go (about) 1 /* 2 Copyright 2011 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package signhandler implements the HTTP interface to signing and verifying 18 // Camlistore JSON blobs. 19 package signhandler 20 21 import ( 22 "crypto" 23 "fmt" 24 "log" 25 "net/http" 26 "strings" 27 "sync" 28 29 "camlistore.org/pkg/blob" 30 "camlistore.org/pkg/blobserver" 31 "camlistore.org/pkg/blobserver/gethandler" 32 "camlistore.org/pkg/httputil" 33 "camlistore.org/pkg/jsonconfig" 34 "camlistore.org/pkg/jsonsign" 35 "camlistore.org/pkg/osutil" 36 "camlistore.org/pkg/schema" 37 38 "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp" 39 ) 40 41 const kMaxJSONLength = 1024 * 1024 42 43 type Handler struct { 44 // Optional path to non-standard secret gpg keyring file 45 secretRing string 46 47 pubKey string // armored 48 pubKeyBlobRef blob.Ref 49 pubKeyFetcher blob.Fetcher 50 51 pubKeyBlobRefServeSuffix string // "camli/sha1-xxxx" 52 pubKeyHandler http.Handler 53 54 pubKeyDest blobserver.Storage // Where our public key is published 55 56 pubKeyUploadMu sync.RWMutex 57 pubKeyUploaded bool 58 59 entity *openpgp.Entity 60 signer *schema.Signer 61 } 62 63 func (h *Handler) Signer() *schema.Signer { return h.signer } 64 65 func (h *Handler) secretRingPath() string { 66 if h.secretRing != "" { 67 return h.secretRing 68 } 69 return osutil.SecretRingFile() 70 } 71 72 func init() { 73 blobserver.RegisterHandlerConstructor("jsonsign", newJSONSignFromConfig) 74 } 75 76 func newJSONSignFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (http.Handler, error) { 77 pubKeyDestPrefix := conf.OptionalString("publicKeyDest", "") 78 79 // either a short form ("26F5ABDA") or one the longer forms. 80 keyId := conf.RequiredString("keyId") 81 82 h := &Handler{ 83 secretRing: conf.OptionalString("secretRing", ""), 84 } 85 var err error 86 if err = conf.Validate(); err != nil { 87 return nil, err 88 } 89 90 h.entity, err = jsonsign.EntityFromSecring(keyId, h.secretRingPath()) 91 if err != nil { 92 return nil, err 93 } 94 95 h.pubKey, err = jsonsign.ArmoredPublicKey(h.entity) 96 97 ms := new(blob.MemoryStore) 98 h.pubKeyBlobRef, err = ms.AddBlob(crypto.SHA1, h.pubKey) 99 if err != nil { 100 return nil, err 101 } 102 h.pubKeyFetcher = ms 103 104 if pubKeyDestPrefix != "" { 105 sto, err := ld.GetStorage(pubKeyDestPrefix) 106 if err != nil { 107 return nil, err 108 } 109 h.pubKeyDest = sto 110 } 111 h.pubKeyBlobRefServeSuffix = "camli/" + h.pubKeyBlobRef.String() 112 h.pubKeyHandler = &gethandler.Handler{ 113 Fetcher: ms, 114 } 115 116 h.signer, err = schema.NewSigner(h.pubKeyBlobRef, strings.NewReader(h.pubKey), h.entity) 117 if err != nil { 118 return nil, err 119 } 120 121 return h, nil 122 } 123 124 func (h *Handler) uploadPublicKey() error { 125 h.pubKeyUploadMu.RLock() 126 if h.pubKeyUploaded { 127 h.pubKeyUploadMu.RUnlock() 128 return nil 129 } 130 h.pubKeyUploadMu.RUnlock() 131 132 sto := h.pubKeyDest 133 134 h.pubKeyUploadMu.Lock() 135 defer h.pubKeyUploadMu.Unlock() 136 if h.pubKeyUploaded { 137 return nil 138 } 139 _, err := blobserver.StatBlob(sto, h.pubKeyBlobRef) 140 if err == nil { 141 h.pubKeyUploaded = true 142 return nil 143 } 144 _, err = blobserver.Receive(sto, h.pubKeyBlobRef, strings.NewReader(h.pubKey)) 145 log.Printf("uploadPublicKey(%T, %v) = %v", sto, h.pubKeyBlobRef, err) 146 if err == nil { 147 h.pubKeyUploaded = true 148 } 149 return err 150 } 151 152 func (h *Handler) DiscoveryMap(base string) map[string]interface{} { 153 m := map[string]interface{}{ 154 "publicKeyId": h.entity.PrimaryKey.KeyIdString(), 155 "signHandler": base + "camli/sig/sign", 156 "verifyHandler": base + "camli/sig/verify", 157 } 158 if h.pubKeyBlobRef.Valid() { 159 m["publicKeyBlobRef"] = h.pubKeyBlobRef.String() 160 m["publicKey"] = base + h.pubKeyBlobRefServeSuffix 161 } 162 return m 163 } 164 165 func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { 166 base := httputil.PathBase(req) 167 subPath := httputil.PathSuffix(req) 168 switch req.Method { 169 case "GET", "HEAD": 170 switch subPath { 171 case "": 172 http.Redirect(rw, req, base+"camli/sig/discovery", http.StatusFound) 173 return 174 case h.pubKeyBlobRefServeSuffix: 175 h.pubKeyHandler.ServeHTTP(rw, req) 176 return 177 case "camli/sig/sign": 178 fallthrough 179 case "camli/sig/verify": 180 http.Error(rw, "POST required", 400) 181 return 182 case "camli/sig/discovery": 183 httputil.ReturnJSON(rw, h.DiscoveryMap(base)) 184 return 185 } 186 case "POST": 187 switch subPath { 188 case "camli/sig/sign": 189 h.handleSign(rw, req) 190 return 191 case "camli/sig/verify": 192 h.handleVerify(rw, req) 193 return 194 } 195 } 196 http.Error(rw, "Unsupported path or method.", http.StatusBadRequest) 197 } 198 199 func (h *Handler) handleVerify(rw http.ResponseWriter, req *http.Request) { 200 req.ParseForm() 201 sjson := req.FormValue("sjson") 202 if sjson == "" { 203 http.Error(rw, "missing \"sjson\" parameter", http.StatusBadRequest) 204 return 205 } 206 207 m := make(map[string]interface{}) 208 209 // TODO: use a different fetcher here that checks memory, disk, 210 // the internet, etc. 211 fetcher := h.pubKeyFetcher 212 213 vreq := jsonsign.NewVerificationRequest(sjson, fetcher) 214 if vreq.Verify() { 215 m["signatureValid"] = 1 216 m["signerKeyId"] = vreq.SignerKeyId 217 m["verifiedData"] = vreq.PayloadMap 218 } else { 219 errStr := vreq.Err.Error() 220 m["signatureValid"] = 0 221 m["errorMessage"] = errStr 222 } 223 224 rw.WriteHeader(http.StatusOK) // no HTTP response code fun, error info in JSON 225 httputil.ReturnJSON(rw, m) 226 } 227 228 func (h *Handler) handleSign(rw http.ResponseWriter, req *http.Request) { 229 req.ParseForm() 230 231 badReq := func(s string) { 232 http.Error(rw, s, http.StatusBadRequest) 233 log.Printf("bad request: %s", s) 234 return 235 } 236 237 jsonStr := req.FormValue("json") 238 if jsonStr == "" { 239 badReq("missing \"json\" parameter") 240 return 241 } 242 if len(jsonStr) > kMaxJSONLength { 243 badReq("parameter \"json\" too large") 244 return 245 } 246 247 sreq := &jsonsign.SignRequest{ 248 UnsignedJSON: jsonStr, 249 Fetcher: h.pubKeyFetcher, 250 ServerMode: true, 251 SecretKeyringPath: h.secretRing, 252 } 253 signedJSON, err := sreq.Sign() 254 if err != nil { 255 // TODO: some aren't really a "bad request" 256 badReq(fmt.Sprintf("%v", err)) 257 return 258 } 259 h.uploadPublicKey() 260 rw.Write([]byte(signedJSON)) 261 } 262 263 func (h *Handler) Sign(bb *schema.Builder) (string, error) { 264 bb.SetSigner(h.pubKeyBlobRef) 265 unsigned, err := bb.JSON() 266 if err != nil { 267 return "", err 268 } 269 sreq := &jsonsign.SignRequest{ 270 UnsignedJSON: unsigned, 271 Fetcher: h.pubKeyFetcher, 272 ServerMode: true, 273 SecretKeyringPath: h.secretRing, 274 } 275 claimTime, err := bb.Blob().ClaimDate() 276 if err != nil { 277 if !schema.IsMissingField(err) { 278 return "", err 279 } 280 } else { 281 sreq.SignatureTime = claimTime 282 } 283 h.uploadPublicKey() 284 return sreq.Sign() 285 }