@@ -94,6 +94,18 @@ func MasterKeysFromRecipients(commaSeparatedRecipients string) ([]*MasterKey, er
9494 return keys , nil
9595}
9696
97+ // errSet is a collection of captured errors.
98+ type errSet []error
99+
100+ // Error joins the errors into a "; " separated string.
101+ func (e errSet ) Error () string {
102+ str := make ([]string , len (e ))
103+ for i , err := range e {
104+ str [i ] = err .Error ()
105+ }
106+ return strings .Join (str , "; " )
107+ }
108+
97109// MasterKeyFromRecipient takes a Bech32-encoded age public key, parses it, and
98110// returns a new MasterKey.
99111func MasterKeyFromRecipient (recipient string ) (* MasterKey , error ) {
@@ -197,11 +209,13 @@ func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
197209// Decrypt decrypts the EncryptedKey with the parsed or loaded identities, and
198210// returns the result.
199211func (key * MasterKey ) Decrypt () ([]byte , error ) {
212+ var errs errSet
200213 if len (key .parsedIdentities ) == 0 {
201- ids , err := key .loadIdentities ()
202- if err != nil {
214+ var ids ParsedIdentities
215+ ids , errs = key .loadIdentities ()
216+ if len (ids ) == 0 {
203217 log .Info ("Decryption failed" )
204- return nil , fmt .Errorf ("failed to load age identities: %w" , err )
218+ return nil , fmt .Errorf ("failed to load age identities: %w" , errs )
205219 }
206220 ids .ApplyToMasterKey (key )
207221 }
@@ -211,7 +225,11 @@ func (key *MasterKey) Decrypt() ([]byte, error) {
211225 r , err := age .Decrypt (ar , key .parsedIdentities ... )
212226 if err != nil {
213227 log .Info ("Decryption failed" )
214- return nil , fmt .Errorf ("failed to create reader for decrypting sops data key with age: %w" , err )
228+ var loadErrors string
229+ if len (errs ) > 0 {
230+ loadErrors = fmt .Sprintf (". Errors while loading age identities: %s" , errs .Error ())
231+ }
232+ return nil , fmt .Errorf ("failed to create reader for decrypting sops data key with age: %w%s" , err , loadErrors )
215233 }
216234
217235 var b bytes.Buffer
@@ -289,14 +307,15 @@ func getUserConfigDir() (string, error) {
289307// environment configurations (e.g. SopsAgeKeyEnv, SopsAgeKeyFileEnv,
290308// SopsAgeSshPrivateKeyFileEnv, SopsAgeKeyUserConfigPath). It will load all
291309// found references, and expects at least one configuration to be present.
292- func (key * MasterKey ) loadIdentities () (ParsedIdentities , error ) {
310+ func (key * MasterKey ) loadIdentities () (ParsedIdentities , errSet ) {
293311 var identities ParsedIdentities
294312
313+ var errs errSet
314+
295315 sshIdentity , err := loadAgeSSHIdentity ()
296316 if err != nil {
297- return nil , fmt .Errorf ("failed to get SSH identity: %w" , err )
298- }
299- if sshIdentity != nil {
317+ errs = append (errs , fmt .Errorf ("failed to get SSH identity: %w" , err ))
318+ } else if sshIdentity != nil {
300319 identities = append (identities , sshIdentity )
301320 }
302321
@@ -309,39 +328,39 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, error) {
309328 if ageKeyFile , ok := os .LookupEnv (SopsAgeKeyFileEnv ); ok {
310329 f , err := os .Open (ageKeyFile )
311330 if err != nil {
312- return nil , fmt .Errorf ("failed to open %s file: %w" , SopsAgeKeyFileEnv , err )
331+ errs = append (errs , fmt .Errorf ("failed to open %s file: %w" , SopsAgeKeyFileEnv , err ))
332+ } else {
333+ defer f .Close ()
334+ readers [SopsAgeKeyFileEnv ] = f
313335 }
314- defer f .Close ()
315- readers [SopsAgeKeyFileEnv ] = f
316336 }
317337
318338 if ageKeyCmd , ok := os .LookupEnv (SopsAgeKeyCmdEnv ); ok {
319339 args , err := shlex .Split (ageKeyCmd )
320340 if err != nil {
321- return nil , fmt .Errorf ("failed to parse command %s from %s: %w" , ageKeyCmd , SopsAgeKeyCmdEnv , err )
341+ errs = append (errs , fmt .Errorf ("failed to parse command %s from %s: %w" , ageKeyCmd , SopsAgeKeyCmdEnv , err ))
342+ } else {
343+ out , err := exec .Command (args [0 ], args [1 :]... ).Output ()
344+ if err != nil {
345+ errs = append (errs , fmt .Errorf ("failed to execute command %s from %s: %w" , ageKeyCmd , SopsAgeKeyCmdEnv , err ))
346+ } else {
347+ readers [SopsAgeKeyCmdEnv ] = bytes .NewReader (out )
348+ }
322349 }
323- out , err := exec .Command (args [0 ], args [1 :]... ).Output ()
324- if err != nil {
325- return nil , fmt .Errorf ("failed to execute command %s from %s: %w" , ageKeyCmd , SopsAgeKeyCmdEnv , err )
326- }
327- readers [SopsAgeKeyCmdEnv ] = bytes .NewReader (out )
328350 }
329351
330352 userConfigDir , err := getUserConfigDir ()
331353 if err != nil && len (readers ) == 0 && len (identities ) == 0 {
332- return nil , fmt .Errorf ("user config directory could not be determined: %w" , err )
333- }
334- if userConfigDir != "" {
354+ errs = append (errs , fmt .Errorf ("user config directory could not be determined: %w" , err ))
355+ } else if userConfigDir != "" {
335356 ageKeyFilePath := filepath .Join (userConfigDir , filepath .FromSlash (SopsAgeKeyUserConfigPath ))
336357 f , err := os .Open (ageKeyFilePath )
337358 if err != nil && ! errors .Is (err , os .ErrNotExist ) {
338- return nil , fmt .Errorf ("failed to open file: %w" , err )
339- }
340- if errors .Is (err , os .ErrNotExist ) && len (readers ) == 0 && len (identities ) == 0 {
359+ errs = append (errs , fmt .Errorf ("failed to open file: %w" , err ))
360+ } else if errors .Is (err , os .ErrNotExist ) && len (readers ) == 0 && len (identities ) == 0 {
341361 // If we have no other readers, presence of the file is required.
342- return nil , fmt .Errorf ("failed to open file: %w" , err )
343- }
344- if err == nil {
362+ errs = append (errs , fmt .Errorf ("failed to open file: %w" , err ))
363+ } else if err == nil {
345364 defer f .Close ()
346365 readers [ageKeyFilePath ] = f
347366 }
@@ -350,11 +369,12 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, error) {
350369 for n , r := range readers {
351370 ids , err := unwrapIdentities (n , r )
352371 if err != nil {
353- return nil , err
372+ errs = append (errs , err )
373+ } else {
374+ identities = append (identities , ids ... )
354375 }
355- identities = append (identities , ids ... )
356376 }
357- return identities , nil
377+ return identities , errs
358378}
359379
360380// parseRecipient attempts to parse a string containing an encoded age public
0 commit comments