Home Download Docs Code Community
     1	/*
     2	Copyright 2011 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 jsonsign
    18	
    19	import (
    20		"bytes"
    21		"errors"
    22		"fmt"
    23		"io"
    24		"os"
    25		"path/filepath"
    26		"strings"
    27	
    28		"perkeep.org/internal/osutil"
    29	
    30		"go4.org/wkfs"
    31		"golang.org/x/crypto/openpgp"
    32		"golang.org/x/crypto/openpgp/armor"
    33		"golang.org/x/crypto/openpgp/packet"
    34	)
    35	
    36	const publicKeyMaxSize = 256 * 1024
    37	
    38	// ParseArmoredPublicKey tries to parse an armored public key from r,
    39	// taking care to bound the amount it reads.
    40	// The returned fingerprint is 40 capital hex digits.
    41	// The returned armoredKey is a copy of the contents read.
    42	func ParseArmoredPublicKey(r io.Reader) (fingerprint, armoredKey string, err error) {
    43		var buf bytes.Buffer
    44		pk, err := openArmoredPublicKeyFile(io.NopCloser(io.TeeReader(r, &buf)))
    45		if err != nil {
    46			return
    47		}
    48		return fingerprintString(pk), buf.String(), nil
    49	}
    50	
    51	// fingerprintString returns the fingerprint (40 characters) capital hex GPG
    52	// key ID of the provided public key.
    53	func fingerprintString(pubKey *packet.PublicKey) string {
    54		return fmt.Sprintf("%X", pubKey.Fingerprint)
    55	}
    56	
    57	func openArmoredPublicKeyFile(reader io.ReadCloser) (*packet.PublicKey, error) {
    58		defer reader.Close()
    59	
    60		var lr = io.LimitReader(reader, publicKeyMaxSize)
    61		block, _ := armor.Decode(lr)
    62		if block == nil {
    63			return nil, errors.New("Couldn't find PGP block in public key file")
    64		}
    65		if block.Type != "PGP PUBLIC KEY BLOCK" {
    66			return nil, errors.New("invalid public key blob")
    67		}
    68		p, err := packet.Read(block.Body)
    69		if err != nil {
    70			return nil, fmt.Errorf("Invalid public key blob: %v", err)
    71		}
    72	
    73		pk, ok := p.(*packet.PublicKey)
    74		if !ok {
    75			return nil, fmt.Errorf("Invalid public key blob; not a public key packet")
    76		}
    77		return pk, nil
    78	}
    79	
    80	// EntityFromSecring returns the openpgp Entity from keyFile that matches keyID.
    81	// If empty, keyFile defaults to osutil.SecretRingFile().
    82	func EntityFromSecring(keyID, keyFile string) (*openpgp.Entity, error) {
    83		if keyID == "" {
    84			return nil, errors.New("empty keyID passed to EntityFromSecring")
    85		}
    86		keyID = strings.ToUpper(keyID)
    87		if keyFile == "" {
    88			keyFile = osutil.SecretRingFile()
    89		}
    90		secring, err := wkfs.Open(keyFile)
    91		if err != nil {
    92			return nil, fmt.Errorf("jsonsign: failed to open keyring: %v", err)
    93		}
    94		defer secring.Close()
    95	
    96		el, err := readKeyRing(secring)
    97		if err != nil {
    98			return nil, fmt.Errorf("readKeyRing of %q: %v", keyFile, err)
    99		}
   100		var entity *openpgp.Entity
   101		for _, e := range el {
   102			pk := e.PrivateKey
   103			if pk == nil || (keyID != fmt.Sprintf("%X", pk.Fingerprint) &&
   104				pk.KeyIdString() != keyID &&
   105				pk.KeyIdShortString() != keyID) {
   106				continue
   107			}
   108			entity = e
   109		}
   110		if entity == nil {
   111			found := []string{}
   112			for _, e := range el {
   113				pk := e.PrivateKey
   114				if pk == nil {
   115					continue
   116				}
   117				found = append(found, pk.KeyIdString())
   118			}
   119			return nil, fmt.Errorf("didn't find a key in %q for keyID %q; other keyIDs in file = %v", keyFile, keyID, found)
   120		}
   121		return entity, nil
   122	}
   123	
   124	var newlineBytes = []byte("\n")
   125	
   126	func ArmoredPublicKey(entity *openpgp.Entity) (string, error) {
   127		var buf bytes.Buffer
   128		wc, err := armor.Encode(&buf, openpgp.PublicKeyType, nil)
   129		if err != nil {
   130			return "", err
   131		}
   132		err = entity.PrivateKey.PublicKey.Serialize(wc)
   133		if err != nil {
   134			return "", err
   135		}
   136		wc.Close()
   137		if !bytes.HasSuffix(buf.Bytes(), newlineBytes) {
   138			buf.WriteString("\n")
   139		}
   140		return buf.String(), nil
   141	}
   142	
   143	// NewEntity returns a new OpenPGP entity.
   144	func NewEntity() (*openpgp.Entity, error) {
   145		name := "" // intentionally empty
   146		comment := "camlistore"
   147		email := "" // intentionally empty
   148		return openpgp.NewEntity(name, comment, email, nil)
   149	}
   150	
   151	func WriteKeyRing(w io.Writer, el openpgp.EntityList) error {
   152		armoredWriter, err := armor.Encode(w, openpgp.PrivateKeyType, nil)
   153		if err != nil {
   154			return err
   155		}
   156		for _, ent := range el {
   157			if err := ent.SerializePrivate(armoredWriter, nil); err != nil {
   158				return err
   159			}
   160		}
   161		return armoredWriter.Close()
   162	}
   163	
   164	// readKeyRing reads a keyring, armored or not.
   165	func readKeyRing(r io.Reader) (openpgp.EntityList, error) {
   166		var buffer bytes.Buffer
   167		if el, err := openpgp.ReadArmoredKeyRing(io.TeeReader(r, &buffer)); err == nil {
   168			return el, err
   169		}
   170		return openpgp.ReadKeyRing(&buffer)
   171	}
   172	
   173	// KeyIdFromRing returns the public keyID contained in the secret
   174	// ring file secRing. It expects only one keyID in this secret ring
   175	// and returns an error otherwise.
   176	func KeyIdFromRing(secRing string) (keyID string, err error) {
   177		f, err := wkfs.Open(secRing)
   178		if err != nil {
   179			return "", fmt.Errorf("Could not open secret ring file %v: %v", secRing, err)
   180		}
   181		defer f.Close()
   182		el, err := readKeyRing(f)
   183		if err != nil {
   184			return "", fmt.Errorf("Could not read secret ring file %s: %v", secRing, err)
   185		}
   186		if len(el) != 1 {
   187			return "", fmt.Errorf("Secret ring file %v contained %d identities; expected 1", secRing, len(el))
   188		}
   189		ent := el[0]
   190		return ent.PrimaryKey.KeyIdString(), nil
   191	}
   192	
   193	// GenerateNewSecRing creates a new secret ring file secRing, with
   194	// a new GPG identity. It returns the public keyID of that identity.
   195	// It returns an error if the file already exists.
   196	func GenerateNewSecRing(secRing string) (keyID string, err error) {
   197		ent, err := NewEntity()
   198		if err != nil {
   199			return "", fmt.Errorf("generating new identity: %v", err)
   200		}
   201		if err := os.MkdirAll(filepath.Dir(secRing), 0700); err != nil {
   202			return "", err
   203		}
   204		f, err := wkfs.OpenFile(secRing, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
   205		if err != nil {
   206			return "", err
   207		}
   208		err = WriteKeyRing(f, openpgp.EntityList([]*openpgp.Entity{ent}))
   209		if err != nil {
   210			f.Close()
   211			return "", fmt.Errorf("Could not write new key ring to %s: %v", secRing, err)
   212		}
   213		if err := f.Close(); err != nil {
   214			return "", fmt.Errorf("Could not close %v: %v", secRing, err)
   215		}
   216		return ent.PrimaryKey.KeyIdString(), nil
   217	}
Website layout inspired by memcached.
Content by the authors.