Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions age/encrypted_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (i *LazyScryptIdentity) Unwrap(stanzas []*age.Stanza) (fileKey []byte, err
return fileKey, err
}

func unwrapIdentities(location string, reader io.Reader) (ParsedIdentities, error) {
func unwrapIdentities(location string, reader io.Reader, allowMultipleKeysPerLine bool) (ParsedIdentities, error) {
b := bufio.NewReader(reader)
p, _ := b.Peek(14) // length of "age-encryption" and "-----BEGIN AGE"
peeked := string(p)
Expand Down Expand Up @@ -180,7 +180,7 @@ func unwrapIdentities(location string, reader io.Reader) (ParsedIdentities, erro
return ids, nil
// An unencrypted age identity file.
default:
ids, err := parseIdentities(b)
ids, err := parseIdentities(b, allowMultipleKeysPerLine)
if err != nil {
return nil, fmt.Errorf("failed to parse '%s' age identities: %w", location, err)
}
Expand Down
57 changes: 44 additions & 13 deletions age/keysource.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (i *ParsedIdentities) Import(identity ...string) error {
// one identity per line
r := strings.NewReader(strings.Join(identity, "\n"))

identities, err := parseIdentities(r)
identities, err := parseIdentities(r, false)
if err != nil {
return fmt.Errorf("failed to parse and add to age identities: %w", err)
}
Expand Down Expand Up @@ -399,17 +399,25 @@ func getUserConfigDir() (string, error) {
return os.UserConfigDir()
}

type identityReader struct {
reader io.Reader
allowMultipleKeysPerLine bool
}

// loadIdentities attempts to load the age identities based on runtime
// environment configurations (e.g. SopsAgeKeyEnv, SopsAgeKeyFileEnv,
// SopsAgeSshPrivateKeyFileEnv, SopsAgeKeyUserConfigPath). It will load all
// found references, and expects at least one configuration to be present.
func (key *MasterKey) loadIdentities() (ParsedIdentities, []string, errSet) {
identities, unusedLocations, errs := key.loadAgeSSHIdentities()

var readers = make(map[string]io.Reader, 0)
var readers = make(map[string]identityReader, 0)

if ageKey, ok := os.LookupEnv(SopsAgeKeyEnv); ok {
readers[SopsAgeKeyEnv] = strings.NewReader(ageKey)
readers[SopsAgeKeyEnv] = identityReader{
reader: strings.NewReader(ageKey),
allowMultipleKeysPerLine: true,
}
} else {
unusedLocations = append(unusedLocations, SopsAgeKeyEnv)
}
Expand All @@ -420,7 +428,10 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, []string, errSet) {
errs = append(errs, fmt.Errorf("failed to open %s file: %w", SopsAgeKeyFileEnv, err))
} else {
defer f.Close()
readers[SopsAgeKeyFileEnv] = f
readers[SopsAgeKeyFileEnv] = identityReader{
reader: f,
allowMultipleKeysPerLine: false,
}
}
} else {
unusedLocations = append(unusedLocations, SopsAgeKeyFileEnv)
Expand All @@ -431,7 +442,10 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, []string, errSet) {
if err != nil {
errs = append(errs, err)
} else {
readers[SopsAgeKeyCmdEnv] = bytes.NewReader(out)
readers[SopsAgeKeyCmdEnv] = identityReader{
reader: bytes.NewReader(out),
allowMultipleKeysPerLine: false,
}
}
} else {
unusedLocations = append(unusedLocations, SopsAgeKeyCmdEnv)
Expand All @@ -449,12 +463,15 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, []string, errSet) {
unusedLocations = append(unusedLocations, ageKeyFilePath)
} else if err == nil {
defer f.Close()
readers[ageKeyFilePath] = f
readers[ageKeyFilePath] = identityReader{
reader: f,
allowMultipleKeysPerLine: false,
}
}
}

for location, r := range readers {
ids, err := unwrapIdentities(location, r)
ids, err := unwrapIdentities(location, r.reader, r.allowMultipleKeysPerLine)
if err != nil {
errs = append(errs, err)
} else {
Expand Down Expand Up @@ -505,7 +522,9 @@ func parseRecipient(recipient string) (age.Recipient, error) {
// parseIdentities attempts to parse one or more age identities from the provided reader.
// One identity per line.
// Empty lines and lines starting with "#" are ignored.
func parseIdentities(r io.Reader) (ParsedIdentities, error) {
// If allowMultipleKeysPerLine is true, every non-empty lines is split by words,
// and every word is parsed as an identity.
func parseIdentities(r io.Reader, allowMultipleKeysPerLine bool) (ParsedIdentities, error) {
var identities ParsedIdentities

scanner := bufio.NewScanner(r)
Expand All @@ -517,12 +536,24 @@ func parseIdentities(r io.Reader) (ParsedIdentities, error) {
continue
}

parsed, err := parseIdentity(line)
if err != nil {
return nil, err
if allowMultipleKeysPerLine {
lineScanner := bufio.NewScanner(strings.NewReader(line))
lineScanner.Split(bufio.ScanWords)
for lineScanner.Scan() {
word := lineScanner.Text()
parsed, err := parseIdentity(word)
if err != nil {
return nil, err
}
identities = append(identities, parsed)
}
} else {
parsed, err := parseIdentity(line)
if err != nil {
return nil, err
}
identities = append(identities, parsed)
}

identities = append(identities, parsed)
}

return identities, nil
Expand Down
Loading