github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/schema/sign.go (about)

     1  /*
     2  Copyright 2013 The Camlistore Authors
     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 schema
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"os"
    25  	"strings"
    26  	"time"
    27  
    28  	"camlistore.org/pkg/blob"
    29  	"camlistore.org/pkg/jsonsign"
    30  	"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp"
    31  )
    32  
    33  // A Signer signs the JSON schema blobs that require signing, such as claims
    34  // and permanodes.
    35  type Signer struct {
    36  	keyId      string // short one; 8 capital hex digits
    37  	pubref     blob.Ref
    38  	privEntity *openpgp.Entity
    39  
    40  	// baseSigReq is the prototype signing request used with the jsonsig
    41  	// package.
    42  	baseSigReq jsonsign.SignRequest
    43  }
    44  
    45  func (s *Signer) String() string {
    46  	return fmt.Sprintf("[*schema.Signer for key=%s pubkey=%s]", s.keyId, s.pubref)
    47  }
    48  
    49  // NewSigner returns an Signer given an armored public key's blobref,
    50  // its armored content, and its associated private key entity.
    51  // The privateKeySource must be either an *openpgp.Entity or a string filename to a secret key.
    52  func NewSigner(pubKeyRef blob.Ref, armoredPubKey io.Reader, privateKeySource interface{}) (*Signer, error) {
    53  	hash := pubKeyRef.Hash()
    54  	keyId, armoredPubKeyString, err := jsonsign.ParseArmoredPublicKey(io.TeeReader(armoredPubKey, hash))
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	if !pubKeyRef.HashMatches(hash) {
    59  		return nil, fmt.Errorf("pubkey ref of %v doesn't match provided armored public key", pubKeyRef)
    60  	}
    61  
    62  	var privateKey *openpgp.Entity
    63  	switch v := privateKeySource.(type) {
    64  	case *openpgp.Entity:
    65  		privateKey = v
    66  	case string:
    67  		privateKey, err = jsonsign.EntityFromSecring(keyId, v)
    68  		if err != nil {
    69  			return nil, err
    70  		}
    71  	default:
    72  		return nil, fmt.Errorf("invalid privateKeySource type %T", v)
    73  	}
    74  	if privateKey == nil {
    75  		return nil, errors.New("nil privateKey")
    76  	}
    77  
    78  	return &Signer{
    79  		keyId:      keyId,
    80  		pubref:     pubKeyRef,
    81  		privEntity: privateKey,
    82  		baseSigReq: jsonsign.SignRequest{
    83  			ServerMode: true, // shouldn't matter, since we're supplying the rest of the fields
    84  			Fetcher: memoryBlobFetcher{
    85  				pubKeyRef: func() (int64, io.ReadCloser) {
    86  					return int64(len(armoredPubKeyString)), ioutil.NopCloser(strings.NewReader(armoredPubKeyString))
    87  				},
    88  			},
    89  			EntityFetcher: entityFetcherFunc(func(wantKeyId string) (*openpgp.Entity, error) {
    90  				if privateKey.PrivateKey.KeyIdString() != wantKeyId &&
    91  					privateKey.PrivateKey.KeyIdShortString() != wantKeyId {
    92  					return nil, fmt.Errorf("jsonsign code unexpectedly requested keyId %q; only have %q",
    93  						wantKeyId, keyId)
    94  				}
    95  				return privateKey, nil
    96  			}),
    97  		},
    98  	}, nil
    99  }
   100  
   101  // SignJSON signs the provided json at the optional time t.
   102  // If t is the zero Time, the current time is used.
   103  func (s *Signer) SignJSON(json string, t time.Time) (string, error) {
   104  	sr := s.baseSigReq
   105  	sr.UnsignedJSON = json
   106  	sr.SignatureTime = t
   107  	return sr.Sign()
   108  }
   109  
   110  type memoryBlobFetcher map[blob.Ref]func() (size int64, rc io.ReadCloser)
   111  
   112  func (m memoryBlobFetcher) FetchStreaming(br blob.Ref) (file io.ReadCloser, size int64, err error) {
   113  	fn, ok := m[br]
   114  	if !ok {
   115  		return nil, 0, os.ErrNotExist
   116  	}
   117  	size, file = fn()
   118  	return
   119  }
   120  
   121  type entityFetcherFunc func(keyId string) (*openpgp.Entity, error)
   122  
   123  func (f entityFetcherFunc) FetchEntity(keyId string) (*openpgp.Entity, error) {
   124  	return f(keyId)
   125  }