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