1package leaf 2 3import ( 4 "context" 5 "fmt" 6 "path/filepath" 7 8 "github.com/gopasspw/gopass/internal/backend" 9 "github.com/gopasspw/gopass/internal/backend/crypto/age" 10 "github.com/gopasspw/gopass/internal/out" 11 "github.com/gopasspw/gopass/pkg/ctxutil" 12 "github.com/gopasspw/gopass/pkg/debug" 13) 14 15func (s *Store) initCryptoBackend(ctx context.Context) error { 16 cb, err := backend.DetectCrypto(ctx, s.storage) 17 if err != nil { 18 return err 19 } 20 s.crypto = cb 21 return nil 22} 23 24// Crypto returns the crypto backend 25func (s *Store) Crypto() backend.Crypto { 26 return s.crypto 27} 28 29// ImportMissingPublicKeys will try to import any missing public keys from the 30// .public-keys folder in the password store 31func (s *Store) ImportMissingPublicKeys(ctx context.Context) error { 32 // do not import any keys for age, where public key == key id 33 if _, ok := s.crypto.(*age.Age); ok { 34 debug.Log("not importing public keys for age") 35 return nil 36 } 37 rs, err := s.GetRecipients(ctx, "") 38 if err != nil { 39 return fmt.Errorf("failed to get recipients: %w", err) 40 } 41 for _, r := range rs { 42 debug.Log("Checking recipients %s ...", r) 43 // check if this recipient is missing 44 // we could list all keys outside the loop and just do the lookup here 45 // but this way we ensure to use the exact same lookup logic as 46 // gpg does on encryption 47 kl, err := s.crypto.FindRecipients(ctx, r) 48 if err != nil { 49 out.Errorf(ctx, "[%s] Failed to get public key for %s: %s", s.alias, r, err) 50 } 51 if len(kl) > 0 { 52 debug.Log("[%s] Keyring contains %d public keys for %s", s.alias, len(kl), r) 53 continue 54 } 55 56 // get info about this public key 57 names, err := s.decodePublicKey(ctx, r) 58 if err != nil { 59 out.Errorf(ctx, "[%s] Failed to decode public key %s: %s", s.alias, r, err) 60 continue 61 } 62 63 // we need to ask the user before importing 64 // any key material into his keyring! 65 if imf := ctxutil.GetImportFunc(ctx); imf != nil { 66 if !imf(ctx, r, names) { 67 continue 68 } 69 } 70 71 debug.Log("[%s] Public Key %s not found in keyring, importing", s.alias, r) 72 73 // try to load this recipient 74 if err := s.importPublicKey(ctx, r); err != nil { 75 out.Errorf(ctx, "[%s] Failed to import public key for %s: %s", s.alias, r, err) 76 continue 77 } 78 out.Printf(ctx, "[%s] Imported public key for %s into Keyring", s.alias, r) 79 } 80 return nil 81} 82 83func (s *Store) decodePublicKey(ctx context.Context, r string) ([]string, error) { 84 for _, kd := range []string{keyDir, oldKeyDir} { 85 filename := filepath.Join(kd, r) 86 if !s.storage.Exists(ctx, filename) { 87 debug.Log("Public Key %s not found at %s", r, filename) 88 continue 89 } 90 buf, err := s.storage.Get(ctx, filename) 91 if err != nil { 92 return nil, fmt.Errorf("unable to read Public Key %q %q: %w", r, filename, err) 93 } 94 return s.crypto.ReadNamesFromKey(ctx, buf) 95 } 96 return nil, fmt.Errorf("public key %q not found", r) 97} 98 99// export an ASCII armored public key 100func (s *Store) exportPublicKey(ctx context.Context, exp keyExporter, r string) (string, error) { 101 filename := filepath.Join(keyDir, r) 102 103 // do not overwrite existing keys 104 if s.storage.Exists(ctx, filename) { 105 return "", nil 106 } 107 108 pk, err := exp.ExportPublicKey(ctx, r) 109 if err != nil { 110 return "", fmt.Errorf("failed to export public key: %w", err) 111 } 112 113 // ECC keys are at least 700 byte, RSA should be a lot bigger 114 if len(pk) < 32 { 115 return "", fmt.Errorf("exported key too small") 116 } 117 118 if err := s.storage.Set(ctx, filename, pk); err != nil { 119 return "", fmt.Errorf("failed to write exported public key to store: %w", err) 120 } 121 122 return filename, nil 123} 124 125type keyImporter interface { 126 ImportPublicKey(ctx context.Context, key []byte) error 127} 128 129// import an public key into the default keyring 130func (s *Store) importPublicKey(ctx context.Context, r string) error { 131 im, ok := s.crypto.(keyImporter) 132 if !ok { 133 debug.Log("importing public keys not supported by %T", s.crypto) 134 return nil 135 } 136 137 for _, kd := range []string{keyDir, oldKeyDir} { 138 filename := filepath.Join(kd, r) 139 if !s.storage.Exists(ctx, filename) { 140 debug.Log("Public Key %s not found at %s", r, filename) 141 continue 142 } 143 pk, err := s.storage.Get(ctx, filename) 144 if err != nil { 145 return err 146 } 147 return im.ImportPublicKey(ctx, pk) 148 } 149 return fmt.Errorf("public key not found in store") 150} 151 152type locker interface { 153 Lock() 154} 155 156// Lock clears the credential caches of all supported backends 157func (s *Store) Lock() error { 158 f, ok := s.crypto.(locker) 159 if !ok { 160 debug.Log("locking not supported by %T in %q", s.crypto, s.alias) 161 } 162 if f == nil { 163 debug.Log("backend %q invalid", s.alias) 164 return nil 165 } 166 f.Lock() 167 debug.Log("locked backend %T for %q", s.crypto, s.alias) 168 return nil 169} 170