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 }