github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/jsonsign/verify.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 jsonsign
    18  
    19  import (
    20  	"bytes"
    21  	"crypto"
    22  	"encoding/json"
    23  	"errors"
    24  	"fmt"
    25  	"log"
    26  	"strings"
    27  
    28  	"camlistore.org/pkg/blob"
    29  	"camlistore.org/pkg/camerrors"
    30  	"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/armor"
    31  	"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/packet"
    32  )
    33  
    34  const sigSeparator = `,"camliSig":"`
    35  
    36  // reArmor takes a camliSig (single line armor) and turns it back into an PGP-style
    37  // multi-line armored string
    38  func reArmor(line string) string {
    39  	lastEq := strings.LastIndex(line, "=")
    40  	if lastEq == -1 {
    41  		return ""
    42  	}
    43  	buf := new(bytes.Buffer)
    44  	fmt.Fprintf(buf, "-----BEGIN PGP SIGNATURE-----\n\n")
    45  	payload := line[0:lastEq]
    46  	crc := line[lastEq:]
    47  	for len(payload) > 0 {
    48  		chunkLen := len(payload)
    49  		if chunkLen > 60 {
    50  			chunkLen = 60
    51  		}
    52  		fmt.Fprintf(buf, "%s\n", payload[0:chunkLen])
    53  		payload = payload[chunkLen:]
    54  	}
    55  	fmt.Fprintf(buf, "%s\n-----BEGIN PGP SIGNATURE-----\n", crc)
    56  	return buf.String()
    57  }
    58  
    59  // See doc/json-signing/* for background and details
    60  // on these variable names.
    61  type VerifyRequest struct {
    62  	fetcher blob.StreamingFetcher // fetcher used to find public key blob
    63  
    64  	ba  []byte // "bytes all"
    65  	bp  []byte // "bytes payload" (the part that is signed)
    66  	bpj []byte // "bytes payload, JSON" (BP + "}")
    67  	bs  []byte // "bytes signature", "{" + separator + camliSig, valid JSON
    68  
    69  	CamliSigner     blob.Ref
    70  	CamliSig        string
    71  	PublicKeyPacket *packet.PublicKey
    72  
    73  	// set if Verify() returns true:
    74  	PayloadMap  map[string]interface{} // The JSON values from BPJ
    75  	SignerKeyId string                 // e.g. "2931A67C26F5ABDA"
    76  
    77  	Err error // last error encountered
    78  }
    79  
    80  func (vr *VerifyRequest) fail(msg string) bool {
    81  	vr.Err = errors.New("jsonsign: " + msg)
    82  	return false
    83  }
    84  
    85  func (vr *VerifyRequest) ParseSigMap() bool {
    86  	sigMap := make(map[string]interface{})
    87  	if err := json.Unmarshal(vr.bs, &sigMap); err != nil {
    88  		return vr.fail("invalid JSON in signature")
    89  	}
    90  
    91  	if len(sigMap) != 1 {
    92  		return vr.fail("signature JSON didn't have exactly 1 key")
    93  	}
    94  
    95  	sigVal, hasCamliSig := sigMap["camliSig"]
    96  	if !hasCamliSig {
    97  		return vr.fail("no 'camliSig' key in signature")
    98  	}
    99  
   100  	var ok bool
   101  	vr.CamliSig, ok = sigVal.(string)
   102  	if !ok {
   103  		return vr.fail("camliSig not a string")
   104  	}
   105  
   106  	return true
   107  }
   108  
   109  func (vr *VerifyRequest) ParsePayloadMap() bool {
   110  	vr.PayloadMap = make(map[string]interface{})
   111  	pm := vr.PayloadMap
   112  
   113  	if err := json.Unmarshal(vr.bpj, &pm); err != nil {
   114  		return vr.fail("parse error; payload JSON is invalid")
   115  	}
   116  
   117  	if _, hasVersion := pm["camliVersion"]; !hasVersion {
   118  		return vr.fail("missing 'camliVersion' in the JSON payload")
   119  	}
   120  
   121  	signer, hasSigner := pm["camliSigner"]
   122  	if !hasSigner {
   123  		return vr.fail("missing 'camliSigner' in the JSON payload")
   124  	}
   125  
   126  	if _, ok := signer.(string); !ok {
   127  		return vr.fail("invalid 'camliSigner' in the JSON payload")
   128  	}
   129  
   130  	var ok bool
   131  	vr.CamliSigner, ok = blob.Parse(signer.(string))
   132  	if !ok {
   133  		return vr.fail("malformed 'camliSigner' blobref in the JSON payload")
   134  	}
   135  	return true
   136  }
   137  
   138  func (vr *VerifyRequest) FindAndParsePublicKeyBlob() bool {
   139  	reader, _, err := vr.fetcher.FetchStreaming(vr.CamliSigner)
   140  	if err != nil {
   141  		log.Printf("error fetching public key blob %v: %v", vr.CamliSigner, err)
   142  		// TODO(mpl): we're losing some info here, so maybe
   143  		// create an error type that contains the reason,
   144  		// instead of logging the reason.
   145  		vr.Err = camerrors.ErrMissingKeyBlob
   146  		return false
   147  	}
   148  	defer reader.Close()
   149  	pk, err := openArmoredPublicKeyFile(reader)
   150  	if err != nil {
   151  		return vr.fail(fmt.Sprintf("error opening public key file: %v", err))
   152  	}
   153  	vr.PublicKeyPacket = pk
   154  	return true
   155  }
   156  
   157  func (vr *VerifyRequest) VerifySignature() bool {
   158  	armorData := reArmor(vr.CamliSig)
   159  	block, _ := armor.Decode(bytes.NewBufferString(armorData))
   160  	if block == nil {
   161  		return vr.fail("can't parse camliSig armor")
   162  	}
   163  	var p packet.Packet
   164  	var err error
   165  	p, err = packet.Read(block.Body)
   166  	if err != nil {
   167  		return vr.fail("error reading PGP packet from camliSig: " + err.Error())
   168  	}
   169  	sig, ok := p.(*packet.Signature)
   170  	if !ok {
   171  		return vr.fail("PGP packet isn't a signature packet")
   172  	}
   173  	if sig.Hash != crypto.SHA1 && sig.Hash != crypto.SHA256 {
   174  		return vr.fail("I can only verify SHA1 or SHA256 signatures")
   175  	}
   176  	if sig.SigType != packet.SigTypeBinary {
   177  		return vr.fail("I can only verify binary signatures")
   178  	}
   179  	hash := sig.Hash.New()
   180  	hash.Write(vr.bp) // payload bytes
   181  	err = vr.PublicKeyPacket.VerifySignature(hash, sig)
   182  	if err != nil {
   183  		return vr.fail(fmt.Sprintf("bad signature: %s", err))
   184  	}
   185  	vr.SignerKeyId = vr.PublicKeyPacket.KeyIdString()
   186  	return true
   187  }
   188  
   189  func NewVerificationRequest(sjson string, fetcher blob.StreamingFetcher) (vr *VerifyRequest) {
   190  	if fetcher == nil {
   191  		panic("NewVerificationRequest fetcher is nil")
   192  	}
   193  	vr = new(VerifyRequest)
   194  	vr.ba = []byte(sjson)
   195  	vr.fetcher = fetcher
   196  
   197  	sigIndex := bytes.LastIndex(vr.ba, []byte(sigSeparator))
   198  	if sigIndex == -1 {
   199  		vr.Err = errors.New("jsonsign: no 13-byte camliSig separator found in sjson")
   200  		return
   201  	}
   202  
   203  	// "Bytes Payload"
   204  	vr.bp = vr.ba[:sigIndex]
   205  
   206  	// "Bytes Payload JSON".  Note we re-use the memory (the ",")
   207  	// from BA in BPJ, so we can't re-use that "," byte for
   208  	// the opening "{" in "BS".
   209  	vr.bpj = vr.ba[:sigIndex+1]
   210  	vr.bpj[sigIndex] = '}'
   211  	vr.bs = []byte("{" + sjson[sigIndex+1:])
   212  	return
   213  }
   214  
   215  // TODO: turn this into (bool, os.Error) return, probably, or *Details, os.Error.
   216  func (vr *VerifyRequest) Verify() bool {
   217  	if vr.Err != nil {
   218  		return false
   219  	}
   220  
   221  	if vr.ParseSigMap() &&
   222  		vr.ParsePayloadMap() &&
   223  		vr.FindAndParsePublicKeyBlob() &&
   224  		vr.VerifySignature() {
   225  		return true
   226  	}
   227  
   228  	// Don't allow dumbs callers to accidentally check this
   229  	// if it's not valid.
   230  	vr.PayloadMap = nil
   231  	if vr.Err == nil {
   232  		// The other functions should have filled this in
   233  		// already, but just in case:
   234  		vr.Err = errors.New("jsonsign: verification failed")
   235  	}
   236  	return false
   237  }