Home Download Docs Code Community
     1	/*
     2	Copyright 2013 The Perkeep 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		"context"
    21		"errors"
    22		"fmt"
    23		"io"
    24		"os"
    25		"strings"
    26		"time"
    27	
    28		"perkeep.org/pkg/blob"
    29		"perkeep.org/pkg/jsonsign"
    30	
    31		"golang.org/x/crypto/openpgp"
    32	)
    33	
    34	// A Signer signs the JSON schema blobs that require signing, such as claims
    35	// and permanodes.
    36	type Signer struct {
    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.KeyIDLong(), s.pubref)
    47	}
    48	
    49	// KeyIDLong returns the long (16 digit) capital hex GPG key ID.
    50	func (s *Signer) KeyIDLong() string {
    51		return s.privEntity.PrivateKey.KeyIdString()
    52	}
    53	
    54	// Entity returns the signer's entity, which includes its public and private keys.
    55	func (s *Signer) Entity() *openpgp.Entity {
    56		return s.privEntity
    57	}
    58	
    59	// NewSigner returns an Signer given an armored public key's blobref,
    60	// its armored content, and its associated private key entity.
    61	// The privateKeySource must be either an *openpgp.Entity or a string filename to a secret key.
    62	func NewSigner(pubKeyRef blob.Ref, armoredPubKey io.Reader, privateKeySource interface{}) (*Signer, error) {
    63		hash := pubKeyRef.Hash()
    64		fingerprint, armoredPubKeyString, err := jsonsign.ParseArmoredPublicKey(io.TeeReader(armoredPubKey, hash))
    65		if err != nil {
    66			return nil, err
    67		}
    68		if !pubKeyRef.HashMatches(hash) {
    69			return nil, fmt.Errorf("pubkey ref of %v doesn't match provided armored public key", pubKeyRef)
    70		}
    71	
    72		var privateKey *openpgp.Entity
    73		switch v := privateKeySource.(type) {
    74		case *openpgp.Entity:
    75			privateKey = v
    76		case string:
    77			privateKey, err = jsonsign.EntityFromSecring(fingerprint, v)
    78			if err != nil {
    79				return nil, err
    80			}
    81		default:
    82			return nil, fmt.Errorf("invalid privateKeySource type %T", v)
    83		}
    84		if privateKey == nil {
    85			return nil, errors.New("nil privateKey")
    86		}
    87	
    88		return &Signer{
    89			pubref:     pubKeyRef,
    90			privEntity: privateKey,
    91			baseSigReq: jsonsign.SignRequest{
    92				ServerMode: true, // shouldn't matter, since we're supplying the rest of the fields
    93				Fetcher: memoryBlobFetcher{
    94					pubKeyRef: func() (uint32, io.ReadCloser) {
    95						return uint32(len(armoredPubKeyString)), io.NopCloser(strings.NewReader(armoredPubKeyString))
    96					},
    97				},
    98				EntityFetcher: entityFetcherFunc(func(wantFingerprint string) (*openpgp.Entity, error) {
    99					if fingerprint != wantFingerprint {
   100						return nil, fmt.Errorf("jsonsign code unexpectedly requested fingerprint %q; only have %q",
   101							wantFingerprint, fingerprint)
   102					}
   103					return privateKey, nil
   104				}),
   105			},
   106		}, nil
   107	}
   108	
   109	// SignJSON signs the provided json at the optional time t.
   110	// If t is the zero Time, the current time is used.
   111	func (s *Signer) SignJSON(ctx context.Context, json string, t time.Time) (string, error) {
   112		sr := s.baseSigReq
   113		sr.UnsignedJSON = json
   114		sr.SignatureTime = t
   115		return sr.Sign(ctx)
   116	}
   117	
   118	type memoryBlobFetcher map[blob.Ref]func() (size uint32, rc io.ReadCloser)
   119	
   120	func (m memoryBlobFetcher) Fetch(ctx context.Context, br blob.Ref) (file io.ReadCloser, size uint32, err error) {
   121		fn, ok := m[br]
   122		if !ok {
   123			return nil, 0, os.ErrNotExist
   124		}
   125		size, file = fn()
   126		return
   127	}
   128	
   129	type entityFetcherFunc func(keyID string) (*openpgp.Entity, error)
   130	
   131	func (f entityFetcherFunc) FetchEntity(keyID string) (*openpgp.Entity, error) {
   132		return f(keyID)
   133	}
Website layout inspired by memcached.
Content by the authors.