1// Package cli implements a GPG CLI crypto backend.
2package cli
3
4import (
5	"context"
6	"os"
7
8	"github.com/gopasspw/gopass/internal/backend/crypto/gpg"
9	"github.com/gopasspw/gopass/pkg/debug"
10	lru "github.com/hashicorp/golang-lru"
11)
12
13var (
14	// defaultArgs contains the default GPG args for non-interactive use. Note: Do not use '--batch'
15	// as this will disable (necessary) passphrase questions!
16	defaultArgs = []string{"--quiet", "--yes", "--compress-algo=none", "--no-encrypt-to", "--no-auto-check-trustdb"}
17	// Ext is the file extension used by this backend
18	Ext = "gpg"
19	// IDFile is the name of the recipients file used by this backend
20	IDFile = ".gpg-id"
21)
22
23// GPG is a gpg wrapper
24type GPG struct {
25	binary    string
26	args      []string
27	pubKeys   gpg.KeyList
28	privKeys  gpg.KeyList
29	listCache *lru.TwoQueueCache
30	throwKids bool
31}
32
33// Config is the gpg wrapper config
34type Config struct {
35	Binary string
36	Args   []string
37	Umask  int
38}
39
40// New creates a new GPG wrapper
41func New(ctx context.Context, cfg Config) (*GPG, error) {
42	// ensure created files don't have group or world perms set
43	// this setting should be inherited by sub-processes
44	umask(cfg.Umask)
45
46	// make sure GPG_TTY is set (if possible)
47	if gt := os.Getenv("GPG_TTY"); gt == "" {
48		if t := tty(); t != "" {
49			_ = os.Setenv("GPG_TTY", t)
50		}
51	}
52
53	gcfg, err := gpgConfig()
54	if err != nil {
55		debug.Log("failed to read GPG config: %s", err)
56	}
57	_, throwKids := gcfg["throw-keyids"]
58
59	g := &GPG{
60		binary:    "gpg",
61		args:      append(defaultArgs, cfg.Args...),
62		throwKids: throwKids,
63	}
64
65	debug.Log("initializing LRU cache")
66	cache, err := lru.New2Q(1024)
67	if err != nil {
68		return nil, err
69	}
70	g.listCache = cache
71	debug.Log("LRU cache initialized")
72
73	debug.Log("detecting binary")
74	bin, err := Binary(ctx, cfg.Binary)
75	if err != nil {
76		return nil, err
77	}
78	g.binary = bin
79	debug.Log("binary detected")
80
81	return g, nil
82}
83
84// Initialized always returns nil
85func (g *GPG) Initialized(ctx context.Context) error {
86	return nil
87}
88
89// Name returns gpg
90func (g *GPG) Name() string {
91	return "gpg"
92}
93
94// Ext returns gpg
95func (g *GPG) Ext() string {
96	return Ext
97}
98
99// IDFile returns .gpg-id
100func (g *GPG) IDFile() string {
101	return IDFile
102}
103
104// Concurrency returns 1 to avoid concurrency issues
105// with many GPG setups.
106func (g *GPG) Concurrency() int {
107	return 1
108}
109