cmd/age: support "-R -" if stdin is not in use

This commit is contained in:
Filippo Valsorda
2021-01-02 18:39:17 +01:00
committed by Filippo Valsorda
parent dc8716d8fc
commit 4147b86ac8
3 changed files with 36 additions and 14 deletions

View File

@@ -69,6 +69,8 @@ age1lggyhqrw2nlhcxprm67z43rta597azn8gknawjehu9d9dl0jq3yqqvfafg
$ age -R recipients.txt example.jpg > example.jpg.age
```
If the argument to `-R` (or `-i`) is `-`, the file is read from standard input.
### Passphrases
Files can be encrypted with a passphrase by using `-p/--passphrase`. By default age will automatically generate a secure passphrase. Passphrase protected files are automatically detected at decrypt time.
@@ -92,6 +94,16 @@ $ age -d -i ~/.ssh/id_ed25519 example.jpg.age > example.jpg
Note that SSH key support employs more complex cryptography, and embeds a public key tag in the encrypted file, making it possible to track files that are encrypted to a specific public key.
#### Encrypting to a GitHub user
Combining SSH key support and `-R`, you can easily encrypt a file to the SSH keys listed on a GitHub profile.
```
$ curl https://github.com/benjojo.keys | age -R - example.jpg > example.jpg.age
```
Keep in mind that people might not protect SSH keys long-term, since they are revokable when used only for authentication, and that SSH keys held on YubiKeys can't be used to decrypt files.
## Installation
On macOS or Linux, you can use Homebrew:

View File

@@ -241,14 +241,7 @@ func encryptKeys(keys, files []string, in io.Reader, out io.Writer, armor bool)
recipients = append(recipients, r)
}
for _, name := range files {
f, err := os.Open(name)
if err != nil {
logFatalf("Error: failed to open recipient file: %v", err)
}
recs, err := parseRecipients(f, func(format string, a ...interface{}) {
a = append([]interface{}{name}, a...)
_log.Printf("Warning: recipients file %q: "+format, a...)
})
recs, err := parseRecipientsFile(name)
if err != nil {
logFatalf("Error: failed to parse recipient file %q: %v", name, err)
}

View File

@@ -12,6 +12,7 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"strings"
@@ -32,7 +33,23 @@ func parseRecipient(arg string) (age.Recipient, error) {
return nil, fmt.Errorf("unknown recipient type: %q", arg)
}
func parseRecipients(f io.Reader, warnf func(string, ...interface{})) ([]age.Recipient, error) {
func parseRecipientsFile(name string) ([]age.Recipient, error) {
var f *os.File
if name == "-" {
if stdinInUse {
return nil, fmt.Errorf("standard input is used for multiple purposes")
}
stdinInUse = true
f = os.Stdin
} else {
var err error
f, err = os.Open(name)
if err != nil {
return nil, fmt.Errorf("failed to open recipient file: %v", err)
}
defer f.Close()
}
const recipientFileSizeLimit = 16 << 20 // 16 MiB
const lineLengthLimit = 8 << 10 // 8 KiB, same as sshd(8)
var recs []age.Recipient
@@ -45,26 +62,26 @@ func parseRecipients(f io.Reader, warnf func(string, ...interface{})) ([]age.Rec
continue
}
if len(line) > lineLengthLimit {
return nil, fmt.Errorf("line %d is too long", n)
return nil, fmt.Errorf("%q: line %d is too long", name, n)
}
r, err := parseRecipient(line)
if err != nil {
if t, ok := sshKeyType(line); ok {
// Skip unsupported but valid SSH public keys with a warning.
warnf("ignoring unsupported SSH key of type %q at line %d", t, n)
log.Printf("Warning: recipients file %q: ignoring unsupported SSH key of type %q at line %d", name, t, n)
continue
}
// Hide the error since it might unintentionally leak the contents
// of confidential files.
return nil, fmt.Errorf("malformed recipient at line %d", n)
return nil, fmt.Errorf("%q: malformed recipient at line %d", name, n)
}
recs = append(recs, r)
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("failed to read recipients file: %v", err)
return nil, fmt.Errorf("%q: failed to read recipients file: %v", name, err)
}
if len(recs) == 0 {
return nil, fmt.Errorf("no recipients found")
return nil, fmt.Errorf("%q: no recipients found", name)
}
return recs, nil
}