From 6f4154465913bbe7fcb45f2740f099278f9066e6 Mon Sep 17 00:00:00 2001 From: stephen mcquay Date: Sun, 25 Feb 2018 10:10:08 -0800 Subject: [PATCH] Adds pm key export --- cmd/pm/main.go | 11 ++++++++- keyring/keyring.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/cmd/pm/main.go b/cmd/pm/main.go index 41ec93e..8d7ae59 100644 --- a/cmd/pm/main.go +++ b/cmd/pm/main.go @@ -19,6 +19,7 @@ const keyUsage = `pm keyring: interact with pm's OpenPGP keyring subcommands: create (c) -- create a fresh keypair + export (e) -- export a public key to stdout list (ls) -- list configured key info ` @@ -40,7 +41,7 @@ func main() { if len(os.Args[1:]) < 2 { fatalf("pm keyring: insufficient args\n\nusage: %v", keyUsage) } - sub := os.Args[2] + sub, args := os.Args[2], os.Args[3:] switch sub { case "ls", "list": if err := keyring.ListKeys(root, os.Stdout); err != nil { @@ -71,6 +72,14 @@ func main() { if err := keyring.NewKeyPair(root, name, email); err != nil { fatalf("creating keypair: %v\n", err) } + case "export", "e": + if len(args) != 1 { + fatalf("missing email argument\n") + } + email := args[0] + if err := keyring.Export(root, os.Stdout, email); err != nil { + fatalf("exporting public key for %q: %v\n", email, err) + } default: fatalf("unknown keyring subcommand: %q\n\nusage: %v", sub, keyUsage) } diff --git a/keyring/keyring.go b/keyring/keyring.go index a138c70..dfb221d 100644 --- a/keyring/keyring.go +++ b/keyring/keyring.go @@ -9,6 +9,7 @@ import ( "github.com/pkg/errors" "golang.org/x/crypto/openpgp" + "golang.org/x/crypto/openpgp/armor" "mcquay.me/fs" ) @@ -102,6 +103,37 @@ func ListKeys(root string, w io.Writer) error { return nil } +// Export prints pubkey information associated with email to w. +func Export(root string, w io.Writer, email string) error { + if err := ensureDir(root); err != nil { + return errors.Wrap(err, "can't find or create pgp dir") + } + srn, prn := getNames(root) + _, pubs, err := getELs(srn, prn) + if err != nil { + return errors.Wrap(err, "getting existing keyrings") + } + + e, err := findKey(pubs, email) + if err != nil { + return errors.Wrap(err, "find key") + } + + aw, err := armor.Encode(w, openpgp.PublicKeyType, nil) + if err != nil { + return errors.Wrap(err, "creating armor encoder") + } + + if err := e.Serialize(aw); err != nil { + return errors.Wrap(err, "serializing key") + } + if err := aw.Close(); err != nil { + return errors.Wrap(err, "closing armor encoder") + } + fmt.Fprintf(w, "\n") + return nil +} + func pGPDir(root string) string { return filepath.Join(root, "var", "lib", "pm", "pgp") } @@ -153,3 +185,30 @@ func getELs(secring, pubring string) (openpgp.EntityList, openpgp.EntityList, er } return sr, pr, nil } + +func findKey(el openpgp.EntityList, id string) (*openpgp.Entity, error) { + var e *openpgp.Entity + if strings.Contains(id, "@") { + es := openpgp.EntityList{} + for _, p := range el { + for _, v := range p.Identities { + if id == v.UserId.Email { + es = append(es, p) + } + } + } + if len(es) == 1 { + return es[0], nil + } + if len(es) > 1 { + return nil, errors.New("too many keys matched; try searching by key id?") + } + } else { + for _, p := range el { + if id == p.PrimaryKey.KeyIdShortString() { + return p, nil + } + } + } + return e, fmt.Errorf("key %q not found", id) +}